From a0dce77a2fed96098733e438f2610f75c494c09b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 25 Feb 2020 15:14:58 +0100 Subject: [PATCH 001/278] RpcEvent: streamTime and recordingTime are optional --- CMakeLists.txt | 2 +- src/WSEvents.cpp | 13 ++++++++++--- src/protocol/OBSRemoteProtocol.cpp | 4 ++-- src/rpc/RpcEvent.cpp | 2 +- src/rpc/RpcEvent.h | 11 ++++++----- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 520d4254..072b5c5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if (WIN32 OR APPLE) include(external/FindLibObs.cmake) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 619aea7a..ab9cd1d2 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -240,10 +240,17 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private void WSEvents::broadcastUpdate(const char* updateType, obs_data_t* additionalFields = nullptr) { - uint64_t streamTime = getStreamingTime(); - uint64_t recordingTime = getRecordingTime(); - RpcEvent event(QString(updateType), streamTime, recordingTime, additionalFields); + std::optional streamTime; + if (obs_frontend_streaming_active()) { + streamTime = std::make_optional(getStreamingTime()); + } + std::optional recordingTime; + if (obs_frontend_recording_active()) { + recordingTime = std::make_optional(getRecordingTime()); + } + + RpcEvent event(QString(updateType), streamTime, recordingTime, additionalFields); _srv->broadcast(event); } diff --git a/src/protocol/OBSRemoteProtocol.cpp b/src/protocol/OBSRemoteProtocol.cpp index e5b1da1a..4daf2ba3 100644 --- a/src/protocol/OBSRemoteProtocol.cpp +++ b/src/protocol/OBSRemoteProtocol.cpp @@ -68,12 +68,12 @@ std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event) obs_data_set_string(eventData, "update-type", updateType.toUtf8().constData()); if (obs_frontend_streaming_active()) { - QString streamingTimecode = Utils::nsToTimestamp(event.streamTime()); + QString streamingTimecode = Utils::nsToTimestamp(event.streamTime().value()); obs_data_set_string(eventData, "stream-timecode", streamingTimecode.toUtf8().constData()); } if (obs_frontend_recording_active()) { - QString recordingTimecode = Utils::nsToTimestamp(event.recordingTime()); + QString recordingTimecode = Utils::nsToTimestamp(event.recordingTime().value()); obs_data_set_string(eventData, "rec-timecode", recordingTimecode.toUtf8().constData()); } diff --git a/src/rpc/RpcEvent.cpp b/src/rpc/RpcEvent.cpp index a8d3b06b..a971b6a2 100644 --- a/src/rpc/RpcEvent.cpp +++ b/src/rpc/RpcEvent.cpp @@ -20,7 +20,7 @@ with this program. If not, see RpcEvent::RpcEvent( const QString& updateType, - uint64_t streamTime, uint64_t recordingTime, + std::optional streamTime, std::optional recordingTime, obs_data_t* additionalFields ) : _updateType(updateType), diff --git a/src/rpc/RpcEvent.h b/src/rpc/RpcEvent.h index 6dd0df99..af69a10e 100644 --- a/src/rpc/RpcEvent.h +++ b/src/rpc/RpcEvent.h @@ -18,6 +18,7 @@ with this program. If not, see #pragma once +#include #include #include @@ -28,7 +29,7 @@ class RpcEvent public: explicit RpcEvent( const QString& updateType, - uint64_t streamTime, uint64_t recordingTime, + std::optional streamTime, std::optional recordingTime, obs_data_t* additionalFields = nullptr ); @@ -37,12 +38,12 @@ public: return _updateType; } - const uint64_t streamTime() const + const std::optional streamTime() const { return _streamTime; } - const uint64_t recordingTime() const + const std::optional recordingTime() const { return _recordingTime; } @@ -54,7 +55,7 @@ public: private: QString _updateType; - uint64_t _streamTime; - uint64_t _recordingTime; + std::optional _streamTime; + std::optional _recordingTime; OBSDataAutoRelease _additionalFields; }; From 4338cf57e6ef1ac98149dd2402d54eeec9fe34f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 25 Feb 2020 15:24:26 +0100 Subject: [PATCH 002/278] OBSRemoteProtocol: rely only on event data when encoding an event --- src/protocol/OBSRemoteProtocol.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/protocol/OBSRemoteProtocol.cpp b/src/protocol/OBSRemoteProtocol.cpp index 4daf2ba3..a7555b6e 100644 --- a/src/protocol/OBSRemoteProtocol.cpp +++ b/src/protocol/OBSRemoteProtocol.cpp @@ -67,13 +67,15 @@ std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event) 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().value()); + std::optional streamTime = event.streamTime(); + if (streamTime.has_value()) { + QString streamingTimecode = Utils::nsToTimestamp(streamTime.value()); obs_data_set_string(eventData, "stream-timecode", streamingTimecode.toUtf8().constData()); } - if (obs_frontend_recording_active()) { - QString recordingTimecode = Utils::nsToTimestamp(event.recordingTime().value()); + std::optional recordingTime = event.recordingTime(); + if (recordingTime.has_value()) { + QString recordingTimecode = Utils::nsToTimestamp(recordingTime.value()); obs_data_set_string(eventData, "rec-timecode", recordingTimecode.toUtf8().constData()); } From f2f5661625688e3514ff76b2efa8fb63bd0d6100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 25 Feb 2020 15:32:47 +0100 Subject: [PATCH 003/278] cmake: fix redundant c++ standard flag --- CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 072b5c5b..4cb3ea4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,16 +8,13 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) + if (WIN32 OR APPLE) include(external/FindLibObs.cmake) endif() add_definitions(-DASIO_STANDALONE) -if (UNIX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -endif() - if (WIN32 OR APPLE) include(external/FindLibObs.cmake) endif() From c741a7389322cbe54a6051dc22515284a98c7e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 18 Mar 2020 01:18:20 +0100 Subject: [PATCH 004/278] request(SetTBarPosition): wip --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_StudioMode.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index f2ae6c47..a5914744 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -130,6 +130,7 @@ const QHash WSRequestHandler::messageMap { { "EnableStudioMode", &WSRequestHandler::EnableStudioMode }, { "DisableStudioMode", &WSRequestHandler::DisableStudioMode }, { "ToggleStudioMode", &WSRequestHandler::ToggleStudioMode }, + { "SetTBarPosition", &WSRequestHandler::SetTBarPosition }, { "SetTextGDIPlusProperties", &WSRequestHandler::SetTextGDIPlusProperties }, { "GetTextGDIPlusProperties", &WSRequestHandler::GetTextGDIPlusProperties }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index d17384ee..a644bca4 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -148,6 +148,7 @@ class WSRequestHandler { RpcResponse EnableStudioMode(const RpcRequest&); RpcResponse DisableStudioMode(const RpcRequest&); RpcResponse ToggleStudioMode(const RpcRequest&); + RpcResponse SetTBarPosition(const RpcRequest&); RpcResponse SetTextGDIPlusProperties(const RpcRequest&); RpcResponse GetTextGDIPlusProperties(const RpcRequest&); diff --git a/src/WSRequestHandler_StudioMode.cpp b/src/WSRequestHandler_StudioMode.cpp index fb958f3b..219435ed 100644 --- a/src/WSRequestHandler_StudioMode.cpp +++ b/src/WSRequestHandler_StudioMode.cpp @@ -177,3 +177,30 @@ RpcResponse WSRequestHandler::ToggleStudioMode(const RpcRequest& request) { return request.success(); } + +/** + * Set the position of the T-Bar (in Studio Mode) to the specified value. Will return an error if OBS is not in studio mode + * or if the current transition doesn't support T-Bar control. + * + * @param {double} `position` T-Bar position. This value will be clamped between 0.0 and 1.0. + * + * @api requests + * @name TransitionToProgram + * @category studio mode + * @since 4.8.0 + */ +RpcResponse WSRequestHandler::SetTBarPosition(const RpcRequest& request) { + if (!obs_frontend_preview_program_mode_active()) { + return request.failed("studio mode not enabled"); + } + + OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); + if (obs_transition_fixed(currentTransition)) { + return request.failed("current transition doesn't support t-bar control"); + } + + double position = obs_data_get_double(request.parameters(), "position"); + obs_transition_set_manual_time(currentTransition, position); + + return request.success(); +} From 297d920fdc679452883d24651ef5f1dfd18b9f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 18 Mar 2020 01:19:02 +0100 Subject: [PATCH 005/278] fix --- src/WSRequestHandler_StudioMode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_StudioMode.cpp b/src/WSRequestHandler_StudioMode.cpp index 219435ed..cd103c83 100644 --- a/src/WSRequestHandler_StudioMode.cpp +++ b/src/WSRequestHandler_StudioMode.cpp @@ -185,7 +185,7 @@ RpcResponse WSRequestHandler::ToggleStudioMode(const RpcRequest& request) { * @param {double} `position` T-Bar position. This value will be clamped between 0.0 and 1.0. * * @api requests - * @name TransitionToProgram + * @name SetTBarPosition * @category studio mode * @since 4.8.0 */ From 846d52ebe58d5f9f819680afa9d8a3e7516ab380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 16:09:16 +0200 Subject: [PATCH 006/278] requests(gdiplus source): allow functions to operate on v2 sources --- src/WSRequestHandler_Sources.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index e043ee0e..036f987e 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -8,6 +8,11 @@ #include "WSRequestHandler.h" +bool isTextGDIPlusSource(const QString& sourceKind) +{ + return (sourceKind == "text_gdiplus" || sourceKind == "text_gdiplus_v2"); +} + /** * List all sources available in the running OBS instance * @@ -536,8 +541,8 @@ RpcResponse WSRequestHandler::GetTextGDIPlusProperties(const RpcRequest& request return request.failed("specified source doesn't exist"); } - QString sourceId = obs_source_get_id(source); - if (sourceId != "text_gdiplus") { + QString sourceKind = obs_source_get_id(source); + if (!isTextGDIPlusSource(sourceKind)) { return request.failed("not a text gdi plus source"); } @@ -601,8 +606,8 @@ RpcResponse WSRequestHandler::SetTextGDIPlusProperties(const RpcRequest& request return request.failed("specified source doesn't exist"); } - QString sourceId = obs_source_get_id(source); - if (sourceId != "text_gdiplus") { + QString sourceKind = obs_source_get_id(source); + if (!isTextGDIPlusSource(sourceKind)) { return request.failed("not a text gdi plus source"); } From 898e761988cd051da11d00db0d724c9cb562bafa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 16:13:28 +0200 Subject: [PATCH 007/278] requests(freetype2 source): allow functions to operate on v2 sources --- src/WSRequestHandler_Sources.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 036f987e..579f76b4 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -13,6 +13,11 @@ bool isTextGDIPlusSource(const QString& sourceKind) return (sourceKind == "text_gdiplus" || sourceKind == "text_gdiplus_v2"); } +bool isTextFreeType2Source(const QString& sourceKind) +{ + return (sourceKind == "text_ft2" || sourceKind == "text_ft2_v2"); +} + /** * List all sources available in the running OBS instance * @@ -772,8 +777,8 @@ RpcResponse WSRequestHandler::GetTextFreetype2Properties(const RpcRequest& reque return request.failed("specified source doesn't exist"); } - QString sourceId = obs_source_get_id(source); - if (sourceId != "text_ft2_source") { + QString sourceKind = obs_source_get_id(source); + if (!isTextFreeType2Source(sourceKind)) { return request.failed("not a freetype 2 source"); } @@ -820,8 +825,8 @@ RpcResponse WSRequestHandler::SetTextFreetype2Properties(const RpcRequest& reque return request.failed("specified source doesn't exist"); } - QString sourceId = obs_source_get_id(source); - if (sourceId != "text_ft2_source") { + QString sourceKind = obs_source_get_id(source); + if (!isTextFreeType2Source(sourceKind)) { return request.failed("not text freetype 2 source"); } From c1660f8ca21e5c76bf967e8f6c9ba6a1c25e16d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 19:39:19 +0200 Subject: [PATCH 008/278] chore: bump unreleased requests/events to 4.8.0 --- src/WSEvents.cpp | 2 +- src/WSRequestHandler_General.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 99d823a6..e2c0684f 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -1493,7 +1493,7 @@ void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) { * @api events * @name SceneItemLockChanged * @category sources - * @since unreleased + * @since 4.8.0 */ void WSEvents::OnSceneItemLockChanged(void* param, calldata_t* data) { auto instance = reinterpret_cast(param); diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index 56eeec8b..416ac013 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -328,7 +328,7 @@ RpcResponse WSRequestHandler::GetVideoInfo(const RpcRequest& request) { * @api requests * @name OpenProjector * @category general - * @since unreleased + * @since 4.8.0 */ RpcResponse WSRequestHandler::OpenProjector(const RpcRequest& request) { const char* type = obs_data_get_string(request.parameters(), "type"); From be7fa79327bd291558b137409abb6558e573c073 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Sun, 29 Mar 2020 17:39:56 +0000 Subject: [PATCH 009/278] docs(ci): Update protocol.md - c1660f8 [skip ci] --- docs/generated/comments.json | 8 ++++---- docs/generated/protocol.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 2c54dc9a..d9f46288 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -2718,7 +2718,7 @@ "api": "events", "name": "SceneItemLockChanged", "category": "sources", - "since": "unreleased", + "since": "4.8.0", "returns": [ { "type": "String", @@ -2756,7 +2756,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.8.0" } ], "heading": { @@ -3513,7 +3513,7 @@ "api": "requests", "name": "OpenProjector", "category": "general", - "since": "unreleased", + "since": "4.8.0", "params": [ { "type": "String (Optional)", @@ -3551,7 +3551,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.8.0" } ], "heading": { diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 77f780d5..48497157 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -1081,7 +1081,7 @@ An item's visibility has been toggled. ### SceneItemLockChanged -- Unreleased +- Added in v4.8.0 An item's locked status has been toggled. @@ -1404,7 +1404,7 @@ _No specified parameters._ ### OpenProjector -- Unreleased +- Added in v4.8.0 Open a projector window or create a projector on a monitor. Requires OBS v24.0.4 or newer. From 3709ea1a9534e0e695e4517563cb2d67c422a70b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 19:11:03 +0200 Subject: [PATCH 010/278] docs(clarity): move scene items events into a proper "Scene Items" category Namely: - SourceOrderChanged - SceneItemAdded - SceneItemRemoved - SceneItemVisibilityChanged - SceneItemLockChanged - SceneItemTransformChanged - SceneItemSelected - SceneItemDeselected --- src/WSEvents.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index e2c0684f..37ada8ee 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -1345,7 +1345,7 @@ void WSEvents::OnSourceFilterOrderChanged(void* param, calldata_t* data) { * * @api events * @name SourceOrderChanged - * @category sources + * @category scene items * @since 4.0.0 */ void WSEvents::OnSceneReordered(void* param, calldata_t* data) { @@ -1387,7 +1387,7 @@ void WSEvents::OnSceneReordered(void* param, calldata_t* data) { * * @api events * @name SceneItemAdded - * @category sources + * @category scene items * @since 4.0.0 */ void WSEvents::OnSceneItemAdd(void* param, calldata_t* data) { @@ -1420,7 +1420,7 @@ void WSEvents::OnSceneItemAdd(void* param, calldata_t* data) { * * @api events * @name SceneItemRemoved - * @category sources + * @category scene items * @since 4.0.0 */ void WSEvents::OnSceneItemDelete(void* param, calldata_t* data) { @@ -1454,7 +1454,7 @@ void WSEvents::OnSceneItemDelete(void* param, calldata_t* data) { * * @api events * @name SceneItemVisibilityChanged - * @category sources + * @category scene items * @since 4.0.0 */ void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) { @@ -1492,7 +1492,7 @@ void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) { * * @api events * @name SceneItemLockChanged - * @category sources + * @category scene items * @since 4.8.0 */ void WSEvents::OnSceneItemLockChanged(void* param, calldata_t* data) { @@ -1530,7 +1530,7 @@ void WSEvents::OnSceneItemLockChanged(void* param, calldata_t* data) { * * @api events * @name SceneItemTransformChanged - * @category sources + * @category scene items * @since 4.6.0 */ void WSEvents::OnSceneItemTransform(void* param, calldata_t* data) { @@ -1566,7 +1566,7 @@ void WSEvents::OnSceneItemTransform(void* param, calldata_t* data) { * * @api events * @name SceneItemSelected - * @category sources + * @category scene items * @since 4.6.0 */ void WSEvents::OnSceneItemSelected(void* param, calldata_t* data) { @@ -1601,7 +1601,7 @@ void WSEvents::OnSceneItemSelected(void* param, calldata_t* data) { * * @api events * @name SceneItemDeselected - * @category sources + * @category scene items * @since 4.6.0 */ void WSEvents::OnSceneItemDeselected(void* param, calldata_t* data) { From c486bc64c460ecdf211527d9847b457850f0816c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 19:35:57 +0200 Subject: [PATCH 011/278] requests(scene items): refactor docs --- src/WSRequestHandler_SceneItems.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 5e736802..87fac51d 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -6,10 +6,10 @@ * Gets the scene specific properties of the specified source item. * Coordinates are relative to the item's parent (the scene or group it belongs to). * -* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene. -* @param {String} `item` The name of the source. +* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. +* @param {String} `item` Scene Item name. * -* @return {String} `name` The name of the source. +* @return {String} `name` Scene Item name. * @return {int} `position.x` The x position of the source from the left. * @return {int} `position.y` The y position of the source from the top. * @return {int} `position.alignment` The point on the source that the item is manipulated from. @@ -71,8 +71,8 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request) * Sets the scene specific properties of a source. Unspecified properties will remain unchanged. * Coordinates are relative to the item's parent (the scene or group it belongs to). * -* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene. -* @param {String} `item` The name of the source. +* @param {String (optional)} `scene-name` Name of the scene the source item belongs to. Defaults to the current scene. +* @param {String} `item` Scene Item name. * @param {int (optional)} `position.x` The new x position of the source. * @param {int (optional)} `position.y` The new y position of the source. * @param {int (optional)} `position.alignment` The new alignment of the source. @@ -263,8 +263,8 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) /** * Reset a scene item. * -* @param {String (optional)} `scene-name` Name of the scene the source belongs to. Defaults to the current scene. -* @param {String} `item` Name of the source item. +* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. +* @param {String} `item` Scene item name. * * @api requests * @name ResetSceneItem @@ -496,10 +496,10 @@ RpcResponse WSRequestHandler::SetSceneItemCrop(const RpcRequest& request) { /** * Deletes a scene item. * - * @param {String (optional)} `scene` Name of the scene the source belongs to. Defaults to the current scene. - * @param {Object} `item` item to delete (required) - * @param {String} `item.name` name of the scene item (prefer `id`, including both is acceptable). - * @param {int} `item.id` id of the scene item. + * @param {String (optional)} `scene` Name of the scene the scene item belongs to. Defaults to the current scene. + * @param {Object} `item` Scene item to delete (required) + * @param {String} `item.name` Scene Item name (prefer `id`, including both is acceptable). + * @param {int} `item.id` Scene Item ID. * * @api requests * @name DeleteSceneItem @@ -533,9 +533,9 @@ RpcResponse WSRequestHandler::DeleteSceneItem(const RpcRequest& request) { * * @param {String (optional)} `fromScene` Name of the scene to copy the item from. Defaults to the current scene. * @param {String (optional)} `toScene` Name of the scene to create the item in. Defaults to the current scene. - * @param {Object} `item` item to duplicate (required) - * @param {String} `item.name` name of the scene item (prefer `id`, including both is acceptable). - * @param {int} `item.id` id of the scene item. + * @param {Object} `item` Scene Item to duplicate from the source scene (required) + * @param {String} `item.name` Scene Item name (prefer `id`, including both is acceptable). + * @param {int} `item.id` Scene Item ID. * * @return {String} `scene` Name of the scene where the new item was created * @return {Object} `item` New item info From a6c6a42669c7730bd2e6d19cec82fe5ca7d90f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 19:37:51 +0200 Subject: [PATCH 012/278] requests(scene items): doc fixes again --- src/WSRequestHandler_SceneItems.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 87fac51d..55058fed 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -305,9 +305,9 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) { /** * Show or hide a specified source item in a specified scene. * -* @param {String} `source` Scene item name in the specified scene. +* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the currently active scene. +* @param {String} `source` Scene Item name. * @param {boolean} `render` true = shown ; false = hidden -* @param {String (optional)} `scene-name` Name of the scene where the source resides. Defaults to the currently active scene. * * @api requests * @name SetSceneItemRender @@ -348,8 +348,8 @@ RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) { /** * Sets the coordinates of a specified source item. * -* @param {String (optional)} `scene-name` The name of the scene that the source item belongs to. Defaults to the current scene. -* @param {String} `item` The name of the source item. +* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. +* @param {String} `item` Scene Item name. * @param {double} `x` X coordinate. * @param {double} `y` Y coordinate. @@ -393,8 +393,8 @@ RpcResponse WSRequestHandler::SetSceneItemPosition(const RpcRequest& request) { /** * Set the transform of the specified source item. * -* @param {String (optional)} `scene-name` The name of the scene that the source item belongs to. Defaults to the current scene. -* @param {String} `item` The name of the source item. +* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. +* @param {String} `item` Scene Item name. * @param {double} `x-scale` Width scale factor. * @param {double} `y-scale` Height scale factor. * @param {double} `rotation` Source item rotation (in degrees). @@ -448,8 +448,8 @@ RpcResponse WSRequestHandler::SetSceneItemTransform(const RpcRequest& request) { /** * Sets the crop coordinates of the specified source item. * -* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene. -* @param {String} `item` The name of the source. +* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. +* @param {String} `item` Scene Item name. * @param {int} `top` Pixel position of the top of the source item. * @param {int} `bottom` Pixel position of the bottom of the source item. * @param {int} `left` Pixel position of the left of the source item. From 4e2302936f2a8cb4f92cd9a240fcfd648624d57a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 21:41:38 +0200 Subject: [PATCH 013/278] refactor bits --- src/Utils.cpp | 56 +++++++++++++++++++++++++-------------------------- src/Utils.h | 4 ++-- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index 978162be..a104e05c 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -180,34 +180,6 @@ obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) { return data; } -obs_sceneitem_t* Utils::GetSceneItemFromItem(obs_scene_t* scene, obs_data_t* itemInfo) { - if (!scene) { - return nullptr; - } - - OBSDataItemAutoRelease idInfoItem = obs_data_item_byname(itemInfo, "id"); - int id = obs_data_item_get_int(idInfoItem); - - OBSDataItemAutoRelease nameInfoItem = obs_data_item_byname(itemInfo, "name"); - const char* name = obs_data_item_get_string(nameInfoItem); - - if (idInfoItem) { - obs_sceneitem_t* sceneItem = GetSceneItemFromId(scene, id); - obs_source_t* sceneItemSource = obs_sceneitem_get_source(sceneItem); - - QString sceneItemName = obs_source_get_name(sceneItemSource); - if (nameInfoItem && (QString(name) != sceneItemName)) { - return nullptr; - } - - return sceneItem; - } else if (nameInfoItem) { - return GetSceneItemFromName(scene, name); - } - - return nullptr; -} - obs_sceneitem_t* Utils::GetSceneItemFromName(obs_scene_t* scene, QString name) { if (!scene) { return nullptr; @@ -297,6 +269,34 @@ obs_sceneitem_t* Utils::GetSceneItemFromId(obs_scene_t* scene, int64_t id) { return search.result; } +obs_sceneitem_t* Utils::GetSceneItemFromItem(obs_scene_t* scene, obs_data_t* itemInfo) { + if (!scene) { + return nullptr; + } + + OBSDataItemAutoRelease idInfoItem = obs_data_item_byname(itemInfo, "id"); + int id = obs_data_item_get_int(idInfoItem); + + OBSDataItemAutoRelease nameInfoItem = obs_data_item_byname(itemInfo, "name"); + const char* name = obs_data_item_get_string(nameInfoItem); + + if (idInfoItem) { + obs_sceneitem_t* sceneItem = GetSceneItemFromId(scene, id); + obs_source_t* sceneItemSource = obs_sceneitem_get_source(sceneItem); + + QString sceneItemName = obs_source_get_name(sceneItemSource); + if (nameInfoItem && (QString(name) != sceneItemName)) { + return nullptr; + } + + return sceneItem; + } else if (nameInfoItem) { + return GetSceneItemFromName(scene, name); + } + + return nullptr; +} + bool Utils::IsValidAlignment(const uint32_t alignment) { switch (alignment) { case OBS_ALIGN_CENTER: diff --git a/src/Utils.h b/src/Utils.h index 5934ad4e..7ae51297 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -39,11 +39,11 @@ namespace Utils { obs_data_array_t* GetSceneItems(obs_source_t* source); obs_data_t* GetSceneItemData(obs_sceneitem_t* item); - // These two functions support nested lookup into groups + // These functions support nested lookup into groups obs_sceneitem_t* GetSceneItemFromName(obs_scene_t* scene, QString name); obs_sceneitem_t* GetSceneItemFromId(obs_scene_t* scene, int64_t id); - obs_sceneitem_t* GetSceneItemFromItem(obs_scene_t* scene, obs_data_t* item); + obs_scene_t* GetSceneFromNameOrCurrent(QString sceneName); obs_data_t* GetSceneItemPropertiesData(obs_sceneitem_t* item); From 51ec3ede1f8aae6861aa527fbf9b50deaca153c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 21:42:10 +0200 Subject: [PATCH 014/278] utils: add GetSceneItemFromRequestField --- src/Utils.cpp | 15 +++++++++++++++ src/Utils.h | 1 + 2 files changed, 16 insertions(+) diff --git a/src/Utils.cpp b/src/Utils.cpp index a104e05c..e80e7e50 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -297,6 +297,21 @@ obs_sceneitem_t* Utils::GetSceneItemFromItem(obs_scene_t* scene, obs_data_t* ite return nullptr; } +obs_sceneitem_t* Utils::GetSceneItemFromRequestField(obs_scene_t* scene, obs_data_item_t* dataItem) +{ + obs_data_type dataType = obs_data_item_gettype(dataItem); + + if (dataType == OBS_DATA_OBJECT) { + OBSDataAutoRelease itemData = obs_data_item_get_obj(dataItem); + return GetSceneItemFromItem(scene, itemData); + } else if (dataType == OBS_DATA_STRING) { + const char* name = obs_data_item_get_string(dataItem); + return GetSceneItemFromName(scene, name); + } + + return nullptr; +} + bool Utils::IsValidAlignment(const uint32_t alignment) { switch (alignment) { case OBS_ALIGN_CENTER: diff --git a/src/Utils.h b/src/Utils.h index 7ae51297..dbe2481f 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -43,6 +43,7 @@ namespace Utils { obs_sceneitem_t* GetSceneItemFromName(obs_scene_t* scene, QString name); obs_sceneitem_t* GetSceneItemFromId(obs_scene_t* scene, int64_t id); obs_sceneitem_t* GetSceneItemFromItem(obs_scene_t* scene, obs_data_t* item); + obs_sceneitem_t* GetSceneItemFromRequestField(obs_scene_t* scene, obs_data_item_t* dataItem); obs_scene_t* GetSceneFromNameOrCurrent(QString sceneName); obs_data_t* GetSceneItemPropertiesData(obs_sceneitem_t* item); From 0fcf770043cee6286db5f3d28b04472ee77c80f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 22:07:55 +0200 Subject: [PATCH 015/278] requests(scene items): implement object mode on "item" request field --- src/WSRequestHandler_SceneItems.cpp | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 55058fed..651604dd 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -45,18 +45,14 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request) return request.failed("missing request parameters"); } - QString itemName = obs_data_get_string(request.parameters(), "item"); - if (itemName.isEmpty()) { - return request.failed("invalid request parameters"); - } - QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); if (!scene) { return request.failed("requested scene doesn't exist"); } - OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); + OBSDataItemAutoRelease itemField = obs_data_item_byname("item"); + OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemField); if (!sceneItem) { return request.failed("specified scene item doesn't exist"); } @@ -100,19 +96,14 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) return request.failed("missing request parameters"); } - QString itemName = obs_data_get_string(request.parameters(), "item"); - if (itemName.isEmpty()) { - return request.failed("invalid request parameters"); - } - QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); if (!scene) { return request.failed("requested scene doesn't exist"); } - OBSSceneItemAutoRelease sceneItem = - Utils::GetSceneItemFromName(scene, itemName); + OBSDataItemAutoRelease itemField = obs_data_item_byname("item"); + OBSSceneItemAutoRelease sceneItem =Utils::GetSceneItemFromRequestField(scene, itemField); if (!sceneItem) { return request.failed("specified scene item doesn't exist"); } @@ -272,24 +263,18 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) * @since 4.2.0 */ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) { - // TODO: remove this request, or refactor it to ResetSource - if (!request.hasField("item")) { return request.failed("missing request parameters"); } - const char* itemName = obs_data_get_string(request.parameters(), "item"); - if (!itemName) { - return request.failed("invalid request parameters"); - } - const char* sceneName = obs_data_get_string(request.parameters(), "scene-name"); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); if (!scene) { return request.failed("requested scene doesn't exist"); } - OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); + OBSDataItemAutoRelease itemField = obs_data_item_byname("item"); + OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField); if (!sceneItem) { return request.failed("specified scene item doesn't exist"); } From be14947668eecee4c030b0844f3180de50b945fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 22:12:54 +0200 Subject: [PATCH 016/278] oops --- src/WSRequestHandler_SceneItems.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 651604dd..4e3b8e62 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -51,14 +51,16 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request) return request.failed("requested scene doesn't exist"); } - OBSDataItemAutoRelease itemField = obs_data_item_byname("item"); - OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemField); + OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item"); + OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField); if (!sceneItem) { return request.failed("specified scene item doesn't exist"); } OBSDataAutoRelease data = Utils::GetSceneItemPropertiesData(sceneItem); - obs_data_set_string(data, "name", itemName.toUtf8()); + + OBSSourceAutoRelease sceneItemSource = obs_sceneitem_get_source(sceneItem); + obs_data_set_string(data, "name", obs_source_get_name(sceneItemSource)); return request.success(data); } @@ -102,7 +104,7 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) return request.failed("requested scene doesn't exist"); } - OBSDataItemAutoRelease itemField = obs_data_item_byname("item"); + OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item"); OBSSceneItemAutoRelease sceneItem =Utils::GetSceneItemFromRequestField(scene, itemField); if (!sceneItem) { return request.failed("specified scene item doesn't exist"); @@ -273,7 +275,7 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) { return request.failed("requested scene doesn't exist"); } - OBSDataItemAutoRelease itemField = obs_data_item_byname("item"); + OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item"); OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField); if (!sceneItem) { return request.failed("specified scene item doesn't exist"); From fbebf1c7d3df41c1bc6b32d509deb79a0fcdfc2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 23:04:20 +0200 Subject: [PATCH 017/278] requests(scene item): fix docs --- src/WSRequestHandler_SceneItems.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 4e3b8e62..d616e723 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -7,7 +7,9 @@ * Coordinates are relative to the item's parent (the scene or group it belongs to). * * @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. -* @param {String} `item` Scene Item name. +* @param {String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object). +* @param {String (optional)} `item.name` Scene Item name (if the `item` field is an object) +* @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object) * * @return {String} `name` Scene Item name. * @return {int} `position.x` The x position of the source from the left. @@ -70,7 +72,9 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request) * Coordinates are relative to the item's parent (the scene or group it belongs to). * * @param {String (optional)} `scene-name` Name of the scene the source item belongs to. Defaults to the current scene. -* @param {String} `item` Scene Item name. +* @param {String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object). +* @param {String (optional)} `item.name` Scene Item name (if the `item` field is an object) +* @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object) * @param {int (optional)} `position.x` The new x position of the source. * @param {int (optional)} `position.y` The new y position of the source. * @param {int (optional)} `position.alignment` The new alignment of the source. @@ -257,7 +261,9 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) * Reset a scene item. * * @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. -* @param {String} `item` Scene item name. +* @param {String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object). +* @param {String (optional)} `item.name` Scene Item name (if the `item` field is an object) +* @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object) * * @api requests * @name ResetSceneItem From 83f702fbabd132bb1de6d896d390ba6ac2c7705b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 23:28:07 +0200 Subject: [PATCH 018/278] requests(GetSceneItemProperties): add itemId response field --- src/WSRequestHandler_SceneItems.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index d616e723..ed089880 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -12,6 +12,7 @@ * @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object) * * @return {String} `name` Scene Item name. +* @return {int} `itemId` Scene Item ID. * @return {int} `position.x` The x position of the source from the left. * @return {int} `position.y` The y position of the source from the top. * @return {int} `position.alignment` The point on the source that the item is manipulated from. @@ -63,6 +64,7 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request) OBSSourceAutoRelease sceneItemSource = obs_sceneitem_get_source(sceneItem); obs_data_set_string(data, "name", obs_source_get_name(sceneItemSource)); + obs_data_set_int(data, "itemId", obs_sceneitem_get_id(sceneItem)); return request.success(data); } From 8b731f3ba496a3dec706da0aec2c0b4abbcdfc63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Mar 2020 23:55:45 +0200 Subject: [PATCH 019/278] fixes --- src/Utils.cpp | 4 +-- src/WSRequestHandler_SceneItems.cpp | 53 ++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index e80e7e50..dd5ad14a 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -299,13 +299,13 @@ obs_sceneitem_t* Utils::GetSceneItemFromItem(obs_scene_t* scene, obs_data_t* ite obs_sceneitem_t* Utils::GetSceneItemFromRequestField(obs_scene_t* scene, obs_data_item_t* dataItem) { - obs_data_type dataType = obs_data_item_gettype(dataItem); + enum obs_data_type dataType = obs_data_item_gettype(dataItem); if (dataType == OBS_DATA_OBJECT) { OBSDataAutoRelease itemData = obs_data_item_get_obj(dataItem); return GetSceneItemFromItem(scene, itemData); } else if (dataType == OBS_DATA_STRING) { - const char* name = obs_data_item_get_string(dataItem); + QString name = obs_data_item_get_string(dataItem); return GetSceneItemFromName(scene, name); } diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index ed089880..829d36eb 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -48,13 +48,15 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request) return request.failed("missing request parameters"); } - QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); + OBSData params = request.parameters(); + + QString sceneName = obs_data_get_string(params, "scene-name"); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); if (!scene) { return request.failed("requested scene doesn't exist"); } - OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item"); + OBSDataItemAutoRelease itemField = obs_data_item_byname(params, "item"); OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField); if (!sceneItem) { return request.failed("specified scene item doesn't exist"); @@ -62,7 +64,7 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request) OBSDataAutoRelease data = Utils::GetSceneItemPropertiesData(sceneItem); - OBSSourceAutoRelease sceneItemSource = obs_sceneitem_get_source(sceneItem); + OBSSource sceneItemSource = obs_sceneitem_get_source(sceneItem); obs_data_set_string(data, "name", obs_source_get_name(sceneItemSource)); obs_data_set_int(data, "itemId", obs_sceneitem_get_id(sceneItem)); @@ -104,13 +106,15 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) return request.failed("missing request parameters"); } - QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); + OBSData params = request.parameters(); + + QString sceneName = obs_data_get_string(params, "scene-name"); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); if (!scene) { return request.failed("requested scene doesn't exist"); } - OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item"); + OBSDataItemAutoRelease itemField = obs_data_item_byname(params, "item"); OBSSceneItemAutoRelease sceneItem =Utils::GetSceneItemFromRequestField(scene, itemField); if (!sceneItem) { return request.failed("specified scene item doesn't exist"); @@ -125,51 +129,59 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) vec2 oldPosition; OBSDataAutoRelease positionError = obs_data_create(); obs_sceneitem_get_pos(sceneItem, &oldPosition); - OBSDataAutoRelease reqPosition = obs_data_get_obj(request.parameters(), "position"); + + OBSDataAutoRelease reqPosition = obs_data_get_obj(params, "position"); vec2 newPosition = oldPosition; + if (obs_data_has_user_value(reqPosition, "x")) { newPosition.x = obs_data_get_int(reqPosition, "x"); } if (obs_data_has_user_value(reqPosition, "y")) { newPosition.y = obs_data_get_int(reqPosition, "y"); } + if (obs_data_has_user_value(reqPosition, "alignment")) { const uint32_t alignment = obs_data_get_int(reqPosition, "alignment"); if (Utils::IsValidAlignment(alignment)) { obs_sceneitem_set_alignment(sceneItem, alignment); - } - else { + } else { badRequest = true; obs_data_set_string(positionError, "alignment", "invalid"); obs_data_set_obj(errorData, "position", positionError); } } + obs_sceneitem_set_pos(sceneItem, &newPosition); } if (request.hasField("rotation")) { - obs_sceneitem_set_rot(sceneItem, (float)obs_data_get_double(request.parameters(), "rotation")); + obs_sceneitem_set_rot(sceneItem, (float)obs_data_get_double(params, "rotation")); } if (request.hasField("scale")) { vec2 oldScale; obs_sceneitem_get_scale(sceneItem, &oldScale); - OBSDataAutoRelease reqScale = obs_data_get_obj(request.parameters(), "scale"); vec2 newScale = oldScale; + + OBSDataAutoRelease reqScale = obs_data_get_obj(params, "scale"); + if (obs_data_has_user_value(reqScale, "x")) { newScale.x = obs_data_get_double(reqScale, "x"); } if (obs_data_has_user_value(reqScale, "y")) { newScale.y = obs_data_get_double(reqScale, "y"); } + obs_sceneitem_set_scale(sceneItem, &newScale); } if (request.hasField("crop")) { obs_sceneitem_crop oldCrop; obs_sceneitem_get_crop(sceneItem, &oldCrop); - OBSDataAutoRelease reqCrop = obs_data_get_obj(request.parameters(), "crop"); + + OBSDataAutoRelease reqCrop = obs_data_get_obj(params, "crop"); obs_sceneitem_crop newCrop = oldCrop; + if (obs_data_has_user_value(reqCrop, "top")) { newCrop.top = obs_data_get_int(reqCrop, "top"); } @@ -182,21 +194,23 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) if (obs_data_has_user_value(reqCrop, "left")) { newCrop.left = obs_data_get_int(reqCrop, "left"); } + obs_sceneitem_set_crop(sceneItem, &newCrop); } if (request.hasField("visible")) { - obs_sceneitem_set_visible(sceneItem, obs_data_get_bool(request.parameters(), "visible")); + obs_sceneitem_set_visible(sceneItem, obs_data_get_bool(params, "visible")); } if (request.hasField("locked")) { - obs_sceneitem_set_locked(sceneItem, obs_data_get_bool(request.parameters(), "locked")); + obs_sceneitem_set_locked(sceneItem, obs_data_get_bool(params, "locked")); } if (request.hasField("bounds")) { bool badBounds = false; OBSDataAutoRelease boundsError = obs_data_create(); - OBSDataAutoRelease reqBounds = obs_data_get_obj(request.parameters(), "bounds"); + OBSDataAutoRelease reqBounds = obs_data_get_obj(params, "bounds"); + if (obs_data_has_user_value(reqBounds, "type")) { QString newBoundsType = obs_data_get_string(reqBounds, "type"); if (newBoundsType == "OBS_BOUNDS_NONE") { @@ -225,16 +239,20 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) obs_data_set_string(boundsError, "type", "invalid"); } } + vec2 oldBounds; obs_sceneitem_get_bounds(sceneItem, &oldBounds); vec2 newBounds = oldBounds; + if (obs_data_has_user_value(reqBounds, "x")) { newBounds.x = obs_data_get_double(reqBounds, "x"); } if (obs_data_has_user_value(reqBounds, "y")) { newBounds.y = obs_data_get_double(reqBounds, "y"); } + obs_sceneitem_set_bounds(sceneItem, &newBounds); + if (obs_data_has_user_value(reqBounds, "alignment")) { const uint32_t bounds_alignment = obs_data_get_int(reqBounds, "alignment"); if (Utils::IsValidAlignment(bounds_alignment)) { @@ -245,6 +263,7 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) obs_data_set_string(boundsError, "alignment", "invalid"); } } + if (badBounds) { obs_data_set_obj(errorData, "bounds", boundsError); } @@ -277,13 +296,15 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) { return request.failed("missing request parameters"); } - const char* sceneName = obs_data_get_string(request.parameters(), "scene-name"); + OBSData params = request.parameters(); + + const char* sceneName = obs_data_get_string(params, "scene-name"); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); if (!scene) { return request.failed("requested scene doesn't exist"); } - OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item"); + OBSDataItemAutoRelease itemField = obs_data_item_byname(params, "item"); OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField); if (!sceneItem) { return request.failed("specified scene item doesn't exist"); From 3604995c2fa5b6f0c6abba6d46ed562a323bde77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Mon, 30 Mar 2020 00:02:36 +0200 Subject: [PATCH 020/278] requests(DeleteSceneItem + DuplicateSceneItem): backport dual "item" behaviour to existing functions --- src/WSRequestHandler_SceneItems.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 829d36eb..c1fd8522 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -115,7 +115,7 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) } OBSDataItemAutoRelease itemField = obs_data_item_byname(params, "item"); - OBSSceneItemAutoRelease sceneItem =Utils::GetSceneItemFromRequestField(scene, itemField); + OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField); if (!sceneItem) { return request.failed("specified scene item doesn't exist"); } @@ -533,8 +533,8 @@ RpcResponse WSRequestHandler::DeleteSceneItem(const RpcRequest& request) { return request.failed("requested scene doesn't exist"); } - OBSDataAutoRelease item = obs_data_get_obj(request.parameters(), "item"); - OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item); + OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item"); + OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField); if (!sceneItem) { return request.failed("item with id/name combination not found in specified scene"); } @@ -586,8 +586,8 @@ RpcResponse WSRequestHandler::DuplicateSceneItem(const RpcRequest& request) { return request.failed("requested toScene doesn't exist"); } - OBSDataAutoRelease item = obs_data_get_obj(request.parameters(), "item"); - OBSSceneItemAutoRelease referenceItem = Utils::GetSceneItemFromItem(fromScene, item); + OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item"); + OBSSceneItemAutoRelease referenceItem = Utils::GetSceneItemFromRequestField(fromScene, itemField); if (!referenceItem) { return request.failed("item with id/name combination not found in specified scene"); } From 67e89e79be18c42df7d7a7fa43c13386e848ed07 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Sun, 29 Mar 2020 22:20:01 +0000 Subject: [PATCH 021/278] docs(ci): Update protocol.md - e467666 [skip ci] --- docs/generated/comments.json | 182 ++++++++++++++++++++++------------- docs/generated/protocol.md | 56 ++++++----- 2 files changed, 146 insertions(+), 92 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index d9f46288..cddebe18 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -2473,7 +2473,9 @@ "lead": "", "type": "class", "examples": [] - }, + } + ], + "scene items": [ { "subheads": [], "description": "Scene items have been reordered.", @@ -2485,7 +2487,7 @@ ], "api": "events", "name": "SourceOrderChanged", - "category": "sources", + "category": "scene items", "since": "4.0.0", "returns": [ { @@ -2518,7 +2520,7 @@ "categories": [ { "name": "", - "description": "sources" + "description": "scene items" } ], "sinces": [ @@ -2545,7 +2547,7 @@ ], "api": "events", "name": "SceneItemAdded", - "category": "sources", + "category": "scene items", "since": "4.0.0", "returns": [ { @@ -2573,7 +2575,7 @@ "categories": [ { "name": "", - "description": "sources" + "description": "scene items" } ], "sinces": [ @@ -2600,7 +2602,7 @@ ], "api": "events", "name": "SceneItemRemoved", - "category": "sources", + "category": "scene items", "since": "4.0.0", "returns": [ { @@ -2628,7 +2630,7 @@ "categories": [ { "name": "", - "description": "sources" + "description": "scene items" } ], "sinces": [ @@ -2656,7 +2658,7 @@ ], "api": "events", "name": "SceneItemVisibilityChanged", - "category": "sources", + "category": "scene items", "since": "4.0.0", "returns": [ { @@ -2689,7 +2691,7 @@ "categories": [ { "name": "", - "description": "sources" + "description": "scene items" } ], "sinces": [ @@ -2717,7 +2719,7 @@ ], "api": "events", "name": "SceneItemLockChanged", - "category": "sources", + "category": "scene items", "since": "4.8.0", "returns": [ { @@ -2750,7 +2752,7 @@ "categories": [ { "name": "", - "description": "sources" + "description": "scene items" } ], "sinces": [ @@ -2778,7 +2780,7 @@ ], "api": "events", "name": "SceneItemTransformChanged", - "category": "sources", + "category": "scene items", "since": "4.6.0", "returns": [ { @@ -2811,7 +2813,7 @@ "categories": [ { "name": "", - "description": "sources" + "description": "scene items" } ], "sinces": [ @@ -2838,7 +2840,7 @@ ], "api": "events", "name": "SceneItemSelected", - "category": "sources", + "category": "scene items", "since": "4.6.0", "returns": [ { @@ -2866,7 +2868,7 @@ "categories": [ { "name": "", - "description": "sources" + "description": "scene items" } ], "sinces": [ @@ -2893,7 +2895,7 @@ ], "api": "events", "name": "SceneItemDeselected", - "category": "sources", + "category": "scene items", "since": "4.6.0", "returns": [ { @@ -2921,7 +2923,7 @@ "categories": [ { "name": "", - "description": "sources" + "description": "scene items" } ], "sinces": [ @@ -4383,11 +4385,14 @@ "subheads": [], "description": "Gets the scene specific properties of the specified source item.\nCoordinates are relative to the item's parent (the scene or group it belongs to).", "param": [ - "{String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.", - "{String} `item` The name of the source." + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).", + "{String (optional)} `item.name` Scene Item name (if the `item` field is an object)", + "{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)" ], "return": [ - "{String} `name` The name of the source.", + "{String} `name` Scene Item name.", + "{int} `itemId` Scene Item ID.", "{int} `position.x` The x position of the source from the left.", "{int} `position.y` The y position of the source from the top.", "{int} `position.alignment` The point on the source that the item is manipulated from.", @@ -4421,7 +4426,12 @@ { "type": "String", "name": "name", - "description": "The name of the source." + "description": "Scene Item name." + }, + { + "type": "int", + "name": "itemId", + "description": "Scene Item ID." }, { "type": "int", @@ -4548,12 +4558,22 @@ { "type": "String (optional)", "name": "scene-name", - "description": "the name of the scene that the source item belongs to. Defaults to the current scene." + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." }, { - "type": "String", + "type": "String | Object", "name": "item", - "description": "The name of the source." + "description": "Scene Item name (if this field is a string) or specification (if it is an object)." + }, + { + "type": "String (optional)", + "name": "item.name", + "description": "Scene Item name (if the `item` field is an object)" + }, + { + "type": "int (optional)", + "name": "item.id", + "description": "Scene Item ID (if the `item` field is an object)" } ], "names": [ @@ -4586,8 +4606,10 @@ "subheads": [], "description": "Sets the scene specific properties of a source. Unspecified properties will remain unchanged.\nCoordinates are relative to the item's parent (the scene or group it belongs to).", "param": [ - "{String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.", - "{String} `item` The name of the source.", + "{String (optional)} `scene-name` Name of the scene the source item belongs to. Defaults to the current scene.", + "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).", + "{String (optional)} `item.name` Scene Item name (if the `item` field is an object)", + "{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)", "{int (optional)} `position.x` The new x position of the source.", "{int (optional)} `position.y` The new y position of the source.", "{int (optional)} `position.alignment` The new alignment of the source.", @@ -4613,12 +4635,22 @@ { "type": "String (optional)", "name": "scene-name", - "description": "the name of the scene that the source item belongs to. Defaults to the current scene." + "description": "Name of the scene the source item belongs to. Defaults to the current scene." }, { - "type": "String", + "type": "String | Object", "name": "item", - "description": "The name of the source." + "description": "Scene Item name (if this field is a string) or specification (if it is an object)." + }, + { + "type": "String (optional)", + "name": "item.name", + "description": "Scene Item name (if the `item` field is an object)" + }, + { + "type": "int (optional)", + "name": "item.id", + "description": "Scene Item ID (if the `item` field is an object)" }, { "type": "int (optional)", @@ -4731,8 +4763,10 @@ "subheads": [], "description": "Reset a scene item.", "param": [ - "{String (optional)} `scene-name` Name of the scene the source belongs to. Defaults to the current scene.", - "{String} `item` Name of the source item." + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).", + "{String (optional)} `item.name` Scene Item name (if the `item` field is an object)", + "{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)" ], "api": "requests", "name": "ResetSceneItem", @@ -4742,12 +4776,22 @@ { "type": "String (optional)", "name": "scene-name", - "description": "Name of the scene the source belongs to. Defaults to the current scene." + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." }, { - "type": "String", + "type": "String | Object", "name": "item", - "description": "Name of the source item." + "description": "Scene Item name (if this field is a string) or specification (if it is an object)." + }, + { + "type": "String (optional)", + "name": "item.name", + "description": "Scene Item name (if the `item` field is an object)" + }, + { + "type": "int (optional)", + "name": "item.id", + "description": "Scene Item ID (if the `item` field is an object)" } ], "names": [ @@ -4780,9 +4824,9 @@ "subheads": [], "description": "Show or hide a specified source item in a specified scene.", "param": [ - "{String} `source` Scene item name in the specified scene.", - "{boolean} `render` true = shown ; false = hidden", - "{String (optional)} `scene-name` Name of the scene where the source resides. Defaults to the currently active scene." + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the currently active scene.", + "{String} `source` Scene Item name.", + "{boolean} `render` true = shown ; false = hidden" ], "api": "requests", "name": "SetSceneItemRender", @@ -4790,20 +4834,20 @@ "since": "0.3", "deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.", "params": [ + { + "type": "String (optional)", + "name": "scene-name", + "description": "Name of the scene the scene item belongs to. Defaults to the currently active scene." + }, { "type": "String", "name": "source", - "description": "Scene item name in the specified scene." + "description": "Scene Item name." }, { "type": "boolean", "name": "render", "description": "true = shown ; false = hidden" - }, - { - "type": "String (optional)", - "name": "scene-name", - "description": "Name of the scene where the source resides. Defaults to the currently active scene." } ], "names": [ @@ -4842,8 +4886,8 @@ "subheads": [], "description": "Sets the coordinates of a specified source item.", "param": [ - "{String (optional)} `scene-name` The name of the scene that the source item belongs to. Defaults to the current scene.", - "{String} `item` The name of the source item.", + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{String} `item` Scene Item name.", "{double} `x` X coordinate.", "{double} `y` Y coordinate." ], @@ -4856,12 +4900,12 @@ { "type": "String (optional)", "name": "scene-name", - "description": "The name of the scene that the source item belongs to. Defaults to the current scene." + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." }, { "type": "String", "name": "item", - "description": "The name of the source item." + "description": "Scene Item name." }, { "type": "double", @@ -4910,8 +4954,8 @@ "subheads": [], "description": "Set the transform of the specified source item.", "param": [ - "{String (optional)} `scene-name` The name of the scene that the source item belongs to. Defaults to the current scene.", - "{String} `item` The name of the source item.", + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{String} `item` Scene Item name.", "{double} `x-scale` Width scale factor.", "{double} `y-scale` Height scale factor.", "{double} `rotation` Source item rotation (in degrees)." @@ -4925,12 +4969,12 @@ { "type": "String (optional)", "name": "scene-name", - "description": "The name of the scene that the source item belongs to. Defaults to the current scene." + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." }, { "type": "String", "name": "item", - "description": "The name of the source item." + "description": "Scene Item name." }, { "type": "double", @@ -4984,8 +5028,8 @@ "subheads": [], "description": "Sets the crop coordinates of the specified source item.", "param": [ - "{String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.", - "{String} `item` The name of the source.", + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{String} `item` Scene Item name.", "{int} `top` Pixel position of the top of the source item.", "{int} `bottom` Pixel position of the bottom of the source item.", "{int} `left` Pixel position of the left of the source item.", @@ -5000,12 +5044,12 @@ { "type": "String (optional)", "name": "scene-name", - "description": "the name of the scene that the source item belongs to. Defaults to the current scene." + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." }, { "type": "String", "name": "item", - "description": "The name of the source." + "description": "Scene Item name." }, { "type": "int", @@ -5064,10 +5108,10 @@ "subheads": [], "description": "Deletes a scene item.", "param": [ - "{String (optional)} `scene` Name of the scene the source belongs to. Defaults to the current scene.", - "{Object} `item` item to delete (required)", - "{String} `item.name` name of the scene item (prefer `id`, including both is acceptable).", - "{int} `item.id` id of the scene item." + "{String (optional)} `scene` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{Object} `item` Scene item to delete (required)", + "{String} `item.name` Scene Item name (prefer `id`, including both is acceptable).", + "{int} `item.id` Scene Item ID." ], "api": "requests", "name": "DeleteSceneItem", @@ -5077,22 +5121,22 @@ { "type": "String (optional)", "name": "scene", - "description": "Name of the scene the source belongs to. Defaults to the current scene." + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." }, { "type": "Object", "name": "item", - "description": "item to delete (required)" + "description": "Scene item to delete (required)" }, { "type": "String", "name": "item.name", - "description": "name of the scene item (prefer `id`, including both is acceptable)." + "description": "Scene Item name (prefer `id`, including both is acceptable)." }, { "type": "int", "name": "item.id", - "description": "id of the scene item." + "description": "Scene Item ID." } ], "names": [ @@ -5127,9 +5171,9 @@ "param": [ "{String (optional)} `fromScene` Name of the scene to copy the item from. Defaults to the current scene.", "{String (optional)} `toScene` Name of the scene to create the item in. Defaults to the current scene.", - "{Object} `item` item to duplicate (required)", - "{String} `item.name` name of the scene item (prefer `id`, including both is acceptable).", - "{int} `item.id` id of the scene item." + "{Object} `item` Scene Item to duplicate from the source scene (required)", + "{String} `item.name` Scene Item name (prefer `id`, including both is acceptable).", + "{int} `item.id` Scene Item ID." ], "return": [ "{String} `scene` Name of the scene where the new item was created", @@ -5177,17 +5221,17 @@ { "type": "Object", "name": "item", - "description": "item to duplicate (required)" + "description": "Scene Item to duplicate from the source scene (required)" }, { "type": "String", "name": "item.name", - "description": "name of the scene item (prefer `id`, including both is acceptable)." + "description": "Scene Item name (prefer `id`, including both is acceptable)." }, { "type": "int", "name": "item.id", - "description": "id of the scene item." + "description": "Scene Item ID." } ], "names": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 48497157..43faa476 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -99,6 +99,7 @@ auth_response = base64_encode(auth_response_hash) + [SourceFilterRemoved](#sourcefilterremoved) + [SourceFilterVisibilityChanged](#sourcefiltervisibilitychanged) + [SourceFiltersReordered](#sourcefiltersreordered) + * [Scene Items](#scene-items) + [SourceOrderChanged](#sourceorderchanged) + [SceneItemAdded](#sceneitemadded) + [SceneItemRemoved](#sceneitemremoved) @@ -148,7 +149,7 @@ auth_response = base64_encode(auth_response_hash) + [SetCurrentSceneCollection](#setcurrentscenecollection) + [GetCurrentSceneCollection](#getcurrentscenecollection) + [ListSceneCollections](#listscenecollections) - * [Scene Items](#scene-items) + * [Scene Items](#scene-items-1) + [GetSceneItemProperties](#getsceneitemproperties) + [SetSceneItemProperties](#setsceneitemproperties) + [ResetSceneItem](#resetsceneitem) @@ -1004,6 +1005,8 @@ Filters in a source have been reordered. --- +## Scene Items + ### SourceOrderChanged @@ -1860,15 +1863,18 @@ Coordinates are relative to the item's parent (the scene or group it belongs to) | Name | Type | Description | | ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | the name of the scene that the source item belongs to. Defaults to the current scene. | -| `item` | _String_ | The name of the source. | +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). | +| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) | +| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) | **Response Items:** | Name | Type | Description | | ---- | :---: | ------------| -| `name` | _String_ | The name of the source. | +| `name` | _String_ | Scene Item name. | +| `itemId` | _int_ | Scene Item ID. | | `position.x` | _int_ | The x position of the source from the left. | | `position.y` | _int_ | The y position of the source from the top. | | `position.alignment` | _int_ | The point on the source that the item is manipulated from. | @@ -1909,8 +1915,10 @@ Coordinates are relative to the item's parent (the scene or group it belongs to) | Name | Type | Description | | ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | the name of the scene that the source item belongs to. Defaults to the current scene. | -| `item` | _String_ | The name of the source. | +| `scene-name` | _String (optional)_ | Name of the scene the source item belongs to. Defaults to the current scene. | +| `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). | +| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) | +| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) | | `position.x` | _int (optional)_ | The new x position of the source. | | `position.y` | _int (optional)_ | The new y position of the source. | | `position.alignment` | _int (optional)_ | The new alignment of the source. | @@ -1946,8 +1954,10 @@ Reset a scene item. | Name | Type | Description | | ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | Name of the scene the source belongs to. Defaults to the current scene. | -| `item` | _String_ | Name of the source item. | +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). | +| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) | +| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) | **Response Items:** @@ -1968,9 +1978,9 @@ Show or hide a specified source item in a specified scene. | Name | Type | Description | | ---- | :---: | ------------| -| `source` | _String_ | Scene item name in the specified scene. | +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the currently active scene. | +| `source` | _String_ | Scene Item name. | | `render` | _boolean_ | true = shown ; false = hidden | -| `scene-name` | _String (optional)_ | Name of the scene where the source resides. Defaults to the currently active scene. | **Response Items:** @@ -1991,8 +2001,8 @@ Sets the coordinates of a specified source item. | Name | Type | Description | | ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | The name of the scene that the source item belongs to. Defaults to the current scene. | -| `item` | _String_ | The name of the source item. | +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _String_ | Scene Item name. | | `x` | _double_ | X coordinate. | | `y` | _double_ | Y coordinate. | @@ -2015,8 +2025,8 @@ Set the transform of the specified source item. | Name | Type | Description | | ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | The name of the scene that the source item belongs to. Defaults to the current scene. | -| `item` | _String_ | The name of the source item. | +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _String_ | Scene Item name. | | `x-scale` | _double_ | Width scale factor. | | `y-scale` | _double_ | Height scale factor. | | `rotation` | _double_ | Source item rotation (in degrees). | @@ -2040,8 +2050,8 @@ Sets the crop coordinates of the specified source item. | Name | Type | Description | | ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | the name of the scene that the source item belongs to. Defaults to the current scene. | -| `item` | _String_ | The name of the source. | +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _String_ | Scene Item name. | | `top` | _int_ | Pixel position of the top of the source item. | | `bottom` | _int_ | Pixel position of the bottom of the source item. | | `left` | _int_ | Pixel position of the left of the source item. | @@ -2065,10 +2075,10 @@ Deletes a scene item. | Name | Type | Description | | ---- | :---: | ------------| -| `scene` | _String (optional)_ | Name of the scene the source belongs to. Defaults to the current scene. | -| `item` | _Object_ | item to delete (required) | -| `item.name` | _String_ | name of the scene item (prefer `id`, including both is acceptable). | -| `item.id` | _int_ | id of the scene item. | +| `scene` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _Object_ | Scene item to delete (required) | +| `item.name` | _String_ | Scene Item name (prefer `id`, including both is acceptable). | +| `item.id` | _int_ | Scene Item ID. | **Response Items:** @@ -2090,9 +2100,9 @@ Duplicates a scene item. | ---- | :---: | ------------| | `fromScene` | _String (optional)_ | Name of the scene to copy the item from. Defaults to the current scene. | | `toScene` | _String (optional)_ | Name of the scene to create the item in. Defaults to the current scene. | -| `item` | _Object_ | item to duplicate (required) | -| `item.name` | _String_ | name of the scene item (prefer `id`, including both is acceptable). | -| `item.id` | _int_ | id of the scene item. | +| `item` | _Object_ | Scene Item to duplicate from the source scene (required) | +| `item.name` | _String_ | Scene Item name (prefer `id`, including both is acceptable). | +| `item.id` | _int_ | Scene Item ID. | **Response Items:** From 2ca85d05f005f91fc2f0757b6532bbd8d0429859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 1 Apr 2020 08:51:48 +0200 Subject: [PATCH 022/278] ci(macos): fix install_name_tool packaging step --- CI/package-macos.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CI/package-macos.sh b/CI/package-macos.sh index 5210ba5b..b269dc89 100755 --- a/CI/package-macos.sh +++ b/CI/package-macos.sh @@ -22,12 +22,12 @@ export FILENAME="obs-websocket-$VERSION.pkg" echo "[obs-websocket] Modifying obs-websocket.so" install_name_tool \ - -add_rpath @executable_path/../Frameworks/QtWidgets.framework/Versions/5/ \ - -add_rpath @executable_path/../Frameworks/QtGui.framework/Versions/5/ \ - -add_rpath @executable_path/../Frameworks/QtCore.framework/Versions/5/ \ - -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @rpath/QtWidgets \ - -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @rpath/QtGui \ - -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \ + -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets \ + @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets \ + -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui \ + @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui \ + -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore \ + @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore \ ./build/obs-websocket.so # Check if replacement worked From f4997375e96741900e95b07740dc6fb1490a4a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Mon, 6 Apr 2020 19:09:01 +0200 Subject: [PATCH 023/278] cmake: add version + fix /MP msvc flag --- CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c351d6c1..1b65766b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.2) -project(obs-websocket) +cmake_minimum_required(VERSION 3.5) +project(obs-websocket VERSION 4.8.0) set(CMAKE_PREFIX_PATH "${QTDIR}") set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -85,7 +85,8 @@ if(WIN32) endif() if(MSVC) - add_compile_options("/MP") + # Enable Multicore Builds + add_definitions(/MP) endif() add_definitions(-D_WEBSOCKETPP_CPP11_STL_) From f7512c3cc06ad2ce216f8143880454e8b3a96e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Mon, 6 Apr 2020 19:40:04 +0200 Subject: [PATCH 024/278] cmake(msvc): disable FH4 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b65766b..3273367c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,8 +85,8 @@ if(WIN32) endif() if(MSVC) - # Enable Multicore Builds - add_definitions(/MP) + # Enable Multicore Builds and disable FH4 (to not depend on VCRUNTIME140_1.DLL) + add_definitions(/MP /d2FH4-) endif() add_definitions(-D_WEBSOCKETPP_CPP11_STL_) From 9451814299d45bead3b62eed0c0d508072f81a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 7 Apr 2020 17:34:28 +0200 Subject: [PATCH 025/278] ci(windows): fix installer suffix --- CI/package-windows.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/package-windows.cmd b/CI/package-windows.cmd index fe752995..e3c3d445 100644 --- a/CI/package-windows.cmd +++ b/CI/package-windows.cmd @@ -9,4 +9,4 @@ REM Package ZIP archive 7z a "obs-websocket-%PackageVersion%-Windows.zip" "..\release\*" REM Build installer -iscc ..\installer\installer.iss /O. /F"obs-websocket-%PackageVersion%-Windows" +iscc ..\installer\installer.iss /O. /F"obs-websocket-%PackageVersion%-Windows-Installer" From 2ac97dea359c6f77cacc1f89721082b6779d903d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 7 Apr 2020 17:35:49 +0200 Subject: [PATCH 026/278] installers: bump version --- CI/macos/obs-websocket.pkgproj | 2 +- installer/installer.iss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/macos/obs-websocket.pkgproj b/CI/macos/obs-websocket.pkgproj index 59d17ad7..0fd5758f 100644 --- a/CI/macos/obs-websocket.pkgproj +++ b/CI/macos/obs-websocket.pkgproj @@ -518,7 +518,7 @@ OVERWRITE_PERMISSIONS VERSION - 4.7.0 + 4.8.0 PROJECT_COMMENTS diff --git a/installer/installer.iss b/installer/installer.iss index b605b4a6..243f3a31 100644 --- a/installer/installer.iss +++ b/installer/installer.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "obs-websocket" -#define MyAppVersion "4.7.0" +#define MyAppVersion "4.8.0" #define MyAppPublisher "Stephane Lepin" #define MyAppURL "http://github.com/Palakis/obs-websocket" From dbc62ad63aa531751285b11329781295b1e8984c Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 10 Apr 2020 08:08:02 -0700 Subject: [PATCH 027/278] [Docs] Fix link to GetCurrentScene For some reason the link to the `GetCurrentScene` section of the docs was put into a codeblock --- docs/generated/protocol.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 43faa476..5325ee13 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -2176,7 +2176,7 @@ _No specified parameters._ | Name | Type | Description | | ---- | :---: | ------------| | `current-scene` | _String_ | Name of the currently active scene. | -| `scenes` | _Array<Scene>_ | Ordered list of the current profile's scenes (See `[GetCurrentScene](#getcurrentscene)` for more information). | +| `scenes` | _Array<Scene>_ | Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information). | --- From b6927562608c17acb56a143500cdb7632f79180f Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 10 Apr 2020 08:14:25 -0700 Subject: [PATCH 028/278] The actual fix --- src/WSRequestHandler_Scenes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp index 9399ac5d..40693a78 100644 --- a/src/WSRequestHandler_Scenes.cpp +++ b/src/WSRequestHandler_Scenes.cpp @@ -60,7 +60,7 @@ RpcResponse WSRequestHandler::GetCurrentScene(const RpcRequest& request) { * Get a list of scenes in the currently active profile. * * @return {String} `current-scene` Name of the currently active scene. - * @return {Array} `scenes` Ordered list of the current profile's scenes (See `[GetCurrentScene](#getcurrentscene)` for more information). + * @return {Array} `scenes` Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information). * * @api requests * @name GetSceneList From a833822eae68be72471fe260e84d8f17a7ac775f Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Fri, 10 Apr 2020 16:27:48 +0000 Subject: [PATCH 029/278] docs(ci): Update protocol.md - cfa8d8d [skip ci] --- docs/generated/comments.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index cddebe18..0731d3d7 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -5357,7 +5357,7 @@ "description": "Get a list of scenes in the currently active profile.", "return": [ "{String} `current-scene` Name of the currently active scene.", - "{Array} `scenes` Ordered list of the current profile's scenes (See `[GetCurrentScene](#getcurrentscene)` for more information)." + "{Array} `scenes` Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information)." ], "api": "requests", "name": "GetSceneList", @@ -5372,7 +5372,7 @@ { "type": "Array", "name": "scenes", - "description": "Ordered list of the current profile's scenes (See `[GetCurrentScene](#getcurrentscene)` for more information)." + "description": "Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information)." } ], "names": [ From 54e79936e5895458cc239c5307c7816a32b5cc6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 10 Apr 2020 21:41:45 +0200 Subject: [PATCH 030/278] ci(macos): add automated codesigning + notarization (#464) --- CI/macos/obs-websocket.pkgproj | 2 +- CI/package-macos.sh | 69 +++++++++++++++++++++++++++++----- azure-pipelines.yml | 25 ++++++++++++ 3 files changed, 85 insertions(+), 11 deletions(-) diff --git a/CI/macos/obs-websocket.pkgproj b/CI/macos/obs-websocket.pkgproj index 0fd5758f..e3b175e4 100644 --- a/CI/macos/obs-websocket.pkgproj +++ b/CI/macos/obs-websocket.pkgproj @@ -514,7 +514,7 @@ CONCLUSION_ACTION 0 IDENTIFIER - fr.palakis.obswebsocket + fr.palakis.obs-websocket OVERWRITE_PERMISSIONS VERSION diff --git a/CI/package-macos.sh b/CI/package-macos.sh index b269dc89..f309cf34 100755 --- a/CI/package-macos.sh +++ b/CI/package-macos.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -e @@ -12,30 +12,79 @@ fi echo "[obs-websocket] Preparing package build" export QT_CELLAR_PREFIX="$(/usr/bin/find /usr/local/Cellar/qt -d 1 | sort -t '.' -k 1,1n -k 2,2n -k 3,3n | tail -n 1)" -export GIT_HASH=$(git rev-parse --short HEAD) -export GIT_BRANCH_OR_TAG=$(git name-rev --name-only HEAD | awk -F/ '{print $NF}') +GIT_HASH=$(git rev-parse --short HEAD) +GIT_BRANCH_OR_TAG=$(git name-rev --name-only HEAD | awk -F/ '{print $NF}') -export VERSION="$GIT_HASH-$GIT_BRANCH_OR_TAG" -export LATEST_VERSION="$GIT_BRANCH_OR_TAG" +VERSION="$GIT_HASH-$GIT_BRANCH_OR_TAG" -export FILENAME="obs-websocket-$VERSION.pkg" +FILENAME_UNSIGNED="obs-websocket-$VERSION-Unsigned.pkg" +FILENAME="obs-websocket-$VERSION.pkg" echo "[obs-websocket] Modifying obs-websocket.so" install_name_tool \ -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets \ - @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets \ + @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets \ -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui \ - @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui \ + @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui \ -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore \ - @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore \ + @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore \ ./build/obs-websocket.so # Check if replacement worked echo "[obs-websocket] Dependencies for obs-websocket" otool -L ./build/obs-websocket.so +if [[ "$RELEASE_MODE" == "True" ]]; then + echo "[obs-websocket] Signing plugin binary: obs-websocket.so" + codesign --sign "$CODE_SIGNING_IDENTITY" ./build/obs-websocket.so +else + echo "[obs-websocket] Skipped plugin codesigning" +fi + echo "[obs-websocket] Actual package build" packagesbuild ./CI/macos/obs-websocket.pkgproj echo "[obs-websocket] Renaming obs-websocket.pkg to $FILENAME" -mv ./release/obs-websocket.pkg ./release/$FILENAME +mv ./release/obs-websocket.pkg ./release/$FILENAME_UNSIGNED + +if [[ "$RELEASE_MODE" == "True" ]]; then + echo "[obs-websocket] Signing installer: $FILENAME" + productsign \ + --sign "$INSTALLER_SIGNING_IDENTITY" \ + ./release/$FILENAME_UNSIGNED \ + ./release/$FILENAME + rm ./release/$FILENAME_UNSIGNED + + echo "[obs-websocket] Submitting installer $FILENAME for notarization" + zip -r ./release/$FILENAME.zip ./release/$FILENAME + UPLOAD_RESULT=$(xcrun altool \ + --notarize-app \ + --primary-bundle-id "fr.palakis.obs-websocket" \ + --username "$AC_USERNAME" \ + --password "$AC_PASSWORD" \ + --asc-provider "$AC_PROVIDER_SHORTNAME" \ + --file "./release/$FILENAME.zip") + rm ./release/$FILENAME.zip + + REQUEST_UUID=$(echo $UPLOAD_RESULT | awk -F ' = ' '/RequestUUID/ {print $2}') + echo "Request UUID: $REQUEST_UUID" + + echo "[obs-websocket] Wait for notarization result" + # Pieces of code borrowed from rednoah/notarized-app + while sleep 30 && date; do + CHECK_RESULT=$(xcrun altool \ + --notarization-info "$REQUEST_UUID" \ + --username "$AC_USERNAME" \ + --password "$AC_PASSWORD" \ + --asc-provider "$AC_PROVIDER_SHORTNAME") + echo $CHECK_RESULT + + if ! grep -q "Status: in progress" <<< "$CHECK_RESULT"; then + echo "[obs-websocket] Staple ticket to installer: $FILENAME" + xcrun stapler staple ./release/$FILENAME + break + fi + done +else + echo "[obs-websocket] Skipped installer codesigning and notarization" +fi \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 11c09166..0f3f10b9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,3 +1,14 @@ +variables: + isReleaseMode: ${{ startsWith(variables['Build.SourceBranch'], 'refs/tags/') }} + +trigger: + branches: + include: + - master + tags: + include: + - '*' + jobs: - job: 'GenerateDocs' condition: | @@ -149,8 +160,22 @@ jobs: - script: ./CI/build-macos.sh displayName: 'Build obs-websocket' + - task: InstallAppleCertificate@1 + displayName: 'Install release signing certificates' + condition: eq(variables['isReleaseMode'], true) + inputs: + certSecureFile: 'Certificates.p12' + certPwd: $(secrets.macOS.certificatesImportPassword) + - script: ./CI/package-macos.sh displayName: 'Package obs-websocket' + env: + RELEASE_MODE: $(isReleaseMode) + CODE_SIGNING_IDENTITY: $(secrets.macOS.codeSigningIdentity) + INSTALLER_SIGNING_IDENTITY: $(secrets.macOS.installerSigningIdentity) + AC_USERNAME: $(secrets.macOS.notarization.username) + AC_PASSWORD: $(secrets.macOS.notarization.password) + AC_PROVIDER_SHORTNAME: $(secrets.macOS.notarization.providerShortName) - task: PublishBuildArtifacts@1 inputs: From 71391914aea80dafce7128c50760635a7760d672 Mon Sep 17 00:00:00 2001 From: Jess Date: Tue, 14 Apr 2020 11:21:57 -0700 Subject: [PATCH 031/278] Added financial contributors to the README --- README.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 83862294..416da6fa 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ WebSockets API for OBS Studio. Follow the main author on Twitter for news & updates : [@LePalakis](https://twitter.com/LePalakis) -[![Build Status - Windows](https://ci.appveyor.com/api/projects/status/github/Palakis/obs-websocket)](https://ci.appveyor.com/project/Palakis/obs-websocket/history) [![Build Status - Linux](https://travis-ci.org/Palakis/obs-websocket.svg?branch=master)](https://travis-ci.org/Palakis/obs-websocket) +[![Financial Contributors on Open Collective](https://opencollective.com/obs-websocket/all/badge.svg?label=financial+contributors)](https://opencollective.com/obs-websocket) [![Build Status - Windows](https://ci.appveyor.com/api/projects/status/github/Palakis/obs-websocket)](https://ci.appveyor.com/project/Palakis/obs-websocket/history) [![Build Status - Linux](https://travis-ci.org/Palakis/obs-websocket.svg?branch=master)](https://travis-ci.org/Palakis/obs-websocket) ## Downloads @@ -135,3 +135,33 @@ They have contributed financially to the project and made possible the addition [MediaUnit](http://www.mediaunit.no) is a Norwegian media company developing products and services for the media industry, primarly focused on web and events. [![MediaUnit](.github/images/mediaunit_logo_black.png)](http://www.mediaunit.no/) + +## Contributors + +### Code Contributors + +This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. + + +### Financial Contributors + +Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/obs-websocket/contribute)] + +#### Individuals + + + +#### Organizations + +Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/obs-websocket/contribute)] + + + + + + + + + + + From 631a008fa0274b1187fa37e116f59fe4f25fc7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 15 Apr 2020 10:36:12 +0200 Subject: [PATCH 032/278] readme: fixes --- README.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 83862294..e1b775a8 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ It is **highly recommended** to protect obs-websocket with a password against un ### Possible use cases - Remote control OBS from a phone or tablet on the same local network -- Change your stream overlay/graphics based on the current scene (like the AGDQ overlay does) +- Change your stream overlay/graphics based on the current scene - Automate scene switching with a third-party program (e.g. : auto-pilot, foot pedal, ...) ### For developers @@ -46,19 +46,14 @@ See the [build instructions](BUILDING.md). ### Branches -The two main development branches are: - -- `4.x-current`: actively-maintained codebase for 4.x releases. Backwards-compatible (unless stated otherwise) with existing clients until 5.0. -- `5.x`: upcoming 5.0 version - -**New features and fixes must be based off and contributed to `4.x-current`**, as obs-websocket 5.0 is not in active development yet. +Development happens on `4.x-current` ### Pull Requests -Pull Requests must never be based off your fork's main branch (in our case, `4.x-current` or `5.x`). Start your work in a new branch +Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). Start your work in a new branch based on the main one (e.g.: `cool-new-feature`, `fix-palakis-mistakes`, ...) and open a Pull Request once you feel ready to show your work. -If your Pull Request is not ready to merge yet, tag it with the `work in progress` label. You can also use the `help needed` label if you have questions, need a hand or want to ask for input. +**If your Pull Request is not ready to merge yet, create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). ### Code style & formatting From 758441d65d47581281ad83bf91eb8af4f5ad193a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 17 Apr 2020 15:00:51 +0200 Subject: [PATCH 033/278] readme: fix CI badges --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e1b775a8..ac081832 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ WebSockets API for OBS Studio. Follow the main author on Twitter for news & updates : [@LePalakis](https://twitter.com/LePalakis) -[![Build Status - Windows](https://ci.appveyor.com/api/projects/status/github/Palakis/obs-websocket)](https://ci.appveyor.com/project/Palakis/obs-websocket/history) [![Build Status - Linux](https://travis-ci.org/Palakis/obs-websocket.svg?branch=master)](https://travis-ci.org/Palakis/obs-websocket) +[![Build Status](https://dev.azure.com/Palakis/obs-websocket/_apis/build/status/Palakis.obs-websocket?branchName=4.x-current)](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current) ## Downloads From 05baf6b8ac58483d3cdc9c84cbdd9ef2ffc60424 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 21 Apr 2020 05:30:21 -0700 Subject: [PATCH 034/278] Add simpleobsws to list of language APIs I've created a simple async Python library for people who just want the simple stuff. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ac081832..a0afb404 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Here's a list of available language APIs for obs-websocket : - C#/VB.NET: [obs-websocket-dotnet](https://github.com/Palakis/obs-websocket-dotnet) - Python 2 and 3: [obs-websocket-py](https://github.com/Elektordi/obs-websocket-py) by Guillaume Genty a.k.a Elektordi - Python 3.5+ with asyncio: [obs-ws-rc](https://github.com/KirillMysnik/obs-ws-rc) by Kirill Mysnik +- Python 3.6+ with asyncio: [simpleobsws](https://github.com/IRLToolkit/simpleobsws) by tt2468 - Java 8+: [obs-websocket-java](https://github.com/Twasi/websocket-obs-java) by TwasiNET - Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf From 76635ff31ba9fb173071af3ca15f95956e1b6809 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 21 Apr 2020 17:29:59 +0200 Subject: [PATCH 035/278] CI: Add Github Actions workflow for PRs and pushes to main branch --- .github/workflows/pr_push.yml | 412 ++++++++++++++++++++++++++++++++++ CI/macos/Brewfile | 10 + 2 files changed, 422 insertions(+) create mode 100644 .github/workflows/pr_push.yml create mode 100644 CI/macos/Brewfile diff --git a/.github/workflows/pr_push.yml b/.github/workflows/pr_push.yml new file mode 100644 index 00000000..62836706 --- /dev/null +++ b/.github/workflows/pr_push.yml @@ -0,0 +1,412 @@ +name: 'CI Multiplatform Build' + +on: + push: + paths-ignore: + - 'docs/**' + branches: + - 4.x-current + pull_request: + paths-ignore: + - '**.md' + branches: + - 4.x-current + +jobs: + windows: + name: 'Windows 32+64bit' + runs-on: [windows-latest] + if: contains(github.event.head_commit.message, '[skip ci]') != true + env: + QT_VERSION: '5.10.1' + WINDOWS_DEPS_VERSION: '2017' + CMAKE_GENERATOR: "Visual Studio 16 2019" + CMAKE_SYSTEM_VERSION: "10.0" + steps: + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.0.0 + - name: 'Checkout' + uses: actions/checkout@v2 + with: + path: ${{ github.workspace }}/obs-websocket + submodules: 'recursive' + - name: 'Checkout OBS' + uses: actions/checkout@v2 + with: + repository: obsproject/obs-studio + path: ${{ github.workspace }}/obs-studio + submodules: 'recursive' + - name: 'Get OBS-Studio git info' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git fetch --prune --unshallow + echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD) + echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git checkout ${{ env.OBS_GIT_TAG }} + git submodule update + - name: 'Get obs-websocket git info' + shell: bash + working-directory: ${{ github.workspace }}/obs-websocket + run: | + git fetch --prune --unshallow + echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }} + echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Install prerequisite: QT' + run: | + curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C - + 7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT" + - name: 'Install prerequisite: Pre-built OBS dependencies' + run: | + curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C - + 7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps" + - name: 'Restore OBS 32-bit build v${{ env.OBS_GIT_TAG }} from cache' + id: build-cache-obs-32 + uses: actions/cache@v1 + env: + CACHE_NAME: 'build-cache-obs-32' + with: + path: ${{ github.workspace }}/obs-studio/build32 + key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }} + restore-keys: | + ${{ runner.os }}-${{ env.CACHE_NAME }}- + - name: 'Configure OBS 32-bit' + if: steps.build-cache-obs-32.outputs.cache-hit != 'true' + working-directory: ${{ github.workspace }}/obs-studio + run: | + mkdir .\build32 + cd .\build32 + cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. + - name: 'Build OBS-Studio 32-bit' + if: steps.build-cache-obs-32.outputs.cache-hit != 'true' + working-directory: ${{ github.workspace }}/obs-studio + run: | + msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj + msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj + - name: 'Restore OBS 64-bit build v${{ env.OBS_GIT_TAG }} from cache' + id: build-cache-obs-64 + uses: actions/cache@v1 + env: + CACHE_NAME: 'build-cache-obs-64' + with: + path: ${{ github.workspace }}/obs-studio/build64 + key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }} + restore-keys: | + ${{ runner.os }}-${{ env.CACHE_NAME }}- + - name: 'Configure OBS 64-bit' + if: steps.build-cache-obs-64.outputs.cache-hit != 'true' + working-directory: ${{ github.workspace }}/obs-studio + run: | + mkdir .\build64 + cd .\build64 + cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. + - name: 'Build OBS-Studio 64-bit' + if: steps.build-cache-obs-64.outputs.cache-hit != 'true' + working-directory: ${{ github.workspace }}/obs-studio + run: | + msbuild /m /p:Configuration=RelWithDebInfo .\build64\libobs\libobs.vcxproj + msbuild /m /p:Configuration=RelWithDebInfo .\build64\UI\obs-frontend-api\obs-frontend-api.vcxproj + - name: 'Configure obs-websocket 64-bit' + working-directory: ${{ github.workspace }}/obs-websocket + run: | + mkdir .\build64 + cd .\build64 + cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build64\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build64\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" .. + - name: 'Configure obs-websocket 32-bit' + working-directory: ${{ github.workspace }}/obs-websocket + run: | + mkdir .\build32 + cd .\build32 + cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build32\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build32\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" .. + - name: 'Build obs-websocket 64-bit' + working-directory: ${{ github.workspace }}/obs-websocket + run: msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-websocket.sln + - name: 'Build obs-websocket 32-bit' + working-directory: ${{ github.workspace }}/obs-websocket + run: msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-websocket.sln + - name: 'Set PR artifact filename' + shell: bash + run: | + FILENAME="obs-websocket-${{ env.GIT_HASH }}-Windows" + echo "::set-env name=FILENAME::$FILENAME" + - name: 'Package obs-websocket' + working-directory: ${{ github.workspace }}/obs-websocket + run: | + mkdir package + cd package + 7z a "${{ env.WIN_FILENAME }}.zip" "..\release\*" + iscc ..\installer\installer.iss /O. /F"${{ env.WIN_FILENAME }}-Installer" + - name: 'Publish ${{ env.WIN_FILENAME }}.zip' + if: success() + uses: actions/upload-artifact@v2-preview + with: + name: '${{ env.GIT_HASH }}-Windows' + path: ${{ github.workspace }}/obs-websocket/package/*.zip + - name: 'Publish ${{ env.WIN_FILENAME }}-Installer.exe' + if: success() + uses: actions/upload-artifact@v2-preview + with: + name: '${{ env.GIT_HASH }}-Windows-Installer' + path: ${{ github.workspace }}/obs-websocket/package/*.exe + ubuntu64: + name: "Linux/Ubuntu 64-bit" + runs-on: [ubuntu-latest] + if: contains(github.event.head_commit.message, '[skip ci]') != true + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + path: ${{ github.workspace }}/obs-websocket + submodules: 'recursive' + - name: 'Checkout OBS' + uses: actions/checkout@v2 + with: + repository: obsproject/obs-studio + path: ${{ github.workspace }}/obs-studio + submodules: 'recursive' + - name: 'Get OBS-Studio git info' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git fetch --prune --unshallow + echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD) + echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git checkout ${{ env.OBS_GIT_TAG }} + git submodule update + - name: 'Get obs-websocket git info' + working-directory: ${{ github.workspace }}/obs-websocket + run: | + git fetch --prune --unshallow + echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }} + echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Install prerequisites (Apt)' + shell: bash + run: | + sudo dpkg --add-architecture amd64 + sudo apt-get -qq update + sudo apt-get install -y \ + build-essential \ + checkinstall \ + cmake \ + libasound2-dev \ + libavcodec-dev \ + libavdevice-dev \ + libavfilter-dev \ + libavformat-dev \ + libavutil-dev \ + libcurl4-openssl-dev \ + libfdk-aac-dev \ + libfontconfig-dev \ + libfreetype6-dev \ + libgl1-mesa-dev \ + libjack-jackd2-dev \ + libjansson-dev \ + libluajit-5.1-dev \ + libpulse-dev \ + libqt5x11extras5-dev \ + libspeexdsp-dev \ + libswresample-dev \ + libswscale-dev \ + libudev-dev \ + libv4l-dev \ + libva-dev \ + libvlc-dev \ + libx11-dev \ + libx264-dev \ + libxcb-randr0-dev \ + libxcb-shm0-dev \ + libxcb-xinerama0-dev \ + libxcomposite-dev \ + libxinerama-dev \ + libmbedtls-dev \ + pkg-config \ + python3-dev \ + qtbase5-dev \ + libqt5svg5-dev \ + swig + - name: 'Configure OBS-Studio' + working-directory: ${{ github.workspace }}/obs-studio + shell: bash + run: | + mkdir ./build + cd ./build + cmake -DBUILD_CAPTIONS=YES -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr .. + - name: 'Build OBS-Studio' + working-directory: ${{ github.workspace }}/obs-studio + shell: bash + run: | + set -e + cd ./build + make -j4 libobs obs-frontend-api + - name: 'Install OBS-Studio' + working-directory: ${{ github.workspace }}/obs-studio + shell: bash + run: | + cd ./build + sudo cp ./libobs/libobs.so /usr/lib + sudo cp ./UI/obs-frontend-api/libobs-frontend-api.so /usr/lib + sudo mkdir -p /usr/include/obs + sudo cp ../UI/obs-frontend-api/obs-frontend-api.h /usr/include/obs/obs-frontend-api.h + - name: 'Configure obs-websocket' + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + mkdir ./build + cd ./build + cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr .. + - name: 'Build obs-websocket' + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + set -e + cd ./build + make -j4 + - name: 'Set PR artifact filename' + shell: bash + run: | + FILENAME="obs-websocket-1-${{ env.GIT_HASH }}-1_amd64.deb" + echo "::set-env name=FILENAME::$FILENAME" + - name: 'Package ${{ env.FILENAME }}' + if: success() + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + VERSION="1-${{ env.GIT_HASH }}-git" + cd ./build + sudo checkinstall -y --type=debian --fstrans=no -nodoc \ + --backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion=$VERSION \ + --pkglicense="GPLv2.0" --maintainer="${{ github.event.pusher.email }}" --pkggroup="video" \ + --pkgsource="${{ github.event.repository.html_url }}" \ + --requires="obs-studio,libqt5core5a,libqt5widgets5,qt5-image-formats-plugins" \ + --pakdir="../package" + sudo chmod ao+r ../package/* + cd - + - name: 'Publish ${{ env.FILENAME }}' + if: success() + uses: actions/upload-artifact@v2-preview + with: + name: '${{ env.GIT_HASH }}-linux' + path: '${{ github.workspace }}/obs-websocket/package/*.deb' + macos64: + name: "macOS 64-bit" + runs-on: [macos-latest] + if: contains(github.event.head_commit.message, '[skip ci]') != true + env: + MACOS_DEPS_VERSION: '2020-04-18' + QT_VERSION: '5.14.1' + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + path: ${{ github.workspace }}/obs-websocket + submodules: 'recursive' + - name: 'Checkout OBS' + uses: actions/checkout@v2 + with: + repository: obsproject/obs-studio + path: ${{ github.workspace }}/obs-studio + submodules: 'recursive' + - name: 'Get OBS-Studio git info' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git fetch --prune --unshallow + echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD) + echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git checkout ${{ env.OBS_GIT_TAG }} + git submodule update + - name: 'Get obs-websocket git info' + working-directory: ${{ github.workspace }}/obs-websocket + run: | + git fetch --prune --unshallow + echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }} + echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Install prerequisites (Homebrew)' + shell: bash + run: | + brew bundle --file ${{ github.workspace }}/obs-websocket/CI/macos/Brewfile + - name: 'Install prerequisite: Pre-built OBS dependencies' + shell: bash + run: | + curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz + tar -xf ${{ github.workspace }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp" + - name: 'Configure OBS Studio' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + mkdir build + cd build + cmake -DBUILD_CAPTIONS=YES -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DENABLE_SCRIPTING=NO -DDepsPath=/tmp/obsdeps -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/${{ env.QT_VERSION }}/lib/cmake .. + - name: 'Build OBS Studio libraries' + working-directory: ${{ github.workspace }}/obs-studio + shell: bash + run: | + set -e + cd ./build + make -j4 libobs obs-frontend-api + - name: 'Configure obs-websocket' + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + mkdir build + cd build + cmake -DQTDIR=/usr/local/Cellar/qt/${{ env.QT_VERSION }} -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DLIBOBS_LIB=${{ github.workspace }}/obs-studio/libobs -DOBS_FRONTEND_LIB="${{ github.workspace }}/obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr .. + - name: 'Build obs-websocket' + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + set -e + cd ./build + make -j4 + - name: 'Install prerequisite: Packages app' + if: success() + shell: bash + run: | + curl -L -O https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg + sudo installer -pkg ${{ github.workspace }}/Packages.pkg -target / + - name: 'Set PR artifact filename' + shell: bash + run: | + FILENAME_UNSIGNED="obs-websocket-${{ env.GIT_HASH }}-macOS-Unsigned.pkg" + echo "::set-env name=FILENAME_UNSIGNED::$FILENAME_UNSIGNED" + - name: 'Fix linked dynamic library paths' + if: success() + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./build/obs-websocket.so + install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./build/obs-websocket.so + install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./build/obs-websocket.so + echo "Dependencies for obs-websocket" + otool -L ./build/obs-websocket.so + - name: 'Package ${{ env.FILENAME }}' + if: success() + working-directory: ./obs-websocket + shell: bash + run: | + packagesbuild ./CI/macos/obs-websocket.pkgproj + mv ./release/obs-websocket.pkg ./release/${{ env.FILENAME_UNSIGNED }} + - name: 'Publish ${{ env.FILENAME_UNSIGNED }} artifact' + if: success() + uses: actions/upload-artifact@v2-preview + with: + name: '${{ env.GIT_HASH }}-macOS' + path: ${{ github.workspace }}/obs-websocket/release/*.pkg diff --git a/CI/macos/Brewfile b/CI/macos/Brewfile new file mode 100644 index 00000000..20fabb71 --- /dev/null +++ b/CI/macos/Brewfile @@ -0,0 +1,10 @@ +tap "akeru-inc/tap" +brew "jack" +brew "speexdsp" +brew "cmake" +brew "freetype" +brew "fdk-aac" +brew "https://gist.githubusercontent.com/DDRBoxman/9c7a2b08933166f4b61ed9a44b242609/raw/ef4de6c587c6bd7f50210eccd5bd51ff08e6de13/qt.rb" +brew "swig", link: false +brew "https://gist.githubusercontent.com/DDRBoxman/4cada55c51803a2f963fa40ce55c9d3e/raw/572c67e908bfbc1bcb8c476ea77ea3935133f5b5/swig.rb" +brew "akeru-inc/tap/xcnotary" \ No newline at end of file From a479f529af1e5bf4214d39cec36fad6e6f101a14 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 21 Apr 2020 17:42:07 +0200 Subject: [PATCH 036/278] CI: Add Github Actions workflow for release tags --- .github/workflows/tag_release.yml | 484 ++++++++++++++++++++++++++++++ CI/macos/Brewfile | 10 + 2 files changed, 494 insertions(+) create mode 100644 .github/workflows/tag_release.yml create mode 100644 CI/macos/Brewfile diff --git a/.github/workflows/tag_release.yml b/.github/workflows/tag_release.yml new file mode 100644 index 00000000..698ce49a --- /dev/null +++ b/.github/workflows/tag_release.yml @@ -0,0 +1,484 @@ +name: 'CI Multiplatform Release' + +on: + push: + paths-ignore: + - 'docs/**' + tags: + - '[45].[0-9]+.[0-9]+' + +jobs: + windows: + name: 'Windows 32+64bit' + runs-on: [windows-latest] + env: + QT_VERSION: '5.10.1' + WINDOWS_DEPS_VERSION: '2017' + CMAKE_GENERATOR: "Visual Studio 16 2019" + CMAKE_SYSTEM_VERSION: "10.0" + steps: + - name: 'Add msbuild to PATH' + uses: microsoft/setup-msbuild@v1.0.0 + - name: 'Checkout' + uses: actions/checkout@v2 + with: + path: ${{ github.workspace }}/obs-websocket + submodules: 'recursive' + - name: 'Checkout OBS' + uses: actions/checkout@v2 + with: + repository: obsproject/obs-studio + path: ${{ github.workspace }}/obs-studio + submodules: 'recursive' + - name: 'Get OBS-Studio git info' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git fetch --prune --unshallow + echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD) + echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git checkout ${{ env.OBS_GIT_TAG }} + git submodule update + - name: 'Get obs-websocket git info' + shell: bash + working-directory: ${{ github.workspace }}/obs-websocket + run: | + git fetch --prune --unshallow + echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }} + echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Install prerequisite: QT' + run: | + curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C - + 7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT" + - name: 'Install prerequisite: Pre-built OBS dependencies' + run: | + curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C - + 7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps" + - name: 'Restore OBS 32-bit build v${{ env.OBS_GIT_TAG }} from cache' + id: build-cache-obs-32 + uses: actions/cache@v1 + env: + CACHE_NAME: 'build-cache-obs-32' + with: + path: ${{ github.workspace }}/obs-studio/build32 + key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }} + restore-keys: | + ${{ runner.os }}-${{ env.CACHE_NAME }}- + - name: 'Configure OBS 32-bit' + if: steps.build-cache-obs-32.outputs.cache-hit != 'true' + working-directory: ${{ github.workspace }}/obs-studio + run: | + mkdir .\build32 + cd .\build32 + cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. + - name: 'Build OBS-Studio 32-bit' + if: steps.build-cache-obs-32.outputs.cache-hit != 'true' + working-directory: ${{ github.workspace }}/obs-studio + run: | + msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj + msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj + - name: 'Restore OBS 64-bit build v${{ env.OBS_GIT_TAG }} from cache' + id: build-cache-obs-64 + uses: actions/cache@v1 + env: + CACHE_NAME: 'build-cache-obs-64' + with: + path: ${{ github.workspace }}/obs-studio/build64 + key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }} + restore-keys: | + ${{ runner.os }}-${{ env.CACHE_NAME }}- + - name: 'Configure OBS 64-bit' + if: steps.build-cache-obs-64.outputs.cache-hit != 'true' + working-directory: ${{ github.workspace }}/obs-studio + run: | + mkdir .\build64 + cd .\build64 + cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. + - name: 'Build OBS-Studio 64-bit' + if: steps.build-cache-obs-64.outputs.cache-hit != 'true' + working-directory: ${{ github.workspace }}/obs-studio + run: | + msbuild /m /p:Configuration=RelWithDebInfo .\build64\libobs\libobs.vcxproj + msbuild /m /p:Configuration=RelWithDebInfo .\build64\UI\obs-frontend-api\obs-frontend-api.vcxproj + - name: 'Configure obs-websocket 64-bit' + working-directory: ${{ github.workspace }}/obs-websocket + run: | + mkdir .\build64 + cd .\build64 + cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build64\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build64\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" .. + - name: 'Configure obs-websocket 32-bit' + working-directory: ${{ github.workspace }}/obs-websocket + run: | + mkdir .\build32 + cd .\build32 + cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build32\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build32\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" .. + - name: 'Build obs-websocket 64-bit' + working-directory: ${{ github.workspace }}/obs-websocket + run: msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-websocket.sln + - name: 'Build obs-websocket 32-bit' + working-directory: ${{ github.workspace }}/obs-websocket + run: msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-websocket.sln + - name: 'Set release filename' + shell: bash + run: | + FILENAME="obs-websocket-${{ env.GIT_TAG }}-Windows" + echo "::set-env name=WIN_FILENAME::$FILENAME" + - name: 'Package obs-websocket' + working-directory: ${{ github.workspace }}/obs-websocket + run: | + mkdir package + cd package + 7z a "${{ env.WIN_FILENAME }}.zip" "..\release\*" + iscc ..\installer\installer.iss /O. /F"${{ env.WIN_FILENAME }}-Installer" + - name: 'Publish ${{ env.WIN_FILENAME }}.zip' + if: success() + uses: actions/upload-artifact@v2-preview + with: + name: '${{ env.GIT_TAG }}-Windows' + path: ${{ github.workspace }}/obs-websocket/package/*.zip + - name: 'Publish ${{ env.WIN_FILENAME }}-Installer.exe' + if: success() + uses: actions/upload-artifact@v2-preview + with: + name: '${{ env.GIT_TAG }}-Windows-Installer' + path: ${{ github.workspace }}/obs-websocket/package/*.exe + ubuntu64: + name: "Linux/Ubuntu 64-bit" + runs-on: [ubuntu-latest] + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + path: ${{ github.workspace }}/obs-websocket + submodules: 'recursive' + - name: 'Checkout OBS' + uses: actions/checkout@v2 + with: + repository: obsproject/obs-studio + path: ${{ github.workspace }}/obs-studio + submodules: 'recursive' + - name: 'Get OBS-Studio git info' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git fetch --prune --unshallow + echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD) + echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git checkout ${{ env.OBS_GIT_TAG }} + git submodule update + - name: 'Get obs-websocket git info' + working-directory: ${{ github.workspace }}/obs-websocket + run: | + git fetch --prune --unshallow + echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }} + echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Install prerequisites (Apt)' + shell: bash + run: | + sudo dpkg --add-architecture amd64 + sudo apt-get -qq update + sudo apt-get install -y \ + build-essential \ + checkinstall \ + cmake \ + libasound2-dev \ + libavcodec-dev \ + libavdevice-dev \ + libavfilter-dev \ + libavformat-dev \ + libavutil-dev \ + libcurl4-openssl-dev \ + libfdk-aac-dev \ + libfontconfig-dev \ + libfreetype6-dev \ + libgl1-mesa-dev \ + libjack-jackd2-dev \ + libjansson-dev \ + libluajit-5.1-dev \ + libpulse-dev \ + libqt5x11extras5-dev \ + libspeexdsp-dev \ + libswresample-dev \ + libswscale-dev \ + libudev-dev \ + libv4l-dev \ + libva-dev \ + libvlc-dev \ + libx11-dev \ + libx264-dev \ + libxcb-randr0-dev \ + libxcb-shm0-dev \ + libxcb-xinerama0-dev \ + libxcomposite-dev \ + libxinerama-dev \ + libmbedtls-dev \ + pkg-config \ + python3-dev \ + qtbase5-dev \ + libqt5svg5-dev \ + swig + - name: 'Configure OBS-Studio' + working-directory: ${{ github.workspace }}/obs-studio + shell: bash + run: | + mkdir ./build + cd ./build + cmake -DBUILD_CAPTIONS=YES -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr .. + - name: 'Build OBS-Studio' + working-directory: ${{ github.workspace }}/obs-studio + shell: bash + run: | + set -e + cd ./build + make -j4 libobs obs-frontend-api + - name: 'Install OBS-Studio' + working-directory: ${{ github.workspace }}/obs-studio + shell: bash + run: | + cd ./build + sudo cp ./libobs/libobs.so /usr/lib + sudo cp ./UI/obs-frontend-api/libobs-frontend-api.so /usr/lib + sudo mkdir -p /usr/include/obs + sudo cp ../UI/obs-frontend-api/obs-frontend-api.h /usr/include/obs/obs-frontend-api.h + - name: 'Configure obs-websocket' + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + mkdir ./build + cd ./build + cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr .. + - name: 'Build obs-websocket' + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + set -e + cd ./build + make -j4 + - name: 'Set release filename' + shell: bash + run: | + FILENAME="obs-websocket-${{ env.GIT_TAG }}-1_amd64.deb" + echo "::set-env name=LINUX_FILENAME::$FILENAME" + - name: 'Package ${{ env.LINUX_FILENAME }}' + if: success() + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + VERSION="${{ env.GIT_TAG }}" + cd ./build + sudo checkinstall -y --type=debian --fstrans=no -nodoc \ + --backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion=$VERSION \ + --pkglicense="GPLv2.0" --maintainer="${{ github.event.pusher.email }}" --pkggroup="video" \ + --pkgsource="${{ github.event.repository.html_url }}" \ + --requires="obs-studio,libqt5core5a,libqt5widgets5,qt5-image-formats-plugins" \ + --pakdir="../package" + sudo chmod ao+r ../package/* + cd - + - name: 'Publish ${{ env.LINUX_FILENAME }}' + if: success() + uses: actions/upload-artifact@v2-preview + with: + name: '${{ env.GIT_TAG }}-linux' + path: '${{ github.workspace }}/obs-websocket/package/*.deb' + macos64: + name: "macOS 64-bit" + runs-on: [macos-latest] + env: + MACOS_DEPS_VERSION: '2020-04-18' + QT_VERSION: '5.14.1' + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + path: ${{ github.workspace }}/obs-websocket + submodules: 'recursive' + - name: 'Checkout OBS' + uses: actions/checkout@v2 + with: + repository: obsproject/obs-studio + path: ${{ github.workspace }}/obs-studio + submodules: 'recursive' + - name: 'Get OBS-Studio git info' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git fetch --prune --unshallow + echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD) + echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + git checkout ${{ env.OBS_GIT_TAG }} + git submodule update + - name: 'Get obs-websocket git info' + working-directory: ${{ github.workspace }}/obs-websocket + run: | + git fetch --prune --unshallow + echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }} + echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD) + echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0) + - name: 'Install prerequisites (Homebrew)' + shell: bash + run: | + brew bundle --file ${{ github.workspace }}/obs-websocket/CI/macos/Brewfile + - name: 'Install prerequisite: Pre-built OBS dependencies' + shell: bash + run: | + curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz + tar -xf ${{ github.workspace }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp" + - name: 'Configure OBS Studio' + shell: bash + working-directory: ${{ github.workspace }}/obs-studio + run: | + mkdir build + cd build + cmake -DBUILD_CAPTIONS=YES -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DENABLE_SCRIPTING=NO -DDepsPath=/tmp/obsdeps -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/${{ env.QT_VERSION }}/lib/cmake .. + - name: 'Build OBS Studio libraries' + working-directory: ${{ github.workspace }}/obs-studio + shell: bash + run: | + set -e + cd ./build + make -j4 libobs obs-frontend-api + - name: 'Configure obs-websocket' + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + mkdir build + cd build + cmake -DQTDIR=/usr/local/Cellar/qt/${{ env.QT_VERSION }} -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DLIBOBS_LIB=${{ github.workspace }}/obs-studio/libobs -DOBS_FRONTEND_LIB="${{ github.workspace }}/obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr .. + - name: 'Build obs-websocket' + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + set -e + cd ./build + make -j4 + - name: 'Install prerequisite: Packages app' + if: success() + shell: bash + run: | + curl -L -O https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg + sudo installer -pkg ${{ github.workspace }}/Packages.pkg -target / + - name: 'Set release filename' + if: success() && startsWith(github.ref, 'refs/tags') + shell: bash + run: | + FILENAME_UNSIGNED="obs-websocket-${{ env.GIT_TAG }}-macOS-Unsigned.pkg" + FILENAME="obs-websocket-${{ env.GIT_TAG }}-macOS.pkg" + echo "::set-env name=MAC_FILENAME_UNSIGNED::$FILENAME_UNSIGNED" + echo "::set-env name=MAC_FILENAME::$FILENAME" + - name: 'Fix linked dynamic library paths' + if: success() + working-directory: ${{ github.workspace }}/obs-websocket + shell: bash + run: | + install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./build/obs-websocket.so + install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./build/obs-websocket.so + install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./build/obs-websocket.so + echo "Dependencies for obs-websocket" + otool -L ./build/obs-websocket.so + - name: 'Install Apple Developer Certificate' + if: success() + uses: apple-actions/import-codesign-certs@253ddeeac23f2bdad1646faac5c8c2832e800071 + with: + p12-file-base64: ${{ secrets.MACOS_CERT_CODESIGN }} + p12-password: ${{ secrets.MACOS_CERT_PASS }} + - name: 'Code signing' + if: success() + working-directory: ./obs-websocket + shell: bash + run: | + set -e + codesign --sign "${{ secrets.MACOS_IDENT_CODESIGN }}" ./build/obs-websocket.so + packagesbuild ./CI/macos/obs-websocket.pkgproj + mv ./release/obs-websocket.pkg ./release/${{ env.MAC_FILENAME_UNSIGNED }} + productsign --sign "${{ secrets.MACOS_IDENT_INSTALLER }}" ./release/${{ env.MAC_FILENAME_UNSIGNED }} ./release/${{ env.MAC_FILENAME }} + rm ./release/${{ env.MAC_FILENAME_UNSIGNED }} + - name: 'Notarization' + if: success() + working-directory: ./obs-websocket + shell: bash + run: | + set -e + xcrun altool --store-password-in-keychain-item "AC_PASSWORD" -u "${{ secrets.MACOS_IDENT_USER }}" -p "${{ secrets.MACOS_IDENT_PASS }}" + xcnotary precheck ./release/${{ env.MAC_FILENAME }} + if [ "$?" -eq 0 ]; then xcnotary notarize ./release/${{ env.MAC_FILENAME }} --developer-account "${{ secrets.MACOS_IDENT_USER }}" --developer-password-keychain-item "AC_PASSWORD" --provider "${{ secrets.MACOS_IDENT_PROVIDER }}"; fi + - name: 'Publish ${{ env.MAC_FILENAME }} artifact' + if: success() && startsWith(github.ref, 'refs/tags') + uses: actions/upload-artifact@v2-preview + with: + name: '${{ env.GIT_TAG }}-macOS' + path: ${{ github.workspace }}/obs-websocket/release/*.pkg + make-release: + name: 'Create and upload release' + runs-on: [ubuntu-latest] + needs: [windows, ubuntu64, macos64] + steps: + - name: 'Get the version' + shell: bash + id: get_version + run: | + echo ::set-env name=TAG_VERSION::${GITHUB_REF/refs\/tags\//} + - name: 'Create Release' + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.TAG_VERSION }} + release_name: obs-websocket ${{ env.TAG_VERSION }} + draft: false + prerelease: false + - name: 'Download release artifacts' + uses: actions/download-artifact@v2-preview + - name: 'Upload Windows .zip artifact to release' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-Windows/obs-websocket-${{ env.TAG_VERSION }}-Windows.zip + asset_name: obs-websocket-${{ env.TAG_VERSION }}-Windows.zip + asset_content_type: application/zip + - name: 'Upload Windows .exe artifact to release' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-Windows-Installer/obs-websocket-${{ env.TAG_VERSION }}-Windows-Installer.exe + asset_name: obs-websocket-${{ env.TAG_VERSION }}-Windows-Installer.exe + asset_content_type: application/zip + - name: 'Upload Linux artifact to release' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-linux/obs-websocket_${{ env.TAG_VERSION }}-1_amd64.deb + asset_name: obs-websocket-${{ env.TAG_VERSION }}-1_amd64.deb + asset_content_type: application/octet-stream + - name: 'Upload macOS artifact to release' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-macOS/obs-websocket-${{ env.TAG_VERSION }}-macOS.pkg + asset_name: obs-websocket-${{ env.TAG_VERSION }}-macOS.pkg + asset_content_type: application/octet-stream diff --git a/CI/macos/Brewfile b/CI/macos/Brewfile new file mode 100644 index 00000000..20fabb71 --- /dev/null +++ b/CI/macos/Brewfile @@ -0,0 +1,10 @@ +tap "akeru-inc/tap" +brew "jack" +brew "speexdsp" +brew "cmake" +brew "freetype" +brew "fdk-aac" +brew "https://gist.githubusercontent.com/DDRBoxman/9c7a2b08933166f4b61ed9a44b242609/raw/ef4de6c587c6bd7f50210eccd5bd51ff08e6de13/qt.rb" +brew "swig", link: false +brew "https://gist.githubusercontent.com/DDRBoxman/4cada55c51803a2f963fa40ce55c9d3e/raw/572c67e908bfbc1bcb8c476ea77ea3935133f5b5/swig.rb" +brew "akeru-inc/tap/xcnotary" \ No newline at end of file From 19f9593ac19bdae5f4bed624c8c097be2946a617 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 21 Apr 2020 19:55:43 -0700 Subject: [PATCH 037/278] Supress websocketpp logging entries --- src/WSServer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WSServer.cpp b/src/WSServer.cpp index c9f09362..c3b164e7 100644 --- a/src/WSServer.cpp +++ b/src/WSServer.cpp @@ -44,6 +44,7 @@ WSServer::WSServer() _connections(), _clMutex(QMutex::Recursive) { + _server.get_alog().clear_channels(websocketpp::log::alevel::frame_header | websocketpp::log::alevel::frame_payload | websocketpp::log::alevel::control); _server.init_asio(); #ifndef _WIN32 _server.set_reuse_addr(true); From f61bc809ddd38b530954a94eaefb63d423ceb6d6 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Mon, 27 Apr 2020 08:30:39 -0700 Subject: [PATCH 038/278] [Bug Fix] Fix FreeType2 source type recognition OBS appears to be using a new format for the FreeType2 source, so we add the new kinds. One thing I'm unsure on is if anything still uses the old naming stuff `text_ft2`/`text_ft2_v2`, since if not then we should not check --- src/WSRequestHandler_Sources.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 579f76b4..8dfdef98 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -15,7 +15,7 @@ bool isTextGDIPlusSource(const QString& sourceKind) bool isTextFreeType2Source(const QString& sourceKind) { - return (sourceKind == "text_ft2" || sourceKind == "text_ft2_v2"); + return (sourceKind == "text_ft2" || sourceKind == "text_ft2_v2" || sourceKind == "text_ft2_source" || sourceKind == "text_ft2_source_v2"); } /** From 2719da3685907bc71bbe7d693c491711da87ff96 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Mon, 27 Apr 2020 10:55:16 -0700 Subject: [PATCH 039/278] [Bug Fix] Don't build with captions on MacOS According to [this](https://github.com/obsproject/obs-studio/blob/master/UI/frontend-plugins/frontend-tools/CMakeLists.txt#L73), captions support is not active on MacOS, so we should not build it here. --- CI/install-build-obs-macos.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/CI/install-build-obs-macos.sh b/CI/install-build-obs-macos.sh index 0dc610aa..4af63b49 100755 --- a/CI/install-build-obs-macos.sh +++ b/CI/install-build-obs-macos.sh @@ -34,7 +34,6 @@ git checkout $OBSLatestTag mkdir build && cd build echo "[obs-websocket] Building obs-studio.." cmake .. \ - -DBUILD_CAPTIONS=true \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \ -DDISABLE_PLUGINS=true \ -DENABLE_SCRIPTING=0 \ From 4fbc45b40bdd36ea4b07b3b648d8b8151d0b15fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 28 Apr 2020 09:34:41 +0200 Subject: [PATCH 040/278] ci(linux): make the deb package require OBS 25.0.7 --- CI/package-ubuntu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/package-ubuntu.sh b/CI/package-ubuntu.sh index 367e002c..08323bc0 100755 --- a/CI/package-ubuntu.sh +++ b/CI/package-ubuntu.sh @@ -17,7 +17,7 @@ PAGER="cat" sudo checkinstall -y --type=debian --fstrans=no --nodoc \ --pkglicense="GPLv2.0" --maintainer="stephane.lepin@gmail.com" \ --pkggroup="video" \ --pkgsource="https://github.com/Palakis/obs-websocket" \ - --requires="obs-studio,libqt5core5a,libqt5widgets5,qt5-image-formats-plugins" \ + --requires="obs-studio>=25.0.7,libqt5core5a,libqt5widgets5,qt5-image-formats-plugins" \ --pakdir="../package" sudo chmod ao+r ../package/* From 5843521cf145dc39f43ad36c0306c6aaa0ff4bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 28 Apr 2020 09:46:45 +0200 Subject: [PATCH 041/278] cmake(linux): use the standard LIBDIR and DATAROOTDIR install locations --- CMakeLists.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3273367c..8a6c3871 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,13 +187,10 @@ if(UNIX AND NOT APPLE) execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE UNAME_MACHINE) install(TARGETS obs-websocket - LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/obs-plugins") - # Dirty fix for Ubuntu - install(TARGETS obs-websocket - LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/${UNAME_MACHINE}-linux-gnu/obs-plugins") + LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins") install(FILES ${locale_files} - DESTINATION "${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/obs-websocket/locale") + DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/obs/obs-plugins/obs-websocket/locale") endif() # --- End of section --- From b2d39ab2d7264ce6936db385212f4ee00fb23553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 28 Apr 2020 09:52:11 +0200 Subject: [PATCH 042/278] cmake(linux): include GNUInstallDirs --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a6c3871..5648a963 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,11 +180,12 @@ endif() # --- Linux-specific build settings and tasks --- if(UNIX AND NOT APPLE) + include(GNUInstallDirs) + set_target_properties(obs-websocket PROPERTIES PREFIX "") target_link_libraries(obs-websocket obs-frontend-api) file(GLOB locale_files data/locale/*.ini) - execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE UNAME_MACHINE) install(TARGETS obs-websocket LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins") From 645cbf988893a5336a1709e938b19bfc44734e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 28 Apr 2020 10:18:54 +0200 Subject: [PATCH 043/278] ci(linux): fix debian package requires --- CI/package-ubuntu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/package-ubuntu.sh b/CI/package-ubuntu.sh index 08323bc0..5f6998d6 100755 --- a/CI/package-ubuntu.sh +++ b/CI/package-ubuntu.sh @@ -17,7 +17,7 @@ PAGER="cat" sudo checkinstall -y --type=debian --fstrans=no --nodoc \ --pkglicense="GPLv2.0" --maintainer="stephane.lepin@gmail.com" \ --pkggroup="video" \ --pkgsource="https://github.com/Palakis/obs-websocket" \ - --requires="obs-studio>=25.0.7,libqt5core5a,libqt5widgets5,qt5-image-formats-plugins" \ + --requires="obs-studio \(\>= 25.0.7\), libqt5core5a, libqt5widgets5, qt5-image-formats-plugins" \ --pakdir="../package" sudo chmod ao+r ../package/* From a7d02a79a96d26e29b60a918d6c649b3fb6de82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 28 Apr 2020 10:29:48 +0200 Subject: [PATCH 044/278] ci(linux): set package summary --- CI/package-ubuntu.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/CI/package-ubuntu.sh b/CI/package-ubuntu.sh index 5f6998d6..0f7f3c6d 100755 --- a/CI/package-ubuntu.sh +++ b/CI/package-ubuntu.sh @@ -14,6 +14,7 @@ cd ./build PAGER="cat" sudo checkinstall -y --type=debian --fstrans=no --nodoc \ --backup=no --deldoc=yes --install=no \ --pkgname=obs-websocket --pkgversion="$PKG_VERSION" \ + --summary="WebSockets Remote Control API for OBS Studio" \ --pkglicense="GPLv2.0" --maintainer="stephane.lepin@gmail.com" \ --pkggroup="video" \ --pkgsource="https://github.com/Palakis/obs-websocket" \ From 1ce0fd643c0070785810e7ceaf9017788d2f77aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 28 Apr 2020 10:29:48 +0200 Subject: [PATCH 045/278] Revert "ci(linux): set package summary" This reverts commit a7d02a79a96d26e29b60a918d6c649b3fb6de82f. --- CI/package-ubuntu.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/CI/package-ubuntu.sh b/CI/package-ubuntu.sh index 0f7f3c6d..5f6998d6 100755 --- a/CI/package-ubuntu.sh +++ b/CI/package-ubuntu.sh @@ -14,7 +14,6 @@ cd ./build PAGER="cat" sudo checkinstall -y --type=debian --fstrans=no --nodoc \ --backup=no --deldoc=yes --install=no \ --pkgname=obs-websocket --pkgversion="$PKG_VERSION" \ - --summary="WebSockets Remote Control API for OBS Studio" \ --pkglicense="GPLv2.0" --maintainer="stephane.lepin@gmail.com" \ --pkggroup="video" \ --pkgsource="https://github.com/Palakis/obs-websocket" \ From 47492c3fa267ed5686b46779489921bea19e127f Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 30 Apr 2020 09:46:22 -0700 Subject: [PATCH 046/278] Implement fix for Ubuntu users #478 Broke building on linux. This implements a new variable to apply the fix. --- CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5648a963..a6bbd38f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,8 +187,13 @@ if(UNIX AND NOT APPLE) file(GLOB locale_files data/locale/*.ini) - install(TARGETS obs-websocket - LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins") + if(${USE_UBUNTU_FIX}) + install(TARGETS obs-websocket + LIBRARY DESTINATION "/usr/lib/obs-plugins") + else() + install(TARGETS obs-websocket + LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins") + endif() install(FILES ${locale_files} DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/obs/obs-plugins/obs-websocket/locale") From ba75c45cee1d7d6fcf0a7c251fe78e618b0569be Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 30 Apr 2020 09:58:10 -0700 Subject: [PATCH 047/278] Update build docs to add Ubuntu fix flag --- BUILDING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILDING.md b/BUILDING.md index 83f93a99..0770fd59 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -24,7 +24,7 @@ sudo apt-get install libboost-all-dev git clone --recursive https://github.com/Palakis/obs-websocket.git cd obs-websocket mkdir build && cd build -cmake -DLIBOBS_INCLUDE_DIR="" -DCMAKE_INSTALL_PREFIX=/usr .. +cmake -DLIBOBS_INCLUDE_DIR="" -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=true .. make -j4 sudo make install ``` From 0f434004a8fe9faf66a15ba240fba5dcf08873d6 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 30 Apr 2020 10:08:56 -0700 Subject: [PATCH 048/278] Add websocketpp error message to error dialog box --- data/locale/en-US.ini | 4 ++-- src/WSServer.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 8725e18d..607ab2f2 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -10,7 +10,7 @@ OBSWebsocket.NotifyConnect.Message="Client %1 connected" OBSWebsocket.NotifyDisconnect.Title="WebSocket client disconnected" OBSWebsocket.NotifyDisconnect.Message="Client %1 disconnected" OBSWebsocket.Server.StartFailed.Title="WebSockets Server failure" -OBSWebsocket.Server.StartFailed.Message="The WebSockets server failed to start, maybe because:\n - TCP port %1 may currently be in use elsewhere on this system, possibly by another application. Try setting a different TCP port in the WebSocket server settings, or stop any application that could be using this port.\n - An unknown network error happened on your system. Try again by changing settings, restarting OBS or restarting your system." +OBSWebsocket.Server.StartFailed.Message="The WebSockets server failed to start, maybe because:\n - TCP port %1 may currently be in use elsewhere on this system, possibly by another application. Try setting a different TCP port in the WebSocket server settings, or stop any application that could be using this port.\n - Error message: %2" OBSWebsocket.ProfileChanged.Started="WebSockets server enabled in this profile. Server started." OBSWebsocket.ProfileChanged.Stopped="WebSockets server disabled in this profile. Server stopped." -OBSWebsocket.ProfileChanged.Restarted="WebSockets server port changed in this profile. Server restarted." \ No newline at end of file +OBSWebsocket.ProfileChanged.Restarted="WebSockets server port changed in this profile. Server restarted." diff --git a/src/WSServer.cpp b/src/WSServer.cpp index c9f09362..effb935d 100644 --- a/src/WSServer.cpp +++ b/src/WSServer.cpp @@ -83,7 +83,7 @@ void WSServer::start(quint16 port) obs_frontend_push_ui_translation(obs_module_get_string); QString errorTitle = tr("OBSWebsocket.Server.StartFailed.Title"); - QString errorMessage = tr("OBSWebsocket.Server.StartFailed.Message").arg(_serverPort); + QString errorMessage = tr("OBSWebsocket.Server.StartFailed.Message").arg(_serverPort).arg(errorCodeMessage.c_str()); obs_frontend_pop_ui_translation(); QMainWindow* mainWindow = reinterpret_cast(obs_frontend_get_main_window()); From 97836cc5eb402921ccc7f110a7c9abfb10376d91 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 30 Apr 2020 10:58:10 -0700 Subject: [PATCH 049/278] Remove extra source type strings --- src/WSRequestHandler_Sources.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 8dfdef98..ae926ef6 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -15,7 +15,7 @@ bool isTextGDIPlusSource(const QString& sourceKind) bool isTextFreeType2Source(const QString& sourceKind) { - return (sourceKind == "text_ft2" || sourceKind == "text_ft2_v2" || sourceKind == "text_ft2_source" || sourceKind == "text_ft2_source_v2"); + return (sourceKind == "text_ft2_source" || sourceKind == "text_ft2_source_v2"); } /** From b2aa54f3f8ac145bea11bbadd42dc1c00d994543 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 30 Apr 2020 16:25:31 -0700 Subject: [PATCH 050/278] Add obs-websocket-http obs-websocket-http is a simple python script which allows users to run obs-websocket requests using an http-based api system. It is very simple and is a useful converter. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a0afb404..1c210f96 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ It is **highly recommended** to protect obs-websocket with a password against un ### For developers The server is a typical Websockets server running by default on port 4444 (the port number can be changed in the Settings dialog). -The protocol understood by the server is documented in [PROTOCOL.md](docs/generated/protocol.md). +The protocol understood by the server is documented in [PROTOCOL.md](docs/generated/protocol.md). Here's a list of available language APIs for obs-websocket : - Javascript (browser & nodejs): [obs-websocket-js](https://github.com/haganbmj/obs-websocket-js) by Brendan Hagan @@ -36,6 +36,7 @@ Here's a list of available language APIs for obs-websocket : - Python 3.6+ with asyncio: [simpleobsws](https://github.com/IRLToolkit/simpleobsws) by tt2468 - Java 8+: [obs-websocket-java](https://github.com/Twasi/websocket-obs-java) by TwasiNET - Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf +- HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468 I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop me an email at `stephane /dot/ lepin /at/ gmail /dot/ com` ! From 4ded810ba97c0d721590148e0c55cd5e5de18951 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 30 Apr 2020 20:09:13 -0700 Subject: [PATCH 051/278] Add optional `fileFormat` and `fileCompressionRatio` parameters to `TakeSourceScreenshot` --- src/WSRequestHandler_Sources.cpp | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 579f76b4..29ee2882 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1437,6 +1437,8 @@ RpcResponse WSRequestHandler::SetSourceFilterVisibility(const RpcRequest& reques * @param {String} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name. * @param {String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module) * @param {String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path. +* @param {String (optional)} `fileFormat` Format to save the image file as. If not specified, tries to guess based on file extension. +* @param {int (optional)} `fileCompressionQuality` Compression ratio between -1 and 100 to save the file with. -1 is automatic, 1 is smallest file/most compression, 100 is largest file/least compression. Varies with image type. * @param {int (optional)} `width` Screenshot width. Defaults to the source's base width. * @param {int (optional)} `height` Screenshot height. Defaults to the source's base height. * @@ -1544,7 +1546,7 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { QByteArrayList supportedFormats = QImageWriter::supportedImageFormats(); if (!supportedFormats.contains(pictureFormat)) { - QString errorMessage = QString("Unsupported picture format: %1").arg(pictureFormat); + QString errorMessage = QString("unsupported picture format: %1").arg(pictureFormat); return request.failed(errorMessage.toUtf8()); } @@ -1552,7 +1554,7 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { QBuffer buffer(&encodedImgBytes); buffer.open(QBuffer::WriteOnly); if (!sourceImage.save(&buffer, pictureFormat)) { - return request.failed("Embed image encoding failed"); + return request.failed("embed image encoding failed"); } buffer.close(); @@ -1569,7 +1571,31 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { QFileInfo filePathInfo(filePathStr); QString absoluteFilePath = filePathInfo.absoluteFilePath(); - if (!sourceImage.save(absoluteFilePath)) { + const char* fileFormat; + if (request.hasField("fileFormat")) { + fileFormat = obs_data_get_string(request.parameters(), "fileFormat"); + QByteArrayList supportedFormats = QImageWriter::supportedImageFormats(); + + if (!supportedFormats.contains(fileFormat)) { + QString errorMessage = QString("unsupported file format: %1").arg(fileFormat); + return request.failed(errorMessage.toUtf8()); + } + } + else { + fileFormat = nullptr; + } + + int fileCompressionQuality {-1}; + if (request.hasField("fileCompressionQuality")) { + fileCompressionQuality = obs_data_get_int(request.parameters(), "fileCompressionQuality"); + + if (fileCompressionQuality < -1 || fileCompressionQuality > 100) { + QString errorMessage = QString("compression quality out of range: %1").arg(fileCompressionQuality); + return request.failed(errorMessage.toUtf8()); + } + } + + if (!sourceImage.save(absoluteFilePath, fileFormat, fileCompressionQuality)) { return request.failed("Image save failed"); } obs_data_set_string(response, "imageFile", absoluteFilePath.toUtf8()); From 333ffa0e895fb16300b84b9b0c4acf7125534334 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 30 Apr 2020 20:17:11 -0700 Subject: [PATCH 052/278] `compressionQuality` should be for embed and file saving --- src/WSRequestHandler_Sources.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 29ee2882..06f33380 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1438,7 +1438,7 @@ RpcResponse WSRequestHandler::SetSourceFilterVisibility(const RpcRequest& reques * @param {String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module) * @param {String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path. * @param {String (optional)} `fileFormat` Format to save the image file as. If not specified, tries to guess based on file extension. -* @param {int (optional)} `fileCompressionQuality` Compression ratio between -1 and 100 to save the file with. -1 is automatic, 1 is smallest file/most compression, 100 is largest file/least compression. Varies with image type. +* @param {int (optional)} `compressionQuality` Compression ratio between -1 and 100 to write the image with. -1 is automatic, 1 is smallest file/most compression, 100 is largest file/least compression. Varies with image type. * @param {int (optional)} `width` Screenshot width. Defaults to the source's base width. * @param {int (optional)} `height` Screenshot height. Defaults to the source's base height. * @@ -1541,6 +1541,16 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { OBSDataAutoRelease response = obs_data_create(); + int compressionQuality {-1}; + if (request.hasField("compressionQuality")) { + compressionQuality = obs_data_get_int(request.parameters(), "compressionQuality"); + + if (compressionQuality < -1 || compressionQuality > 100) { + QString errorMessage = QString("compression quality out of range: %1").arg(compressionQuality); + return request.failed(errorMessage.toUtf8()); + } + } + if (request.hasField("embedPictureFormat")) { const char* pictureFormat = obs_data_get_string(request.parameters(), "embedPictureFormat"); @@ -1553,7 +1563,7 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { QByteArray encodedImgBytes; QBuffer buffer(&encodedImgBytes); buffer.open(QBuffer::WriteOnly); - if (!sourceImage.save(&buffer, pictureFormat)) { + if (!sourceImage.save(&buffer, pictureFormat, compressionQuality)) { return request.failed("embed image encoding failed"); } buffer.close(); @@ -1585,17 +1595,7 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { fileFormat = nullptr; } - int fileCompressionQuality {-1}; - if (request.hasField("fileCompressionQuality")) { - fileCompressionQuality = obs_data_get_int(request.parameters(), "fileCompressionQuality"); - - if (fileCompressionQuality < -1 || fileCompressionQuality > 100) { - QString errorMessage = QString("compression quality out of range: %1").arg(fileCompressionQuality); - return request.failed(errorMessage.toUtf8()); - } - } - - if (!sourceImage.save(absoluteFilePath, fileFormat, fileCompressionQuality)) { + if (!sourceImage.save(absoluteFilePath, fileFormat, compressionQuality)) { return request.failed("Image save failed"); } obs_data_set_string(response, "imageFile", absoluteFilePath.toUtf8()); From 8d88bc19eda31a3c0a0826afbd3539f760fa336f Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 8 May 2020 13:15:24 -0700 Subject: [PATCH 053/278] Deprecate SetBrowserSourceProperties and GetBrowserSourceProperties Both of these requests are doing essentially the same thing as `GetSourceSettings` and `SetSourceSettings`, so there is no reason to have the extra bloat. --- src/WSRequestHandler_Sources.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 579f76b4..0d49053d 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -919,6 +919,7 @@ RpcResponse WSRequestHandler::SetTextFreetype2Properties(const RpcRequest& reque * @name GetBrowserSourceProperties * @category sources * @since 4.1.0 + * @deprecated Since 4.8.0. Prefer the use of GetSourceSettings. */ RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& request) { @@ -960,6 +961,7 @@ RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& reque * @api requests * @name SetBrowserSourceProperties * @category sources + * @deprecated Since 4.8.0. Prefer the use of SetSourceSettings. * @since 4.1.0 */ RpcResponse WSRequestHandler::SetBrowserSourceProperties(const RpcRequest& request) From a3bc9f768ab0e1d071b1daa5b5cba40c900986dd Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 8 May 2020 15:30:40 -0700 Subject: [PATCH 054/278] Add error system to EnableStudioMode and DisableStudioMode As talked about in #144 this adds a check to `EnableStudioMode` and `DisableStudioMode` in order to be consistent with `StartRecording`/`StopRecording` --- src/WSRequestHandler_StudioMode.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/WSRequestHandler_StudioMode.cpp b/src/WSRequestHandler_StudioMode.cpp index fb958f3b..badae29d 100644 --- a/src/WSRequestHandler_StudioMode.cpp +++ b/src/WSRequestHandler_StudioMode.cpp @@ -133,6 +133,9 @@ RpcResponse WSRequestHandler::TransitionToProgram(const RpcRequest& request) { * @since 4.1.0 */ RpcResponse WSRequestHandler::EnableStudioMode(const RpcRequest& request) { + if (obs_frontend_preview_program_mode_active()) { + return request.failed("studio mode already active"); + } obs_queue_task(OBS_TASK_UI, [](void* param) { obs_frontend_set_preview_program_mode(true); @@ -150,6 +153,9 @@ RpcResponse WSRequestHandler::EnableStudioMode(const RpcRequest& request) { * @since 4.1.0 */ RpcResponse WSRequestHandler::DisableStudioMode(const RpcRequest& request) { + if (!obs_frontend_preview_program_mode_active()) { + return request.failed("studio mode not active"); + } obs_queue_task(OBS_TASK_UI, [](void* param) { obs_frontend_set_preview_program_mode(false); From 728ea1670185972bbf21a46a447c80521f34350d Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sun, 10 May 2020 18:39:24 -0700 Subject: [PATCH 055/278] Introduce `useDecibel` bool to `GetVolume` and `SetVolume` to give a better, built-in option for people to use decibels of attenuation instead of amplitude. --- src/WSRequestHandler_Sources.cpp | 53 +++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 579f76b4..44bf1b7c 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -156,12 +156,13 @@ RpcResponse WSRequestHandler::GetSourceTypesList(const RpcRequest& request) } /** -* Get the volume of the specified source. +* Get the volume of the specified source. Default response uses mul format, NOT SLIDER PERCENTAGE. * * @param {String} `source` Source name. +* @param {boolean (optional)} `useDecibel` Output volume in decibels of attenuation instead of amplitude/mul. * * @return {String} `name` Source name. -* @return {double} `volume` Volume of the source. Between `0.0` and `1.0`. +* @return {double} `volume` Volume of the source. Between `0.0` and `1.0` if using mul, under `0.0` if using dB (since it is attenuating). * @return {boolean} `muted` Indicates whether the source is muted. * * @api requests @@ -175,6 +176,11 @@ RpcResponse WSRequestHandler::GetVolume(const RpcRequest& request) return request.failed("missing request parameters"); } + bool useDecibel = false; + if (request.hasField("useDecibel")) { + useDecibel = obs_data_get_bool(request.parameters(), "useDecibel"); + } + QString sourceName = obs_data_get_string(request.parameters(), "source"); if (sourceName.isEmpty()) { return request.failed("invalid request parameters"); @@ -187,33 +193,45 @@ RpcResponse WSRequestHandler::GetVolume(const RpcRequest& request) OBSDataAutoRelease response = obs_data_create(); obs_data_set_string(response, "name", obs_source_get_name(source)); - obs_data_set_double(response, "volume", obs_source_get_volume(source)); + if (!useDecibel) { + obs_data_set_double(response, "volume", obs_source_get_volume(source)); + } else { + float volume = obs_source_get_volume(source); + obs_data_set_double(response, "volume", obs_mul_to_db(volume)); + } obs_data_set_bool(response, "muted", obs_source_muted(source)); return request.success(response); } /** - * Set the volume of the specified source. - * - * @param {String} `source` Source name. - * @param {double} `volume` Desired volume. Must be between `0.0` and `1.0`. - * - * @api requests - * @name SetVolume - * @category sources - * @since 4.0.0 - */ +* Set the volume of the specified source. Default request format uses mul, NOT SLIDER PERCENTAGE. +* +* @param {String} `source` Source name. +* @param {double} `volume` Desired volume. Must be between `0.0` and `1.0` for mul, and under 0.0 for dB. Note: OBS will interpret dB values under -100.0 as Inf. +* @param {boolean (optional)} `useDecibel` Interperet `volume` data as decibels instead of amplitude/mul. +* +* @api requests +* @name SetVolume +* @category sources +* @since 4.0.0 +*/ RpcResponse WSRequestHandler::SetVolume(const RpcRequest& request) { if (!request.hasField("source") || !request.hasField("volume")) { return request.failed("missing request parameters"); } + bool useDecibel = false; + if (request.hasField("useDecibel")) { + useDecibel = obs_data_get_bool(request.parameters(), "useDecibel"); + } + QString sourceName = obs_data_get_string(request.parameters(), "source"); float sourceVolume = obs_data_get_double(request.parameters(), "volume"); - if (sourceName.isEmpty() || sourceVolume < 0.0 || sourceVolume > 1.0) { + if ((useDecibel && sourceVolume > 0.0) || +(!useDecibel && (sourceVolume < 0.0 || sourceVolume > 1.0)) || (sourceName.isEmpty())) { return request.failed("invalid request parameters"); } @@ -222,7 +240,12 @@ RpcResponse WSRequestHandler::SetVolume(const RpcRequest& request) return request.failed("specified source doesn't exist"); } - obs_source_set_volume(source, sourceVolume); + if (!useDecibel) { + obs_source_set_volume(source, sourceVolume); + } else { + float mul = obs_db_to_mul(sourceVolume); + obs_source_set_volume(source, mul); + } return request.success(); } From 88c72cd80a2d3f2405bdab23ca5c757617c7f151 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sun, 10 May 2020 20:07:57 -0700 Subject: [PATCH 056/278] Add audio monitoring request items. --- src/WSRequestHandler.cpp | 2 + src/WSRequestHandler.h | 2 + src/WSRequestHandler_Sources.cpp | 93 ++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index f2ae6c47..58cddc23 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -97,6 +97,8 @@ const QHash WSRequestHandler::messageMap { { "GetSourceTypesList", &WSRequestHandler::GetSourceTypesList }, { "GetSourceSettings", &WSRequestHandler::GetSourceSettings }, { "SetSourceSettings", &WSRequestHandler::SetSourceSettings }, + { "GetAudioMonitor", &WSRequestHandler::GetAudioMonitor }, + { "SetAudioMonitor", &WSRequestHandler::SetAudioMonitor }, { "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot }, { "GetSourceFilters", &WSRequestHandler::GetSourceFilters }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index d17384ee..b55c2fb5 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -112,6 +112,8 @@ class WSRequestHandler { RpcResponse GetSourceTypesList(const RpcRequest&); RpcResponse GetSourceSettings(const RpcRequest&); RpcResponse SetSourceSettings(const RpcRequest&); + RpcResponse GetAudioMonitor(const RpcRequest&); + RpcResponse SetAudioMonitor(const RpcRequest&); RpcResponse TakeSourceScreenshot(const RpcRequest&); RpcResponse GetSourceFilters(const RpcRequest&); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 579f76b4..12bce74c 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1424,6 +1424,99 @@ RpcResponse WSRequestHandler::SetSourceFilterVisibility(const RpcRequest& reques return request.success(); } +/** +* Get the audio monitoring type of the specified source. +* +* @param {String} `sourceName` Source name. +* +* @return {String} `monitorType` The monitor type in use. Options: `none`, `monitor_only`, `monitor_and_output`. +* +* @api requests +* @name GetAudioMonitor +* @category sources +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::GetAudioMonitor(const RpcRequest& request) + { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + OBSDataAutoRelease response = obs_data_create(); + + QString monitorType; + enum obs_monitoring_type mtype = obs_source_get_monitoring_type(source); + switch (mtype) { + case OBS_MONITORING_TYPE_NONE: + monitorType = "none"; + break; + case OBS_MONITORING_TYPE_MONITOR_ONLY: + monitorType = "monitor_only"; + break; + case OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT: + monitorType = "monitor_and_output"; + break; + default: + monitorType = "unknown"; + break; + } + obs_data_set_string(response, "monitorType", monitorType.toUtf8()); + + return request.success(response); +} + +/** +* Set the audio monitoring type of the specified source. +* +* @param {String} `sourceName` Source name. +* @param {String} `monitorType` The monitor type to use. Options: `none`, `monitor_only`, `monitor_and_output`. +* +* @api requests +* @name SetAudioMonitor +* @category sources +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::SetAudioMonitor(const RpcRequest& request) + { + if (!request.hasField("sourceName") || !request.hasField("monitorType")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + QString monitorType = obs_data_get_string(request.parameters(), "monitorType"); + + if (sourceName.isEmpty() || monitorType.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + if (monitorType == "none") { + obs_source_set_monitoring_type(source, OBS_MONITORING_TYPE_NONE); + } else if (monitorType == "monitor_only") { + obs_source_set_monitoring_type(source, OBS_MONITORING_TYPE_MONITOR_ONLY); + } else if (monitorType == "monitor_and_output") { + obs_source_set_monitoring_type(source, OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT); + } else { + return request.failed("invalid monitorType"); + } + return request.success(); +} + /** * Takes a picture snapshot of a source and then can either or both: * - Send it over as a Data URI (base64-encoded data) in the response (by specifying `embedPictureFormat` in the request) From 1c6670c9b01a01277fbe627a2d6ef2f78543f636 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sun, 10 May 2020 21:03:38 -0700 Subject: [PATCH 057/278] Create new category and add `MediaPlayPause` request --- CMakeLists.txt | 1 + src/WSRequestHandler.cpp | 5 +++- src/WSRequestHandler.h | 2 ++ src/WSRequestHandler_MediaControl.cpp | 35 +++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/WSRequestHandler_MediaControl.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5648a963..c7e34a88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ set(obs-websocket_SOURCES src/WSRequestHandler_StudioMode.cpp src/WSRequestHandler_Transitions.cpp src/WSRequestHandler_Outputs.cpp + src/WSRequestHandler_MediaControl.cpp src/WSEvents.cpp src/Config.cpp src/Utils.cpp diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index f2ae6c47..1325a73b 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -143,7 +143,10 @@ const QHash WSRequestHandler::messageMap { { "ListOutputs", &WSRequestHandler::ListOutputs }, { "GetOutputInfo", &WSRequestHandler::GetOutputInfo }, { "StartOutput", &WSRequestHandler::StartOutput }, - { "StopOutput", &WSRequestHandler::StopOutput } + { "StopOutput", &WSRequestHandler::StopOutput }, + + { "MediaPlayPause", &WSRequestHandler::MediaPlayPause } + }; const QSet WSRequestHandler::authNotRequired { diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index d17384ee..deef55bb 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -162,4 +162,6 @@ class WSRequestHandler { RpcResponse GetOutputInfo(const RpcRequest&); RpcResponse StartOutput(const RpcRequest&); RpcResponse StopOutput(const RpcRequest&); + + RpcResponse MediaPlayPause(const RpcRequest&); }; diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp new file mode 100644 index 00000000..e72fe197 --- /dev/null +++ b/src/WSRequestHandler_MediaControl.cpp @@ -0,0 +1,35 @@ +#include "Utils.h" + +#include "WSRequestHandler.h" + +/** +* Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* @param {boolean} `playPause` Whether to pause or play the source. `false` for play, `true` for pause. +* +* @api requests +* @name MediaPlayPause +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::MediaPlayPause(const RpcRequest& request) { + if ((!request.hasField("sourceName")) || (!request.hasField("playPause"))) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + bool playPause = obs_data_get_bool(request.parameters(), "playPause"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + obs_source_media_play_pause(source, playPause); + return request.success(); + +} From 37cf8e9d29d9d3c63d83e7f1c519901add72ea63 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sun, 10 May 2020 21:13:48 -0700 Subject: [PATCH 058/278] Rename `MediaPlayPause` to `PlayPauseMedia` Works better with the other upcoming media control request names --- src/WSRequestHandler.cpp | 3 +-- src/WSRequestHandler.h | 2 +- src/WSRequestHandler_MediaControl.cpp | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 1325a73b..2049e27e 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -145,8 +145,7 @@ const QHash WSRequestHandler::messageMap { { "StartOutput", &WSRequestHandler::StartOutput }, { "StopOutput", &WSRequestHandler::StopOutput }, - { "MediaPlayPause", &WSRequestHandler::MediaPlayPause } - + { "PlayPauseMedia", &WSRequestHandler::PlayPauseMedia } }; const QSet WSRequestHandler::authNotRequired { diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index deef55bb..cf79f214 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -163,5 +163,5 @@ class WSRequestHandler { RpcResponse StartOutput(const RpcRequest&); RpcResponse StopOutput(const RpcRequest&); - RpcResponse MediaPlayPause(const RpcRequest&); + RpcResponse PlayPauseMedia(const RpcRequest&); }; diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp index e72fe197..019e5f7e 100644 --- a/src/WSRequestHandler_MediaControl.cpp +++ b/src/WSRequestHandler_MediaControl.cpp @@ -9,11 +9,11 @@ * @param {boolean} `playPause` Whether to pause or play the source. `false` for play, `true` for pause. * * @api requests -* @name MediaPlayPause +* @name PlayPauseMedia * @category media control * @since 4.8.0 */ -RpcResponse WSRequestHandler::MediaPlayPause(const RpcRequest& request) { +RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) { if ((!request.hasField("sourceName")) || (!request.hasField("playPause"))) { return request.failed("missing request parameters"); } @@ -31,5 +31,5 @@ RpcResponse WSRequestHandler::MediaPlayPause(const RpcRequest& request) { obs_source_media_play_pause(source, playPause); return request.success(); - } + From acfdb2613522cede1a03c9cc9becd440d2b5d132 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sun, 10 May 2020 23:21:24 -0700 Subject: [PATCH 059/278] A lot more requests. --- src/WSRequestHandler.cpp | 11 +- src/WSRequestHandler.h | 9 + src/WSRequestHandler_MediaControl.cpp | 312 ++++++++++++++++++++++++++ 3 files changed, 331 insertions(+), 1 deletion(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 2049e27e..6f637d19 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -145,7 +145,16 @@ const QHash WSRequestHandler::messageMap { { "StartOutput", &WSRequestHandler::StartOutput }, { "StopOutput", &WSRequestHandler::StopOutput }, - { "PlayPauseMedia", &WSRequestHandler::PlayPauseMedia } + { "PlayPauseMedia", &WSRequestHandler::PlayPauseMedia }, + { "RestartMedia", &WSRequestHandler::RestartMedia }, + { "StopMedia", &WSRequestHandler::StopMedia }, + { "NextMedia", &WSRequestHandler::NextMedia }, + { "PreviousMedia", &WSRequestHandler::PreviousMedia }, + { "GetMediaDuration", &WSRequestHandler::GetMediaDuration }, + { "GetMediaTime", &WSRequestHandler::GetMediaTime }, + { "SetMediaTime", &WSRequestHandler::SetMediaTime }, + { "ScrubMedia", &WSRequestHandler::ScrubMedia }, + { "GetMediaState", &WSRequestHandler::GetMediaState } }; const QSet WSRequestHandler::authNotRequired { diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index cf79f214..6b82eb0d 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -164,4 +164,13 @@ class WSRequestHandler { RpcResponse StopOutput(const RpcRequest&); RpcResponse PlayPauseMedia(const RpcRequest&); + RpcResponse RestartMedia(const RpcRequest&); + RpcResponse StopMedia(const RpcRequest&); + RpcResponse NextMedia(const RpcRequest&); + RpcResponse PreviousMedia(const RpcRequest&); + RpcResponse GetMediaDuration(const RpcRequest&); + RpcResponse GetMediaTime(const RpcRequest&); + RpcResponse SetMediaTime(const RpcRequest&); + RpcResponse ScrubMedia(const RpcRequest&); + RpcResponse GetMediaState(const RpcRequest&); }; diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp index 019e5f7e..0298e533 100644 --- a/src/WSRequestHandler_MediaControl.cpp +++ b/src/WSRequestHandler_MediaControl.cpp @@ -33,3 +33,315 @@ RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) { return request.success(); } +/** +* Restart a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @api requests +* @name RestartMedia +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::RestartMedia(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + obs_source_media_restart(source); + return request.success(); +} + +/** +* Stop a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @api requests +* @name StopMedia +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::StopMedia(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + obs_source_media_stop(source); + return request.success(); +} + +/** +* Skip to the next media item in the playlist. Supports only vlc media source (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @api requests +* @name NextMedia +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::NextMedia(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + obs_source_media_next(source); + return request.success(); +} + +/** +* Go to the previous media item in the playlist. Supports only vlc media source (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @api requests +* @name PreviousMedia +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::PreviousMedia(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + obs_source_media_previous(source); + return request.success(); +} + +/** +* Get the length of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* Note: For some reason, for the first 5 or so seconds that the media is playing, the total duration can be off by upwards of 50ms. +* +* @param {String} `sourceName` Source name. +* +* @return {int} `mediaDuration` The total length of media in milliseconds.. +* +* @api requests +* @name GetMediaDuration +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_int(response, "timeStamp", obs_source_media_get_duration(source)); + return request.success(response); +} + +/** +* Get the current timestamp of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @return {int} `timeStamp` The time in milliseconds since the start of the media. +* +* @api requests +* @name GetMediaTime +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_int(response, "timeStamp", obs_source_media_get_time(source)); + return request.success(response); +} + +/** +* Set the timestamp of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* @param {int} `timeStamp` Milliseconds to set the timestamp to. +* +* @api requests +* @name SetMediaTime +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) { + if (!request.hasField("sourceName") || !request.hasField("timeStamp")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + int64_t timeStamp = (int64_t)obs_data_get_int(request.parameters(), "timeStamp"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + obs_source_media_set_time(source, timeStamp); + return request.success(); +} + +/** +* Scrub media using a supplied offset. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* Note: Due to processing/network delays, this request is not perfect. The processing rate of this request has also not been tested. +* +* @param {String} `sourceName` Source name. +* @param {int} `timeOffset` Millisecond offset (positive or negative) to offset the current media position. +* +* @api requests +* @name ScrubMedia +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) { + if (!request.hasField("sourceName") || !request.hasField("timeOffset")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + int64_t timeOffset = (int64_t)obs_data_get_int(request.parameters(), "timeOffset"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + int64_t newTime = obs_source_media_get_time(source) + timeOffset; + if (newTime < 0) { + newTime = 0; + } + + obs_source_media_set_time(source, newTime); + return request.success(); +} + +/** +* Get the current playing state of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* +* @param {String} `sourceName` Source name. +* +* @return {String} `mediaState` The media state of the provided source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error` +* +* @api requests +* @name GetMediaState +* @category media control +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) { + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + QString mediaState; + enum obs_media_state mstate = obs_source_media_get_state(source); + switch (mstate) { + case OBS_MEDIA_STATE_NONE: + mediaState = "none"; + break; + case OBS_MEDIA_STATE_PLAYING: + mediaState = "playing"; + break; + case OBS_MEDIA_STATE_OPENING: + mediaState = "opening"; + break; + case OBS_MEDIA_STATE_BUFFERING: + mediaState = "buffering"; + break; + case OBS_MEDIA_STATE_PAUSED: + mediaState = "paused"; + break; + case OBS_MEDIA_STATE_STOPPED: + mediaState = "stopped"; + break; + case OBS_MEDIA_STATE_ENDED: + mediaState = "ended"; + break; + case OBS_MEDIA_STATE_ERROR: + mediaState = "error"; + break; + default: + mediaState = "unknown"; + } + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_string(response, "mediaState", mediaState.toUtf8()); + + return request.success(response); +} From f9c81f99f2f5987d88b63334771817eb30d9deb8 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 12 May 2020 02:24:27 -0700 Subject: [PATCH 060/278] Install into both dirs on Ubuntu instead of only one --- CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a6bbd38f..56130453 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,10 +190,9 @@ if(UNIX AND NOT APPLE) if(${USE_UBUNTU_FIX}) install(TARGETS obs-websocket LIBRARY DESTINATION "/usr/lib/obs-plugins") - else() - install(TARGETS obs-websocket - LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins") endif() + install(TARGETS obs-websocket + LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins") install(FILES ${locale_files} DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/obs/obs-plugins/obs-websocket/locale") From ff21f5b3571c49483cb421f6bd69f3abf2258d2b Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Tue, 12 May 2020 09:37:41 +0000 Subject: [PATCH 061/278] docs(ci): Update protocol.md - 98db248 [skip ci] --- docs/generated/comments.json | 14 ++++++++++++++ docs/generated/protocol.md | 2 ++ 2 files changed, 16 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 0731d3d7..28f82e48 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -6870,6 +6870,7 @@ "name": "GetBrowserSourceProperties", "category": "sources", "since": "4.1.0", + "deprecated": "Since 4.8.0. Prefer the use of GetSourceSettings.", "returns": [ { "type": "String", @@ -6942,6 +6943,12 @@ "description": "4.1.0" } ], + "deprecateds": [ + { + "name": "", + "description": "Since 4.8.0. Prefer the use of GetSourceSettings." + } + ], "heading": { "level": 2, "text": "GetBrowserSourceProperties" @@ -6968,6 +6975,7 @@ "api": "requests", "name": "SetBrowserSourceProperties", "category": "sources", + "deprecated": "Since 4.8.0. Prefer the use of SetSourceSettings.", "since": "4.1.0", "params": [ { @@ -7033,6 +7041,12 @@ "description": "sources" } ], + "deprecateds": [ + { + "name": "", + "description": "Since 4.8.0. Prefer the use of SetSourceSettings." + } + ], "sinces": [ { "name": "", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 5325ee13..e5f47ec1 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -2643,6 +2643,7 @@ _No additional response items._ ### GetBrowserSourceProperties +- **⚠️ Deprecated. Since 4.8.0. Prefer the use of GetSourceSettings. ⚠️** - Added in v4.1.0 @@ -2674,6 +2675,7 @@ Get current properties for a Browser Source. ### SetBrowserSourceProperties +- **⚠️ Deprecated. Since 4.8.0. Prefer the use of SetSourceSettings. ⚠️** - Added in v4.1.0 From 4eb7bed2ff139932a50ae44c3f38930bdbe036dd Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 12 May 2020 15:44:57 -0700 Subject: [PATCH 062/278] Lots of changes - Move `SetTBarPosition` to transitions category - Add another check to `SetTBarPosition` so that we dont break the fabric of time with the wrong values - Create `GetTransitionPosition` request in transitions category --- src/WSRequestHandler.cpp | 3 +- src/WSRequestHandler.h | 8 ++--- src/WSRequestHandler_StudioMode.cpp | 29 +--------------- src/WSRequestHandler_Transitions.cpp | 50 ++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index a5914744..018b2638 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -84,6 +84,8 @@ const QHash WSRequestHandler::messageMap { { "SetCurrentTransition", &WSRequestHandler::SetCurrentTransition }, { "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration }, { "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration }, + { "SetTBarPosition", &WSRequestHandler::SetTBarPosition }, + { "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition }, { "SetVolume", &WSRequestHandler::SetVolume }, { "GetVolume", &WSRequestHandler::GetVolume }, @@ -130,7 +132,6 @@ const QHash WSRequestHandler::messageMap { { "EnableStudioMode", &WSRequestHandler::EnableStudioMode }, { "DisableStudioMode", &WSRequestHandler::DisableStudioMode }, { "ToggleStudioMode", &WSRequestHandler::ToggleStudioMode }, - { "SetTBarPosition", &WSRequestHandler::SetTBarPosition }, { "SetTextGDIPlusProperties", &WSRequestHandler::SetTextGDIPlusProperties }, { "GetTextGDIPlusProperties", &WSRequestHandler::GetTextGDIPlusProperties }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index a644bca4..5dc30894 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -99,6 +99,10 @@ class WSRequestHandler { RpcResponse GetTransitionList(const RpcRequest&); RpcResponse GetCurrentTransition(const RpcRequest&); RpcResponse SetCurrentTransition(const RpcRequest&); + RpcResponse SetTransitionDuration(const RpcRequest&); + RpcResponse GetTransitionDuration(const RpcRequest&); + RpcResponse SetTBarPosition(const RpcRequest&); + RpcResponse GetTransitionPosition(const RpcRequest&); RpcResponse SetVolume(const RpcRequest&); RpcResponse GetVolume(const RpcRequest&); @@ -138,9 +142,6 @@ class WSRequestHandler { RpcResponse SendCaptions(const RpcRequest&); #endif - RpcResponse SetTransitionDuration(const RpcRequest&); - RpcResponse GetTransitionDuration(const RpcRequest&); - RpcResponse GetStudioModeStatus(const RpcRequest&); RpcResponse GetPreviewScene(const RpcRequest&); RpcResponse SetPreviewScene(const RpcRequest&); @@ -148,7 +149,6 @@ class WSRequestHandler { RpcResponse EnableStudioMode(const RpcRequest&); RpcResponse DisableStudioMode(const RpcRequest&); RpcResponse ToggleStudioMode(const RpcRequest&); - RpcResponse SetTBarPosition(const RpcRequest&); RpcResponse SetTextGDIPlusProperties(const RpcRequest&); RpcResponse GetTextGDIPlusProperties(const RpcRequest&); diff --git a/src/WSRequestHandler_StudioMode.cpp b/src/WSRequestHandler_StudioMode.cpp index cd103c83..042db61b 100644 --- a/src/WSRequestHandler_StudioMode.cpp +++ b/src/WSRequestHandler_StudioMode.cpp @@ -176,31 +176,4 @@ RpcResponse WSRequestHandler::ToggleStudioMode(const RpcRequest& request) { }, nullptr, true); return request.success(); -} - -/** - * Set the position of the T-Bar (in Studio Mode) to the specified value. Will return an error if OBS is not in studio mode - * or if the current transition doesn't support T-Bar control. - * - * @param {double} `position` T-Bar position. This value will be clamped between 0.0 and 1.0. - * - * @api requests - * @name SetTBarPosition - * @category studio mode - * @since 4.8.0 - */ -RpcResponse WSRequestHandler::SetTBarPosition(const RpcRequest& request) { - if (!obs_frontend_preview_program_mode_active()) { - return request.failed("studio mode not enabled"); - } - - OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); - if (obs_transition_fixed(currentTransition)) { - return request.failed("current transition doesn't support t-bar control"); - } - - double position = obs_data_get_double(request.parameters(), "position"); - obs_transition_set_manual_time(currentTransition, position); - - return request.success(); -} +} \ No newline at end of file diff --git a/src/WSRequestHandler_Transitions.cpp b/src/WSRequestHandler_Transitions.cpp index 7c22687c..a2ba2125 100644 --- a/src/WSRequestHandler_Transitions.cpp +++ b/src/WSRequestHandler_Transitions.cpp @@ -120,3 +120,53 @@ RpcResponse WSRequestHandler::GetTransitionDuration(const RpcRequest& request) { obs_data_set_int(response, "transition-duration", obs_frontend_get_transition_duration()); return request.success(response); } + +/** + * Set the manual position of the T-Bar (in Studio Mode) to the specified value. Will return an error if OBS is not in studio mode + * or if the current transition doesn't support T-Bar control. + * + * @param {double} `position` T-Bar position. This value must be between 0.0 and 1.0. + * + * @api requests + * @name SetTBarPosition + * @category transitions + * @since 4.8.0 + */ +RpcResponse WSRequestHandler::SetTBarPosition(const RpcRequest& request) { + if (!obs_frontend_preview_program_mode_active()) { + return request.failed("studio mode not enabled"); + } + + OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); + if (obs_transition_fixed(currentTransition)) { + return request.failed("current transition doesn't support t-bar control"); + } + + double position = obs_data_get_double(request.parameters(), "position"); + if (position < 0.0 || position > 1.0) { + return request.failed("position is out of range"); + } + + obs_transition_set_manual_time(currentTransition, position); + + return request.success(); +} + +/** + * Get the position of the current transition. + * + * @return {double} `position` current transition position. This value will be between 0.0 and 1.0. Note: Transition returns 1.0 when not active. + * + * @api requests + * @name GetTransitionPosition + * @category transitions + * @since 4.8.0 + */ +RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) { + OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_double(response, "position", obs_transition_get_time(currentTransition)); + + return request.success(response); +} \ No newline at end of file From 71392613b2ad56248aa54e4a3733f2b682165fd6 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 12 May 2020 17:46:49 -0700 Subject: [PATCH 063/278] [Docs] Bump version to 4.8 --- docs/partials/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/partials/introduction.md b/docs/partials/introduction.md index c8de4503..c63a52cd 100644 --- a/docs/partials/introduction.md +++ b/docs/partials/introduction.md @@ -1,4 +1,4 @@ -# obs-websocket 4.7.0 protocol reference +# obs-websocket 4.8.0 protocol reference # General Introduction Messages are exchanged between the client and the server as JSON objects. From 10ed2738f53e4cff6de8f3e810460d6910143dfc Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 13 May 2020 00:10:57 -0700 Subject: [PATCH 064/278] Add `ReleaseTBar` request and update `SetTBarPosition` to depend on OBS pr 2927 --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_Transitions.cpp | 41 +++++++++++++++++++++++++--- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 018b2638..660ed2c4 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -84,6 +84,7 @@ const QHash WSRequestHandler::messageMap { { "SetCurrentTransition", &WSRequestHandler::SetCurrentTransition }, { "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration }, { "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration }, + { "ReleaseTBar", &WSRequestHandler::ReleaseTBar }, { "SetTBarPosition", &WSRequestHandler::SetTBarPosition }, { "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 5dc30894..a6c7804c 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -101,6 +101,7 @@ class WSRequestHandler { RpcResponse SetCurrentTransition(const RpcRequest&); RpcResponse SetTransitionDuration(const RpcRequest&); RpcResponse GetTransitionDuration(const RpcRequest&); + RpcResponse ReleaseTBar(const RpcRequest&); RpcResponse SetTBarPosition(const RpcRequest&); RpcResponse GetTransitionPosition(const RpcRequest&); diff --git a/src/WSRequestHandler_Transitions.cpp b/src/WSRequestHandler_Transitions.cpp index a2ba2125..443b3fcd 100644 --- a/src/WSRequestHandler_Transitions.cpp +++ b/src/WSRequestHandler_Transitions.cpp @@ -121,6 +121,28 @@ RpcResponse WSRequestHandler::GetTransitionDuration(const RpcRequest& request) { return request.success(response); } +/** + * Release the T-Bar. YOU MUST CALL THIS IF YOU SPECIFY `release = false` IN `SetTBarPosition`. + * + * @api requests + * @name ReleaseTBar + * @category transitions + * @since 4.8.0 + */ +RpcResponse WSRequestHandler::ReleaseTBar(const RpcRequest& request) { + if (!obs_frontend_preview_program_mode_active()) { + return request.failed("studio mode not enabled"); + } + + if (obs_transition_fixed(obs_frontend_get_current_transition())) { + return request.failed("current transition doesn't support t-bar control"); + } + + obs_frontend_release_tbar(); + + return request.success(); +} + /** * Set the manual position of the T-Bar (in Studio Mode) to the specified value. Will return an error if OBS is not in studio mode * or if the current transition doesn't support T-Bar control. @@ -137,17 +159,28 @@ RpcResponse WSRequestHandler::SetTBarPosition(const RpcRequest& request) { return request.failed("studio mode not enabled"); } - OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); - if (obs_transition_fixed(currentTransition)) { + if (obs_transition_fixed(obs_frontend_get_current_transition())) { return request.failed("current transition doesn't support t-bar control"); } + if (!request.hasField("position")) { + return request.failed("missing request parameters"); + } + double position = obs_data_get_double(request.parameters(), "position"); if (position < 0.0 || position > 1.0) { return request.failed("position is out of range"); } - - obs_transition_set_manual_time(currentTransition, position); + + bool release = true; + if (request.hasField("release")) { + release = obs_data_get_bool(request.parameters(), "release"); + } + + obs_frontend_set_tbar_position((int)((float)position * 1024.0)); + if (release) { + obs_frontend_release_tbar(); + } return request.success(); } From 7ca902e39a7beecb161016d57c4d72cba98719cf Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 13 May 2020 20:48:54 -0700 Subject: [PATCH 065/278] A few formatting fixes --- src/WSRequestHandler_Transitions.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/WSRequestHandler_Transitions.cpp b/src/WSRequestHandler_Transitions.cpp index 443b3fcd..5aaadfb7 100644 --- a/src/WSRequestHandler_Transitions.cpp +++ b/src/WSRequestHandler_Transitions.cpp @@ -81,7 +81,7 @@ RpcResponse WSRequestHandler::SetCurrentTransition(const RpcRequest& request) { if (!success) { return request.failed("requested transition does not exist"); } - + return request.success(); } @@ -171,12 +171,12 @@ RpcResponse WSRequestHandler::SetTBarPosition(const RpcRequest& request) { if (position < 0.0 || position > 1.0) { return request.failed("position is out of range"); } - + bool release = true; if (request.hasField("release")) { release = obs_data_get_bool(request.parameters(), "release"); } - + obs_frontend_set_tbar_position((int)((float)position * 1024.0)); if (release) { obs_frontend_release_tbar(); @@ -197,9 +197,9 @@ RpcResponse WSRequestHandler::SetTBarPosition(const RpcRequest& request) { */ RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) { OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); - + OBSDataAutoRelease response = obs_data_create(); obs_data_set_double(response, "position", obs_transition_get_time(currentTransition)); return request.success(response); -} \ No newline at end of file +} From 2810787156f6902af1d2a268fd83ffaccff78412 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Thu, 14 May 2020 08:04:35 +0000 Subject: [PATCH 066/278] docs(ci): Update protocol.md - 6b53cb5 [skip ci] --- docs/generated/protocol.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index e5f47ec1..92db4587 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -1,6 +1,6 @@ -# obs-websocket 4.7.0 protocol reference +# obs-websocket 4.8.0 protocol reference # General Introduction Messages are exchanged between the client and the server as JSON objects. From 56371a7d80211241372e1dfad47f2edf9bf24091 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 13 May 2020 23:33:59 -0700 Subject: [PATCH 067/278] Add `GetTransitionPosition` Gets the current position of the active transition. Works on manual and also auto transition modes --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 6 +++--- src/WSRequestHandler_Transitions.cpp | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index f2ae6c47..de6aea06 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -84,6 +84,7 @@ const QHash WSRequestHandler::messageMap { { "SetCurrentTransition", &WSRequestHandler::SetCurrentTransition }, { "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration }, { "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration }, + { "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition }, { "SetVolume", &WSRequestHandler::SetVolume }, { "GetVolume", &WSRequestHandler::GetVolume }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index d17384ee..bf89998d 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -99,6 +99,9 @@ class WSRequestHandler { RpcResponse GetTransitionList(const RpcRequest&); RpcResponse GetCurrentTransition(const RpcRequest&); RpcResponse SetCurrentTransition(const RpcRequest&); + RpcResponse SetTransitionDuration(const RpcRequest&); + RpcResponse GetTransitionDuration(const RpcRequest&); + RpcResponse GetTransitionPosition(const RpcRequest&); RpcResponse SetVolume(const RpcRequest&); RpcResponse GetVolume(const RpcRequest&); @@ -138,9 +141,6 @@ class WSRequestHandler { RpcResponse SendCaptions(const RpcRequest&); #endif - RpcResponse SetTransitionDuration(const RpcRequest&); - RpcResponse GetTransitionDuration(const RpcRequest&); - RpcResponse GetStudioModeStatus(const RpcRequest&); RpcResponse GetPreviewScene(const RpcRequest&); RpcResponse SetPreviewScene(const RpcRequest&); diff --git a/src/WSRequestHandler_Transitions.cpp b/src/WSRequestHandler_Transitions.cpp index 7c22687c..9bcee450 100644 --- a/src/WSRequestHandler_Transitions.cpp +++ b/src/WSRequestHandler_Transitions.cpp @@ -120,3 +120,22 @@ RpcResponse WSRequestHandler::GetTransitionDuration(const RpcRequest& request) { obs_data_set_int(response, "transition-duration", obs_frontend_get_transition_duration()); return request.success(response); } + +/** + * Get the position of the current transition. + * + * @return {double} `position` current transition position. This value will be between 0.0 and 1.0. Note: Transition returns 1.0 when not active. + * + * @api requests + * @name GetTransitionPosition + * @category transitions + * @since 4.8.0 + */ +RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) { + OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_double(response, "position", obs_transition_get_time(currentTransition)); + + return request.success(response); +} From 121d9f492056eea33a2e340e53537914fc7c47dd Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 13 May 2020 23:32:14 -0700 Subject: [PATCH 068/278] Remove `GetTransitionPosition` Moving it to its own PR since this PR will not make it into 4.8 --- src/WSRequestHandler.cpp | 1 - src/WSRequestHandler.h | 1 - src/WSRequestHandler_Transitions.cpp | 19 ------------------- 3 files changed, 21 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 660ed2c4..514cf455 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -86,7 +86,6 @@ const QHash WSRequestHandler::messageMap { { "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration }, { "ReleaseTBar", &WSRequestHandler::ReleaseTBar }, { "SetTBarPosition", &WSRequestHandler::SetTBarPosition }, - { "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition }, { "SetVolume", &WSRequestHandler::SetVolume }, { "GetVolume", &WSRequestHandler::GetVolume }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index a6c7804c..c01ed313 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -103,7 +103,6 @@ class WSRequestHandler { RpcResponse GetTransitionDuration(const RpcRequest&); RpcResponse ReleaseTBar(const RpcRequest&); RpcResponse SetTBarPosition(const RpcRequest&); - RpcResponse GetTransitionPosition(const RpcRequest&); RpcResponse SetVolume(const RpcRequest&); RpcResponse GetVolume(const RpcRequest&); diff --git a/src/WSRequestHandler_Transitions.cpp b/src/WSRequestHandler_Transitions.cpp index 5aaadfb7..56505738 100644 --- a/src/WSRequestHandler_Transitions.cpp +++ b/src/WSRequestHandler_Transitions.cpp @@ -184,22 +184,3 @@ RpcResponse WSRequestHandler::SetTBarPosition(const RpcRequest& request) { return request.success(); } - -/** - * Get the position of the current transition. - * - * @return {double} `position` current transition position. This value will be between 0.0 and 1.0. Note: Transition returns 1.0 when not active. - * - * @api requests - * @name GetTransitionPosition - * @category transitions - * @since 4.8.0 - */ -RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) { - OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); - - OBSDataAutoRelease response = obs_data_create(); - obs_data_set_double(response, "position", obs_transition_get_time(currentTransition)); - - return request.success(response); -} From 497443f012a968f469355a3c507ca9695da5c2bb Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 14 May 2020 01:47:55 -0700 Subject: [PATCH 069/278] Update CI scripts to use ubuntu fix --- CI/build-ubuntu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/build-ubuntu.sh b/CI/build-ubuntu.sh index b19158ae..498840ef 100755 --- a/CI/build-ubuntu.sh +++ b/CI/build-ubuntu.sh @@ -2,5 +2,5 @@ set -ex mkdir build && cd build -cmake -DCMAKE_INSTALL_PREFIX=/usr .. +cmake -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=true .. make -j4 From bed6a1b1e28519e0cc7ccb2225f361f9b0a1fe4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Thu, 14 May 2020 11:02:51 +0200 Subject: [PATCH 070/278] ci(windows): simplified OBS Preparation script + shorter build times --- CI/checkout-cmake-obs-windows.cmd | 137 ------------------------------ CI/prepare-obs-windows.cmd | 37 ++++++++ azure-pipelines.yml | 2 +- 3 files changed, 38 insertions(+), 138 deletions(-) delete mode 100644 CI/checkout-cmake-obs-windows.cmd create mode 100644 CI/prepare-obs-windows.cmd diff --git a/CI/checkout-cmake-obs-windows.cmd b/CI/checkout-cmake-obs-windows.cmd deleted file mode 100644 index 0fc6c12c..00000000 --- a/CI/checkout-cmake-obs-windows.cmd +++ /dev/null @@ -1,137 +0,0 @@ -@echo off -SETLOCAL EnableDelayedExpansion - -REM Check if obs-studio build exists. -REM If the obs-studio directory does exist, check if the last OBS tag built -REM matches the latest OBS tag. -REM If the tags match, do not build obs-studio. -REM If the tags do not match, build obs-studio. -REM If the obs-studio directory doesn't exist, build obs-studio. -echo Checking for obs-studio build... - -set OBSLatestTagPrePull=0 -set OBSLatestTagPostPull=0 -echo Latest tag pre-pull: %OBSLatestTagPrePull% -echo Latest tag post-pull: %OBSLatestTagPostPull% - -REM Set up the build flag as undefined. -set "BuildOBS=" - -REM Check the last tag successfully built by CI. -if exist "%OBSPath%\obs-studio-last-tag-built.txt" ( - set /p OBSLastTagBuilt=<"%OBSPath%\obs-studio-last-tag-built.txt" -) else ( - set OBSLastTagBuilt=0 -) - -REM If obs-studio directory exists, run git pull and get the latest tag number. -if exist %OBSPath% ( - echo obs-studio directory exists - echo Updating tag info - cd /D %OBSPath% - git describe --tags --abbrev=0 --exclude="*-rc*" > "%OBSPath%\latest-obs-studio-tag-pre-pull.txt" - set /p OBSLatestTagPrePull=<"%OBSPath%\latest-obs-studio-tag-pre-pull.txt" - git checkout master - git pull - git describe --tags --abbrev=0 --exclude="*-rc*" > "%OBSPath%\latest-obs-studio-tag-post-pull.txt" - set /p OBSLatestTagPostPull=<"%OBSPath%\latest-obs-studio-tag-post-pull.txt" - set /p OBSLatestTag=<"%OBSPath%\latest-obs-studio-tag-post-pull.txt" - echo %OBSLatestTagPostPull%> "%OBSPath%\latest-obs-studio-tag.txt" -) - -REM Check the obs-studio tags for mismatches. -REM If a new tag was pulled, set the build flag. -if not %OBSLatestTagPrePull%==%OBSLatestTagPostPull% ( - echo Latest tag pre-pull: %OBSLatestTagPrePull% - echo Latest tag post-pull: %OBSLatestTagPostPull% - echo Tags do not match. Need to rebuild OBS. - set BuildOBS=true -) - -REM If the latest git tag doesn't match the last built tag, set the build flag. -if not %OBSLatestTagPostPull%==%OBSLastTagBuilt% ( - echo Last built OBS tag: %OBSLastTagBuilt% - echo Latest tag post-pull: %OBSLatestTagPostPull% - echo Tags do not match. Need to rebuild OBS. - set BuildOBS=true -) - -REM If obs-studio directory does not exist, clone the git repo, get the latest -REM tag number, and set the build flag. -if not exist %OBSPath% ( - echo obs-studio directory does not exist - git clone https://github.com/obsproject/obs-studio %OBSPath% - cd /D %OBSPath%\ - git describe --tags --abbrev=0 --exclude="*-rc*" > "%OBSPath%\obs-studio-latest-tag.txt" - set /p OBSLatestTag=<"%OBSPath%\obs-studio-latest-tag.txt" - set BuildOBS=true -) - -REM If the needed obs-studio libs for this build_config do not exist, -REM set the build flag. -if not exist %OBSPath%\build32\libobs\%build_config%\obs.lib ( - echo obs-studio\build32\libobs\%build_config%\obs.lib does not exist - set BuildOBS=true -) -if not exist %OBSPath%\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib ( - echo obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib does not exist - set BuildOBS=true -) - -REM Some debug info -echo: -echo Latest tag pre-pull: %OBSLatestTagPrePull% -echo Latest tag post-pull: %OBSLatestTagPostPull% -echo Latest tag: %OBSLatestTag% -echo Last built OBS tag: %OBSLastTagBuilt% - -if defined BuildOBS ( - echo BuildOBS: true -) else ( - echo BuildOBS: false -) -echo: - -REM If the build flag is set, build obs-studio. -if defined BuildOBS ( - echo Building obs-studio... - cd /D %OBSPath% - echo git checkout %OBSLatestTag% - git checkout %OBSLatestTag% - echo: - - echo Removing previous build dirs... - if exist build32 rmdir /s /q "%OBSPath%\build32" - if exist build64 rmdir /s /q "%OBSPath%\build64" - - echo Making new build dirs... - mkdir build32 - mkdir build64 - - echo Running cmake for obs-studio %OBSLatestTag% 32-bit... - cd build32 - cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DepsPath32%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. - echo: - echo: - - echo Running cmake for obs-studio %OBSLatestTag% 64-bit... - cd ..\build64 - cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DepsPath64%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. - echo: - echo: - - REM echo Building obs-studio %OBSLatestTag% 32-bit ^(Build Config: %build_config%^)... - REM call msbuild /m /p:Configuration=%build_config% %OBSPath%\build32\obs-studio.sln - - REM echo Building obs-studio %OBSLatestTag% 64-bit ^(Build Config: %build_config%^)... - REM call msbuild /m /p:Configuration=%build_config% %OBSPath%\build64\obs-studio.sln - - cd .. - git describe --tags --abbrev=0 > "%OBSPath%\obs-studio-last-tag-built.txt" - set /p OBSLastTagBuilt=<"%OBSPath%\obs-studio-last-tag-built.txt" -) else ( - echo Last OBS tag built is: %OBSLastTagBuilt% - echo No need to rebuild OBS. -) - -dir "%OBSPath%\libobs" diff --git a/CI/prepare-obs-windows.cmd b/CI/prepare-obs-windows.cmd new file mode 100644 index 00000000..961fbe17 --- /dev/null +++ b/CI/prepare-obs-windows.cmd @@ -0,0 +1,37 @@ + +@echo off +SETLOCAL EnableDelayedExpansion + +REM If obs-studio directory does not exist, clone the git repo +if not exist %OBSPath% ( + echo obs-studio directory does not exist + git clone https://github.com/obsproject/obs-studio %OBSPath% + cd /D %OBSPath%\ + git describe --tags --abbrev=0 --exclude="*-rc*" > "%OBSPath%\obs-studio-latest-tag.txt" + set /p OBSLatestTag=<"%OBSPath%\obs-studio-latest-tag.txt" +) + +REM Prepare OBS Studio builds + +echo Running CMake... +cd /D %OBSPath% +echo git checkout %OBSLatestTag% +git checkout %OBSLatestTag% +echo: + +if not exist build32 mkdir build32 +if not exist build64 mkdir build64 + +echo Running cmake for obs-studio %OBSLatestTag% 32-bit... +cd build32 +cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DepsPath32%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. +echo: +echo: + +echo Running cmake for obs-studio %OBSLatestTag% 64-bit... +cd ..\build64 +cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DepsPath64%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. +echo: +echo: + +dir "%OBSPath%\libobs" \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0f3f10b9..7fce0542 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -68,7 +68,7 @@ jobs: obs | "$(Agent.OS)" path: $(OBSPath) - - script: ./CI/checkout-cmake-obs-windows.cmd + - script: ./CI/prepare-obs-windows.cmd displayName: 'Checkout & CMake OBS Studio' env: build_config: $(build_config) From 353a9aa671c32f4a5dab8eb41e75b07a57f05f87 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 14 May 2020 02:13:50 -0700 Subject: [PATCH 071/278] Update docs - initial pr draft commit --- BUILDING.md | 12 ++++++++---- CONTRIBUTING.md | 38 ++++++++++++++++++++++++++++++++++++++ README.md | 42 ++++-------------------------------------- 3 files changed, 50 insertions(+), 42 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/BUILDING.md b/BUILDING.md index 83f93a99..67c0c01c 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -29,12 +29,18 @@ make -j4 sudo make install ``` +On other linux OS's, use this cmake command instead: + +```shell +cmake -DLIBOBS_INCLUDE_DIR="" -DCMAKE_INSTALL_PREFIX=/usr .. +``` + ## OS X As a prerequisite, you will need Xcode for your current OSX version, the Xcode command line tools, and [Homebrew](https://brew.sh/). Homebrew's setup will guide you in getting your system set up, you should be good to go once Homebrew is successfully up and running. -Use of the Travis macOS CI scripts is recommended. Please note that these +Use of the macOS CI scripts is recommended. Please note that these scripts install new software and can change several settings on your system. An existing obs-studio development environment is not required, as `install-build-obs-macos.sh` will install it for you. If you already have a @@ -57,6 +63,4 @@ This will result in a ready-to-use `obs-websocket.pkg` installer in the `release ## Automated Builds -- Windows: [![Automated Build status for Windows](https://ci.appveyor.com/api/projects/status/github/Palakis/obs-websocket)](https://ci.appveyor.com/project/Palakis/obs-websocket/history) -- Linux: [![Automated Build status for Linux](https://travis-ci.org/Palakis/obs-websocket.svg?branch=master)](https://travis-ci.org/Palakis/obs-websocket) -- macOS: [![Automated Build status for macOS](https://img.shields.io/azure-devops/build/Palakis/obs-websocket/Palakis.obs-websocket.svg)](https://dev.azure.com/Palakis/obs-websocket/_build) +[![Build Status](https://dev.azure.com/Palakis/obs-websocket/_apis/build/status/Palakis.obs-websocket?branchName=4.x-current)](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..792211b8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,38 @@ +# Contributing + +### Branches + +Development happens on `4.x-current` + +### Pull Requests + +Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). Start your work in a new branch +based on the upstream main one (e.g.: `cool-new-feature`, `fix-palakis-mistakes`, ...) and open a Pull Request once you feel ready to show your work. + +**If your Pull Request is not ready to merge yet, create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). + +### Code style & formatting + +Source code is indented with tabs, with spaces allowed for alignment. + +Regarding protocol changes: new and updated request types / events must always come with accompanying documentation comments (see existing protocol elements for examples). +These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). + +Among other recommendations: favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: + +```cpp +if (success) { + return req->SendOKResponse(); +} else { + return req->SendErrorResponse("something went wrong"); +} +``` + +is better like this: + +```cpp +if (!success) { + return req->SendErrorResponse("something went wrong"); +} +return req->SendOKResponse(); +``` diff --git a/README.md b/README.md index 1c210f96..0eab8873 100644 --- a/README.md +++ b/README.md @@ -46,47 +46,13 @@ See the [build instructions](BUILDING.md). ## Contributing -### Branches - -Development happens on `4.x-current` - -### Pull Requests - -Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). Start your work in a new branch -based on the main one (e.g.: `cool-new-feature`, `fix-palakis-mistakes`, ...) and open a Pull Request once you feel ready to show your work. - -**If your Pull Request is not ready to merge yet, create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). - -### Code style & formatting - -Source code is indented with tabs, with spaces allowed for alignment. - -Regarding protocol changes: new and updated request types / events must always come with accompanying documentation comments (see existing protocol elements for examples). -These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). - -Among other recommendations: favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: - -```cpp -if (success) { - return req->SendOKResponse(); -} else { - return req->SendErrorResponse("something went wrong"); -} -``` - -is better like this: - -```cpp -if (!success) { - return req->SendErrorResponse("something went wrong"); -} -return req->SendOKResponse(); -``` - +See [the contributing document](/CONTRIBUTING.md) ## Translations -**Your help is welcome on translations**. Please join the localization project on Crowdin: https://crowdin.com/project/obs-websocket +**Your help is welcome on translations.** + +Please join the localization project on Crowdin: https://crowdin.com/project/obs-websocket ## Special thanks From 27f82434b69b2d4b8b4e2d46f778ff168388770d Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 14 May 2020 03:01:28 -0700 Subject: [PATCH 072/278] Docs: More work on various things --- .github/CONTRIBUTING.md | 22 -------------- .github/FUNDING.yml | 3 ++ CONTRIBUTING.md | 63 ++++++++++++++++++++++++++++++----------- 3 files changed, 50 insertions(+), 38 deletions(-) delete mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/FUNDING.yml diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index ea45e450..00000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,22 +0,0 @@ -## Contributing to obs-websocket - -### Translating obs-websocket to your language -Localization happens on Crowdin: https://crowdin.com/project/obs-websocket - -### Writing code for obs-websocket -#### Coding Guidelines -- Function and variable names: snake_case for C names, CamelCase for C++ names -- Tabs are 8 columns wide -- 80 columns max. - -#### Commit Guidelines -- Commits follow the 50/72 standard: - - 50 characters max for the title - - One empty line after the title - - Description wrapped to 72 columns max per line. -- Commit titles: - - Use present tense - - Prefix the title with a "scope" name - - e.g: "CI: fix wrong behaviour when packaging for OS X" - - Typical scopes: CI, General, Request, Event, Server - - Look at existing commits for more examples diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..69945f88 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +open_collective: obs-websocket +github: Palakis +custom: https://www.paypal.me/stephanelepin diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 792211b8..0dfd2fa5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,25 +1,28 @@ -# Contributing +# Contributing to obs-websocket + +### Translating obs-websocket to your language + +Localization happens on Crowdin: https://crowdin.com/project/obs-websocket ### Branches -Development happens on `4.x-current` +**Development happens on `4.x-current`** -### Pull Requests +### Writing code for obs-websocket -Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). Start your work in a new branch -based on the upstream main one (e.g.: `cool-new-feature`, `fix-palakis-mistakes`, ...) and open a Pull Request once you feel ready to show your work. +#### Code Formatting Guidelines -**If your Pull Request is not ready to merge yet, create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). +- Function and variable names: snake_case for C names, MixedCaps for C++ names +- Request and Event names should use MixedCaps names +- Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) +- Tabs are 8 columns wide +- 80 columns max code width. (Docs can be larger) +- New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples). + These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). -### Code style & formatting - -Source code is indented with tabs, with spaces allowed for alignment. - -Regarding protocol changes: new and updated request types / events must always come with accompanying documentation comments (see existing protocol elements for examples). -These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). - -Among other recommendations: favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: +#### Code Best-Practices +- Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: ```cpp if (success) { return req->SendOKResponse(); @@ -27,12 +30,40 @@ if (success) { return req->SendErrorResponse("something went wrong"); } ``` - is better like this: - ```cpp if (!success) { return req->SendErrorResponse("something went wrong"); } return req->SendOKResponse(); ``` + +#### Commit Guidelines + +- Commits follow the 50/72 standard: + - 50 characters max for the commit title (excluding scope name) + - One empty line after the title + - Description wrapped to 72 columns max width per line. +- Commit titles: + - Use present tense + - Prefix the title with a "scope" name + - e.g: "CI: fix wrong behaviour when packaging for OS X" + - Typical scopes: CI, General, Requests, Events, Server + +**Example commit:** + +``` +Requests: Add GetTransitionPosition + +Adds a new request called `GetTransitionPosition` which gets the current +transition's state from 0.0f to 1.0f. Works with both auto and manual +transitions. +``` + +#### Pull Requests + +- Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). + - Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) +- Only open a pull request if you are ready to show off your work. +- If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers), + create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). From 100565081cd60a5387ad8f066dc370d515ed428f Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 14 May 2020 03:11:17 -0700 Subject: [PATCH 073/278] Docs: More changes --- CONTRIBUTING.md | 60 ++++++++++++++++++++++++++++--------------------- README.md | 2 +- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0dfd2fa5..a319e63d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,26 +1,31 @@ # Contributing to obs-websocket -### Translating obs-websocket to your language +## Translating obs-websocket to your language -Localization happens on Crowdin: https://crowdin.com/project/obs-websocket +Localization happens on [Crowdin](https://crowdin.com/project/obs-websocket) -### Branches +## Branches **Development happens on `4.x-current`** -### Writing code for obs-websocket +## Writing code for obs-websocket -#### Code Formatting Guidelines +### Code Formatting Guidelines -- Function and variable names: snake_case for C names, MixedCaps for C++ names -- Request and Event names should use MixedCaps names -- Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) -- Tabs are 8 columns wide -- 80 columns max code width. (Docs can be larger) -- New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples). + - Function and variable names: snake_case for C names, MixedCaps for C++ names + + - Request and Event names should use MixedCaps names + + - Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) + + - Tabs are 8 columns wide + + - 80 columns max code width. (Docs can be larger) + + - New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples). These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). -#### Code Best-Practices +### Code Best-Practices - Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: ```cpp @@ -38,17 +43,18 @@ if (!success) { return req->SendOKResponse(); ``` -#### Commit Guidelines +### Commit Guidelines -- Commits follow the 50/72 standard: - - 50 characters max for the commit title (excluding scope name) - - One empty line after the title - - Description wrapped to 72 columns max width per line. -- Commit titles: - - Use present tense - - Prefix the title with a "scope" name - - e.g: "CI: fix wrong behaviour when packaging for OS X" - - Typical scopes: CI, General, Requests, Events, Server + - Commits follow the 50/72 standard: + - 50 characters max for the commit title (excluding scope name) + - One empty line after the title + - Description wrapped to 72 columns max width per line. + + - Commit titles: + - Use present tense + - Prefix the title with a "scope" name + - e.g: "CI: fix wrong behaviour when packaging for OS X" + - Typical scopes: CI, General, Requests, Events, Server **Example commit:** @@ -60,10 +66,12 @@ transition's state from 0.0f to 1.0f. Works with both auto and manual transitions. ``` -#### Pull Requests +### Pull Requests -- Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). + - Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). - Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) -- Only open a pull request if you are ready to show off your work. -- If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers), + + - Only open a pull request if you are ready to show off your work. + + - If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers), create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). diff --git a/README.md b/README.md index 0eab8873..8fc8c1a7 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ See [the contributing document](/CONTRIBUTING.md) **Your help is welcome on translations.** -Please join the localization project on Crowdin: https://crowdin.com/project/obs-websocket +Please join the localization project on [Crowdin](https://crowdin.com/project/obs-websocket) ## Special thanks From 7a7a8b7ed123f52fb7467cba64b27be9bc4c5e28 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 14 May 2020 03:19:14 -0700 Subject: [PATCH 074/278] Docs: Even more changes --- CONTRIBUTING.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a319e63d..2b2fff1d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,22 +12,22 @@ Localization happens on [Crowdin](https://crowdin.com/project/obs-websocket) ### Code Formatting Guidelines - - Function and variable names: snake_case for C names, MixedCaps for C++ names +- Function and variable names: snake_case for C names, MixedCaps for C++ names - - Request and Event names should use MixedCaps names +- Request and Event names should use MixedCaps names - - Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) +- Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) - - Tabs are 8 columns wide +- Tabs are 8 columns wide - - 80 columns max code width. (Docs can be larger) +- 80 columns max code width. (Docs can be larger) - - New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples). +- New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples). These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). ### Code Best-Practices -- Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: +- Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: ```cpp if (success) { return req->SendOKResponse(); @@ -45,16 +45,16 @@ return req->SendOKResponse(); ### Commit Guidelines - - Commits follow the 50/72 standard: - - 50 characters max for the commit title (excluding scope name) - - One empty line after the title - - Description wrapped to 72 columns max width per line. +- Commits follow the 50/72 standard: + - 50 characters max for the commit title (excluding scope name) + - One empty line after the title + - Description wrapped to 72 columns max width per line. - - Commit titles: - - Use present tense - - Prefix the title with a "scope" name - - e.g: "CI: fix wrong behaviour when packaging for OS X" - - Typical scopes: CI, General, Requests, Events, Server +- Commit titles: + - Use present tense + - Prefix the title with a "scope" name + - e.g: "CI: fix wrong behaviour when packaging for OS X" + - Typical scopes: CI, General, Requests, Events, Server **Example commit:** @@ -68,10 +68,10 @@ transitions. ### Pull Requests - - Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). - - Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) +- Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). + - Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) - - Only open a pull request if you are ready to show off your work. +- Only open a pull request if you are ready to show off your work. - - If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers), +- If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers), create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). From 60d9b72fea1e0c81e102a28ed89262614605a1c2 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 14 May 2020 03:25:34 -0700 Subject: [PATCH 075/278] Docs: Maybe it will complain less now --- CONTRIBUTING.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2b2fff1d..9871f1dc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,22 +12,22 @@ Localization happens on [Crowdin](https://crowdin.com/project/obs-websocket) ### Code Formatting Guidelines -- Function and variable names: snake_case for C names, MixedCaps for C++ names +- Function and variable names: snake_case for C names, MixedCaps for C++ names -- Request and Event names should use MixedCaps names +- Request and Event names should use MixedCaps names -- Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) +- Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) -- Tabs are 8 columns wide +- Tabs are 8 columns wide -- 80 columns max code width. (Docs can be larger) +- 80 columns max code width. (Docs can be larger) -- New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples). - These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). +- New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples). + These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). ### Code Best-Practices -- Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: +- Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: ```cpp if (success) { return req->SendOKResponse(); @@ -45,16 +45,16 @@ return req->SendOKResponse(); ### Commit Guidelines -- Commits follow the 50/72 standard: - - 50 characters max for the commit title (excluding scope name) - - One empty line after the title - - Description wrapped to 72 columns max width per line. +- Commits follow the 50/72 standard: + - 50 characters max for the commit title (excluding scope name) + - One empty line after the title + - Description wrapped to 72 columns max width per line. -- Commit titles: - - Use present tense - - Prefix the title with a "scope" name - - e.g: "CI: fix wrong behaviour when packaging for OS X" - - Typical scopes: CI, General, Requests, Events, Server +- Commit titles: + - Use present tense + - Prefix the title with a "scope" name + - e.g: "CI: fix wrong behaviour when packaging for OS X" + - Typical scopes: CI, General, Requests, Events, Server **Example commit:** @@ -68,10 +68,10 @@ transitions. ### Pull Requests -- Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). - - Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) +- Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). + - Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) -- Only open a pull request if you are ready to show off your work. +- Only open a pull request if you are ready to show off your work. -- If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers), - create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). +- If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers), + create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). From 1da7f47af95e1df776e50ca1d8a8f4aea74c2750 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 14 May 2020 03:43:18 -0700 Subject: [PATCH 076/278] Docs: Add pr template --- .github/pull_request_template.md | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..36c332c5 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,35 @@ + + + + +### Description + + +### Motivation and Context + + + + +### How Has This Been Tested? + +Tested OS(s): + +### Types of changes + + + + + + + + + +### Checklist: + + +- [ ] I have read the [**contributing** document](https://github.com/Palakis/obs-websocket/blob/4.x-current/CONTRIBUTING.md). +- [ ] My code is not on the master branch. +- [ ] The code has been tested. +- [ ] All commit messages are properly formatted and commits squashed where appropriate. +- [ ] I have included updates to all appropriate documentation. + From baba8790bc0e54842779afd9a35fc672c0847837 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 14 May 2020 04:28:57 -0700 Subject: [PATCH 077/278] testing --- CONTRIBUTING.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9871f1dc..60654035 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,22 +12,22 @@ Localization happens on [Crowdin](https://crowdin.com/project/obs-websocket) ### Code Formatting Guidelines -- Function and variable names: snake_case for C names, MixedCaps for C++ names +- Function and variable names: snake_case for C names, MixedCaps for C++ names -- Request and Event names should use MixedCaps names +- Request and Event names should use MixedCaps names -- Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) +- Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) -- Tabs are 8 columns wide +- Tabs are 8 columns wide -- 80 columns max code width. (Docs can be larger) +- 80 columns max code width. (Docs can be larger) -- New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples). - These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). +- New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples). + These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). ### Code Best-Practices -- Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: +- Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: ```cpp if (success) { return req->SendOKResponse(); @@ -45,16 +45,16 @@ return req->SendOKResponse(); ### Commit Guidelines -- Commits follow the 50/72 standard: - - 50 characters max for the commit title (excluding scope name) - - One empty line after the title - - Description wrapped to 72 columns max width per line. +- Commits follow the 50/72 standard: + - 50 characters max for the commit title (excluding scope name) + - One empty line after the title + - Description wrapped to 72 columns max width per line. -- Commit titles: - - Use present tense - - Prefix the title with a "scope" name - - e.g: "CI: fix wrong behaviour when packaging for OS X" - - Typical scopes: CI, General, Requests, Events, Server +- Commit titles: + - Use present tense + - Prefix the title with a "scope" name + - e.g: "CI: fix wrong behaviour when packaging for OS X" + - Typical scopes: CI, General, Requests, Events, Server **Example commit:** @@ -68,10 +68,10 @@ transitions. ### Pull Requests -- Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). - - Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) +- Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). + - Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) -- Only open a pull request if you are ready to show off your work. +- Only open a pull request if you are ready to show off your work. -- If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers), - create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). +- If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers), + create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). From a1ea84b28669f679a2e914151bea09129ea67384 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 14 May 2020 04:46:12 -0700 Subject: [PATCH 078/278] Docs: Its finally complaining less --- .github/pull_request_template.md | 10 +++++----- CONTRIBUTING.md | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 36c332c5..d6fe377d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -27,9 +27,9 @@ Tested OS(s): ### Checklist: -- [ ] I have read the [**contributing** document](https://github.com/Palakis/obs-websocket/blob/4.x-current/CONTRIBUTING.md). -- [ ] My code is not on the master branch. -- [ ] The code has been tested. -- [ ] All commit messages are properly formatted and commits squashed where appropriate. -- [ ] I have included updates to all appropriate documentation. +- [ ] I have read the [**contributing** document](https://github.com/Palakis/obs-websocket/blob/4.x-current/CONTRIBUTING.md). +- [ ] My code is not on the master branch. +- [ ] The code has been tested. +- [ ] All commit messages are properly formatted and commits squashed where appropriate. +- [ ] I have included updates to all appropriate documentation. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 60654035..1931c0c6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,15 +46,15 @@ return req->SendOKResponse(); ### Commit Guidelines - Commits follow the 50/72 standard: - - 50 characters max for the commit title (excluding scope name) - - One empty line after the title - - Description wrapped to 72 columns max width per line. + - 50 characters max for the commit title (excluding scope name) + - One empty line after the title + - Description wrapped to 72 columns max width per line. - Commit titles: - - Use present tense - - Prefix the title with a "scope" name - - e.g: "CI: fix wrong behaviour when packaging for OS X" - - Typical scopes: CI, General, Requests, Events, Server + - Use present tense + - Prefix the title with a "scope" name + - e.g: "CI: fix wrong behaviour when packaging for OS X" + - Typical scopes: CI, General, Requests, Events, Server **Example commit:** @@ -69,7 +69,7 @@ transitions. ### Pull Requests - Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). - - Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) + - Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) - Only open a pull request if you are ready to show off your work. From 5534f9a2486d7370dda42212815855600435f3bd Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Thu, 14 May 2020 20:15:44 +0000 Subject: [PATCH 079/278] docs(ci): Update protocol.md - eb20654 [skip ci] --- docs/generated/comments.json | 41 ++++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 21 ++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 28f82e48..dc7f68c2 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -8678,6 +8678,47 @@ "lead": "", "type": "class", "examples": [] + }, + { + "subheads": [], + "description": "Get the position of the current transition.", + "return": "{double} `position` current transition position. This value will be between 0.0 and 1.0. Note: Transition returns 1.0 when not active.", + "api": "requests", + "name": "GetTransitionPosition", + "category": "transitions", + "since": "4.8.0", + "returns": [ + { + "type": "double", + "name": "position", + "description": "current transition position. This value will be between 0.0 and 1.0. Note: Transition returns 1.0 when not active." + } + ], + "names": [ + { + "name": "", + "description": "GetTransitionPosition" + } + ], + "categories": [ + { + "name": "", + "description": "transitions" + } + ], + "sinces": [ + { + "name": "", + "description": "4.8.0" + } + ], + "heading": { + "level": 2, + "text": "GetTransitionPosition" + }, + "lead": "", + "type": "class", + "examples": [] } ] } diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 92db4587..2f553495 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -215,6 +215,7 @@ auth_response = base64_encode(auth_response_hash) + [SetCurrentTransition](#setcurrenttransition) + [SetTransitionDuration](#settransitionduration) + [GetTransitionDuration](#gettransitionduration) + + [GetTransitionPosition](#gettransitionposition) @@ -3372,3 +3373,23 @@ _No specified parameters._ --- +### GetTransitionPosition + + +- Added in v4.8.0 + +Get the position of the current transition. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `position` | _double_ | current transition position. This value will be between 0.0 and 1.0. Note: Transition returns 1.0 when not active. | + + +--- + From ba4e5959b10d274eb3498afdd7981fb310331d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Thu, 14 May 2020 22:24:58 +0200 Subject: [PATCH 080/278] requests(Get/SetVolume): code nitpicks --- src/WSRequestHandler_Sources.cpp | 34 ++++++++++++-------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 44bf1b7c..3ca1a82b 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -176,11 +176,6 @@ RpcResponse WSRequestHandler::GetVolume(const RpcRequest& request) return request.failed("missing request parameters"); } - bool useDecibel = false; - if (request.hasField("useDecibel")) { - useDecibel = obs_data_get_bool(request.parameters(), "useDecibel"); - } - QString sourceName = obs_data_get_string(request.parameters(), "source"); if (sourceName.isEmpty()) { return request.failed("invalid request parameters"); @@ -191,16 +186,17 @@ RpcResponse WSRequestHandler::GetVolume(const RpcRequest& request) return request.failed("specified source doesn't exist"); } + float volume = obs_source_get_volume(source); + + bool useDecibel = obs_data_get_bool(request.parameters(), "useDecibel"); + if (useDecibel) { + volume = obs_mul_to_db(volume); + } + OBSDataAutoRelease response = obs_data_create(); obs_data_set_string(response, "name", obs_source_get_name(source)); - if (!useDecibel) { - obs_data_set_double(response, "volume", obs_source_get_volume(source)); - } else { - float volume = obs_source_get_volume(source); - obs_data_set_double(response, "volume", obs_mul_to_db(volume)); - } + obs_data_set_double(response, "volume", volume); obs_data_set_bool(response, "muted", obs_source_muted(source)); - return request.success(response); } @@ -222,10 +218,7 @@ RpcResponse WSRequestHandler::SetVolume(const RpcRequest& request) return request.failed("missing request parameters"); } - bool useDecibel = false; - if (request.hasField("useDecibel")) { - useDecibel = obs_data_get_bool(request.parameters(), "useDecibel"); - } + bool useDecibel = obs_data_get_bool(request.parameters(), "useDecibel"); QString sourceName = obs_data_get_string(request.parameters(), "source"); float sourceVolume = obs_data_get_double(request.parameters(), "volume"); @@ -240,12 +233,11 @@ RpcResponse WSRequestHandler::SetVolume(const RpcRequest& request) return request.failed("specified source doesn't exist"); } - if (!useDecibel) { - obs_source_set_volume(source, sourceVolume); - } else { - float mul = obs_db_to_mul(sourceVolume); - obs_source_set_volume(source, mul); + if (useDecibel) { + sourceVolume = obs_db_to_mul(sourceVolume); } + obs_source_set_volume(source, sourceVolume); + return request.success(); } From 1c85894472f88cbb4c9c409d6379f13d4ddbb6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Thu, 14 May 2020 22:31:10 +0200 Subject: [PATCH 081/278] request(SetVolume): simplified params check --- src/WSRequestHandler_Sources.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 3ca1a82b..7f4df0d1 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -223,8 +223,9 @@ RpcResponse WSRequestHandler::SetVolume(const RpcRequest& request) QString sourceName = obs_data_get_string(request.parameters(), "source"); float sourceVolume = obs_data_get_double(request.parameters(), "volume"); - if ((useDecibel && sourceVolume > 0.0) || -(!useDecibel && (sourceVolume < 0.0 || sourceVolume > 1.0)) || (sourceName.isEmpty())) { + bool isNotValidDecibel = (useDecibel && sourceVolume > 0.0); + bool isNotValidMul = (!useDecibel && (sourceVolume < 0.0 || sourceVolume > 1.0)); + if (sourceName.isEmpty() || isNotValidDecibel || isNotValidMul) { return request.failed("invalid request parameters"); } From 344f5bda697869513bf133279c09a0887777072a Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Thu, 14 May 2020 20:48:16 +0000 Subject: [PATCH 082/278] docs(ci): Update protocol.md - 33b080b [skip ci] --- docs/generated/comments.json | 28 +++++++++++++++++++++------- docs/generated/protocol.md | 10 ++++++---- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index dc7f68c2..6bc300a7 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -5642,11 +5642,14 @@ }, { "subheads": [], - "description": "Get the volume of the specified source.", - "param": "{String} `source` Source name.", + "description": "Get the volume of the specified source. Default response uses mul format, NOT SLIDER PERCENTAGE.", + "param": [ + "{String} `source` Source name.", + "{boolean (optional)} `useDecibel` Output volume in decibels of attenuation instead of amplitude/mul." + ], "return": [ "{String} `name` Source name.", - "{double} `volume` Volume of the source. Between `0.0` and `1.0`.", + "{double} `volume` Volume of the source. Between `0.0` and `1.0` if using mul, under `0.0` if using dB (since it is attenuating).", "{boolean} `muted` Indicates whether the source is muted." ], "api": "requests", @@ -5662,7 +5665,7 @@ { "type": "double", "name": "volume", - "description": "Volume of the source. Between `0.0` and `1.0`." + "description": "Volume of the source. Between `0.0` and `1.0` if using mul, under `0.0` if using dB (since it is attenuating)." }, { "type": "boolean", @@ -5675,6 +5678,11 @@ "type": "String", "name": "source", "description": "Source name." + }, + { + "type": "boolean (optional)", + "name": "useDecibel", + "description": "Output volume in decibels of attenuation instead of amplitude/mul." } ], "names": [ @@ -5705,10 +5713,11 @@ }, { "subheads": [], - "description": "Set the volume of the specified source.", + "description": "Set the volume of the specified source. Default request format uses mul, NOT SLIDER PERCENTAGE.", "param": [ "{String} `source` Source name.", - "{double} `volume` Desired volume. Must be between `0.0` and `1.0`." + "{double} `volume` Desired volume. Must be between `0.0` and `1.0` for mul, and under 0.0 for dB. Note: OBS will interpret dB values under -100.0 as Inf.", + "{boolean (optional)} `useDecibel` Interperet `volume` data as decibels instead of amplitude/mul." ], "api": "requests", "name": "SetVolume", @@ -5723,7 +5732,12 @@ { "type": "double", "name": "volume", - "description": "Desired volume. Must be between `0.0` and `1.0`." + "description": "Desired volume. Must be between `0.0` and `1.0` for mul, and under 0.0 for dB. Note: OBS will interpret dB values under -100.0 as Inf." + }, + { + "type": "boolean (optional)", + "name": "useDecibel", + "description": "Interperet `volume` data as decibels instead of amplitude/mul." } ], "names": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 2f553495..08b8efe5 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -2267,13 +2267,14 @@ _No specified parameters._ - Added in v4.0.0 -Get the volume of the specified source. +Get the volume of the specified source. Default response uses mul format, NOT SLIDER PERCENTAGE. **Request Fields:** | Name | Type | Description | | ---- | :---: | ------------| | `source` | _String_ | Source name. | +| `useDecibel` | _boolean (optional)_ | Output volume in decibels of attenuation instead of amplitude/mul. | **Response Items:** @@ -2281,7 +2282,7 @@ Get the volume of the specified source. | Name | Type | Description | | ---- | :---: | ------------| | `name` | _String_ | Source name. | -| `volume` | _double_ | Volume of the source. Between `0.0` and `1.0`. | +| `volume` | _double_ | Volume of the source. Between `0.0` and `1.0` if using mul, under `0.0` if using dB (since it is attenuating). | | `muted` | _boolean_ | Indicates whether the source is muted. | @@ -2292,14 +2293,15 @@ Get the volume of the specified source. - Added in v4.0.0 -Set the volume of the specified source. +Set the volume of the specified source. Default request format uses mul, NOT SLIDER PERCENTAGE. **Request Fields:** | Name | Type | Description | | ---- | :---: | ------------| | `source` | _String_ | Source name. | -| `volume` | _double_ | Desired volume. Must be between `0.0` and `1.0`. | +| `volume` | _double_ | Desired volume. Must be between `0.0` and `1.0` for mul, and under 0.0 for dB. Note: OBS will interpret dB values under -100.0 as Inf. | +| `useDecibel` | _boolean (optional)_ | Interperet `volume` data as decibels instead of amplitude/mul. | **Response Items:** From 0eaa9187ead361d78079d874a181401b3f1400f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 15 May 2020 02:35:53 +0200 Subject: [PATCH 083/278] requests(TakeSourceScreenshot): improved docs --- src/WSRequestHandler_Sources.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 06f33380..5e90e158 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1437,7 +1437,7 @@ RpcResponse WSRequestHandler::SetSourceFilterVisibility(const RpcRequest& reques * @param {String} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name. * @param {String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module) * @param {String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path. -* @param {String (optional)} `fileFormat` Format to save the image file as. If not specified, tries to guess based on file extension. +* @param {String (optional)} `fileFormat` Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension. * @param {int (optional)} `compressionQuality` Compression ratio between -1 and 100 to write the image with. -1 is automatic, 1 is smallest file/most compression, 100 is largest file/least compression. Varies with image type. * @param {int (optional)} `width` Screenshot width. Defaults to the source's base width. * @param {int (optional)} `height` Screenshot height. Defaults to the source's base height. @@ -1581,7 +1581,7 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { QFileInfo filePathInfo(filePathStr); QString absoluteFilePath = filePathInfo.absoluteFilePath(); - const char* fileFormat; + const char* fileFormat = nullptr; if (request.hasField("fileFormat")) { fileFormat = obs_data_get_string(request.parameters(), "fileFormat"); QByteArrayList supportedFormats = QImageWriter::supportedImageFormats(); @@ -1591,9 +1591,6 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { return request.failed(errorMessage.toUtf8()); } } - else { - fileFormat = nullptr; - } if (!sourceImage.save(absoluteFilePath, fileFormat, compressionQuality)) { return request.failed("Image save failed"); From 133d3fdda7bdd974c3774a917728cbaf337b9330 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Fri, 15 May 2020 00:48:51 +0000 Subject: [PATCH 084/278] docs(ci): Update protocol.md - 53e98db [skip ci] --- docs/generated/comments.json | 12 ++++++++++++ docs/generated/protocol.md | 2 ++ 2 files changed, 14 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 6bc300a7..d2929e46 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -7631,6 +7631,8 @@ "{String} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name.", "{String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be \"png\", \"jpg\", \"jpeg\" or \"bmp\" (or any other value supported by Qt's Image module)", "{String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path.", + "{String (optional)} `fileFormat` Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension.", + "{int (optional)} `compressionQuality` Compression ratio between -1 and 100 to write the image with. -1 is automatic, 1 is smallest file/most compression, 100 is largest file/least compression. Varies with image type.", "{int (optional)} `width` Screenshot width. Defaults to the source's base width.", "{int (optional)} `height` Screenshot height. Defaults to the source's base height." ], @@ -7676,6 +7678,16 @@ "name": "saveToFilePath", "description": "Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path." }, + { + "type": "String (optional)", + "name": "fileFormat", + "description": "Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension." + }, + { + "type": "int (optional)", + "name": "compressionQuality", + "description": "Compression ratio between -1 and 100 to write the image with. -1 is automatic, 1 is smallest file/most compression, 100 is largest file/least compression. Varies with image type." + }, { "type": "int (optional)", "name": "width", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 08b8efe5..13b7c50b 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -2935,6 +2935,8 @@ preserved if only one of these two parameters is specified. | `sourceName` | _String_ | Source name. Note that, since scenes are also sources, you can also provide a scene name. | | `embedPictureFormat` | _String (optional)_ | Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module) | | `saveToFilePath` | _String (optional)_ | Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path. | +| `fileFormat` | _String (optional)_ | Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension. | +| `compressionQuality` | _int (optional)_ | Compression ratio between -1 and 100 to write the image with. -1 is automatic, 1 is smallest file/most compression, 100 is largest file/least compression. Varies with image type. | | `width` | _int (optional)_ | Screenshot width. Defaults to the source's base width. | | `height` | _int (optional)_ | Screenshot height. Defaults to the source's base height. | From 0b0560019a3426539f7b67ef49cfda92b3bba2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 15 May 2020 04:08:40 +0200 Subject: [PATCH 085/278] requests(Get/SetAudioMonitor): change values to camelCase for consistency --- src/WSRequestHandler_Sources.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 26db95f6..73ddae9f 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1431,7 +1431,7 @@ RpcResponse WSRequestHandler::SetSourceFilterVisibility(const RpcRequest& reques * * @param {String} `sourceName` Source name. * -* @return {String} `monitorType` The monitor type in use. Options: `none`, `monitor_only`, `monitor_and_output`. +* @return {String} `monitorType` The monitor type in use. Options: `none`, `monitorOnly`, `monitorAndOutput`. * * @api requests * @name GetAudioMonitor @@ -1464,10 +1464,10 @@ RpcResponse WSRequestHandler::GetAudioMonitor(const RpcRequest& request) monitorType = "none"; break; case OBS_MONITORING_TYPE_MONITOR_ONLY: - monitorType = "monitor_only"; + monitorType = "monitorOnly"; break; case OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT: - monitorType = "monitor_and_output"; + monitorType = "monitorAndOutput"; break; default: monitorType = "unknown"; @@ -1482,7 +1482,7 @@ RpcResponse WSRequestHandler::GetAudioMonitor(const RpcRequest& request) * Set the audio monitoring type of the specified source. * * @param {String} `sourceName` Source name. -* @param {String} `monitorType` The monitor type to use. Options: `none`, `monitor_only`, `monitor_and_output`. +* @param {String} `monitorType` The monitor type to use. Options: `none`, `monitorOnly`, `monitorAndOutput`. * * @api requests * @name SetAudioMonitor @@ -1509,9 +1509,9 @@ RpcResponse WSRequestHandler::SetAudioMonitor(const RpcRequest& request) if (monitorType == "none") { obs_source_set_monitoring_type(source, OBS_MONITORING_TYPE_NONE); - } else if (monitorType == "monitor_only") { + } else if (monitorType == "monitorOnly") { obs_source_set_monitoring_type(source, OBS_MONITORING_TYPE_MONITOR_ONLY); - } else if (monitorType == "monitor_and_output") { + } else if (monitorType == "monitorAndOutput") { obs_source_set_monitoring_type(source, OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT); } else { return request.failed("invalid monitorType"); From f8c8e42ae97675298b5bf1b874a2d65c96d6c602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 15 May 2020 04:10:40 +0200 Subject: [PATCH 086/278] requests: rename Get/SetAudioMonitor to Get/SetAudioMonitorType --- src/WSRequestHandler.cpp | 4 ++-- src/WSRequestHandler.h | 4 ++-- src/WSRequestHandler_Sources.cpp | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 58cddc23..86a9df70 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -97,8 +97,8 @@ const QHash WSRequestHandler::messageMap { { "GetSourceTypesList", &WSRequestHandler::GetSourceTypesList }, { "GetSourceSettings", &WSRequestHandler::GetSourceSettings }, { "SetSourceSettings", &WSRequestHandler::SetSourceSettings }, - { "GetAudioMonitor", &WSRequestHandler::GetAudioMonitor }, - { "SetAudioMonitor", &WSRequestHandler::SetAudioMonitor }, + { "GetAudioMonitorType", &WSRequestHandler::GetAudioMonitorType }, + { "SetAudioMonitorType", &WSRequestHandler::SetAudioMonitorType }, { "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot }, { "GetSourceFilters", &WSRequestHandler::GetSourceFilters }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index b55c2fb5..300915e7 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -112,8 +112,8 @@ class WSRequestHandler { RpcResponse GetSourceTypesList(const RpcRequest&); RpcResponse GetSourceSettings(const RpcRequest&); RpcResponse SetSourceSettings(const RpcRequest&); - RpcResponse GetAudioMonitor(const RpcRequest&); - RpcResponse SetAudioMonitor(const RpcRequest&); + RpcResponse GetAudioMonitorType(const RpcRequest&); + RpcResponse SetAudioMonitorType(const RpcRequest&); RpcResponse TakeSourceScreenshot(const RpcRequest&); RpcResponse GetSourceFilters(const RpcRequest&); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 73ddae9f..f71a3518 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1434,11 +1434,11 @@ RpcResponse WSRequestHandler::SetSourceFilterVisibility(const RpcRequest& reques * @return {String} `monitorType` The monitor type in use. Options: `none`, `monitorOnly`, `monitorAndOutput`. * * @api requests -* @name GetAudioMonitor +* @name GetAudioMonitorType * @category sources * @since 4.8.0 */ -RpcResponse WSRequestHandler::GetAudioMonitor(const RpcRequest& request) +RpcResponse WSRequestHandler::GetAudioMonitorType(const RpcRequest& request) { if (!request.hasField("sourceName")) { return request.failed("missing request parameters"); @@ -1485,11 +1485,11 @@ RpcResponse WSRequestHandler::GetAudioMonitor(const RpcRequest& request) * @param {String} `monitorType` The monitor type to use. Options: `none`, `monitorOnly`, `monitorAndOutput`. * * @api requests -* @name SetAudioMonitor +* @name SetAudioMonitorType * @category sources * @since 4.8.0 */ -RpcResponse WSRequestHandler::SetAudioMonitor(const RpcRequest& request) +RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request) { if (!request.hasField("sourceName") || !request.hasField("monitorType")) { return request.failed("missing request parameters"); From 590943ed957386a483ec6fca2a8178f7bbd5a0d3 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Fri, 15 May 2020 02:20:46 +0000 Subject: [PATCH 087/278] docs(ci): Update protocol.md - e9c17c9 [skip ci] --- docs/generated/comments.json | 98 ++++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 46 +++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index d2929e46..a6e24f82 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -7624,6 +7624,104 @@ "type": "class", "examples": [] }, + { + "subheads": [], + "description": "Get the audio monitoring type of the specified source.", + "param": "{String} `sourceName` Source name.", + "return": "{String} `monitorType` The monitor type in use. Options: `none`, `monitorOnly`, `monitorAndOutput`.", + "api": "requests", + "name": "GetAudioMonitorType", + "category": "sources", + "since": "4.8.0", + "returns": [ + { + "type": "String", + "name": "monitorType", + "description": "The monitor type in use. Options: `none`, `monitorOnly`, `monitorAndOutput`." + } + ], + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + } + ], + "names": [ + { + "name": "", + "description": "GetAudioMonitorType" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "4.8.0" + } + ], + "heading": { + "level": 2, + "text": "GetAudioMonitorType" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Set the audio monitoring type of the specified source.", + "param": [ + "{String} `sourceName` Source name.", + "{String} `monitorType` The monitor type to use. Options: `none`, `monitorOnly`, `monitorAndOutput`." + ], + "api": "requests", + "name": "SetAudioMonitorType", + "category": "sources", + "since": "4.8.0", + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + }, + { + "type": "String", + "name": "monitorType", + "description": "The monitor type to use. Options: `none`, `monitorOnly`, `monitorAndOutput`." + } + ], + "names": [ + { + "name": "", + "description": "SetAudioMonitorType" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "4.8.0" + } + ], + "heading": { + "level": 2, + "text": "SetAudioMonitorType" + }, + "lead": "", + "type": "class", + "examples": [] + }, { "subheads": [], "description": "\n\nAt least `embedPictureFormat` or `saveToFilePath` must be specified.\n\nClients can specify `width` and `height` parameters to receive scaled pictures. Aspect ratio is\npreserved if only one of these two parameters is specified.", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 13b7c50b..b7ff296b 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -191,6 +191,8 @@ auth_response = base64_encode(auth_response_hash) + [MoveSourceFilter](#movesourcefilter) + [SetSourceFilterSettings](#setsourcefiltersettings) + [SetSourceFilterVisibility](#setsourcefiltervisibility) + + [GetAudioMonitorType](#getaudiomonitortype) + + [SetAudioMonitorType](#setaudiomonitortype) + [TakeSourceScreenshot](#takesourcescreenshot) * [Streaming](#streaming-1) + [GetStreamingStatus](#getstreamingstatus) @@ -2910,6 +2912,50 @@ Change the visibility/enabled state of a filter | `filterEnabled` | _Boolean_ | New filter state | +**Response Items:** + +_No additional response items._ + +--- + +### GetAudioMonitorType + + +- Added in v4.8.0 + +Get the audio monitoring type of the specified source. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `monitorType` | _String_ | The monitor type in use. Options: `none`, `monitorOnly`, `monitorAndOutput`. | + + +--- + +### SetAudioMonitorType + + +- Added in v4.8.0 + +Set the audio monitoring type of the specified source. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | +| `monitorType` | _String_ | The monitor type to use. Options: `none`, `monitorOnly`, `monitorAndOutput`. | + + **Response Items:** _No additional response items._ From 631ed022ed99856632b29998b458c594d2391cdd Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 15 May 2020 16:13:24 -0700 Subject: [PATCH 088/278] Requests: Add SetSourceName (renaming sources) Adds the `SetSourceName` request which can be used to rename sources. Also works on scenes since scenes in OBS are also sources. --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_Sources.cpp | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 05528676..e07b099c 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -91,6 +91,7 @@ const QHash WSRequestHandler::messageMap { { "ToggleMute", &WSRequestHandler::ToggleMute }, { "SetMute", &WSRequestHandler::SetMute }, { "GetMute", &WSRequestHandler::GetMute }, + { "SetSourceName", &WSRequestHandler::SetSourceName }, { "SetSyncOffset", &WSRequestHandler::SetSyncOffset }, { "GetSyncOffset", &WSRequestHandler::GetSyncOffset }, { "GetSpecialSources", &WSRequestHandler::GetSpecialSources }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index e5f6e173..3ff9576b 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -108,6 +108,7 @@ class WSRequestHandler { RpcResponse ToggleMute(const RpcRequest&); RpcResponse SetMute(const RpcRequest&); RpcResponse GetMute(const RpcRequest&); + RpcResponse SetSourceName(const RpcRequest&); RpcResponse SetSyncOffset(const RpcRequest&); RpcResponse GetSyncOffset(const RpcRequest&); RpcResponse GetSpecialSources(const RpcRequest&); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index a9ef5bef..2564460f 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -341,6 +341,46 @@ RpcResponse WSRequestHandler::ToggleMute(const RpcRequest& request) return request.success(); } +/** +* Sets (aka rename) the name of a source. Also works with scenes since scenes are technically sources in OBS. +* +* Note: If the new name already exists as a source, OBS will automatically modify the name to not interfere. +* +* @param {String} `sourceName` Source name. +* @param {String} `newName` New source name. +* +* @api requests +* @name SetSourceName +* @category sources +* @since 4.8.0 +*/ +RpcResponse WSRequestHandler::SetSourceName(const RpcRequest& request) +{ + if (!request.hasField("sourceName") || !request.hasField("newName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + QString newName = obs_data_get_string(request.parameters(), "newName"); + if (sourceName.isEmpty() || newName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + OBSSourceAutoRelease existingSource = obs_get_source_by_name(newName.toUtf8()); + if (!existingSource) { // OBS is supposed to automatically rename colliding source names, but it doesn't. So this gets to be the solution for now. + obs_source_set_name(source, newName.toUtf8()); + + return request.success(); + } else { + return request.failed("a source with that newSourceName already exists"); + } +} + /** * Set the audio sync offset of a specified source. * From e3e62c3903f7142bad93042abc4b660fa0c945bd Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 15 May 2020 16:52:08 -0700 Subject: [PATCH 089/278] General: Fix a few warnings Fix a few build warnings on windows because im ocd --- src/WSEvents.cpp | 4 ++-- src/WSRequestHandler_Scenes.cpp | 2 +- src/WSRequestHandler_Sources.cpp | 2 +- src/WSRequestHandler_Streaming.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 37ada8ee..38257bb1 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -348,7 +348,7 @@ void WSEvents::hookTransitionPlaybackEvents() { obs_frontend_source_list transitions = {}; obs_frontend_get_transitions(&transitions); - for (int i = 0; i < transitions.sources.num; i++) { + for (uint i = 0; i < transitions.sources.num; i++) { obs_source_t* transition = transitions.sources.array[i]; signal_handler_t* sh = obs_source_get_signal_handler(transition); signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this); @@ -366,7 +366,7 @@ void WSEvents::unhookTransitionPlaybackEvents() { obs_frontend_source_list transitions = {}; obs_frontend_get_transitions(&transitions); - for (int i = 0; i < transitions.sources.num; i++) { + for (uint i = 0; i < transitions.sources.num; i++) { obs_source_t* transition = transitions.sources.array[i]; signal_handler_t* sh = obs_source_get_signal_handler(transition); signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this); diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp index 40693a78..5fffdb51 100644 --- a/src/WSRequestHandler_Scenes.cpp +++ b/src/WSRequestHandler_Scenes.cpp @@ -121,7 +121,7 @@ RpcResponse WSRequestHandler::ReorderSceneItems(const RpcRequest& request) { struct obs_sceneitem_order_info info; size_t itemCount = obs_data_array_count(ctx->items); - for (int i = 0; i < itemCount; i++) { + for (uint i = 0; i < itemCount; i++) { OBSDataAutoRelease item = obs_data_array_item(ctx->items, i); OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index a9ef5bef..c5d1cf9f 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1633,7 +1633,7 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { gs_stage_texture(stagesurface, gs_texrender_get_texture(texrender)); if (gs_stagesurface_map(stagesurface, &videoData, &videoLinesize)) { int linesize = sourceImage.bytesPerLine(); - for (int y = 0; y < imgHeight; y++) { + for (uint y = 0; y < imgHeight; y++) { memcpy(sourceImage.scanLine(y), videoData + (y * videoLinesize), linesize); } gs_stagesurface_unmap(stagesurface); diff --git a/src/WSRequestHandler_Streaming.cpp b/src/WSRequestHandler_Streaming.cpp index 744a691f..c8f5732e 100644 --- a/src/WSRequestHandler_Streaming.cpp +++ b/src/WSRequestHandler_Streaming.cpp @@ -103,10 +103,10 @@ RpcResponse WSRequestHandler::StartStreaming(const RpcRequest& request) { && obs_data_has_user_value(newSettings, "key")) { const char* key = obs_data_get_string(newSettings, "key"); - int keylen = strlen(key); + size_t keylen = strlen(key); bool hasQuestionMark = false; - for (int i = 0; i < keylen; i++) { + for (size_t i = 0; i < keylen; i++) { if (key[i] == '?') { hasQuestionMark = true; break; From 7bb91da1d094efc8f00a8d1aa26f4b6198bcd775 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 15 May 2020 16:30:01 -0700 Subject: [PATCH 090/278] Events: Add sourceType string to SourceRenamed Since sources in OBS are also scenes, provide the user a string so that they can differentiate. --- src/WSEvents.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 37ada8ee..4cf069d3 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -1177,6 +1177,7 @@ void WSEvents::OnSourceAudioMixersChanged(void* param, calldata_t* data) { * * @return {String} `previousName` Previous source name * @return {String} `newName` New source name + * @return {String} `sourceType` Type of source (input, scene, filter, transition) * * @api events * @name SourceRenamed @@ -1201,6 +1202,8 @@ void WSEvents::OnSourceRename(void* param, calldata_t* data) { OBSDataAutoRelease fields = obs_data_create(); obs_data_set_string(fields, "previousName", previousName); obs_data_set_string(fields, "newName", newName); + obs_data_set_string(fields, "sourceType", + sourceTypeToString(obs_source_get_type(source))); // TODO: Split into dedicated events for source/scene. Only doing it this way for backwards compatability until 5.0 self->broadcastUpdate("SourceRenamed", fields); } From 31a085db736a3252216305fc1d2944af1dcc9964 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 16 May 2020 12:11:26 -0700 Subject: [PATCH 091/278] Docs: Update `@since` items --- src/WSRequestHandler_MediaControl.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp index 0298e533..89ff5f35 100644 --- a/src/WSRequestHandler_MediaControl.cpp +++ b/src/WSRequestHandler_MediaControl.cpp @@ -11,7 +11,7 @@ * @api requests * @name PlayPauseMedia * @category media control -* @since 4.8.0 +* @since 4.9.0 */ RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) { if ((!request.hasField("sourceName")) || (!request.hasField("playPause"))) { @@ -41,7 +41,7 @@ RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) { * @api requests * @name RestartMedia * @category media control -* @since 4.8.0 +* @since 4.9.0 */ RpcResponse WSRequestHandler::RestartMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -70,7 +70,7 @@ RpcResponse WSRequestHandler::RestartMedia(const RpcRequest& request) { * @api requests * @name StopMedia * @category media control -* @since 4.8.0 +* @since 4.9.0 */ RpcResponse WSRequestHandler::StopMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -99,7 +99,7 @@ RpcResponse WSRequestHandler::StopMedia(const RpcRequest& request) { * @api requests * @name NextMedia * @category media control -* @since 4.8.0 +* @since 4.9.0 */ RpcResponse WSRequestHandler::NextMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -128,7 +128,7 @@ RpcResponse WSRequestHandler::NextMedia(const RpcRequest& request) { * @api requests * @name PreviousMedia * @category media control -* @since 4.8.0 +* @since 4.9.0 */ RpcResponse WSRequestHandler::PreviousMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -160,7 +160,7 @@ RpcResponse WSRequestHandler::PreviousMedia(const RpcRequest& request) { * @api requests * @name GetMediaDuration * @category media control -* @since 4.8.0 +* @since 4.9.0 */ RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -192,7 +192,7 @@ RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) { * @api requests * @name GetMediaTime * @category media control -* @since 4.8.0 +* @since 4.9.0 */ RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -223,7 +223,7 @@ RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) { * @api requests * @name SetMediaTime * @category media control -* @since 4.8.0 +* @since 4.9.0 */ RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) { if (!request.hasField("sourceName") || !request.hasField("timeStamp")) { @@ -255,7 +255,7 @@ RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) { * @api requests * @name ScrubMedia * @category media control -* @since 4.8.0 +* @since 4.9.0 */ RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) { if (!request.hasField("sourceName") || !request.hasField("timeOffset")) { @@ -292,7 +292,7 @@ RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) { * @api requests * @name GetMediaState * @category media control -* @since 4.8.0 +* @since 4.9.0 */ RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) { if (!request.hasField("sourceName")) { From 7918438747d4aa555d8415a012b7fd09c5058b00 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 16 May 2020 12:56:42 -0700 Subject: [PATCH 092/278] Events: Add media events Adds the following events: - `MediaPlaying` - `MediaPaused` - `MediaRestarted` - `MediaStopped` - `MediaNext` - `MediaPrevious` - `MediaStarted` - `MediaEnded` --- src/WSEvents.cpp | 208 +++++++++++++++++++++++++++++++++++++++++++++++ src/WSEvents.h | 9 ++ 2 files changed, 217 insertions(+) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 37ada8ee..166f424a 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -275,6 +275,15 @@ void WSEvents::connectSourceSignals(obs_source_t* source) { signal_handler_connect(sh, "filter_add", OnSourceFilterAdded, this); signal_handler_connect(sh, "filter_remove", OnSourceFilterRemoved, this); signal_handler_connect(sh, "reorder_filters", OnSourceFilterOrderChanged, this); + + signal_handler_connect(sh, "media_play", OnMediaPlaying, this); + signal_handler_connect(sh, "media_pause", OnMediaPaused, this); + signal_handler_connect(sh, "media_restart", OnMediaRestarted, this); + signal_handler_connect(sh, "media_stopped", OnMediaStopped, this); + signal_handler_connect(sh, "media_next", OnMediaNext, this); + signal_handler_connect(sh, "media_previous", OnMediaPrevious, this); + signal_handler_connect(sh, "media_started", OnMediaStarted, this); + signal_handler_connect(sh, "media_ended", OnMediaEnded, this); if (sourceType == OBS_SOURCE_TYPE_SCENE) { signal_handler_connect(sh, "reorder", OnSceneReordered, this); @@ -322,6 +331,15 @@ void WSEvents::disconnectSourceSignals(obs_source_t* source) { signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this); signal_handler_disconnect(sh, "transition_stop", OnTransitionEnd, this); signal_handler_disconnect(sh, "transition_video_stop", OnTransitionVideoEnd, this); + + signal_handler_disconnect(sh, "media_play", OnMediaPlaying, this); + signal_handler_disconnect(sh, "media_pause", OnMediaPaused, this); + signal_handler_disconnect(sh, "media_restart", OnMediaRestarted, this); + signal_handler_disconnect(sh, "media_stopped", OnMediaStopped, this); + signal_handler_disconnect(sh, "media_next", OnMediaNext, this); + signal_handler_disconnect(sh, "media_previous", OnMediaPrevious, this); + signal_handler_disconnect(sh, "media_started", OnMediaStarted, this); + signal_handler_disconnect(sh, "media_ended", OnMediaEnded, this); } void WSEvents::connectFilterSignals(obs_source_t* filter) { @@ -1335,6 +1353,196 @@ void WSEvents::OnSourceFilterOrderChanged(void* param, calldata_t* data) { self->broadcastUpdate("SourceFiltersReordered", fields); } +/** + * A media source has started playing. + * + * @return {String} `sourceName` Source name + * + * @api events + * @name MediaPlaying + * @category media + * @since 4.9.0 + */ +void WSEvents::OnMediaPlaying(void* param, calldata_t* data) { + auto self = reinterpret_cast(param); + + OBSSource source = calldata_get_pointer(data, "source"); + if (!source) { + return; + } + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); + self->broadcastUpdate("MediaPlaying", fields); +} + +/** + * A media source has been paused. + * + * @return {String} `sourceName` Source name + * + * @api events + * @name MediaPaused + * @category media + * @since 4.9.0 + */ +void WSEvents::OnMediaPaused(void* param, calldata_t* data) { + auto self = reinterpret_cast(param); + + OBSSource source = calldata_get_pointer(data, "source"); + if (!source) { + return; + } + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); + self->broadcastUpdate("MediaPaused", fields); +} + +/** + * A media source has been restarted. + * + * @return {String} `sourceName` Source name + * + * @api events + * @name MediaRestarted + * @category media + * @since 4.9.0 + */ +void WSEvents::OnMediaRestarted(void* param, calldata_t* data) { + auto self = reinterpret_cast(param); + + OBSSource source = calldata_get_pointer(data, "source"); + if (!source) { + return; + } + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); + self->broadcastUpdate("MediaRestarted", fields); +} + +/** + * A media source has been stopped. + * + * @return {String} `sourceName` Source name + * + * @api events + * @name MediaStopped + * @category media + * @since 4.9.0 + */ +void WSEvents::OnMediaStopped(void* param, calldata_t* data) { + auto self = reinterpret_cast(param); + + OBSSource source = calldata_get_pointer(data, "source"); + if (!source) { + return; + } + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); + self->broadcastUpdate("MediaStopped", fields); +} + +/** + * A media source has gone to the next item in the playlist. + * + * @return {String} `sourceName` Source name + * + * @api events + * @name MediaNext + * @category media + * @since 4.9.0 + */ +void WSEvents::OnMediaNext(void* param, calldata_t* data) { + auto self = reinterpret_cast(param); + + OBSSource source = calldata_get_pointer(data, "source"); + if (!source) { + return; + } + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); + self->broadcastUpdate("MediaNext", fields); +} + +/** + * A media source has gone to the previous item in the playlist. + * + * @return {String} `sourceName` Source name + * + * @api events + * @name MediaPrevious + * @category media + * @since 4.9.0 + */ +void WSEvents::OnMediaPrevious(void* param, calldata_t* data) { + auto self = reinterpret_cast(param); + + OBSSource source = calldata_get_pointer(data, "source"); + if (!source) { + return; + } + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); + self->broadcastUpdate("MediaPrevious", fields); +} + +/** + * A media source has been started. (Does not mean that it is playing. See [`MediaPlaying`](#mediaplaying)) + * + * Note: For VLC, this means that the source and therefore playlist has started. + * For the normal Media Source, this just means that the source has been started. + * + * @return {String} `sourceName` Source name + * + * @api events + * @name MediaStarted + * @category media + * @since 4.9.0 + */ +void WSEvents::OnMediaStarted(void* param, calldata_t* data) { + auto self = reinterpret_cast(param); + + OBSSource source = calldata_get_pointer(data, "source"); + if (!source) { + return; + } + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); + self->broadcastUpdate("MediaStarted", fields); +} + +/** + * A media source has ended. + * + * Note: For VLC, this means that the source and therefore playlist has ended. + * For the normal Media Source, this just means that the source has been stopped/ended. + * + * @return {String} `sourceName` Source name + * + * @api events + * @name MediaEnded + * @category media + * @since 4.9.0 + */ +void WSEvents::OnMediaEnded(void* param, calldata_t* data) { + auto self = reinterpret_cast(param); + + OBSSource source = calldata_get_pointer(data, "source"); + if (!source) { + return; + } + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); + self->broadcastUpdate("MediaEnded", fields); +} + /** * Scene items have been reordered. * diff --git a/src/WSEvents.h b/src/WSEvents.h index 06ce7f2c..0d3a04cb 100644 --- a/src/WSEvents.h +++ b/src/WSEvents.h @@ -133,6 +133,15 @@ private: static void OnSourceFilterRemoved(void* param, calldata_t* data); static void OnSourceFilterVisibilityChanged(void* param, calldata_t* data); static void OnSourceFilterOrderChanged(void* param, calldata_t* data); + + static void OnMediaPlaying(void* param, calldata_t* data); + static void OnMediaPaused(void* param, calldata_t* data); + static void OnMediaRestarted(void* param, calldata_t* data); + static void OnMediaStopped(void* param, calldata_t* data); + static void OnMediaNext(void* param, calldata_t* data); + static void OnMediaPrevious(void* param, calldata_t* data); + static void OnMediaStarted(void* param, calldata_t* data); + static void OnMediaEnded(void* param, calldata_t* data); static void OnSceneReordered(void* param, calldata_t* data); static void OnSceneItemAdd(void* param, calldata_t* data); From 19b33a8558182ce7ab748c764d8f15dd30d5f412 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Mon, 18 May 2020 14:44:25 -0700 Subject: [PATCH 093/278] Requests: Add scene transition override requests Adds these requests: - `SetSceneTransitionOverride` - `RemoveSceneTransitionOverride` - `GetSceneTransitionOverride` --- src/WSRequestHandler.cpp | 3 + src/WSRequestHandler.h | 3 + src/WSRequestHandler_Scenes.cpp | 116 ++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 05528676..29d8e652 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -46,6 +46,9 @@ const QHash WSRequestHandler::messageMap { { "SetCurrentScene", &WSRequestHandler::SetCurrentScene }, { "GetCurrentScene", &WSRequestHandler::GetCurrentScene }, { "GetSceneList", &WSRequestHandler::GetSceneList }, + { "SetSceneTransitionOverride", &WSRequestHandler::SetSceneTransitionOverride }, + { "RemoveSceneTransitionOverride", &WSRequestHandler::RemoveSceneTransitionOverride }, + { "GetSceneTransitionOverride", &WSRequestHandler::GetSceneTransitionOverride }, { "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat { "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index e5f6e173..ed7faaed 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -64,6 +64,9 @@ class WSRequestHandler { RpcResponse SetCurrentScene(const RpcRequest&); RpcResponse GetCurrentScene(const RpcRequest&); RpcResponse GetSceneList(const RpcRequest&); + RpcResponse SetSceneTransitionOverride(const RpcRequest&); + RpcResponse RemoveSceneTransitionOverride(const RpcRequest&); + RpcResponse GetSceneTransitionOverride(const RpcRequest&); RpcResponse SetSceneItemRender(const RpcRequest&); RpcResponse SetSceneItemPosition(const RpcRequest&); diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp index 40693a78..ec4f74b0 100644 --- a/src/WSRequestHandler_Scenes.cpp +++ b/src/WSRequestHandler_Scenes.cpp @@ -148,3 +148,119 @@ RpcResponse WSRequestHandler::ReorderSceneItems(const RpcRequest& request) { return request.success(); } + +/** + * Set a scene to use a specific transition override. + * + * @param {String} `sceneName` Name of the scene to switch to. + * @param {String} `transitionName` Name of the transition to use. + * @param {int (Optional)} `transitionDuration` Duration in milliseconds of the transition if transition is not fixed. Defaults 300 if there is no current override and this value is not given. + * + * @api requests + * @name SetSceneTransitionOverride + * @category scenes + * @since 4.9.0 + */ +RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& request) { + if (!request.hasField("sceneName") || !request.hasField("transitionName")) { + return request.failed("missing request parameters"); + } + + QString sceneName = obs_data_get_string(request.parameters(), "sceneName"); + OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8()); + if (!source) { + return request.failed("requested scene does not exist"); + } else if (!(obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE)) { + return request.failed("requested scene is invalid"); + } + + QString transitionName = obs_data_get_string(request.parameters(), "transitionName"); + if (!Utils::GetTransitionFromName(transitionName)) { + return request.failed("requested transition does not exist"); + } + + obs_data_t *sourceData = obs_source_get_private_settings(source); + + int transitionDuration = obs_data_get_int(sourceData, "transition_duration"); + if (request.hasField("transitionDuration")) { + transitionDuration = obs_data_get_int(request.parameters(), "transitionDuration"); + } else if (!transitionDuration) { + transitionDuration = 300; + } + + obs_data_set_string(sourceData, "transition", transitionName.toUtf8()); + obs_data_set_int(sourceData, "transition_duration", transitionDuration); + obs_data_release(sourceData); + + return request.success(); +} + +/** + * Remove any transition override on a scene. + * + * @param {String} `sceneName` Name of the scene to switch to. + * + * @api requests + * @name RemoveSceneTransitionOverride + * @category scenes + * @since 4.9.0 + */ +RpcResponse WSRequestHandler::RemoveSceneTransitionOverride(const RpcRequest& request) { + if (!request.hasField("sceneName")) { + return request.failed("missing request parameters"); + } + + QString sceneName = obs_data_get_string(request.parameters(), "sceneName"); + OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8()); + if (!source) { + return request.failed("requested scene does not exist"); + } else if (!(obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE)) { + return request.failed("requested scene is invalid"); + } + + obs_data_t *sourceData = obs_source_get_private_settings(source); + obs_data_erase(sourceData, "transition"); + obs_data_erase(sourceData, "transition_duration"); + obs_data_release(sourceData); + + return request.success(); +} + +/** + * Get the current scene transition override. + * + * @param {String} `sceneName` Name of the scene to switch to. + * + * @return {String} `transitionName` Name of the current overriding transition. Empty string if no override is set. + * @return {int} `transitionDuration` Transition duration. `-1` if no override is set. + * + * @api requests + * @name GetSceneTransitionOverride + * @category scenes + * @since 4.9.0 + */ +RpcResponse WSRequestHandler::GetSceneTransitionOverride(const RpcRequest& request) { + if (!request.hasField("sceneName")) { + return request.failed("missing request parameters"); + } + + QString sceneName = obs_data_get_string(request.parameters(), "sceneName"); + OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8()); + if (!source) { + return request.failed("requested scene does not exist"); + } else if (!(obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE)) { + return request.failed("requested scene is invalid"); + } + + OBSDataAutoRelease data = obs_data_create(); + obs_data_t *sourceData = obs_source_get_private_settings(source); + obs_data_set_string(data, "transitionName", obs_data_get_string(sourceData, "transition")); + if (!obs_data_get_int(sourceData, "transition_duration")) { + obs_data_set_int(data, "transitionDuration", -1); + } else { + obs_data_set_int(data, "transitionDuration", obs_data_get_int(sourceData, "transition_duration")); + } + obs_data_release(sourceData); + + return request.success(data); +} \ No newline at end of file From b00a93dd6b76b42030b13ddd0c4aa66bf1f36867 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 19 May 2020 20:20:35 -0700 Subject: [PATCH 094/278] Requests: Add GetMediaSourcesList and minor refactor Adds `GetMediaSourcesList` which returns a list of all media sources, along with their individual media playback states. In the process I refactored a switch which coverts the enum to a QString into its own helper function. --- src/WSRequestHandler.cpp | 3 +- src/WSRequestHandler.h | 1 + src/WSRequestHandler_MediaControl.cpp | 115 ++++++++++++++++++-------- 3 files changed, 85 insertions(+), 34 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 702d75fc..3ef985df 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -157,7 +157,8 @@ const QHash WSRequestHandler::messageMap { { "GetMediaTime", &WSRequestHandler::GetMediaTime }, { "SetMediaTime", &WSRequestHandler::SetMediaTime }, { "ScrubMedia", &WSRequestHandler::ScrubMedia }, - { "GetMediaState", &WSRequestHandler::GetMediaState } + { "GetMediaState", &WSRequestHandler::GetMediaState }, + { "GetMediaSourcesList", &WSRequestHandler::GetMediaSourcesList } }; const QSet WSRequestHandler::authNotRequired { diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 6f31265b..7b98b314 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -175,4 +175,5 @@ class WSRequestHandler { RpcResponse SetMediaTime(const RpcRequest&); RpcResponse ScrubMedia(const RpcRequest&); RpcResponse GetMediaState(const RpcRequest&); + RpcResponse GetMediaSourcesList(const RpcRequest&); }; diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp index 89ff5f35..ea754c6a 100644 --- a/src/WSRequestHandler_MediaControl.cpp +++ b/src/WSRequestHandler_MediaControl.cpp @@ -2,6 +2,46 @@ #include "WSRequestHandler.h" +bool isMediaSource(const QString& sourceKind) +{ + return (sourceKind == "vlc_source" || sourceKind == "ffmpeg_source"); +} + +QString getSourceMediaState(obs_source_t *source) +{ + QString mediaState; + enum obs_media_state mstate = obs_source_media_get_state(source); + switch (mstate) { + case OBS_MEDIA_STATE_NONE: + mediaState = "none"; + break; + case OBS_MEDIA_STATE_PLAYING: + mediaState = "playing"; + break; + case OBS_MEDIA_STATE_OPENING: + mediaState = "opening"; + break; + case OBS_MEDIA_STATE_BUFFERING: + mediaState = "buffering"; + break; + case OBS_MEDIA_STATE_PAUSED: + mediaState = "paused"; + break; + case OBS_MEDIA_STATE_STOPPED: + mediaState = "stopped"; + break; + case OBS_MEDIA_STATE_ENDED: + mediaState = "ended"; + break; + case OBS_MEDIA_STATE_ERROR: + mediaState = "error"; + break; + default: + mediaState = "unknown"; + } + return mediaState; +} + /** * Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) * @@ -287,7 +327,7 @@ RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) { * * @param {String} `sourceName` Source name. * -* @return {String} `mediaState` The media state of the provided source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error` +* @return {String} `mediaState` The media state of the provided source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown` * * @api requests * @name GetMediaState @@ -309,39 +349,48 @@ RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) { return request.failed("specified source doesn't exist"); } - QString mediaState; - enum obs_media_state mstate = obs_source_media_get_state(source); - switch (mstate) { - case OBS_MEDIA_STATE_NONE: - mediaState = "none"; - break; - case OBS_MEDIA_STATE_PLAYING: - mediaState = "playing"; - break; - case OBS_MEDIA_STATE_OPENING: - mediaState = "opening"; - break; - case OBS_MEDIA_STATE_BUFFERING: - mediaState = "buffering"; - break; - case OBS_MEDIA_STATE_PAUSED: - mediaState = "paused"; - break; - case OBS_MEDIA_STATE_STOPPED: - mediaState = "stopped"; - break; - case OBS_MEDIA_STATE_ENDED: - mediaState = "ended"; - break; - case OBS_MEDIA_STATE_ERROR: - mediaState = "error"; - break; - default: - mediaState = "unknown"; - } - OBSDataAutoRelease response = obs_data_create(); - obs_data_set_string(response, "mediaState", mediaState.toUtf8()); + obs_data_set_string(response, "mediaState", getSourceMediaState(source).toUtf8()); return request.success(response); } + +/** +* List the media state of all media sources (vlc and media source) +* +* @return {Array} `mediaSources` Array of sources +* @return {String} `mediaSources.*.sourceName` Unique source name +* @return {String} `mediaSources.*.sourceTypeId` Non-unique source internal type (a.k.a `ffmpeg_source` or `vlc_source`) +* @return {String} `mediaSources.*.mediaState` The current state of media for that source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown` +* +* @api requests +* @name GetMediaSourcesList +* @category sources +* @since 4.9.0 +*/ +RpcResponse WSRequestHandler::GetMediaSourcesList(const RpcRequest& request) +{ + OBSDataArrayAutoRelease sourcesArray = obs_data_array_create(); + + auto sourceEnumProc = [](void* privateData, obs_source_t* source) -> bool { + obs_data_array_t* sourcesArray = (obs_data_array_t*)privateData; + + QString sourceTypeId = obs_source_get_id(source); + if (isMediaSource(sourceTypeId)) { + OBSDataAutoRelease sourceData = obs_data_create(); + obs_data_set_string(sourceData, "sourceName", obs_source_get_name(source)); + obs_data_set_string(sourceData, "sourceTypeId", sourceTypeId.toUtf8()); + + QString mediaState = getSourceMediaState(source); + obs_data_set_string(sourceData, "mediaState", mediaState.toUtf8()); + + obs_data_array_push_back(sourcesArray, sourceData); + } + return true; + }; + obs_enum_sources(sourceEnumProc, sourcesArray); + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_array(response, "mediaSources", sourcesArray); + return request.success(response); +} \ No newline at end of file From 1f75b305c34be783c531016e8cbb45f275ba509f Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Tue, 26 May 2020 07:42:47 +0000 Subject: [PATCH 095/278] docs(ci): Update protocol.md - 0802d03 [skip ci] --- docs/generated/comments.json | 8 +++++++- docs/generated/protocol.md | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index a6e24f82..5c74beb4 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -2198,7 +2198,8 @@ "description": "A source has been renamed.", "return": [ "{String} `previousName` Previous source name", - "{String} `newName` New source name" + "{String} `newName` New source name", + "{String} `sourceType` Type of source (input, scene, filter, transition)" ], "api": "events", "name": "SourceRenamed", @@ -2214,6 +2215,11 @@ "type": "String", "name": "newName", "description": "New source name" + }, + { + "type": "String", + "name": "sourceType", + "description": "Type of source (input, scene, filter, transition)" } ], "names": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index b7ff296b..5ea71f45 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -930,6 +930,7 @@ A source has been renamed. | ---- | :---: | ------------| | `previousName` | _String_ | Previous source name | | `newName` | _String_ | New source name | +| `sourceType` | _String_ | Type of source (input, scene, filter, transition) | --- From ed29ec528d7169ea68695652183292fa83a9fe99 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Tue, 26 May 2020 11:58:51 +0000 Subject: [PATCH 096/278] docs(ci): Update protocol.md - 481b26d [skip ci] --- docs/generated/comments.json | 49 ++++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 24 ++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 5c74beb4..104a2167 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -5919,6 +5919,55 @@ "type": "class", "examples": [] }, + { + "subheads": [], + "description": "\n\nNote: If the new name already exists as a source, OBS will automatically modify the name to not interfere.", + "param": [ + "{String} `sourceName` Source name.", + "{String} `newName` New source name." + ], + "api": "requests", + "name": "SetSourceName", + "category": "sources", + "since": "4.8.0", + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + }, + { + "type": "String", + "name": "newName", + "description": "New source name." + } + ], + "names": [ + { + "name": "", + "description": "SetSourceName" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "4.8.0" + } + ], + "heading": { + "level": 2, + "text": "SetSourceName" + }, + "lead": "Sets (aka rename) the name of a source. Also works with scenes since scenes are technically sources in OBS.", + "type": "class", + "examples": [] + }, { "subheads": [], "description": "Set the audio sync offset of a specified source.", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 5ea71f45..deefbb4c 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -172,6 +172,7 @@ auth_response = base64_encode(auth_response_hash) + [GetMute](#getmute) + [SetMute](#setmute) + [ToggleMute](#togglemute) + + [SetSourceName](#setsourcename) + [SetSyncOffset](#setsyncoffset) + [GetSyncOffset](#getsyncoffset) + [GetSourceSettings](#getsourcesettings) @@ -2372,6 +2373,29 @@ Inverts the mute status of a specified source. | `source` | _String_ | Source name. | +**Response Items:** + +_No additional response items._ + +--- + +### SetSourceName + + +- Added in v4.8.0 + + + +Note: If the new name already exists as a source, OBS will automatically modify the name to not interfere. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | +| `newName` | _String_ | New source name. | + + **Response Items:** _No additional response items._ From 517788e86abd04a05d734b41b20103adce3d6ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 26 May 2020 23:26:37 +0200 Subject: [PATCH 097/278] request(scenes): refactor Set/Get/RemoveSceneTransitionOverride + use current transition duration --- src/WSRequestHandler_Scenes.cpp | 63 ++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp index ec4f74b0..bebea23f 100644 --- a/src/WSRequestHandler_Scenes.cpp +++ b/src/WSRequestHandler_Scenes.cpp @@ -154,7 +154,7 @@ RpcResponse WSRequestHandler::ReorderSceneItems(const RpcRequest& request) { * * @param {String} `sceneName` Name of the scene to switch to. * @param {String} `transitionName` Name of the transition to use. - * @param {int (Optional)} `transitionDuration` Duration in milliseconds of the transition if transition is not fixed. Defaults 300 if there is no current override and this value is not given. + * @param {int (Optional)} `transitionDuration` Duration in milliseconds of the transition if transition is not fixed. Defaults to the current transition duration specified in the UI if this value is not given. * * @api requests * @name SetSceneTransitionOverride @@ -170,7 +170,10 @@ RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& reque OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8()); if (!source) { return request.failed("requested scene does not exist"); - } else if (!(obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE)) { + } + + enum obs_source_type sourceType = obs_source_get_type(source); + if (sourceType != OBS_SOURCE_TYPE_SCENE) { return request.failed("requested scene is invalid"); } @@ -179,18 +182,15 @@ RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& reque return request.failed("requested transition does not exist"); } - obs_data_t *sourceData = obs_source_get_private_settings(source); + int transitionOverrideDuration = ( + request.hasField("transitionDuration") + ? obs_data_get_int(request.parameters(), "transitionDuration") + : obs_frontend_get_transition_duration() + ); - int transitionDuration = obs_data_get_int(sourceData, "transition_duration"); - if (request.hasField("transitionDuration")) { - transitionDuration = obs_data_get_int(request.parameters(), "transitionDuration"); - } else if (!transitionDuration) { - transitionDuration = 300; - } - - obs_data_set_string(sourceData, "transition", transitionName.toUtf8()); - obs_data_set_int(sourceData, "transition_duration", transitionDuration); - obs_data_release(sourceData); + OBSDataAutoRelease sourceData = obs_source_get_private_settings(source); + obs_data_set_string(sourceData, "transition", transitionName.toUtf8().constData()); + obs_data_set_int(sourceData, "transition_duration", transitionOverrideDuration); return request.success(); } @@ -214,14 +214,16 @@ RpcResponse WSRequestHandler::RemoveSceneTransitionOverride(const RpcRequest& re OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8()); if (!source) { return request.failed("requested scene does not exist"); - } else if (!(obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE)) { + } + + enum obs_source_type sourceType = obs_source_get_type(source); + if (sourceType != OBS_SOURCE_TYPE_SCENE) { return request.failed("requested scene is invalid"); } - obs_data_t *sourceData = obs_source_get_private_settings(source); + OBSDataAutoRelease sourceData = obs_source_get_private_settings(source); obs_data_erase(sourceData, "transition"); obs_data_erase(sourceData, "transition_duration"); - obs_data_release(sourceData); return request.success(); } @@ -248,19 +250,24 @@ RpcResponse WSRequestHandler::GetSceneTransitionOverride(const RpcRequest& reque OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8()); if (!source) { return request.failed("requested scene does not exist"); - } else if (!(obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE)) { + } + + enum obs_source_type sourceType = obs_source_get_type(source); + if (sourceType != OBS_SOURCE_TYPE_SCENE) { return request.failed("requested scene is invalid"); } - OBSDataAutoRelease data = obs_data_create(); - obs_data_t *sourceData = obs_source_get_private_settings(source); - obs_data_set_string(data, "transitionName", obs_data_get_string(sourceData, "transition")); - if (!obs_data_get_int(sourceData, "transition_duration")) { - obs_data_set_int(data, "transitionDuration", -1); - } else { - obs_data_set_int(data, "transitionDuration", obs_data_get_int(sourceData, "transition_duration")); - } - obs_data_release(sourceData); + OBSDataAutoRelease sourceData = obs_source_get_private_settings(source); + const char* transitionOverrideName = obs_data_get_string(sourceData, "transition"); - return request.success(data); -} \ No newline at end of file + bool hasDurationOverride = obs_data_has_autoselect_value(sourceData, "transition_duration"); + int transitionOverrideDuration = obs_data_get_int(sourceData, "transition_duration"); + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "transitionName", transitionOverrideName); + obs_data_set_int(fields, "transitionDuration", + (hasDurationOverride ? transitionOverrideDuration : -1) + ); + + return request.success(fields); +} From 9ddc22fa940d43d667bd0c585300854d615dd0e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 26 May 2020 23:36:18 +0200 Subject: [PATCH 098/278] request(SetSceneTransitionOverride): override duration only if it is explicity set so or not already defined --- src/WSRequestHandler_Scenes.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp index bebea23f..27e0cf3c 100644 --- a/src/WSRequestHandler_Scenes.cpp +++ b/src/WSRequestHandler_Scenes.cpp @@ -154,7 +154,7 @@ RpcResponse WSRequestHandler::ReorderSceneItems(const RpcRequest& request) { * * @param {String} `sceneName` Name of the scene to switch to. * @param {String} `transitionName` Name of the transition to use. - * @param {int (Optional)} `transitionDuration` Duration in milliseconds of the transition if transition is not fixed. Defaults to the current transition duration specified in the UI if this value is not given. + * @param {int (Optional)} `transitionDuration` Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given. * * @api requests * @name SetSceneTransitionOverride @@ -182,16 +182,18 @@ RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& reque return request.failed("requested transition does not exist"); } - int transitionOverrideDuration = ( - request.hasField("transitionDuration") - ? obs_data_get_int(request.parameters(), "transitionDuration") - : obs_frontend_get_transition_duration() - ); - OBSDataAutoRelease sourceData = obs_source_get_private_settings(source); obs_data_set_string(sourceData, "transition", transitionName.toUtf8().constData()); - obs_data_set_int(sourceData, "transition_duration", transitionOverrideDuration); + if (request.hasField("transitionDuration")) { + int transitionOverrideDuration = obs_data_get_int(request.parameters(), "transitionDuration"); + obs_data_set_int(sourceData, "transition_duration", transitionOverrideDuration); + } else if(!obs_data_has_user_value(sourceData, "transition_duration")) { + obs_data_set_int(sourceData, "transition_duration", + obs_frontend_get_transition_duration() + ); + } + return request.success(); } @@ -260,7 +262,7 @@ RpcResponse WSRequestHandler::GetSceneTransitionOverride(const RpcRequest& reque OBSDataAutoRelease sourceData = obs_source_get_private_settings(source); const char* transitionOverrideName = obs_data_get_string(sourceData, "transition"); - bool hasDurationOverride = obs_data_has_autoselect_value(sourceData, "transition_duration"); + bool hasDurationOverride = obs_data_has_user_value(sourceData, "transition_duration"); int transitionOverrideDuration = obs_data_get_int(sourceData, "transition_duration"); OBSDataAutoRelease fields = obs_data_create(); From 5d12dfa3682358e82a3ac39d78fdfa6501162c4e Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Tue, 26 May 2020 21:49:10 +0000 Subject: [PATCH 099/278] docs(ci): Update protocol.md - c428201 [skip ci] --- docs/generated/comments.json | 153 +++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 69 ++++++++++++++++ 2 files changed, 222 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 104a2167..fea62b1f 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -5467,6 +5467,159 @@ "lead": "", "type": "class", "examples": [] + }, + { + "subheads": [], + "description": "Set a scene to use a specific transition override.", + "param": [ + "{String} `sceneName` Name of the scene to switch to.", + "{String} `transitionName` Name of the transition to use.", + "{int (Optional)} `transitionDuration` Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given." + ], + "api": "requests", + "name": "SetSceneTransitionOverride", + "category": "scenes", + "since": "4.9.0", + "params": [ + { + "type": "String", + "name": "sceneName", + "description": "Name of the scene to switch to." + }, + { + "type": "String", + "name": "transitionName", + "description": "Name of the transition to use." + }, + { + "type": "int (Optional)", + "name": "transitionDuration", + "description": "Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given." + } + ], + "names": [ + { + "name": "", + "description": "SetSceneTransitionOverride" + } + ], + "categories": [ + { + "name": "", + "description": "scenes" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "SetSceneTransitionOverride" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Remove any transition override on a scene.", + "param": "{String} `sceneName` Name of the scene to switch to.", + "api": "requests", + "name": "RemoveSceneTransitionOverride", + "category": "scenes", + "since": "4.9.0", + "params": [ + { + "type": "String", + "name": "sceneName", + "description": "Name of the scene to switch to." + } + ], + "names": [ + { + "name": "", + "description": "RemoveSceneTransitionOverride" + } + ], + "categories": [ + { + "name": "", + "description": "scenes" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "RemoveSceneTransitionOverride" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get the current scene transition override.", + "param": "{String} `sceneName` Name of the scene to switch to.", + "return": [ + "{String} `transitionName` Name of the current overriding transition. Empty string if no override is set.", + "{int} `transitionDuration` Transition duration. `-1` if no override is set." + ], + "api": "requests", + "name": "GetSceneTransitionOverride", + "category": "scenes", + "since": "4.9.0", + "returns": [ + { + "type": "String", + "name": "transitionName", + "description": "Name of the current overriding transition. Empty string if no override is set." + }, + { + "type": "int", + "name": "transitionDuration", + "description": "Transition duration. `-1` if no override is set." + } + ], + "params": [ + { + "type": "String", + "name": "sceneName", + "description": "Name of the scene to switch to." + } + ], + "names": [ + { + "name": "", + "description": "GetSceneTransitionOverride" + } + ], + "categories": [ + { + "name": "", + "description": "scenes" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "GetSceneTransitionOverride" + }, + "lead": "", + "type": "class", + "examples": [] } ], "sources": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index deefbb4c..f7b999a2 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -164,6 +164,9 @@ auth_response = base64_encode(auth_response_hash) + [GetCurrentScene](#getcurrentscene) + [GetSceneList](#getscenelist) + [ReorderSceneItems](#reordersceneitems) + + [SetSceneTransitionOverride](#setscenetransitionoverride) + + [RemoveSceneTransitionOverride](#removescenetransitionoverride) + + [GetSceneTransitionOverride](#getscenetransitionoverride) * [Sources](#sources-1) + [GetSourcesList](#getsourceslist) + [GetSourceTypesList](#getsourcetypeslist) @@ -2207,6 +2210,72 @@ Changes the order of scene items in the requested scene. _No additional response items._ +--- + +### SetSceneTransitionOverride + + +- Added in v4.9.0 + +Set a scene to use a specific transition override. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneName` | _String_ | Name of the scene to switch to. | +| `transitionName` | _String_ | Name of the transition to use. | +| `transitionDuration` | _int (Optional)_ | Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given. | + + +**Response Items:** + +_No additional response items._ + +--- + +### RemoveSceneTransitionOverride + + +- Added in v4.9.0 + +Remove any transition override on a scene. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneName` | _String_ | Name of the scene to switch to. | + + +**Response Items:** + +_No additional response items._ + +--- + +### GetSceneTransitionOverride + + +- Added in v4.9.0 + +Get the current scene transition override. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneName` | _String_ | Name of the scene to switch to. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `transitionName` | _String_ | Name of the current overriding transition. Empty string if no override is set. | +| `transitionDuration` | _int_ | Transition duration. `-1` if no override is set. | + + --- ## Sources From f1371034f7aae779f9cd649c902f335381858573 Mon Sep 17 00:00:00 2001 From: Alex Tsernoh Date: Wed, 27 May 2020 00:13:43 +0300 Subject: [PATCH 100/278] Request: Add CreateScene --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_Scenes.cpp | 25 +++++++++++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 27597abb..77411461 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -46,6 +46,7 @@ const QHash WSRequestHandler::messageMap { { "SetCurrentScene", &WSRequestHandler::SetCurrentScene }, { "GetCurrentScene", &WSRequestHandler::GetCurrentScene }, { "GetSceneList", &WSRequestHandler::GetSceneList }, + { "CreateScene", &WSRequestHandler::CreateScene }, { "SetSceneTransitionOverride", &WSRequestHandler::SetSceneTransitionOverride }, { "RemoveSceneTransitionOverride", &WSRequestHandler::RemoveSceneTransitionOverride }, { "GetSceneTransitionOverride", &WSRequestHandler::GetSceneTransitionOverride }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 4c536c3c..2a33c8f4 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -64,6 +64,7 @@ class WSRequestHandler { RpcResponse SetCurrentScene(const RpcRequest&); RpcResponse GetCurrentScene(const RpcRequest&); RpcResponse GetSceneList(const RpcRequest&); + RpcResponse CreateScene(const RpcRequest&); RpcResponse SetSceneTransitionOverride(const RpcRequest&); RpcResponse RemoveSceneTransitionOverride(const RpcRequest&); RpcResponse GetSceneTransitionOverride(const RpcRequest&); diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp index 3760e5ba..97918147 100644 --- a/src/WSRequestHandler_Scenes.cpp +++ b/src/WSRequestHandler_Scenes.cpp @@ -79,6 +79,31 @@ RpcResponse WSRequestHandler::GetSceneList(const RpcRequest& request) { return request.success(data); } +/** + * Create a new scene scene. + * + * @param {String} `scene-name` Name of the scene to create. + * + * @api requests + * @name CreateScene + * @category scenes + * @since 4.8.0 + */ +RpcResponse WSRequestHandler::CreateScene(const RpcRequest& request) { + if (!request.hasField("sceneName")) { + return request.failed("missing request parameters"); + } + + const char* sceneName = obs_data_get_string(request.parameters(), "sceneName"); + OBSSourceAutoRelease source = obs_get_source_by_name(sceneName); + + if (source) { + return request.failed("scene with this name already exists"); + } + obs_scene_create(sceneName); + return request.success(); +} + /** * Changes the order of scene items in the requested scene. * From eb7787fb7dc70f4845cfa354c97e01a0d59edb9d Mon Sep 17 00:00:00 2001 From: Alex Tsernoh Date: Wed, 27 May 2020 11:18:39 +0300 Subject: [PATCH 101/278] Docs: Fix param name --- src/WSRequestHandler_Scenes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp index 97918147..a6d9d293 100644 --- a/src/WSRequestHandler_Scenes.cpp +++ b/src/WSRequestHandler_Scenes.cpp @@ -82,7 +82,7 @@ RpcResponse WSRequestHandler::GetSceneList(const RpcRequest& request) { /** * Create a new scene scene. * - * @param {String} `scene-name` Name of the scene to create. + * @param {String} `sceneName` Name of the scene to create. * * @api requests * @name CreateScene From 37e412e710e75cee2b4f95988a5f77b4ad3b3c77 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 28 May 2020 16:16:31 -0700 Subject: [PATCH 102/278] Docs: Add discord shield to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8fc8c1a7..cf30b9fe 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ WebSockets API for OBS Studio. Follow the main author on Twitter for news & updates : [@LePalakis](https://twitter.com/LePalakis) +[![Discord](https://img.shields.io/discord/715691013825364120.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/WBaSQ3A) + [![Build Status](https://dev.azure.com/Palakis/obs-websocket/_apis/build/status/Palakis.obs-websocket?branchName=4.x-current)](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current) ## Downloads From 3462e7f50eb4afd0cf103b3b4ed93035d6f68968 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 28 May 2020 16:22:11 -0700 Subject: [PATCH 103/278] Docs: Add new logo and update header --- .github/images/obsws_logo.png | Bin 0 -> 58525 bytes README.md | 11 ++++++----- 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 .github/images/obsws_logo.png diff --git a/.github/images/obsws_logo.png b/.github/images/obsws_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2bcee839d3c8ab3f200672588e3a5557e7e08089 GIT binary patch literal 58525 zcmX_o1yq#V_x2132uPPS4$=}LNFzwMbf+L75+a==NFyBr(hNw~(5*;{lysMLcYlZb z|M|I=Yq{>4_q^xbXUDUj{hS-}N?8U6ivkM*f#Ar=N~%F1sKwx~^nG;jUq~6P(jX8z zh@7OjhTGSz84NcP%?tFs(-{2ZBf{2Cvz}rFf5g_5LgCEJ-%L!$Dr`F0T@0~<*EoW4 zj_or8a-Hz=V`Opg>BLZ=>vZ?j_-|J!nKwz6Z1=G_3N%% zoElwLh9fFGtL3t)=$Zv1<01UzNomV1G5V{48@H=J)(59=ptbkua9YtIme8+Ihu=5X zx|c28EkaoH58*^7zPA%MUn_5)5P&~*;hl$bGD?A3S*?U98N~3#lRRO#JOl=ngfPDj z8Xj`dVTaKDx*0Kkq!H8$;V|46unf|{tjvQ_p+}+2)4?Sm$|(F6w|qWCk6|zv_X7#G z=!R}>2RWf&`Ljj>F=vS8_FQkCAGO~4hVr=hbCOmS$O-B=>OB0qpT%Wwg-;ed1;gM% z64uFJLNvGN^l-$eA-p9I1=!z3mc-bbPpC(zZ@o&nXF>^Yg2Ze`Po__dNrFE&KL_N^ z)3$hXG`C(&;)|(6;-Q~D{P}aAX-icHvor;IWfe5>*faUPN0}==_{`{1@CpGHEV_}J zAJ)$cF;)K+L6hz_WI!I<)2t>p+iz2 zMOB3lKky_32M4dQKGX~GVj~t~hp-mzh}&JJCGvxzNLRx9J+jX6u8%@0iz~ zA_-SgiDe5g=q738Y|ZT@B;qJLq3_Biw$vaEThY3}WLX*Q;H@IMiIc6k`P5s{e<&v+ zmzgS7l!f6^C$d}0B|!|}M<(9~%j6|*~+k!BU8-M-Yw_yJU4>7 z_eFvC8K!n}d~tdP7d=7F^_;XYakp7`8jov2{-Wwwk~!TS5*G7=V#yXw9I*#*BbQg~ z{Wp&i$MNWJTZJJCkjIDLfss||Osp7C3Q*VHMSklUzg=fMAPlK=?$`hVT z!Qkf*QPi?%Lq+h8`lra4scA@t5Pa}twtz~LVQ8tb>93}=F&VVB-G>P*LH$s#n?0v& z2*06BjkuH|I@`SA>pJ93r2`SLVy(ru@z7Z!hp$to!P2*4&QGUl|jctlT6bYRH9 z#zv%@q&vF1G8gpelg=s;A%$O5)T^$V02DMVIvyTkG;CtAwwD2~%1p$k9e&_^)34z; zSnapJ=Hnj46l!eV-FNd@w#a(%J#kr5av3L3i#t%Q00J+8?4aV~WI#$I7_nr>li=!k z$_r=vOZ@JOk0WlJh`YPH`Mu7#l+%PmDlPl<(y>=L=hZG4a*X9`P;5q5o)|QHO$aT?!$DIrlb&q|9~DHTRqg<@N7_EWW5k$l>1>2vK<7|Kddg48-ULN!;BJdRlv zrRwbn)|HKA!ZVk_TvrRM^3J9GYb9~$$ISTP;6@OV5$f_J_XcQNA3~_zrMz~#qUmLo zlzyJrPQBlo7pD^NAZB6d=6B;Fmx-ilOL}JM@N^r-&HW7J@7EvSzZb#v>z%AW1trQL zx@0vP_od@FX(I|W3$w)i1HWmPmO$BYWRqOnu4~>UE$zE~d9}s|LOZyN@H*UL%Hj&=+<~SYIiqA@t}HAg~BaEb}ctUoq2c6H-kE? zjj_^5LFc#GZ~BtIspsL!SbNhCm+%P7vEqGLj;{a_N)QcRA}55Q#mvpz<4}B%Be*KTLq)Z!~_KH{+aPeRr&6;0)J>Y>OiPS`m>Ob5Sd{i;NNOR% zGMh1FoBGlhqftZ0a$j)p@MNw%jt6t(h>+I9=s1+mk$&te*hmM<+Eo^5afIjXPZVrpvS-J^mLM2P}xVUm;!(T5Tbr_dIuVb8_f7IA}hs zc9^XXA+*YKii)0~>PaidY`d91t_I$>Hd-u{EG?Dk6>!kV*oq0+Mg6k2?}kf0KyyG8 zs0D)^pi8iwe^M(zftN#e(D0EebE7`rg4CbB?s)LTpcY@qb-Nve`U5NSq#~=qEUTef z%=&gG@!Om0!EdVBCM`yCYDJi>Of-?JNa*$>+SgW~FxYb;)zX*|SSSy1=T#x%b!5T! zMyhPa%m%ZhzP#`ijfsuLu_CF9_lhi1LttGT?f3)#r7y51?n%U(JQaHZaYJi;5ZU=7 zj!5jE-^ba==hHyaPzIildpu=f!DLG)ZN2s0_ti2Haoza}EzT0tD9~VWS{opc2*Snu z>!(~!Xe2_@Pk}o5QndA-x0JFVHXv@_X-si&5@_NGMkXrXyzcmwbarz&0utJ@3{7H1 zJ7!0J&35bg){kqm*`8&eThlQ1Xd$H^nhG!f#RyAhoZ3&ww52v0E?!_-tcY66`Cm$} zgMB8Q$~QP12xIOHP05xb|LuWH)Ic1{z_qnC6Ra}NEtVe@v+>4J9RGY5K?MS0lvf4C zigZnck-mWN)tvXKNRe?%gt~9H8$)PfncwD@^4CQ$WpP52M5x>ErTrqV4EAM)e`jWx z*c8y`2BAl(%X|E7XSRuw*87aWY_($%mv9;GUgkcjB%hYm%ry#|3Qn~1>@gluJI5Wl#pA<00X|KjI<_S(HvYZmeF*jAYN8Q zg%4QdMmt^2)b+(Ns{7Hg!ujmVzMIR#0n&Qh|G!WV2lD74{5>QTO|M^8 z6~S_wgLK2aU%H_A{ERC zbB$vGNHI#jjJXCch+Oqt<)7aJu;1@5N}=jWEjPlX?8JZzzrXc9&D1-7`|+U(S(k@b zWG9iOnEsmUKd%#4X3^}K{czk5`Hq6%D-zMP?C)p~A&QQQG7$?4{zXDU5)ee|@$jkP zYZUgzw2GR}r1^cfvyL9R|7~QH?oDJ#^S$|8vGstQoX1{!Z5VF5O{Wf9<4;lbj>0M| zdi6*`&NHML!@`R`VeGWf{;;Q|u?k2@=;;-R>^$^`+CKWvE`$;`_!VO%q85=Qq)wiA zUEekqe&oB|oUewbpit4!M7V5E>Te7B-dLjQNgj2YZ|s$sO6+&hN9x#^ptf5>I8off zYyblLUmW0YyI9QgL6HuZNtV0C7aN78rZ-pa=*YDeEYc^z;;0z9*_!r^=jd$2v1|c> zfo836sN2rlQgWfN4O0_YD7rb{N0A~UT0E#+EsPXSOp~@`0L$j3wRVs;uT8W(c5oAVgZ4^yB zN+2&4m$A~nqH)_B2`?>wzl6RtDYvY^o?O#f>FZ0{m;C&Lr`rtXJV9h<2S^c{S?#8plu<_=Z#xktSwkDZVnzgAKTC>-y^GoZr`Xu2Y zegR%MU0pWW9iOMEj5wigng#iu!f8CHlSF&}f19!_Bhc0<2u1z=*5e|(NhGGqi))_e z_RsApxVG5e-6fSh$AvYDVG?7XV}@MCB)?%xWy%3T*!3i`j-0dU2=;cQVCC;#JxZ$t zO=RaUdtoR5zWS*-YWiG`PyFuKm_Q zx)FdJy}uAd3!{&1u);sy8KJ!O+DQ)%4Ry3J&Y9dac)^{)4h{RaDqWl!-{kO=-G?P| z02iPtBF8)l9EN6MHYFvJ`-Bfgy=tqDWjb-G%gt7>Y#!@K>xQhgEMH#Di~H|SANK3g zmvT6fx88Gyg1YqkaOi?Yg6%)dzzbnUOrp5qiFwzgD#^=Z-JERA?X?BbKD@>!8hMOW zGwq07@4Out9{!-{!j2^2iLReTZz4G`#It|^l2(5_VR(z^iFS=L`3-LC_4M$_=|2l| z`lM84Y{eAUhLTOxZj!%oa_N`GOqu>HX@GIdbde%Qpu9e)Va+M?coa_ zVO(0#cVb~BN}=pC0*lJi9-GCrPU|twyFGF9>*>leFJJoQ_c3y7^-=KdPqr9I0E{q0 zYGl!Nu?e}^*>~drwR-GdVXco+E1-erSM;~&>v5VMw|+!*Tgwb8vzrk_)pPPz%Fg0) zn5Qcok5R-?Z3>K zs49P4gZ)XvQK8pjX5(4Euz#eeqs-IaKHitQ^y1vqI?ascp?e1;z5D4b>JIa*KB#)K z%RUve$G}%AjLhra{c*;IUkcMi)-n7xg#XV!HemAkGEiZ>j0{RW_UyuR?+fxY&m95n zEn~gAZoqlf5cEZ7&esLgr3#7cKLO?|OaB<(wVFe5YE#+-DyS7-z!(waW_jOD^jQXb z3206KgMZFYga&5A_)wFbmHmmv{N+vq-v0hRNMw<6nV@ubWZ9=TgYr{z<6EbVSc?hA zXWCk((1Q7H{K+Sp_1qQVvyIN)N(}A?0qBG}xy1U<2tQa51<)_(8UB=$Y#)!$SJOL% zb{k)d**NCD>C>xyeYPDO5+bgpHCpN`U0szV z7~h878zo`WG8tTyV&sWs{`RL>oox5|GEr4K@zdBbWCuXZuQAaQ|8^|Ff(V3sdHhUU z$-eO3C@=&qXot8ncm>FY8PKVPTqHYBs5AHGJbghy9!35wwe+&Rk;6yhN~*Z6X}RrV zZ*z`(vashS+>2usw*P;-)qd0EwI#61f>>T@dwp`haPXt%_YI6;QL!eEV*@fB3>+%+ z;>=3cd&UGhpra}CJhemDf}wU<*@Tg@mz%V`+`Rm}@D}o=IIiCR6c;cgw+=ZYrtjqh95*F+a#+yK}fSEKmY6~;THeaDH3!T z3Sf2qf0jjMMiijB%^hL2<>%)=6mofD_WCuB)o0h`=Gf!+B&-@@PI+s$=am%^$En@1 z@6YxgRjPan=6d^wxpD71w%z@bq9jG%pC zCl=NiORXa_!;6*3alM<-xv{JL`sC{b=#Ww;m|jY@6lfI-ik_JyQ?fqrWvWa8%~cH4 z_9+1FcX=}43sny2Sh)I+S})p(RLojip(PKB%zib!b{I=_HRLVh(64^*;%W~Sl`Xn= zYpRBfTF|Lg-(HEUNmgB`K}?+#ib=BkvcQ(VQCiUJOI;RFh}Y@trx2D0jvlkl)bD*X0BpyLLDZRwD8#`&${Yy zwdx3J!7S%OEvvbm4Ta8wmEItM@(+6>+QtHd!muHe-R35%k%IWDyu!@k0BqDRS#cBq zih{go5HRe2ErwDkQty!-vaKK|*S4mbZTO;|m4feWxap5T-RvjY^rxo9&`PXsZo^_2$DMt8yTLQTG#XI?HBeG~wyOb{$k0v$H(62C_h zff+4A2Tc+GBB{dogMo_UOm}Kw^` zPcZ&LR(Ctq$=R#C1lEsAPJSyEc908N#fJiB_XM4{ocd}`Cx-nj`_s15i0EPI+p5=KaC4oOUWL!FeLF zd8~6ud3iZwM7=2PJjjLq;6Omasx!yXnizePBexxcQI2V^)Ovk7M~1(tT?q=wgC9XS zEq{%QCUVY(!jqnrnRgTRu|J0KMLCy%X6#3JspXf42I*SzpZPMV0lz41YSoA4Njr*} ztBIL_Q3kkx#T9K7`^y#r*Kn~KyO|Qoc0_0yv9VsG`BT7ZA*&;Ur&rqE<1J3?_a{Fz zxXq9A=z+TcW}MtVb3{R&Wn$$-3G!O$?O5Bg{~d5GFaPks>a)GvqkJq9Ryz8n3vnJm z5q3tV-~YzjD7pLsv}Nw4oqvr$_-u!kfX{toiNLqX$jS%)n)jf_zK2W|CvRhJQ-M+!2(6IZjvGPB|R* z@zINN%;tnW>V3t?h6tn>AO&ox%v0pwD76h52xrQWC3oufQsj&3+~2tf1o;4Lj`F$F zLyzN)_-sElHtpi}nlRG3-Nzwa_tmVMY#XXay9&k46{>ET5grE|j{u+EF_4TZpKl78 zO@N9JP11jKp1d3`s?t&OV#cqsnxz^k)Ryt^xS&l-EwWHjR_wQ;-{G+2aw}6`be`Op;&OFw-0E9K|gHf$P@a+MmLg5AhhZ7r2h0%NEg3~@P2L8 zYL$2!FIB4(LPPVvoSs7>vB}Gfc#C>=PfYi2d&;hJ-gu&ySI@xqUMOWJXMtWuAer<@ zLa9y!s1tRE<{@2H6{}UIo^_)rd*x~casOrCp}@>zR?vl@oDqvD-L0eOk1-btf*Qo; zEsH@d7hsuUv$t(cA}i8_-3g~^>_4nx5=HNubB@W-xVPubPm;D?%IAOoFT2TN2ae9E z%9Eh(LE�kxabD_v>AEBSs2ao@Maf@XjYdwW{X7k|ttdWB&jtVA}J$|QIK4%_douIZhK+F`aRNlU26?Bd00QmqQ2wyqGJUR}=vYQ^D zFWSD23ydSJV?9-pgAF9!)IVskt(9v#wuAUS`!61SItMQF`%!EAJ_Y9FFV#)Vq<@)04$P5*5-Q+kl`(X4`uYGG6P3M1WDFQ9XJp9TfgLgR|RXJ{ofiYXtrRD z04QiO)$^ucntnpg8|I&p=&`d+9@$}J!+ugX16Sh>_M99>jSn@vr6k|zRl4BVQF|4# zv{f;|L2bnVJ5fP9l-+dmEESTW7C2Ba!AamPns__(3u7o_bOgof@qE~Qtz<$OC3r0 z28gUZVuS`s9_1Y*#1<#fi&rDbd4&dFRIZkwvD&F!!o4b6$6!iX`10k;Lz@&z`vCB( z>B#-IaSO?1LSx@+-ZbjlBFwg`F=)Q=I#ioohhv4k9_z6%oWn)ba# z)u}KKiS2U;53fjIQAbZoN;2XVFF^1A%1eZSOLhO-4P932Gm8!4#*+;j#(QIS5F@Ns z4n|lO9g!k^u?lo;#T<1R`gc7qy{$<18Th2h+BRx; zEsgW$D0~AChV?G;#XdhNht8UlLpfC5gpQo`a)8M|ErOettJj0{$cZ=1m|t zD1&@8gleG{drsbJ39Y$@!y-*5?FoQx+kkIg8*B=~3`jE4lIQ(UPXNAa_I?-e!vfrS z=Ooe+Ji+Ra8~ygWj9|#Joud*-0{g@+~^h=)D7h`K18Vg zkX{?BNpDo_(yTNWDctIpmn3G+RZb0!6uszLpBL0(;SJ{_0Xhb8u>dWE1`nJmRj^qY z-u31HOd{VGV0Ku4IP9x+VmZyu0d4ya0B@jEFC>Y-rnxO*7FIO+SDe}xck4$52P=|s zWooi8Tn;GRbMRNU1+#+BRY}6v06(BCtn}QW+`66UbL_uM1DO(AsxT~mkmJR@3>ste z7^d2kG~@-UBL%<+T>0LR>}D8bL&(`7pV;eiyGF>_jh?q9U!cCjsn2=XJnr~=z~PfO zQj$=)>hJ4QrJQQ&>Z!s_JIdOVU@Ck;4G$>H;z$;R-J-3uq-3t6H*k8*>YAGO%LdEhOKA)pT!Hmxy*8De7&VS} zM_8?!Kav1>Zu9-6>}D#&&Us_rX0+(%hklXOKMSAlAe=^t207>u-0c=sI(@LleE`%R z0v!_p8QH3Nx^Pk4#`iFg+H&9D<$X;#4aEqL3fdPFZCp|BzdRg>Ri8+07_3H4Uj93P<-6t%wod|*+?MbOqUELq zhdeOg;>uJyG>B5>gq-kC>Uja+i>KPWi zwsGf~kGEtU3S6<|G<`sv8PR|~>0=?tiLC9oMG=bFv*oWbIwG3-LnIocmpVR2r={5y z%k^R73G=%jn3uUS5ulY%?bcEQK1A6WC}VtAu4N=886Bo%3pKE47TZ5sBqtDe%~SN+ zVB_FONlJcFDM0T%ng?^A%hGa5$v`16u-|DqHDb!cywznHi8;k2AQ0%|1<|4TyM**> z&7($xD2%|>bYCbYW`xp+BdF%AW_uZJ?r9Vc8a(#uJ`_q!Pvu_WdT`^m@gbDUpMQbOm!A^dx--2ai_(B5_J}C z!Y0q->fDpR@tv`Fm2djZz;LzxdtAPHnYH>?bwh4NmXX9U(EWWiak?u-D0^{4oe`0d zoyNC4Z*K)O^OUL66>$PZMEKd+`06(Z0B~bk>DJ^~BWFD7YBq@2j3A$3ZwSGJSUZq-5IEI^UaIh@jYY9+A zE4PYEE^80yxE6{r#~a-%cO)9LGG7K19TzoISu3fi;Po^=25HCG`(JC`vjh;YE1j zdlH`V4~lbNuIn0+!(c<*f1*cKA$seg-@+?qy-#QA@isl8<5|zWuGcMSKTi@8pk*Dd z_Ad>}&^o_O-h&`C@Md)+C3C{w-#Hp)>uY5y{aiGr$B+5nTWJeO|Z{gBTOy?aU5TsS6p#=4h?+ad3Ad^lI>$EdBoGwpK|jUt`F)kn`BKj!&% z85hi)XPyik+$MFcv`397lq?jq8hts@qjr#sbVD=M_ucB zo@}iT=Z_q?p4XQV0sWPNNKEKzy60@jyxxOWb$J$_VZdaxJhkAr;fIgbfPVJOcZ(|Jm8O6vo-?lr?7tIr@tR^0u!7 z4*)r=5I0$HG8cdTldzG8FZ+H7AIOeAxt)q@(3U-f+3isU4HY zE zW>x`$t&f1Aa>yduU7Z98^?-y)T8|cz!Tb5$Tr)LD(SO{#GUzfjbN4U&ubR}>Ko5KH zW^sxrx>iWlm9yt&v*vIJ*oD&y2b4Yvi1dbVZg>S?cq7+^%?F}ht(Qh?VHV$Uo^h`0 z>+5cBuH2YHxkWPK7Wsfsu;Kjv-ZwT@XV|Gn3&>#=DiXWsRa|u(f6Qf zv2VUQz}}il%xNIogg$In07cGyWnX)(Hb%oAKnG|Uu2^kJA)|eK3Sa?$1mSH8nh!AJ zYe5RNN-9P%FKt2{KVT0CX)_ZK;{adYGE-{BYoK-2McY`+oE zQXiLkig}$QkP$=VIzNxrhskxW9|M|wf|LHr<15e!2)ZSJAHCk5^2IwDA-ZFkmH>Vi#3R?hB4u7Xjn+!?Fc3A^%3FX+1HnHaKOe zh7P2{V+v0-y|e%0Uj;##)z$b|eSyLx@N<`FnxFZ%Z?u)n`k z^BVZFq0_$bX}ktB?HEtYp_XGe;twzdS~sY)ud$#mT#*R47H11 zJsodI3GT`>C-2*usC0IH%L)1H#sIW-`Zo`XX*^RXZNVU1pr;`078^s~+;Hg+D zCiupLL?bV`Q>#rJ$j&^VIV5KfEUi9Q$mms_kJ;S5)YGG|N5MI8Kl(EavXLZc#;8U0 zVOgyoxX$VRt{YDY-h=dh2Yre5Rg&^i_;=|T=BEkyC3WTw;ufRP>BjWab-3afFRTf`j$AUfvEA44!u)H_)(UT|MK zUoBupS#lYpwCSQ?XK<9UJ`oQ@S;IL!lC)~9$m+10DSdvr?n5I9Z%kEgeOEc)=18ch z6(n<#wx;TRu02vQ;?;a4+*5&% zl10FzLhmWv5)!N*O@HWLr?K6JWoKIF`t+Z>K5x__~6- z5~+mOL2_9YXf^pZD42O1)HN!Acm@0G1>ts$gFtQ_dB%-QfN<$(NA^z5W7;<%HXV+H z?wsto$ioxjg*Jb5dTv-uqF->A3S%R$xa%11>0#PFXt5z)=l)rhA|g6rs2I2=KRR$4 zF{mB)holUei|nT;A9aG(;ZKGU9SrLU(03rJK7W)vQ?a3+vQqnh8rT7GOb5r&zLn4!cQ8hRKl812; z%6xz*IUVl-=FH(}P1P_cdz1ge-|&;@(O&t5gqxZS@oV%(9!W``3Lw(4Y1ho=fE4U` z5E2ZIoWYQ@W|7qwsg1|pfT(wWg!BI5$a*`IWYo%rQLo0%_U?UXfgwbU8^VzekS4cH zeDKsVFOn2vz*xKg=4e>uZrob(V~{p#U_+8&?pKi_z`$AWCMp`iC;4#OoIC;C>)Mu4 zeq`pY5;K@>aF&Kh;8EBNfnxH#G)o*=85z%c$+2-+Gs9I>ezKh%ISY0mLj*`RxrTBk;t~JI7J+ z(Y6%Ng?)vAg|uT62PmK!*(FAX&F=(23C+KI?ha@GWgGQ6D`xKvBo{52{HiVdB_ZYz5yCmv36 zg6QtA7sc?n*tQwL=kfb)3o5QIW*i-X4*baA|K zcmM!O_Z@0;u{CUn;Q~2|fiH6;yRzQ+d&Ir17iGp{3tb8_zJge2maO+J!*>3V&(ymt zA?RTTu_JTE(rRikB|Gu!LWb__AXDkvy|iI~*g7501L4WtAucR;tLv1Re9ZnC#r^D= zYVJF7puR{{bsX(2nkgh zYJP{-LEhpCbEI=CJ8ID$0PM}D$79BuUt!Y^#Mq6b0Vs2km7mf`jW5*F-DK$Y>Qtes z8KseNb#!Ub!2DfdZ<8`=TlQvKZ}x@c*!#_S{meK}_L7gCKdcWi7|1rj!9awZj3Wm~ zEe>TB8ceSF&ToU;!7w};$%(nrn>aQ|YswuW{f4pfFeKP7g)g(}A-uZYV{>?Y*qJ)? zU=8>*zt7b@F!wDQRA%b_;oP4lQtmouq<9;5ia$7l1cI5qWZud*3|+l6Nz4oT%a_g_ zpS-BMX}S(VQYmfFCV*0XslA&>>*wCUj=-kH`xPo}eWHIaii*%4q#>-Pu6p zsHvD5ast)814iWYAzme!T$*BoI+yrH!YdXD+)QvWudL{!~^` zQBpSe@aLBbXREn3itFpK&C;YnI+(-}5acy^o>u3g-p2fXfg5yOIy?qe)FA62buGk>72e&ShuT`a12==!|)&1JD9Fef4jYTYtivfl@0J61;}D z%!8EwIjO2yr^#$OxD=}?X?Z6VF9H*K{rAD9jU$kij5m4KX#V;<&ETT^#K`B|!q7Is zxmXJFb=CGIAY&aV^rj%S;xc%))3pQujgP!%K6vWBK!=x;`$=m&Ta;Vr{r7fEbYx(c zfZ^-iR^Nb`qQ#zZP}=J=W^~;x7FYG-c3}K1mRMfRhf~E*V!;t zznk!r{!FoM^^O;!1o7HE@d0>uy4%`nh~r96 zri~)s6TK^TP60GTC<&Y1rV8RL2J>-T&seUPpz96@YT^4hef9z4oXuA#cUKuuREKUJ zXB+tq`gZdg)H-CA3{bos8v$1!U|4EtRE>iixMF`dpC7rB$&JkCDy8UcrgnLUlCp=& z(0JT4Y;fUynE8@A+M^PDP1_=ezx{^;|kW%Y^gKI{*d zrxyh&#nMDDh`DZz699Ls%*9M8W5Ao{aXEP1!Z{O{&Ql zIg}XIkb0i(@Mgrfv9Yt`*o>8okN^$9X_)1m4POdO=%_1T#hxc!cEyfcW<^(;ow1R1W7?ci(pcQu0F9vs#Q!)1z;LXT90J0;~!CMK*6S1LTT%>hiq72FUAGTa3KT&6CyJgBqDW^wauoy+7I> z9_vD3tuV6d9%66<=;i&!5ev|AFVmh*dq*B)E?>V|LDL0f3V{&c=;Y*E5SUl>qeU;z zv4X=QCBWdpnaYh9?!uV*0u;tbJ{vv6nVJ;!nA!2h$b%xNn2{goZ90=-?-qYl+*>a( zG;=ulrQ5haN?D{R4`s7#U&W{YF<^YWX3KAPTGR{2eu5dAYYY>B`JZPD2> zT-U?vZ9a9(t$#`kDN_ZV9k&HXr@M?UCo{bx4M^ua=U6KQ~^RXh`N+(=6%D_uCT`Dk|g@p@F^SG>eUZZyU;P*v#Sol1VpG7pJ) z_`Wk3uG^VRS)qa~@{G?PJpJ5+_qsD&D!xZ^+7-qD8%gDjre*E(Z)yRxX7E^APXWa$ zD_bSzgyDT*3IsNM42G=%K;ud-BBCm7At~SU=rQAtOQVRS`y=&(-TF-qr-+@cbLKHI_k*G%BPNp}IyllY+X%8t`&yP3e{WpjWRH~0-cTuub=34nvIGqNhi5$jdS z(3~q?6d^Nt{_`h!LkVw+B1_zSa#D1N>#R4%o%kf%RI=_)s%P3&EKw9KeZjeom&D4R zT?#^>vbr~^#iY4vm+idc{O|=m2ZxciRk6+3B!5@0j3lve37<$UIUGgM!)&(kym1BP z*GJULrywmB>XmjsuI*@VHvuxhS2?oLCN1xl)qr5$gv7|qr0F@idwik>-t;Ym#%Im+ zXmhxKk~G-s^-W`4y@%R~O>uv(v?Q@XNg{Ld#u}z>|D8^J)&(kEa9}r8-8UKW-HAK3 z4p6iYKlDEYEI*7d`nV7j<4fmyA;a`vH&M+Vf0Hct+Lu%^4E1+17O^)+i^tsg?W&a| ziOow$^sv_q`aKSoF=t(&R-pX>a`R|5Wku<4fwkH z(%0ZyIJ7CPN2=XjHx@Lggj~XF3?K~-3n(W$Q#Q7~OSwI{+>5MINz0F1zdWbplO zI^{4RHF&)(($l&@%Wa`k-GzzWYkzzSAX&M~%%qsGX1c<7k_kI@{;N#Ch#ysuy^z^w z;2iP{*pUe+uM}iF0d3TVO?wZ_(>~WbZ`Rvwrz(=gS^KL&-AA`{!8`nP0XZc-$4dc{ z!uORY=vuxCD*a;M>cMDlAwXL5x6NlS?nYK+DuR(iLqlK`uVt87MFU|uO+zf*k;g@a z0i%K^9Wy>Pj)>}f$>oe|j1w@0&K~`d{z;54I)hz31&r|bekL04J%`C?6zbss4*8VR zMu%Lds+;2v@h}1#Mo(XysJ*{$Fytt5@WGH(&*s&zkCxnXBpZO)K_%6>RTOYPK|z5> zk3NoZ%NFU^{KoRJd#sK|$@|t^layho803tHA|5Aa>ICaj!Rns3!I5?E` zzj5Z-B%%UU=_O(tQ1@Natz$@6<5oCqqY+G7(heZ&c-|QE@0z9A|;Wjaz8B zS?zXx|GL_Y9bkBuMf)jYx9F~IR3>1IW=aH_P5Uo_E@Bza`|_v?rH~l)wFz%zQD5Q& z+4bddWiIxxZ+AF^r6WfUr{N*Ef{a!>7o??2t0vXSVn1CLeX12D0LG+1WVbU}1)7pS zIwua-fYA;IJV5dwGuHhIt%J0|)A`&Ky9yMxBn2bJF5Uv;Qc)WBH3H&Qg(Bl({TidO zOmqIkly!hGeQ`w#Cvgte;IRv=9y?RGs# zgC|7mJ8ckuwli$F@t_45E!Jx{W>MGix+fs1qd#dex6smgpXeaC+h^D2oPL59cmymbOSQd=F4XTB%Nhd)uQ&vgeLo=0z|R@@4B7Upf0*4@VVHhcUiy{i9p<5_YgRB}@Fu4l~2&b+0aQ0<#77>CTb zV2M8JWdibJme$#(9w^p>leGm zpFTKM2R7?N7Ox64*AeHHb_x?^4fkUTktwI+R! zqgZ}%GiZq>R&vm9I*!`EtihmJ#_qPfPCdZHW;5~bBYmO+k-{PqkERUOX-NS0%fS!@ z*<6jl0pW40B(xlReOjL%TEY7^Ejk2))(!vm=Cma`dVL3hUswJiuR8rub6Z5eE(nOn zAcqF~bOSFpmLberyTiq*+34VQcJF1VqY{k+HVLb_$QHPoJ?(MEu3bXa6OSzeCYePT z%la2{X4l6Pc+C?ZN9XAI4^BA^tN-XVT(4WEF9LMfIB--5ye0DvG_N&I$KE7(_bc%C z;eLBLYSK%pT=CoB%?nUFsF;+~T0#39o!}yV*;N`YtneisA5cTOpAxZaa;E|NIbATy zZhF!~$3{CW^LpiSfXvv(bbFKPT`xMQ=Arg2*IJ#KxF}Yr^HEn48cVr?5r8VH!*3Dz zUHIZCgm98&&!Sntvr~KR0y3ygep;$X56dIX6B$wO z3*<4(Vy@Yn-{~xhL~8~=XPL@;LhO9+Ah<{abEH5^3S0=`a%dG}t9|3ko>EU^)Qs2F zRdr8Wca!`HKp%nLm&nGi2{nGyE4ugZZgxl?YFc>{wuaq^4jylgWdm9Ljp&2H+bBBy zOfNJW9MjJfBdXQ!1Z+HVU(kB2gxUV=Apzpe*0E>J9Y4dSdmcZOi(mi3@pN=8_C?r$ zMbICqFfv`}`h1)JXj7siGeZVS>^<=$UDYai%|AuD!xz`!)|@YqnG$TwyzTq>m#duy zejj0e7t6ZLE}XmnLqc%yeP3F68Z&g%FVH)!LiRZ;@&=<-1Z)pa^bC?M{rTY3H25Y; zkuw3KpyTuZqvZjqj-abQ@KuFpw@rd`cZMb*s*{u4{=v z?Z~SA1Ajq{j#K<|>eiT9MfsVT_JJ#L&(BXb?SEFV&`!8oNe*1MLV+I7c( zned757d${OpwuW39i(HC`F?fpuO>*(t0iMrrH=G1(>yDGv3>Z&uU~edVkPuX*eJuS zZwdefC3w;gr?>Tcg6xfe^9ouD(=}+QjH%9x$F?Savv^!u(De1L4lxYGwt{DL_f`thUUU+ zw9%;9iN*i{>ojG#&4O^i@%T}3&qfScBl|8wugQDu+fYxwr2j`QmlXYxM1OOO0zI}G zv#Q5MMMXhGwMX?k6BN&VH<{DA!>zvEitIY+Tw4&!TA$I3(*BB!fu-ZOP$NMQT9Sv1 zT}utnPC8oF9nQCqA^84KLXItQx7c(o!yz0+R6BQ3#nk%#P@2Hw98qfT( zN67QCt;XFI==t`H9K$cc5r{r&f$o(dY8qYU&1-?%qEgnt-NI82i0;x%)JU09`PXq$ zpR+ZQ6Ce|f7Woo5COpp=GONa!n@ueFUV#5SV;H~Y*|TSvl6GENa5+K>4~y6PsNb~T zw`s>Go9`ddhTNOz6Whr8Y|F~eKZIWeG&O{O5P!Ighvz=nI6iv$b0m4xu&L)raA~)3 zzNd0L)L7rhd;!EXrQX7oxMKd0f?Gk`kJY#l?zF#cCx2=7ekO+i5iRu>VKi+G1w}=Z zNY4@vhhauZIL=&)8pdG0V2kHA72umiY@3`tVGmSKccuz+9P8My|a|%Jh9~Cr=YSvW`DeEuqn1aqa zv!%|C{}uQ?Bw}N;b~b7JSSHn<{>;~`k$XPzI4aV3o8Ct6wk+^7qfS7^f7nSUPz2Nh z{kYh5TRim`)9)Hs1YS{csAY!4s|j&4T*O8k0o{u6DcU1y`~`yBF!AbNk)InogB|$& zvtFy{I#K^WylaC6c33|^{rC1**qAA=p)`L*ZoM-69TyS*M_xFl>rDb(t;eK_M&%P3 zK$Au>jte%6*)K3}o2=HGxL(&@-co$)Gq%9z58rBBq5oy5Ck2kykFhX)xC!NGUeT4w zj!M|!Eo%-i^$+*__pv7?GlEn#G*F;4GvR&*;AcfId%l|G4L}KAuqg|9&2{yz{;}ua zwD6hBsW(bFmtyt1vrpP)PoB89A}A2oS}w{u%`^}V81k-X8{#M*<^zyk=zF||>iYoG zr7;_f+u2+Y_ui{hL}4^hC(-Rn*D?XE$wySHicea77MXeANArI#SGHS$VF-{f-IF>$ zD-PKjNL`^Yi{dWd24tC%f(Xv;@w#-@mQ{;^rafqXO>kDQaSoQPOYak3N=gDVpST8f zxcV-60X~IK@bm?$ZyR*gfaYGIK>y9PBwn{i@X5y9SMCuC&PN7{>*nC#K~s};bcbv_ zbC*KH14hagn9-C_oc(>K!RZ{BfrfC`b^Hp=-94b9=U@W9^kEH)~@D{TXiPp#%I|B1L+O8fs-uwL;5>JBYX zwyLUqOF_N$vyeRX$XWZT?|1f+>;n+*(TnyUy+P6KW*p3t#71*MHc==YF^yedmCzE! zS<%;*>tyx+K7nozYi!ze?rFy3+UT@j{KEoSIVgu5Fqim+j)uhJ3Z$u-dGj;;7*~E!R1WZ-apXxrTzZ4fephE zpkda6j(bQIA>EF3@Ep(zm|h&8Hye!$_#=bDgIlk&w;Q`EVNHU6_NN3t%a8@-RP?Bm zY>Q9>xu!sk>D&qQV1CZG)73isGRLL)yB$e{ybHTU2-#b)*Jn1(H8qT0Hm?=K-oB*mHglubXFmfpz-`&+- z?N5^fTKmBBvE}h$sqSx$_nVtBY)or~?1nZ(Cb)i834d@O-xVVUY~Hzn?er&YY|okm97mj*};U3uC7h*l9rEMt>WD z5){+mF}C>S&uaK~RO^>Pv1b*^!18ppqDn$yV14cJlN(yg)hXl8c7V$A;^${?0r1f~ zI2V#DN9Nv&89i(UjM3^9Piay6IE2)eSm!(Mw~~(2o`ecE`vI?}fWz*)1}qHt{8fg* zQM)sZ#{Ro1X;v2T&9;UVbM^jrAG^-&^k1j$%iY(^n{cntGCLIoY(VLM92Ppg+PXUL ze0^gO&v%>a0jR5O18p-(O6}qd$l5?1+AbSNiKrK6VIxR}b$6 z-6$BTwGD+ns#JxP-T28}8XCX?Q&~#Cz6vI)@Ypj%6#wmI%o)qL8J-L5Fw`!+!U%p7 zaqonx1UDjtS(1YSEt#8-NGo=!ZJU1&p+e4s%l%sct0Z!+h z@51+uzu7lTj}-r5QK7$ouY!-fEmYT6shOAv&&4@{(e>N)Q#0sovuUI2DA3xU zN-~QYiGKMQ#}H+8!O4sBt4uw6t1vE{H_GUGJuaQMC?X2)979l=)1uW+pQPdM(*CCI zmB>hJ_SUd^*(bXJjV6!vq88tycdngbCmy3k=Z`F0B96VP8c&aHZqzQjg=4#{PEl~v zM}a+rAd&b`yBs!sw%Yx1xpxD22TdTHzo@lhE)@t9w+9puUD;Hy)JO=BkjeL{`asZ~ zBsoVkQl-{_O+kKv4g$BvUu;T=8~#s^z9K$P!KC7tqIVa6O3~8I@*3S#1bsQBS%?l}`JJ0~P`2GBSQVAuN=RgsE(84zZ4Vtm zo5Q}MVAq64lAOn4S^}=FNB!CQ`w#L#9jiZ2oWy20-%R?k>(j5n>@&?~{vtM?nv-YB zG4^K~lc;!M%%#{`beiOh64teC`MF9ozSvmv?KGXza^UbPJ*agvX$Z(#L|m>87|JuK z^m*I|k2O5r(3mw~_9ciY<1RA;a%M;1Yf#}phJfxr{ME|2oYRQ>#}}Mi=Ys)W)f8cPyJ+Kat#vbQ~e~(13}0)wTfy_ zT#Fv!$@sY&tQPjfa&>jh77u1r&zE?{ zM1-SE64c~=-Q^o7uXc(!UCva~|2GqOK!T&it z{Ojg`Mo;mDFC8HNV$F6U0-{&n#S9G}sNi~HYT`(F?T8F!ikh7hU_M}U8T;m_16I_v zQ?Jp*EJ_+<-n)>+>h;OS!)$R^gw;@ILe3L;2K@1GG)OI0t}3HSnpWEW%lO<|Xb7}< zuxJHeN6!()(A_z#;EDc{rOjkERHII03(M*m%p^hwAP zDGI!7mfEm!zzhw&Wx~yy{^59$SYg=6-+DYG?#Cv?wvc5ICYI>G%#Q^tJxB4E?BK@u`FJ4|^Ui_r<_P7eJOT(AGk8t1aOac@giNjaxmC}TY#@!Igd z=e@p$n8qPtEa_II;t955l<-mh{_I)m+RE8=GB??)@fjbY%_srQk9x zPe8>xQeJ(8dbkkKLNpwk#$#{&O&Qa`EejB>$D>Kv?El5Co=cW{o2lNPBd#*3e+fv- z4_5{5xF&mEHqOh_;}=pw9VHS9Y9wneQP6@!dMj06#2dQL6(hz!)sS)rK&@xLgM4Td zN5b#D{f93wj%V&wrZ|E4r_?vWe_UbhPvxSB^v`A4+3#z&WM)kLcbjS5e^zxa2dJUk zxSZN}8u2%%YU7kbPxk}E7820!A9q;kBgL&|CY*N!AAO0#czJT8_j&oq(E#^+Sessr zdElymfzG?PZB+aP!GsXxfxEfn23VFbbLj5EW@K@1BC4G7vA3HU{UuFOtwz3JR!;&` zxwkLYofsuHr6d~lBu4h}D2D5_y;hj-K5C`Jdit~fli#{Nj!piDvWh08CSXf}=k8cI zTjzQH!#+?rx0uLm0gs7^NwQia_YvtJT!uEtSkP-$WXZI4L^ZMk$nXSsBlo8O`E3-m z&`uPcKLBc7b9DcR0RvdQh}Z5-{_FgEOwipW9N9Uts2m)t54A6RR31d>V;X3o&4<6} zO(>1a;NSXd>vtgbQzeaC`&s+Desj4{|9W&N1P>1n&aG*T{va;riR%{07ohr4a#O!g zjMc7DptyWLUda-x^guP0=Ckd_IBL8Y(5)tl6bNhto=fCofKQM7TLjAg%m#}YJqf88 zAM<|F(cp!6?%f4GMcW^_&w$3Glh#0sxLuw`(vJ{)eqS3HhK}8J%K)6#wzPcz?yis& zW5OV~&T@pr^}0yo~Opq@NoSat=Sy|lHtJpH%OHfXo4028s_waxuS7JMUC&u3ZHx*mynb=t#2nS zX5(ae_%yEz(&_xm=}l=4+KmL|SH}dI27IR4z{Vt1Jk5v$6N0i>?$tdnFn|t;8O4-A zMqdQ(WqD`j0C%F9X!}H?(|f<@DLwU9kYk*9PHjJ_4qoKsc1_c5)G+G@WqF-~-8r)e z0|@F9VJQ@5K`W+#1b1fZQvfkQ*0i{x`Rd;~sht!FfM4Gp!z@KI_$`V+E?c^7C;I47 zDy^_n$%N7haW8QBrD4BUClwUIaUc3rZQ7m)*bmuz{3W*aHruu)pI+lJNP1EHWu+bi zwF`TI@dOAFmqkc}L6cM=B@G*?HMU3M7*L4X9Ws*?CiZ1oG-Ys9zRs5PZFce=1b1qG$#?&pPleSMFlvAr2@Ev5iLV90*^#UqLTE+RA*`=lDuIUHU9 z3>pUjZd|6cGru6rTm90~`7Fw0grTe_!JPrk9;YhV43WOv0kX2J`a~Z-{IGY_@py-> z4={o@OC35A&t*-}dHT_Goo~uafbCwxtwC0{h(i(jBqT!O>R9hF+5gBw_R!uDmz^N)g50J8Xruz*KeanL8|G#AIg;{ z;rZ^d)6aw13@w&U7QOX^C#Asw5;zbfV~dNP)++I3 z(m$aQcVCJK8ZLihM>A3VPt4{t_3E99(BDY#0yD^tzHPZASU`Hcx^KlPsYVPxo2psXiaY-mpl5+rW!<~OdP?^3A?5OWvrQ?WQ4vE& zG)ueRO@H@p#@3@n4)tuU!6`=-QAy*2BU%U&eA?$qTHqvwRD@!eUAh!=Iev_X`Klaj z30F~8V9T@tHTpd3)emX%VGZ)N%H#c(E8;XRU4t{@{w%ip2(Vz*nVBQmL?F21 zqTm+gPlN>fAM5Am=fB>X;F=D$YzLdXVtJu3{55dyI669iHD%7|4F7k&-L?HAivS#j zkf26iibG;92&a~qvwe}}l(eN><2`)|=M9q3fni(*Zy^zAI&bd3vR{3pR%9n*)dPNH z{RaF~jP`0MehFmG=Ns%v&p*WfwO(CQovN|uZN+*U3j({A-s?xczP?wROm8A1wbQuu zBl17di+U`Op+PclzzNXgvDQV2{@^XUYQ~0cq<;5cQM!PIhT{~x+ZpSC9;D$ns=6Gl zEaQ%$vYK?V0NYu=c{>73TNq*YAdJOUQ&7@SotraHefO>iwm8fC((%N=@9d!P_KA78 zL}P%fYmMf=#v9&a!4N?6)$5Undz|B%wq!~o`M(Lqpjd3wdmje-% zqu~6zSs!fEiN;q`DGn!pxNhjp<>X{51DcMyUj98jrF75iWhF+3pd~zC+CdA*)d($~ zLrTirLg>RZl?ZIOr%ySs4i_4WvXK4N*s-{IDxo04%q;Ws*Rt>i`n_c@2mmHxIDhbw z&~#f8bb#r-ZU!Lr_1h|RMw`5LODXVW5E$Zipxt63P~ChN6%}>O#D4$&wr&JgBJt9zW1zIr}D2g!jf60N(Zhx>Rb=F59UMMdtWAu1V9&?dEa zEHP)lA*{!BeHs)L#9xyg>=O7X2CrBC5l$z7LIDw=>isLC#R?0$8=V+~5R)fH51`#@RKQ1=02y`-@iNhuO`~7=CSC%}3ikTdERcRO^ zV53tFM+EOdZ?mc8Gtjq>HMHg=EZ{?y#PlXi$n%r~3_Bpqa36rECjdQv6MO=x!d1C6 zpi{(m<7HPB_&0D%6NRKMZ&DL?hKqG{ zx1$x^(o&-I0BC7~ow8W)f$xWiyDZZ#R>+U#q0Wa7AD)Z36T=1CcczmKEG*cAZeF=L zmjZ8i=q*~8tYcwUqe|g*C(zOrdF!jh<>{YK4;&L8=^nNQR;4GP(jvEQoj-0z^WJ? zK+i~u4z0)irc$M2V>>O_CJSPRdA(6R{}Pn={wWIpR{zZiFlE4DL83#{@wLgQrEY*h ze}u@b0QNTQ`WX^!9Pkb8Ydi(^=m5m^+*%(hm$kE&`f{=eYjr4PW)r7BL!gF~f8HY=i7tuv!3_L|CMw5SELO3n z^6g}!?J*U1N#PttU#J{&yPqe}IehG??|z1HCrWPHOnWfX<4bL+6cqjH<~mT!`#N{v zkJIYDKj5{te&vFYRKvLkKeE#T2+qW=MDU%xk6CdME>NQsGdGAge3!nXQ`jg}Oolm5c#nbg6Pb1#A$@&SH*Mq0^MtBIN9+X?dXs%O@~5Qh z|I!4(YNBUiYI4DVor!~SuDC*GIsNg(3pFC_JK+`*hXw|~PRr~35G;vnT@{k7b29KJ z`Zjt##oHJXv$W{PCe`l!yStc{9N>$O*p%K+@NZ~n$(9&l=5z#7!8gs9nT6Q0ng@!2x+PA8xio4vfzKRUe&dG01Q(mPh(pJW- z({>+w{vj{^VYy-X+e`8-?WA{}SVFqDpa*W!+V8*=^98#GR{t)MQ9PCoONMzaaSzn) zHHZ*WQc`-KN($}NJlu%1-xIsB&Io&YS=P(BVY!ilD{(Dh0#y*IvRDveo≺jiJ0R z8Z+l_R8- zmz4d>`Q;Jza+AmN89GP^G+3&~746P0F8|E9BBh~wBEDNC*GqlIYIQH5uqr%U{A#a1 z3)zD^>y$DS7p}k=M-2Cc8Ba(P;BR+06bsRtJQSvuNjy9AQBhNSJ^hhrI)aN27lF+o zQacSkevxF!x@#9YXHRfTBTuP2)P^jOGPlC=ehMq|VZXgu(OD+w-M#zbu4DRIlOJrq z#M8raIJIy@WOOX+{jtDJi|!Pr{Y^(QFXe*vB~}J;hrG*tn}YY=-1n0@SwKXErS zqdkb}=$QK9!{uw5;)g$hwrTmXCsKv5+6xx}X&jvBmc4a$1sF4G8{1NF$Y`oxeHv72 z!c8$cFwfZ2JH>INhix$pPk*6rdnn*5xMB5`l*?_qlioj`^r#Vok8?s5i{HMdtSlxs z4J}-*0}{V1Z5BpI-0JtdC~iZ9)7p6a!iNXl=v541JTe>-bS6wq=hjC853g;op*8nI z)*61F+{)pvg{NJk+ZZxIVrh$2zoP5Zrtkd9r6f^FB-s+SpSSdp_XDmHiMc^T%M$la0gWUg8?Z z)!<-Yb^3Zgf8e6y_NjPcXl&HgluLBWHLo`J0WXdP*^HNhX&~I_rIJ%mFW;H>M0RWoQpVL8k`)-yOT5{L*>X>+6j68Z9?x z<}bLP!Fk=KPGPjW(_Rr(I@w*Nu5|v^WF^Y7stY02w0B25y8Tg=9p7GT&u3t4P z=uIG>y@2^ag94J{Nw~ci_TU}vqUF&=Qx-ZrPPDK7MxRb`!?uGJo zvbI_DMlXt9*snxugShpsG>zIjhO-Lb+Z7N6{GpC}WQ-glkH-2zWvUv-5PHz?;i-Nl zXaLGeeZ9WI5xks70QzC%LNigPuyaGl)>+TIhBZmG7e3~c4lZJ0Xbd0jAG47heXP?; zd;J6ZL0rkoJ(G{mo8;KwF?ca28_92_U7(Igz%x*1DbJV!p3?r0b?4iXP_w`lg3V*yaw-)GjXKrr5)>p6+2l1UK0IKUg7oRh~L?)}nFN zm%czsOh3np0ZpW3ZW!U-dJIltK0r$N9_fJL5|IJXpcNo?xvpk{LKyRiBW*uoP0m$ArjSFgsKNPIP_MK173cL&%%bXBP;4NI<*;KDtv?6 z{caqtoYzcVXdcW9ui~raPcg>8K~+>#yuP{64BMv2K%JmpgZ0zMpKA85sW=YtghOkK z%D91KCgg6XXoLu0$RjHS4gV1FyZ0h5ya0y{aa3`~$d|-I3BBrf^dg%+9-_&b3XQqz zSk{sLulOBp`QwE(a6|i(obEOGs3>*ELG~jD))h8Dn+(+`@EY7=#|o=jbLrGi z>*)7Cv!wvfLOo0XFOS}ch~_pAzVbXeIr+wp^8n4IX9d^E8^h8GWB?^PrQfRUB;A+p z{RUr}&AbH(!s4&nufLi&jd%!K-2ZmMJ6wdN7r4^jX#1q-VT)1~%A>!3dk3*C(GwpV z_{HzKK!kh`9=wgEBp=TSoBAx)e7CL`g)l2uaTDI1<^srfmiNEY>Kk54?w>2ckZ^!P zX*1MqNg>2h%SgXhtk`h^ZLC8Y38*CvkyFuftT-JJd;~>rd{HW#shYM+lfI8*03r6o zcTP6I2X(T9U!Rwx=weiveXGFDeh^eN-d=-Y{xJc`iVWLq;jD9EE4pBndTno1@o$Jf z#MHxyfafkrHWWf&^Zi|j(%Uk!C|g+fVw45ypd5F#VrVX&8=9JC8F0 zZ$?Rr9lFl&_%E(EElCi+Y^cx{oCNUUy$WzgB3#_}^r9p6bR8S?zrB8qk7G>Hcf9XU zn8b_$~i9bvy&YNrztslylW7p%b*NMia985d}73CnGbs}k&ZZ;egq2ZFb^o4Y*IKAESppJSOMo9n4~is;tdgk8xxht6o+>k=-6xA`9R8Q zdsH-{(!kXv8*OqI144Wq$6|0ueJAW_03(9J@$pDM*WG`}k_@V*0=xIz-F89naOi#F z;ik`C>oUW^UpswBJN1k_4HqE_wiy_Ag9rZ-v*Ar@_r*P>=Nv--qRuFuF|_)^1HI@k zke1f3)G=1xZIRy28n-LSF9*cafj#aWhq}rxaAQMx&C1};iWOM_c9ZiXW8I(f)B5&n z4esg_NO*f8wsIeBEK9YPUO#~MBPN1C=Q?9=INN+yAL+W^Ca8K)i#5fZrBD*(%w(_` zD$F6KXp1LCjCfa$pCRjpRjfKZ{yeM??%LJ$K!LiW57Yq1f#1lDWPBYu_4xetpC?wH zrtphYQ6-Rnm0n+A_wj~?JC=0Q8p_&|4!mwaJidKmDN6!;u**#H&&kW0Z2t=H`xQo%M52Oq9<7!S3!pWL?op2_{CV@iHl zX7(ehDOn~cw|86!46}&P@xw6@t!q2}Bfh~*X!!s>@4#2Rz%b*j<>B$^-F^GRqHbKo z;}4i9fLyZJO9*hL@RslQ^P+W965ekuOk;ZbB<9?0zjfYxq7dqc2?%I;HeUBSSdJJ@ zZ}bo>tH{~9u!x9}ax0*-)d3n~>2Lyir*Of}!b^aP*=ogXUw^Zw47>esqXx%1@6bEU z(5TBd$_G@aQ1a*`cRd#)Lk~G#`H`hd0#AJOe2J@jwgnH0XJK9t=GHY(D-WLATH8o2 z?k~7Yi~u~s6k}N_Q;3ui#)lFK3je5g;$C1|*YUDk#*OcV|6;N#2o0Ny%J!@Cv^es) z`uVK0ExxZvqd;+|Kbb8GoIPHmpNa{jL$vE0XAF!=asDuAJ+I0+G5X{av$J3_LM0O$ zC-&Ys!n3quxwLHjejXu>(QZEG6rP}}j2o7m+r2uD7@!s2`|VXkj0I`D6L6KfqAT?- zNyFF!UNc<4yab5}VRxA7MMgy6^1QzI!|o+wbMv4EjL7!?L^+B6;u z4dp^03o30*Z+-LS_R_v)3^34_8Bpg2dAOj$;5gc=4OZN7wzgqz^ z%E@FUw3Lt-JKsotv0pE;a(+(rZ4&!I$ltj@O`~K~t3flE&cNbIK#ah+AW1b2lSHPw z{duBj>6w?zrU;U&lFrc)zl`X|v0_=kOZ|=~C*+0>+KK0Vm27A8!+CTdspuOW=|>&BDvKW00YkyKS1nY& z6++A@NCG!7woH)1WY;VTO~%Y~tL54C^|kEQa%CrA9Xd)nz7#nCutz;X&co*GrA$Wi9e?RJ8Yz zAINTTpmqBDi2slv4H5i_Gf`FqE)h{FtZBrILdFI-Cil77cbmhZt);SPiS1{peSONh z>O?`{kYx#v>f>FE?4%6UmyE?Uj05Mh?x-#%M+SJq>4#8E^!Qh{2S6uB?#>DIK>`8- z{aHNaU$>2S&)Rvk-b21VJ_{!poNHHq_6c%m1NP@6V`E~3ulOFkI9EHj*?5q}?&kJ8 zd%7ES|<~+N@Xl~@I^`YBulGtyvG^4^Mj*5mD zF7aWk%+oP&fF9DqX@7g(D}BRK4QR(p8#`H_UEFchEf8sE1gJT$E ziqwqkpxwsS{hAjUzMcAA*E(H`tdml{-^qTJtIJ=0at2{0e%qg|_=+YY)|wkL)aeDn zvK|>59O~yZZ^4|b&^9v?o$=mz56b<`t3(~0p5G2g1y$dkM+u-i_QX?5fy>P*+R8bS zxk_?uSs&VK7#U^$ceRz_0*n2rGgtlQ!Ik)H5#IuP-XhGD+V)A)+tc~BxT87{P zLzRpka5KqgZcZxb@lL4GOFd@(eri#C(QTATp%u5y1?TYeV||gRtYM+( z#UCU6#nnRc=8eOck7J<_=_;c_R$+&oOH!JAmsvsHT$K!2ISwcp#B#;kL&;0toVeC+ zl^eGFI$QKbI!ibYdKC<87OEjaxRWSp9=WHBN*cd9=p|Eu2loLy5q($#ae|WTzYB~I z5isYIyjk<8Gd#+EzU>OxMf|`7te}24h}j?|vQGajw{62}!nyU~fTC3)6=$Fb&e&-10!; z@gsJplX5VNSuU5sP zp0gok6_KoUN34yTtTl>X-w=Htt|{r0Lr3y4Uaf3)BC=_ndbe$SRT)6?G3x~J-_nY> zwTmKZb#nm%2K0Hf6d?&bX7bcZoQqo8aP+?kAP5MJIADB-k(&I{VHM-uDh3>gq`lwZ z4m;qj*M91LRimc-(pa+!A`-m0pa{ReS5Dti>KA;IH{$H_XJ=0CVl?EtgQ8NrHV5*^ zz}}J&fA$GTmG2lvqWt9_8LyPu|N6iV=02#cu%OKnC;6oZ*XIs2V@t;+9s4C@BS#_F zO2V|O)<1m8h&=NkntCv>=c;Bwg4IGyLh}agit`aKg&~R=V5;srFqT#rrO7V|Riu#?_FA-M0zRNncBJ0N-!By6+oc= ziLaf)_wE^msxV7}2*U}Kd&6X-WXbD=AO+F`V*!(+Fx_~7%WBEDZ}NpqO%5q#;4D}` zzdVK&hW*_9+mcQ>Gb=)ZBcBG+`Vb~;EoJ*?+U^blcQ*!UmzE#o-JQZi;H!?T!g6r8 zvA0Q)cM1OL8rOcYC$1fZ;l@*{n#N%pB& zuTZ#ZYKcrNW=q!{hj1rp;UzN&H)#wo)oDf8E4tm;P-+A*-2bfmL!aY!|3OMV6TRDI zMfcux(P}R|Nb2`*0A%%8^beNOwvb&VQ19sDRj{4}Ak6Er!JR(?vt5WjdQo&v8r^|C zwE>rM;w5czk?XZPFkN=_3t-!FgIo%=3T5>^SwCAWuJ)3cHUUoLU?*ve1&92sulC-X zEo6_2L{JOz5m(=iX8F-8=O#=A@*RL5OBChp6YdoavJMArKZbvwpSJ+_rUnPb))Qy& zo-rxbC>!&dA^&fn>DqFNGSz!(*=yMVDq&);)(8f9Dt zVGU|q2w6r>Gb7#cYb>w}2ECw}2na%3AkHSia75rE>k$`|>>zlr>&4nxbv+ z>w??E9!x3XZD>d9!<_rnKU@4yWUD*1&ivzTx2_mLIjCX{$xJuBxNpqs?_Mg{=M*|g4Bj#;$PvN6`AKf8 z>!|oa``?`@6fR^>g_M)z%B(2!q%DKOT8-=BaHV=)rhb#j$hd7 ziFji$WA7Nz_u|NY8>pI;%+1YNNeFZoF<9pwjC^pIRv*1+_~i1tSsO5<6qu+w++OO{ zJO%yVBBk$1sXTdhYDVyjjUQP;_<9)crSesn=y@L#O8A{9TMYC&ny_5?eAD)@{LOv5 zfQ#tq>h%0!znFBadE9tg32VlqVS_tilU{Kgg@CPJ4D=$gVAU5viDUbB*!EOa4Qw;J zY;;T5@s=V5u5U@$PDsZQkm@SQ!@XzwLPV^;gzGMjFu$p2lmgn#0VB##G8jf zvp&B%>p}D2;w_npu=kEraYTlQZ=02k;wSlqH> zdew0)_)HD#^DgarQ@M0cM-7yjh^ygqF6_rnzc6^PurZx87%)xRPXC_^0K;KM(#Fby z6!>t6QA8G{%<~J?V4}eIcK&e^EqfgaMTixJfO#&^W-F2~N$DY0fDOx7rLLVlkW<|* zSR26m+CVNFne<7~IYGA966a*^xOJzaxf?WeoH|d5vOsU<|KdkqHBILvkum{1yX-f6 z(PB}O>4#M5)|=~#A`*i4Esx1#td#WKQB576(pwqhWaK?nh( zw_bzMHus6EB{0-jiK{CSSa^iEvHGmYH*flQ(dZwDJ&)g6*osK{m41y=hbH}@eg^_)kAVB|QbXsPP}im&c@H-7W0^~76d(moirRSGn)GCp}=@^T4M z(j6%`_B6DEHpRfQUY8X~{9oj!hxtzEvw}9$^4?Tee90RF^>H7@rQsGr#T9p7+Lew_ z`+m$0q^=w2f2KLLomiV3a7{N6j1!@aIi4Mmc9q|HN{`g&mM_wl%7MVIk6Z0Ib}Rsj zs>MEmJOCAk?IasC#794lma@-z>|6mXG;bFt7qH%|Gzm1GJ=6YB2LjbbUYOmF+``-C z=@`sFNB@9ZVE)XW0ebTA#KM`P*bQw2crp-eA@kjYdXKVsd zq`t5q0?DA32_{Zx6U;rYlnD|p$8t@WRyn@Q`yOzdIJJmE&W~2U%Sm6K6(4A$0GJ>B zu_@#p)e?dpDm*>f@zRa}QE#TBFZRE0_ZH;dzFliQYKPSY+Gp}{V7r1LUlXOOhNDqf zTC!X>I)FqeHUUP@uJS9dlqpYtd~qN2#pS`Sjkg*J#@QUOkk65JMaz>4r1k#(>SS|9 zDYE0-*0${_;BGtyWie1{{z2A(27G&T*^z7Aoi+>$%wq+CS&l!};@CU<9o^ zBbAI809t{xR;|sasb8pX8F!~!`X(*%Xa2m<*AHe@4XK{*?1pj+jfLzMdwhw(nQ8K7 zfR|-FbW$U7VLDhw`X%Em%oc9}!ZdWFvbhb@I5EmTHBO*f8k`A5__tqOOt7{@dH@V@2 zG~ike1kbWaeC}kuE*O)8OP$lka+B zv*HN{Bj3S(WmcP$@U6V^->uxs@k|rnoST~iGld?Sv|F`;FO3>Qv%KJeRamM+Cvg5L z&)lG%<#O1VtSXmsHYFpjmc4%8h&5&MNs5#1>??#%K$2pNE)t?-N{NT~=k_~Zq3JfJ zOR{b$$#>nInS`5WbbT$=SVeQg3qsxw)yBfX83z@{G_mhMx-?misaf#Q>~@a?N*T6s?$qrH?`*JlTlM}R?%98PVxidkT_}6YrVe;0GGyrgM=P-KfZwTv!yhq z;j*@SNm}@}3TrmsIr6f>56rwz6%g37dXNzua6sbM`1?z0YHG>E`qG2emy;?$#}?*c zMJr>oI*?WlrnIS6x()4!p3JzmFJ3FlbvUA(7dVqSQ8F0qqGN;=#c;~`cZd;$Reg+4 zq*f>IA=qy}uX_&8nj&la`o&RHP7%r0{=O!l?NbICK^b^NM8$^YTyWSjdr-wptviN# z=A&dV-n&Xh0mK4naWHozO_1+w;=Ar6e-!;J*DxvdS$6za7Xm`uchl9(512VYv!4DT z2$cApo*4Yu4>{?;`68|%FK0uaQ;v5$Ee#bfx6(OIRzLHc*Q+{wSLRR$fEHkiKL#d9 z+M%&vC4+CS^_EX*ySD3}SS)RJzHOL7kJKbu>)@_ud6&oCsqZmm>lOkk+kJor73x?f zm8N9!y#Z?oZ4_s~q!$J+5VuT)~N*$=m2do%^S z=Rd)FQ_sG4UJGd~t{c9au5*xcZR>1(IPo&Q%l{*wUygx(4MhcZhe$v`fc=uZ=e zb(u7G`w&7N^A?f7pM@B$lN)@i&Hf&i2?+_4Q=ia7UIhJHz54-pAf`(^wQQ{&hmltA%5j`xT$Th%l;-)#MYa&_&gSdiOo9~^!zkGa=gvrVN|L1GEq|AECP9RahavR^ueQxY&de;`j98tW1CR8E) z0fB*=DM`{OjO}8ju13)ye)F$j#F@DXnEbrQ0^xuZCsi>i@vR_VrbQ5_`4(!DygB*B z6I3$kQr}HG1*;#RpuR%^I>5wdR)DM&8qXl4Vc@1AO$~mps+^@7h+kkMgXcOijo9FE$R2?#yFn8+zJ>CLa~I-Xw9I@v^~G zf@%r&pD*jj0BQYp3-|!*2-Q_~_>0xuik_sjY3lA;UU!``+!-J>vFhjm-DPZ67UVgg z0dyw>&HdZ?Ko&bQvJ4I61R}_z44KK_SGiUF zT7{EqcC6DZ%N>^swf3W|{aac3J&S19E4TPWQ|dY*`?2$fSzO20YL66@l*WPP7Vy|R ze0&nl0pKf9Cs?~OGK9k-#qW~`Q6Gfwkcl9<8{Yz!P^re(l?7T_D(=siZctK#2DP;I z1lQLbF$Eg_x|i91LZaI^if{xInJ6gaL0dQ#Y;9>brS0~c`T0s9^HE8teVJXRiDwSx zTEfjgo7@Hhhd;TZEBpHbrGEf(Y^0C0ICVyFz@C_1L`DvqB-!j#MIpTEWY_JSWCI{F z=G#;+;*fmy>Jd_yEwSz>{Lp}vmUsJ%&!pk~E$l)qb`A>LkAzIuJ1d=TPa1O9+J1kh zP(UUDlqA!@FjHete$(*mJ(RYJxKwxMu;+`4|rM{Ckc` zvXCw=2|+nAgP3v-Rg3$JdvK$~*3>))-4%|lKbv-+24vjSF+iMlbF1&0F~Gkk=wv_n zXqTVV^L|m3VB#;)6%~Bq+D8qhCEkk&vzsru#(b=Oz-1bycsC|7)_@(!Jtg)8oOe;+BLdl;Egp10|ArX;xTV1}t~z|GLOUFr zkeYM*z|sRvAH70#eTagVOc5^zuRp-Z|7&S@6!DhnQ%N7@QUTf!2eksJ1apD__32*Y zrswQ%`9{kO-FrxcmI$(q|MQ!ms|y|FXpitw$R+8=o!dl4Q@(&~bRR62Yq#`eEk^yz zUn)I|o!`##!AMgE;g>&wuvUd5;Ssx(6BQo;Aa;#TdpbG*uMB)WOiL#~7cX!2TvfkV zADtxqI!b9l5gF#F`%6KB2RuzVh|&vDXn>)EFZflN2gi_y^fcKsfEZES8(#*9J_Fon zYWFPW0!|z%O|4uI_=g5q z>E>@u!pkRc{)edd4y5{h|HqF?Mp>1Lh>naX%HAVK%FZ4qBV^|w3K=1U$jXdluS4dE zWMyPzJGNxSNy^MTzw7jRf4;wejpy@z-uHE1<8fWr0% zh<3@+)@6h4Mddc#8GzY**NB0V4MpoXc`+Ey)0?`7iap0Je)a~oqGbpuOlB&#ybg^tNjy!QufNs!Mk9wiKB-t>4(J~O(S z19!yVXz{(PPL3@c#**oTVI}~FWh#gk@?*!eqJGKzfaxy1wZR1yN%gt8+)fb_x1ork z4?k`pbpf%sG*o`>x$4x3nCfaHocSee!Tl=eiom{ss*P?vDF96o<{jX0G7Hq~1%+Ze z3!ViMVDfCf@sK^dft>N`-%~#OeQkqP>L&JMuzs$~ygFGvkKC6_bnJe=YOHX>YmgTl zxlZRBJ^De_CZAxanJ%dg+EH&=iD{qCnL9@wbMHA+anWKkEHVUD5g3!4mGfD4V`RwP zu+Xf72MZLjK=^bQAXMvkMA)~=jEqYegDV4?OKfcWie|M1{fZp;w#-6k>&E9EoBrUY z{Hy@cmFZDDwi~XN^lTH#Z#JfhwC|?<63U7tvQ7s$jOkKh4=6UEeEC(wpwmMU^%VPY zYGr2HA}Bn~1}^SS876l1;^fL7gpwMa)m3kTg$RteNeZ`EJt=*Ej&c_!D%y^3IZ^A9 zoAXaNIyE+tYaiuXA{cVmF4A_ry?*2vzG5`MK|M)?9d{&jk=DEXX|t-5)!7DUDaH7VOhF31<(f%loY4_~lRlJVNh4jmV0KoPF8jMp zl&(!vpQ|Jm_{_Dc6c0x2y(s?)|Iy38GSVPm%qXDlAJ&r>?#Z;SnZ5Q(=B32CA z9VhZ{RDRLn)-nI)fZYvshrfC^alld=*228l``A}fu|~-@o4aw-37M>nx>#n9g8`ee z-^Vf(DU3M%Py7k>TF>c*jwgY;2LKr)AMcV|G%EMfp36~x^rQC@)B%!D*7&GC>p*n9 zsKu;@XNXNbQCwcE^&yD5FOMYAUX%`%>TWngh0TBs;nR}}r^NoE$QZ&2o;IGe@G8|9 zs8B*}ly@;dkQO!M*nT_gjE;`WGiH|tL9Mt%cj5dtgXwxG83ES_f1`uveXD!>?ZfS}3R?-e7oI=Fg&%Db+IGb4UF zGlhhx8IiNN^dRA{H3)`EfQpp1#)X85i`FxP?&YgX}lsM zgzof)T~*XY8SRBZ*Vt_J6tO-ioVSHEcm=V+mvndJd?h~b33Q4=n%lU{>dG(i*is6E zFC17Fbo>REc~b4nP@Kf~v1&860~q|yAFFr7KmUUsNo!u=#^jQ%w-;1bi;;*9NIgd5)v9kq z$iL}7F-iIM&3x%i0I3#qu>CG>N9|U|H|DeF=__t#mph!mO6xrdDPo4gC3M^5En^9s zDU4&^fu@j4@1B+|f1^kSDhdJzw*GLp5O#ufZQ+aRGX;e+f5#l=2n1DU5ieD`!jFp? zDnBjUXTCqL@EmwB_Q|qVo1-4_KnzfH`^lQhjRr56TEQ&-jUrp}b>KSBDRcrKGx{;K zTSyDY-O{H4b%mYr$cTqRoHxJ}#P+9*<6#bOA+2vI6ENFya&q+*jgExLb4njnL*v{T zdiuAkmE+)c`@G=|k*6)WND zLDL5e`)`FF{m`mQ>Vf7lw6Ap9a7W|Nbp4t6uB1Rn;bH6JAGgtWULYo1(|HZYulC%O z7iD2#;dg9zZ+n$bYbM>rjdRMz9f-~VwQw0@uKZ`XwO=3}&X@Pd=4#gZ6rjwZc1ZA+ z$3(=gu`*lh62TKu-Idxixz8W4V_)hWIwVJ(HvfPJNEgJMhGYvO~ePI*rE z27}(OL%#2empJ}Px){zcr~s>dCh&_b5i-c+)3Eny`(ZM7Cd&NY2Uo_;L#){A2h{X9ykA6eBSHW!XV9U;Zk91rPRLC)N0`FBu3VdYxBNF4o z34y@ewoj(B?tqqwLi1A!yxFLc@O<3BVc4p>nU$b>R+$YaNz4-2hpH49I zO)3MX7D)T;2EI86_dBlpQQ5PYH$?BE-h3H)p>Xv$)ZS1~b7esBLIN9>n5%zWFm!()Y=51-nJoPpY( z0)r9_g=Y#I{i*_Y0p_y>`*?=Pp2nYAFE93R;_obBVO4BtYSKF{F7@(9jZf82g2Xj{ zNypX8`_>IZms)S9QkeD@%jgy9t7zimn3$MD$~@lLrFv8F(#JLl8*ZJM1yIVK1MD?d z?uq$J?R*ax_|Yj)uZt{AlW+@`gktl>o;QzkRS|Z1>kn8*?>(o2rTchqw@iJ}@c(c5 z@YMd`(45oJ)jy4)YW>g7Ih99@{Q5=b^-A>LK;MTvsbScnq_uI84^cDuTUJw<7|kP$Je zYaeejNgqoQve7u;17hAdZq1936BdBJ(tP6fehWOOx=!}6a5>F17f9;vNn(B@T3U>L zd~Z3?fv5fU@Drp$mq*@bo_ZFaK>28ILrB;(K<@5kT=4PZ$BN_beJ%$xJMFV4Q*4OU zo#t&;uodAC(&ttI-4+w-tDkX}7Axbmq4j{x;8Ub@OV!PtFx~w7N*yTz0?&_y?7wF` zIjzdl@Y76_;!C}+zyT3Y+>Z6zzl?J|XT1_tpT0I&pYV=2bP=`Ur{~FPZ~WJ7ZP1Li z0NnDv>H-Ov1!}atk_no-z(^ROl`iW8^X6r>k)>-tqn*7rvR+;g{od4I20^qsK)35D zrWIa8_DP)QnQs=lT&j>XQc>w(M=oP;USmP;DA-NlT&Q)|Z2$?~fQGbuC@9;7>MoPI z1Leg@?#$9w>R9Z3y45X_0;o34;jedZ{}>o}M&rR}6)N*~tX{v!Fe54_P0}YB z*ag&J5TvNJ%>zU^v)n`J=g&i~Fc^*|_E~NwBTVOBN;>!Eg+`b4#JcX``eQ>DldHb* z&VRWKHNIw1w2_Mc&gaTSARB{cEaFlIT;`XiS@+WqZQh0#mB2pt&Tmjlb6%8~IfysiN)FKX3 zYDo<>P$R%RVm5g$@ogH6p1k`xk#f`>{BsBTK3b!;sPl{pr@7T)+@Cbx{BJ!_Y~}ev ztefi{GcObXxG%+IZ~WnA^uNvJXBO~iL zHr$;esspojcy3&}`^>)K_L(H!B700cRL_WdTyTd<_Xx!c{|QZv7Lai>fdRAD%L@r4 zK#p;+>Yjmj6TQWrI65c^ZP_4bWm%;uWSZW`u=#%n2+jl*J95gfQ!_pp$su+ZgmmMYc2}?> zN0jUDD$x&a^TV}g&b6EQyDok$Ci+Yz#OJcDn|;{PvA?vTseSnI{```68c{6j^_l?f zo~X=z{pwV$YPIuZ4sg5>ytAh2iT*L1KNU|P?#J_(1`InH=87fhw9F`mER{b@AAr_I zjF^0mIUBy>3pIAN(m@k64TF!N;(x0n)7I8D<$l+K(nZB51Hh`Bk&&@GcIWZu?fBdf zZtKmC-7KM!5N^*WV;9&xwO?P={8bCt*R)jDmm5oM(v^|b;onM>qu8!Uu`(e(Qv*R# zt@rxWV1c;j+GU@yc^yf=i+iGhZlrsD3v=K?|M*Bc$J(pl5Ky&AFa>tvU@ki$$L^yR zaTWDQCq5YlR@{95{=F_l8sr5GQGpM%T~CoRq--vS2g9qo?L1MpV7%3{ujz6net%vI z?&7mmcjadNR!#S1d?xdKye}d!&g#r!8E`;X7!7xNihh}G%A1qth5>q#;jkhI`DHZ^ zh92sZlw+-Rg+UjEc(mqb5GPM$43Q=0IF&jDW_I~%gTqG2jYBBJ#o`{ z*L|#{Ll}J@kG+>!QvVaXI)Cz;W0+%gz_)^)-a?XM=}4956*aYq`$FI7Bvm1B4MMV=7DmGae-a!-K7BNF;P^B?1Zoui&t zJB!k3{L{;^YCp~VhK|Wj&!G72hsWrZ05yRsNkke&hRA7Q^%Qwjy4E+-&CO*ITVTxg$~BJ%X5oSD>CK#ClfH^mpJ%^~PMeF4=+M-D{KLReVW3L=>PhfNr7&Nc`Ix0-qwh&QoozySoLR1IzE+0$Ip(!Uf$llVJxr<*K$n864_LP)NMXZsAkqQ53Q&3T-cmVnC ztWR}&{k!f2Kv*ZNJs#X5)m3J z=jS_CYH_q~?^kUHxxQ)h?qv1!fF$rQQBvSFYoGGgzMVhofX1mTk|XAB+PWjv2Z9TS z^%5N$D}hOMHyFIu1g*D7U|pDXFC_UPFhJ-Eli`P@{@~3Ea6sRBo;Xz=X>`95z=s$C zGnbTOXBnEu!N8fPZvx#Ac8gzG&s+?iCPSD%gb`!!K#HU5>+4<9W4A5lotz-pE`V=^ z^ut(X`KjT@m0SN;3(#2k{3jA2FB_zPu2AsepGWu9{%a^<6$xsFso5B^Ba2O@oOYsUgY~3zOAy_r@A`O znSx+^@|_nKnPYbXG>mwK#%xiZw$r|Ro0<2uv%B8{{d#LQ31{-C5$u+Kl~{V%@7^dX z8_3ldz?(hJoo$N*bK(G+D-saOmq!ART)F;Rgx9c?9W2JntFP;TYM!!Pd0$i9ZRheB1m#ArO@+x`XaAAf9td&W{EHv2_&$l% z_u3DwIT<{47)X|OFFU;1rL+v*nZ-eHxM7hCtKj1zyDqfQo(UFMSDei8~jBN=!)t-t=u{4d;S%+L8U5z=2FHb zVdq9^Kn7lS5{@o@yGL2`=ojz0;{xZq8@N;i01JBb>H}M+7^+t)^NcNly8Mc$=<6x( z5>i?hkyQ-Cvnn2qYV!gUX_?K7?b`kQb%7uK*{amKv&=?iR(*jsP=hA4ao|};`0<`$ z<4#-D5V7>F4m&OwzCy-D1Tk^qW}!Uf@{nn-QCUAV)})PZJo-VOEaApA|MiUtNpcQz zp{k(5W5t+MA5@lE=s4e@=QWUh0q@KMXDv-tgc@b)Mr$?*umM=Zb6P8(-tW8K3epmp=ECS`{O?0k+Ne%Y>SugsZ zfw~^ZGfVdc+@M5W1l#z`9Y_du0lm%jA*R)RQoxANe}Q&tjx-^l_X^Ud5>~HOM6rh2 z=5@HQ?GBses%Jw-$p7?$zZ9vqg73xs@_vi>3!^lw(s8~_k`XZVi7~HTaLf)wrDTIU5caQL1Ks4N?ERL~FehskTIn)LX181Qzw;aq->rdMHRS6CRd^rOG z7Yi(qWyZyWRZb?^NBJH%uZ-uisi$LqUS`h4Sd;h)M8HvpIft=#q3+uMf`VRj`ZW*D9p#EykA*>|TA)A9 zP!`x`pb5u#URy?%Ci;{6F$xtzJP6B?Nm z!{ql#9jBzE&AiVG@(+Yxix(Q+z#35l6^f`cDyS2>C3*Ev}1N&-j-#c5p7~?|2Q0>tVD7{AyWuB48*Nd^z`yK`ua9j9#1nPi@2Z~=vAX4 zSRA=NsD1`1aF>>m$)?b+y*LepS$EVPodlR~bW-yukiy(73=mdV9TZ;SL+-u`eOPEJ zAZJ@;+ZO-?z#3-0f1V#cxQO`XiOU#&s}M*+&~*1+ik)QmpK{$1=}wf@d1jAR%_f!k zG3q>j$sx*h7OZOO@eDnSgQw~gS>V!))yNCukiCnlJZJ$Dc-&VW>o^sB;)}e zhL&~|c^3~t?T*iK=IH2AhdacW4j=6_-iaD@2XfwL8zhhI>uH2I`OU)b!s_8wjtP)0md3J0(4)1_Ib$qo9 z zVn5h^J+ly^3*q~JZ!eGM3QT?~-dB04HtS=!*p*WJ=pS_l0wX|Kd_2Q$%m>Q1j#l`c z>J!Nv?54{Fn(pia5Y|F{XFj-8p-qHw;z5buKt^*&9wGN0&1wBnQrmQR&!;Rffm$kI zJ1KFl=5MOa`Q!QT89%e{|3}>9!(PE0kz-dsw&gZynbpx5=lLA6+K4Xyy`vl!CP>`X6SO5Q@qoDX>nJ*G`QiRwXUDsD13g7< zS^8Oy$<=h7XDGV6bnA57)ZlFa(GGGoVP3u7kD>*;tvAI2X!1onvVWE7W;ZLfjQnQQ z3N0fp7L~g`KQU2OyC^0!9n~XiK(?AHQU2XG$rZuQ96F3}WwD749b|~M2@v*LZ`_Pg z<&S=GnmcFpF-^aW6GCNifBKC8si?#z&4R}o1ckBit^+K@@ah7dF8z17ubgM}4-?-` zb(>GJNs9)~%{$NzdgQUk=4_PQ>nR!U&dexj=G{#LA+=JCzxcoM6b`pmYrPq;2>Sad zhX#qr^~6v#H!?h+ihDt^*l6uOG#t6Y^rkOV+^KKPbrBuVBBK$l%;uxE(DjA`9!-rd z3ad}E3h`jIULLfgz7@-sWG=Hk;ig!w8N`POI@ia1By#rSiW$E&GSu5rc5ce8M`jHQ zXz#!XiFv>q^snB{-LzKIl8 z&#xH@Q5qm9x!;LgW@+0QbV>C<-sfkUV*Xqw$mT^s=xcAg^%MAHtRYho&5;zC~#d6;E+ z9pPWZZmihqSA(HpNMab2k(!MVvieb)QTBAl^}~9k)C&W7GPl(o8NXWYx~JVhLC-c~ zD3bG0R>_!{UHNfcLpY5#)n)Lma(3Wms#`OW{-bG~w@PfzOU8&}h*vZy2TF;^@|!5^ zxoBBr8AqPCZ5sdnZ@G&aW>t^JovIm1{K>iWyOV}C>K3=BxpULJ?9J(#7t{UtkM7dF z0>HQvwm%D2Z5c2m%_&aZn0&Xl++PSz@D$47Hk@GmthnFlXt@`+1>3#tzW$J2Yv+K4 zhi!x`#%jtMHRkzaqz*O)1~j)t-!pFr6}Ezg&xST$!G}MekY|1=$@Ja0SDNNNY;<>Q zMcA!19z*W`jl)>+t4nGU()DGiG!i*|+|#*7V(}+D5%9{6SCvxy=m?W(dCx&-Cj4EB@0~qELsPx_Ll+I5JP^2hYILyL+i(NvKvA2f~ zQ~qY_e-sir!Gl~S_lfEb`2m#z?JzE>U-R(MJxy4&dF{`#&bgLzW^rOuPdQ3e@jp9J z_N1IHG>%0Zc~{~%BE@gEzP!L^ljfPzZE@0rCAZcyeDowft&Suzg6~9bvb1^LL1EVt zT6Z*iw1H@ zITng z?5#7aPRIS(XVn}~AOOjJC4@{1aYC}ol;>hL+ZfF2?bzUa_TEsftpU-=(iRP6U|;g? zW?sG+r3%%>HyMk@tDQBcYoVInCP2@o^ug7$(WPIbINtG+Lk8 z(&ZIVM;-Db>*+cj6NO5g#;T$T*m{lOB2x}h?kB<2*U01N2-f_O{9&r$4$5vK>pjqj z-jWtb-S!hmtd~!FazFH{I=|j^X79LTgk}n=;RrRHvDB>n>*gD+kxVw0>j-~Aq;a$? zAz3ARA++5I>6#m=OWRpw#n1X_KdjRlsu~6D2W^=|mObxWK1F?tV(u&FkU+GFN%d>M z+=9Q$4#hm(|I@ZM*Pc7q;#bEr{dE21C$fcTqY(Frp!duHl@3?@uFX&*ctqYY+C|Pf zo{rwr$sg2Z_@Inhe(v<3i((slZ#4!|mtXeSJ_>LsOJ5-sbV8lc?ZzMeVL}dtQ!t6k zl~7#Wq3O3f(A#Ry8z1u})+`}6{i!jDd(j-_kz}q{O6y=0Afk8&Cn@OBWq2Wl0ugtr zFN_VtV&lsjUm$=K3vZu9-ghq!Lbs>-)L}-RuWi>)^R0dqQcd&zWt1f0_Fy+ZQ~ni; z-~JzMAgXzkm+XyeW-eeVoHn@z`4QW}@vjgqxX!{+-^^Py0|IV4zj5zpt-fcy5PbX z;VH{_v-PT;+h}&eo$`h5tcR1oO%F{bAoFkq8gimg$z_%{Nz(1 z63xtI=G~Q<^HTYmigdV@6M>)wM?US` znN~S^%Eh>0cwRD##nfyrkFnh34-{z*B4i^4RZ)@Mf2veaQ0c*s;_GN}Nwr4y^Tw;l z%(gkK2xF$lZ2*Ual_?V{j`UjQsDjOiS1T6`G_UgCauc4sWL3|>U;zr!rxZuxp<_I zP`parHKh8%6B)TMp=6_5i!rJKcZlT@gk&}Tc&gK_oj;ArtzlT{62(*Y`|OJKl+^1vAw!|z(01~w<{=kU>q|~F zSp(NQMn+I%o)y2`KqKoh*jc2BUq#l3u5~5v(RUNZ90=J&PpB#r^gBF~B;)-W8dsEo zsF)paqAFR9x!@Or#KTqMYrc|?+Sr<9UYC1fti>apMb(m^k&+~>3x1BZGS#!`wB}HVM-Fth28Ta)=$B6EYDIW$?P7z0oqg9G^1bfreZY;i zxNZ&^C?C2`A05S6WJX++xy4m4D&a%Nr_xjD_S*ebi+MMX?>{R}32N0na(3SYwVR`z ze;3(*7p0c}rb}(rIv@uMT$d95z>KR7VC~8(E!BF^lXmTkl{S=Da(r#&wR)W)u`)O? z*EDU$GgN36^Qo=(YBVbA;sgcTQ})?5^;ZUFK6;J8T?AJggzX*+Y7_Si87EAtT56Q{Nm->=bLldqV zA)zu%LoRzHsA%U~&uISsjn1CL+52B(%v`^4E|r`^V-zkYWLyzgKl9DN%u|o+lla}k zgH=BYH}1HnK_1~UKte&fX z<@Qx3K`3wDYO0V+*UWb*enanQp7?ri!Sune$2-Yi?DKUCdRHC*ibGg)djU6y11B^WD^JYsio<0u84GV@O9{!P<%yh zEQ^Ze7$4qvFKyKFo+iuJJxjq%{&mcy^~)1wBeQLG%j0$L?M=V86?~w;i+>O0=>L=Y z!eCuwU8Rj+5#mF1>s^8VhxoaetOzzag$H#);hjC}JPev6q54$vIQ~jTY~AYNLnU@F zF0R}HeaSnB6D$En6rldkGvxCjAIKycBVC10=i)86B)dm-h$ojnbp^hpr{&TB%#lL? zz6q+Eh=*#DYq`$pzQ94qD6u%9XeJYVbP2=A;51W{2EvV*RpS#D#Gg-22{IL?JHim1 zf|~0=uIWUcedV|COa)AO9NyGNPrukJfx|pnqI0s^SxbU}9p@?yhc&entiM1qT3EsgVjF{ z&-7cc*LHC>EXWSo>EwQy`0#vf6K^KeOf9oHr{lWIl-o&?8k0bw8Oau+k0FiNLd)qd zxFO8SP_51GGdJ*w`h-Og^tLFzfCZs);k3|*?Ntl=B{RvV{K}_zV9p}Gvx`Y+eiNVD z4vB@tF9_V~)Lxe&$6U`Q(b1vw!NG9;p@GOgrmkTjyGe$+Cb#|+7V(M|9@l}E{P@|A z=4^B_vi~*;dnS5Tyr3Z?i8Eq>@oVgr63u9(@UxweRB0?C@AC1hmyp6b5%4Y|Ffwy3{!%*-b2Hm9;^Gg`?zo$7o@VbxZwO-=(r|@Ylv% zy*=&oUF9=Rq0I4=8H;8}!H4~ADEXPig(L=Git~lhcza3%H_=pYc=6LveN^KuqBTF; zVP)tbeYD&TnZx|ildt#|PTC{WUR6&$Rb3D;WLHWFPb5&@n6dx(f!x)i zT)W}$=WyNr8eXuxzK#1ex?UV{q0?0yp{0-Fd$(|pD58e)oi7oxeeIZsM8Rs%WeOBe-V8H`{0hOfy`oa> zj=#do8`JdR`Kc)GxjMk~qT~(}ca-xtfA;A_O0V7iu)AF7sGhul+1$s-bhMs_K$%D8 z9iuqz+M&a{digr45mnS2-63bo)$G6`K^sjx)`$b<%_Ovh&z3#dWZr=hV~JLACU~jZ z4n^XVb`PA!pZbEC){om^GO0_-qOD301uxMT%P;v{LzeI36_IN~a`y{&x0?@dp){;y z@1A{NlnhlSD#-UJ;E3kuGbmCcjzA|~qO#RX5Q$=GbOKz8x99q(<9fvQQlHB-)j#_C z_%Qo3mzCcYq#Of7h(xeO+K)Xz1==C>w{BUBKV|mOP`|{8W7XL+J@){)NQsa6aKpJW z5+g!q!|VVW_wB_#YO9bJBYKAp0Yp}C0*Ktmp?ywwwcfO*r^=nfbk!YTsH~uLpv-Ye z(5U*MkEAi#1vMP4?~_urTlY}aLOMpqKOjw}UrqtCw}+wn?2iQ*_5z4+qtbUYxkMAr z5T~NXgiv#BEPG4h9&2e5S(0mJUnOlk zQy=wQ+e3!YpY>B4)wcIHSr~RIiCVek`z7_i)3v&+TU3%%J)yqQVYF=C2dY>r9jDN7 zhsG7w;ZJ`bQY_^j9!XBZu!`Hh9W2lr80JyOX>8UP+`WpPtr{kX&e#vTunKuaJRmx^~Pz7opDy!MwqNzlFEDXBHXZBgR^ z#WUUBI`#vK>u2#U#o2IG&(!LUsNlAEe((d9UmU?EHU$=2p7kALuXSx`(WY7KK{V^4 zT>M`vhMjo77k@q+&bKUF9922&Vf*sFD@x;@8d zZHEn4lzNS_e1;=LwN`Y+sxjdC-E`_>sUSnSAVXs(4cZu(#9m{_>77t~^yu0HyeP5D z(M5SjvqAX%uA7{>CS0B!GtI4@{$w+^0Afa02Sqvc?a`0h2^b~AtmYv!FS>0kzQ(xO zJ#z)L0HIb+mmN)pf+Gvk;j*ni6HP}~8WOT+;axaW$pV*oOtQ( z!nDNI5TWhj(5IY8sRN%8{U(PHq8m1Jzj^qsT8P8=R+;(X5fep3**hDBl8x&3EZEqC z68pb1%;la@pl;ZgLx)3MElme=5!26SG>Dsb{1<0eP=cm&0@tisels*v8UzJU;hX8? z(Ds8x77mY>lQHg=X;m1J1nOGfe;S?Zf9i*XubAuc_rxLOO>s9?&5->K3yvw6VlNjx zTJa+#-qjaLYCWa)u2^)0CVxvwC!g3t()e}$-z7)hzk6%*OxSMBaC{IU|CON_aq}65 zysRyV0Hjb(U-PK&_)6CP`!n`V#e7Xbn@#!yMOBUpfEP9*w{p_MRUk0EG0So0@S23D2zjT_|LYNo&u+^@*atpHxJ~{xwcZNUQLxR zio1`>PmzB80^Xe4;ENmPLaXZcmQ%nUZG`G0GUh`FG@{!*K?J39_dchbe3p9|Z8jhp z)zai1QhXy3<1~_&L3+l&)px)2Jna-h2;~lOYh}!M%Z?^ok(;%@hW4JGR81Cjuy_=Kr;C$`)l(iM@z=l|q2zqWD1X_O5}(l=Ob?%O z8Vitq`3JtrxDbMei6sb15JMj|Z{78v|EmRH7o;>`_x9TQaR>LhfzCh^^{GnaIBePo zd>gWb4u4B@I|&32K3>8yr@QXP%x+lK(j&_$W@1F)5XNcvV-o2r^kIvUuu-Q`pO-U5 zsSJXYpxhI_i0$;3zlKf&Mpx1GWg@}9WNHB2zxm9Toc_+kE^pGADJtMZH7?K2Rxws-DF3rYF$M? z9%Bxq!Y^C{z14`SI1(yNGb3p=ayp3brotUuBwjRuxvA3RF?`W=SYxRe`oc)+i6%x1 zizfHoiY3wQoQ!_VJkK(eF$M=_RD;jj;^un%7o?c79D zcxdpO$nP)O`Tw1wt#ZYY28F*f9obFtXCD#9CbyUjif3xZa3HLrmrvQCoj=WUCCNN~Exr4f!?n}w;hl7gYrjZQ;I7zU!X6t=)6{d* zI!V;B0KF5!=R|kGy7#>gcajv|9w(~JjJ+Td#Fs#QwDcvMfos2OD~t*Yzv)S-DZ68} zldgPtiG&`S9?Fai#_7`g~A~O;* z&;Kw=TH1f@JzN#iv~fqbUn8FxyBZx4HSCcX{+vJ@WmZ(^IWp`*ZcYO-!#LOa@lKG# z7RzR4rq!Gd$6Q;yA9`lh2;!V2xwq+QZ<;Scx8V(CO$mRjrUXXcvRsEo+C9clx>n+Wp8m?1z)XC!0i` zj8zlNrC?M54c`5bi6q#hzvd?Wbr@7orn4TZpCIX@z|IIge-4&%Tn zY|$(=I~xc?I~fhh85p{VN z{kRG6S<&p+J8L(S6bgC<6dz(PlntwocNt>ALRG5@1)<09aQ&lm?X7O+AcuxWw$ptc zI8l(+;Yt$&&;S~6DPAcaN@9nb0Rdhvpe^q<@4h}G`8PvhxGJhl=M+}JR_c*)Qzdjo zREg5D&ym)6<3D|3-z0LnDEEy=^5%QA&Mb42h`Ac>?AfzpEE_MuA+IgIF)_5lPkIOF z%~ur0O`G5C&zZ=T!>MniX-xApj*J=7@sgWU3B_T z#8i)jpOc)X$i}z2aX|!eC*{tUo)^GWRsWiVD{Vyjr;9r?#?uR_`Pw<0n`7i?5aG5{tUj1Qt?MlR>D)M1%PGF9X7|6L7OpOp;i~%|{&%8m z2~GZWp&5T)ZB390F@^NM{ovQ0npf!0_Z4-h4V~OzC|nM zKJqB8@v$cmFHqwBZ_G$~4?Cs=J}ePNi|x(Ss5fE|=5nM}=UziY4#}2D<C{w+5&) zt$DMDLBhjlB5@nve|W!% z(K?0JK1cxA?`RGU*m>&Vli&hc`RHcrW*uUqX`L7H76%6>0CdiSgZ5_?nVj! zOjsGH7Mm@W6gQHwlm%Tv`Rvtm2YtxJfwek6GCjO-m-da6J3uAOco%>;3>Dyl-~KzZ zo9n;YeRLG`tQ^{`XKVXf@tC{)X)IYLJGxsEt60%({2nuzV=j(B2Eez{yI4+jlF@<^ z*`8ZYsl8E>wt|`0o-w~mk7Yge2-;b|!h?-+lo$(?~}LHY+YL0OJ&U zyH(j<%Hv@dUl|Mbit0GIIT?J+r8A|NIytt+6e{fACh!8s#`6e54?^mnzuYB@< zpZrRP8G8UnYcL-8yV<>^g^`d=NWoyHc-109ONQ-q7&@9c1E_MeI zbjD~?FRY6$TtUCkXTM4MR0-=wdtuefvzcciW-0bDm0P+N@S(<{&el?B;yYbTB<8ZW>*T}cvx1-p?X4m)a zqx1;))Sy>vMT$pCZHmu?5C~OOX65?oaA3xjkUVsX4=(f+A7CrrSkXrIUs9buA@2{W zkY11sB)WLUxW@xW%e?>ZEns;lo!UU-JBNoFncDl1e^Qq;oJ_QF4rnZE zKQ)+V1eT3tb#B?=G}c#LuJzRYT)5MC4tGOyGRX?D^G}+OiIdE~Y@sEO_s)#unm4J6a@zibgUyms~Oo7d@9v{BUo43v372ED%1-!w*XW%uVi3uG`Sb z$C3Ut5k%DKC1#rBchz7nB~Y(+(%}spozgKh7eSQ39eK+N3sxDW9R?Bz)!AV6trNo$ zRmJ09zAJ@0oxBE#f^ty`ty5wP_XLnSj!x76PhU3%5rCS&NrM#(B!HTTGqCZjjuZ}= z$6s0lhDveZUhM2-DTOxSrNP&THdp@VBUae=sem@7`aVHMDaReJ;*!YG{?hYq`VskS zqF^I?}$q{IIK#8(HFT;T(ou=F}HlrAP*uxfxt5o^KQc zj;dQ_-UclJBAMOAPEH{Xr(0$ujT*K5`iIL1Lx|^bjO#V2W(^*(JKXZUPBxQ^aJ^{w;nR1keY9Kw z!e+55&D}K_pRRN;&kqH5}0qV&+wdk(=P?k-xC|7#k{W z;qc21{8cBDYVdZJho6F}W1})S)-}&^9Z?0#du8KWYV0j&!$p$4d24U#rb?z6YTXfQ zwMz&S5a#yGJh(Vi_?Gaqi|#3V?C57BqoN^olQL8aH}~lpMyHsI*cggE9I@TRoW^px zS(>skB)xM&H^{zYE_PsyAlv!Fzg`6Ah>&<7Wk_Pj&jXhgZrRuZLgjnw$C@C%IJb9C z{K1AfGZ+<{^RyRhP9!Q<@RD47T06?&TQtmtRKq9XM#N#rI~+ZNa<6vmU1VgjY#}Y1 zL8P(ft^vuFzE@!z-F?Xx@Jhy$bSzl%9!Y(h^C>9tp<|_v)Es5f>U5dTiY&xzOq568Qi!t>7^p+QjdR~v?_g2;WViMNu+X#ptCB?o5l>DAb<4tqHs5yZw^M)L&Wl; z-JD!e!9!-H^%6?=9aBM}s^g(>E~iu`%^t zUBgSy+Dusng>PBf`FTAGmp|MPSk^&UJzp_DSS~Edx*K?_%vx1Epb|$ycL5|CWohCO zTc|9BuZH~w{)@cO)DkC0%6Mh*W-rVOtNorszi_z$X^o$4D0>Qis;~9n`Q$iBMwV}{ zz8JPe0=rUq9WCM4z1nOGc(&*ILhnU&auAc3p50Eg-{e77O`=KHM-AGJ5svSti9T<9|HPSlhw8@x46GZmVm=A?Y&YiHbqa@8h}yH3 zTlAc06`XJ#k5^kVw|5$z76n)+$gO|MujmQc5n-tg18=5&jc5|&;|3zh-=;wEj?o&# zXg>FnVK>t-srpwUEC}ZRHtI*)&MT-Pw2mJuWL+7nN+4f9H^OqW9KsV2Z3H5e7p}X? z7mrfkgH3!ic(DITFoK;3=g0FL&hNQ-WfatBv3tP}#E;1tvQ>&`CYeuaS3Fk)#B|?N zpY3o&cNQWnL=Yp~9}2KsQuIZ;70&oZh~+ge&-U=VxrjjAd=9qt=1J!7O1+ILCEPIca~dXl8l?-$wN)!{|Hc2k z^as53uCOkuU~Ftvt+b5R!594ITQ97h#&we+5cF1nGvEFrK*eRq(HaB?vhMWjkx&yO z!ha1yV$>}#^>1fl%maoZw^k$p!RQku%}S?-uYETsXtMX;epAB}b4XtR$;l(3r}+Bp z11cFB3xcT(Iy+||iGqlO0E7J$i1!VotHq~nlA7wTa1kP~EkAFuB6lc>>J zE~vd1t*!zm2%!}TyTa&saPVu&o2XHGJctVK`F!1xb~uPI!rsk7JIcsYo5SNp&p3 zY=|gA*i=%I?5@eCVN_yAWwv6KGA=Dm$(}VVu_);{-)GwMIiE9s&ig*k`+I)(=llCT z?~CBLmo7e?TeK~y=mdL3mt)jq=3<1W^u)klN4_5EZt+65b$q!OPiHpA;rs2^_Z=!( zAG>T9VUp6N)+@RU4_5ae{Q0$(y|#%Q&)t6t0dX^;Y+YYg@ZGRVr;!O~*95KXs}N3K z(xZ(|vWRDeYDBs}!??uy_TrG$PBv8*`o;V?I&9a0h!cVbv9TJib)L(^^Cvmf4qrfb z)S~W2LUtd{N?bNA?I1i3D@7oHj$HQ5DvnyN@JtpkbU^>QzA}d;cM+3!-MiG33SP{B zGxrJhx+$(w*Ov2WK2Uv$^#2+&C&bUwKEYbTaVYpcI>`A)iDUl065W~eW^ zMP+?0wl2z?A6u|B!5syoW7lwQCGLW@N3OJR3M3o);yZu$3m;p$q5&>G6$M3t1Zjws z;(LTdb~;>4dLrNNP{#%d-fo(_!iNVm$}S{YwetG7fbA(a(ETTrAyxJQKvEs6t|cb` zgVbRFiWL_H&tdz_191x8F%>Yz<)+fVwe0xUf8si+sP~o#cB@zsJXw)L0UM)Z zvj*GQ{LNF52W6aHx^Ia;VFsJ?142(8qEFibSn)`AlR_}b0Y#TLmwwiBjG9T1^7hDhW0$dUfkn#P)kfyt!ljHTi2S3DaB*xaNYX=M5U!cJBWG;Dqsn&y*?Q0)2QOLZcAJFwpRQAeM6PBHK?&oWJxIheCe z2{dbBm}mWs=I*G{CN;41)qEheGybzJI9nw4U?Ih1KZ zT)=zS!f8L)62L{0m!K`beIzcx*0E84I!Nmj<_L;wjJqcgAGao!=sJ?7QBb{%UP?sy zp!p&KVYRS~S>a9D2 z^n$T@unGw!rBjJ_b#sFNwQ8N!hJ>q%i(Zk zUAdM=0S1ga`%OvDYQZu_S!TMv- zan<+&GrrQ>E47?S0MC{MZIJAnMW?+wm;Yf{X^%NydPz2Iko^1pF#870m4vA3bcfnd z1&TZ?2tw#=TRO09r!*7SnElx`6_2}e(Z26p$O)N;RmR0PH3@ITz8YSM?5M{cP$7^QC?!JmO3{dq&_RI@%oGq z)`?n!rUNWrg$fkCMdiyx*jKB;NPEhIZ=FlPm zTeaRq)WJ!E-}8q64HqC-GUux7iYZE;Q-W7F~=G4+3#VI#fhLqWwn)CZ^+9syc zo7ckJi|2mgzA+&t^d#1SmOGifMc>$@og68#O^l)nu^D8BRIQ;iuQ3o==7?|F&Q=$z zR>8Vz#=lNIiL=*Fd*ZVMy+M(*`I0!CM^%%`uHV*7(TcW*ky2YN1UIq{|K+)x^J&Y? zIeheaE;SBb4r*W7H8k}BodGo(r#kV1f}7lh=KK$BMg-dMF8BBmexqWvHh?=Hxw$Os zvgK1*v}D-r^VcJ7tv2)ywZh+9a8Pq1)5T%!Hil9%{Kp4E#T%k*N`7u9vkXAc66pM&TjN!PXY0x}PWj?h z6r6blVOCTP$eHA%!pR17Y=|{c8TRpAfg|kKHR)mysMUz!FBAaQ)w-$5r>1Ar3O;;+8_W!KO@h>P%T>W)> z%vq4@n>lN+3;W)cN0b5|#IQ6Rs&EFng%q`LK;;xC^wr&xr7{x_( zOxg6qBQ*`eu2%6f6kQDeHI{NTR#_@W9S+3&l;K(sy2|KU=Y!R{I>I|hU7tJpit1)` zj4F2M+7R;LT}zQ&Dc8(?O!!;sYnzU7=98J5k*yuYej__sw#scaf!lOp9VqRKo07W2 zN>77y#?KnucS2_Pn1f4K#ff*mDbc1{9ZpqUsQ+wQnn*KeI%SUsDoyo2BGm8ENOl&d zo1>OPIxk=}m6DIK{rVgxZu6N04JE^y9UW3SMl_*{LR9SuX|DDA@NcU2CCCd2xer?< zQB+59WKfN)r0xWJBXSKsJ$k%!0wHM!o9|xezOrS3t{vf>!|t8qgqNJi&IYW(QHjBi z(T4!@<~=cDRivA$6JBS}NJ0}-gZD!ioz%p?YX6%7A0qsxN209F$PE7|=9vs|vo+Pp zu{a!oBn~y920woIa{(yQnjpm{Z#EnuS$n7|=D`t$lP(cfFZJJf-LBca|1TfH+cewM#NOe`-Ndo)^0f#r z?+dj1TE0zx`D&w{Ifm^Pe4EJM(Q`z@34V2Z*XXx7;k_egUcQ3Iu1Oa8{Yr>DcC0C#u<#=86jMCC^#W5j_wYkbw0=b1C{CZp8x;= literal 0 HcmV?d00001 diff --git a/README.md b/README.md index cf30b9fe..846c351f 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ -obs-websocket -============== +# obs-websocket + +

+ +

WebSockets API for OBS Studio. -Follow the main author on Twitter for news & updates : [@LePalakis](https://twitter.com/LePalakis) - [![Discord](https://img.shields.io/discord/715691013825364120.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/WBaSQ3A) - [![Build Status](https://dev.azure.com/Palakis/obs-websocket/_apis/build/status/Palakis.obs-websocket?branchName=4.x-current)](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current) +[![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fold_left.svg?style=social&label=Follow%20%40LePalakis)](https://twitter.com/LePalakis) ## Downloads From 865c0c79db1c7678fc5fbca07d4cd6b3e50d2f4b Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 28 May 2020 16:45:05 -0700 Subject: [PATCH 104/278] Docs: Rework some more of the readme --- README.md | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 846c351f..df86da85 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Here's a list of available language APIs for obs-websocket : - Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf - HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468 -I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop me an email at `stephane /dot/ lepin /at/ gmail /dot/ com` ! +I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop a message in `#project-showoff` in the [discord server!](https://discord.gg/WBaSQ3A) ## Compiling obs-websocket @@ -59,36 +59,13 @@ Please join the localization project on [Crowdin](https://crowdin.com/project/ob ## Special thanks -In (almost) order of appearance: - -- [Brendan H.](https://github.com/haganbmj): Code contributions and gooder English in the Protocol specification -- [Mikhail Swift](https://github.com/mikhailswift): Code contributions -- [Tobias Frahmer](https://github.com/Frahmer): Initial German localization -- [Genture](https://github.com/Genteure): Initial Simplified Chinese and Traditional Chinese localizations -- [Larissa Gabilan](https://github.com/laris151): Initial Portuguese localization -- [Andy Asquelt](https://github.com/asquelt): Initial Polish localization -- [Marcel Haazen](https://github.com/nekocentral): Initial Dutch localization -- [Peter Antonvich](https://github.com/pantonvich): Code contributions -- [yinzara](https://github.com/yinzara): Code contributions -- [Chris Angelico](https://github.com/Rosuav): Code contributions -- [Guillaume "Elektordi" Genty](https://github.com/Elektordi): Code contributions -- [Marwin M](https://github.com/dragonbane0): Code contributions -- [Logan S.](https://github.com/lsdaniel): Code contributions -- [RainbowEK](https://github.com/RainbowEK): Code contributions -- [RytoEX](https://github.com/RytoEX): CI script and code contributions -- [Theodore Stoddard](https://github.com/TStod): Code contributions -- [Philip Loche](https://github.com/PicoCentauri): Code contributions -- [Patrick Heyer](https://github.com/PatTheMav): Code contributions and CI fixes -- [Alex Van Camp](https://github.com/Lange): Code contributions -- [Freddie Meyer](https://github.com/DungFu): Code contributions -- [Casey Muller](https://github.com/caseymrm): CI fixes -- [Chris Angelico](https://github.com/Rosuav): Documentation fixes +Thank you so much to all of the contibutors [(here)](https://github.com/Palakis/obs-websocket/graphs/contributors) for your amazing help. And also: special thanks to supporters of the project! ## Supporters -They have contributed financially to the project and made possible the addition of several features into obs-websocket. Many thanks to them! +These supporters have contributed financially to the project and made possible the addition of several features into obs-websocket. Many thanks to them! --- From d2865b9f4fc216316e4e92f8a0cb7382cd533f9b Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 28 May 2020 21:45:52 -0700 Subject: [PATCH 105/278] Requests: Add AddSceneItem New request `AddSceneItem` which allows you to add a source to a scene. Returns an itemId which can be used to manipulate the item further if necessary. --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_SceneItems.cpp | 73 +++++++++++++++++++++++++++-- 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 77411461..cb3eabe0 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -60,6 +60,7 @@ const QHash WSRequestHandler::messageMap { { "SetSceneItemProperties", &WSRequestHandler::SetSceneItemProperties }, { "ResetSceneItem", &WSRequestHandler::ResetSceneItem }, { "DeleteSceneItem", &WSRequestHandler::DeleteSceneItem }, + { "AddSceneItem", &WSRequestHandler::AddSceneItem }, { "DuplicateSceneItem", &WSRequestHandler::DuplicateSceneItem }, { "ReorderSceneItems", &WSRequestHandler::ReorderSceneItems }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 2a33c8f4..a334a1a9 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -78,6 +78,7 @@ class WSRequestHandler { RpcResponse ResetSceneItem(const RpcRequest&); RpcResponse DuplicateSceneItem(const RpcRequest&); RpcResponse DeleteSceneItem(const RpcRequest&); + RpcResponse AddSceneItem(const RpcRequest&); RpcResponse ReorderSceneItems(const RpcRequest&); RpcResponse GetStreamingStatus(const RpcRequest&); diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index c1fd8522..abff904f 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -2,6 +2,18 @@ #include "WSRequestHandler.h" +struct AddSourceData { + obs_source_t *source; + obs_sceneitem_t *sceneItem; + bool setVisible; +}; + +void AddSourceHelper(void *_data, obs_scene_t *scene) { + auto *data = reinterpret_cast(_data); + data->sceneItem = obs_scene_add(scene, data->source); + obs_sceneitem_set_visible(data->sceneItem, data->setVisible); +} + /** * Gets the scene specific properties of the specified source item. * Coordinates are relative to the item's parent (the scene or group it belongs to). @@ -37,7 +49,7 @@ * @return {int} `alignment` The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis. * @return {String (optional)} `parentGroupName` Name of the item's parent (if this item belongs to a group) * @return {Array (optional)} `groupChildren` List of children (if this item is a group) -* +* * @api requests * @name GetSceneItemProperties * @category scene items @@ -164,7 +176,7 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) vec2 newScale = oldScale; OBSDataAutoRelease reqScale = obs_data_get_obj(params, "scale"); - + if (obs_data_has_user_value(reqScale, "x")) { newScale.x = obs_data_get_double(reqScale, "x"); } @@ -252,7 +264,7 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) } obs_sceneitem_set_bounds(sceneItem, &newBounds); - + if (obs_data_has_user_value(reqBounds, "alignment")) { const uint32_t bounds_alignment = obs_data_get_int(reqBounds, "alignment"); if (Utils::IsValidAlignment(bounds_alignment)) { @@ -455,7 +467,7 @@ RpcResponse WSRequestHandler::SetSceneItemTransform(const RpcRequest& request) { obs_sceneitem_set_scale(sceneItem, &scale); obs_sceneitem_set_rot(sceneItem, rotation); - + obs_sceneitem_defer_update_end(sceneItem); return request.success(); @@ -544,6 +556,59 @@ RpcResponse WSRequestHandler::DeleteSceneItem(const RpcRequest& request) { return request.success(); } +/** + * Creates a scene item in a scene. In other words, this is how you add a source into a scene. + * + * @param {String} `sceneName` Name of the scene to create the scene item in + * @param {String} `sourceName` Name of the source to be added + * @param {boolean} `setVisible` Whether to make the sceneitem visible on creation or not. Default `true` + * + * @return {int} `itemId` Numerical ID of the created scene item + * + * @api requests + * @name AddSceneItem + * @category scene items + * @since unreleased + */ +RpcResponse WSRequestHandler::AddSceneItem(const RpcRequest& request) { + if (!request.hasField("sceneName") || !request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + const char* sceneName = obs_data_get_string(request.parameters(), "sceneName"); + OBSSourceAutoRelease sceneSource = obs_get_source_by_name(sceneName); + OBSScene scene = obs_scene_from_source(sceneSource); + if (!scene) { + return request.failed("requested scene is invalid or doesnt exist"); + } + + const char* sourceName = obs_data_get_string(request.parameters(), "sourceName"); + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName); + if (!source) { + return request.failed("requested source does not exist"); + } + + if (source == sceneSource) { + return request.failed("you cannot add a scene as a sceneitem to itself"); + } + + AddSourceData data; + data.source = source; + data.setVisible = true; + if (request.hasField("setVisible")) { + data.setVisible = obs_data_get_bool(request.parameters(), "setVisible"); + } + + obs_enter_graphics(); + obs_scene_atomic_update(scene, AddSourceHelper, &data); + obs_leave_graphics(); + + OBSDataAutoRelease responseData = obs_data_create(); + obs_data_set_int(responseData, "itemID", obs_sceneitem_get_id(data.sceneItem)); + + return request.success(responseData); +} + /** * Duplicates a scene item. * From cf99c688432c6b178b06a724dd9a52f3d3649761 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 28 May 2020 22:45:08 -0700 Subject: [PATCH 106/278] Requests: Fix useDecibel response when volume is -infinity When the volume in OBS is -infinity, `GetVolume` returns a decibel value of either 0, or in some cases no `volume` property at all. This makes `GetVolume` return a decibel value of -100.0 if the real volume is -infinity. --- src/WSRequestHandler_Sources.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 3a224205..b3ba2a55 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -192,6 +192,10 @@ RpcResponse WSRequestHandler::GetVolume(const RpcRequest& request) if (useDecibel) { volume = obs_mul_to_db(volume); } + + if (volume == -INFINITY) { + volume = -100.0; + } OBSDataAutoRelease response = obs_data_create(); obs_data_set_string(response, "name", obs_source_get_name(source)); From 1474499886d95b7e2badc913eaac32fa43dae90f Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 28 May 2020 23:58:11 -0700 Subject: [PATCH 107/278] Requests: Add GetSceneItemList Adds a new `GetSceneItemList` request to get all scene items in a specified scene. --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_SceneItems.cpp | 67 +++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index cb3eabe0..be8efdb3 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -52,6 +52,7 @@ const QHash WSRequestHandler::messageMap { { "GetSceneTransitionOverride", &WSRequestHandler::GetSceneTransitionOverride }, { "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat + { "GetSceneItemList", &WSRequestHandler::GetSceneItemList }, { "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender }, { "SetSceneItemPosition", &WSRequestHandler::SetSceneItemPosition }, { "SetSceneItemTransform", &WSRequestHandler::SetSceneItemTransform }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index a334a1a9..b15a89ec 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -69,6 +69,7 @@ class WSRequestHandler { RpcResponse RemoveSceneTransitionOverride(const RpcRequest&); RpcResponse GetSceneTransitionOverride(const RpcRequest&); + RpcResponse GetSceneItemList(const RpcRequest&); RpcResponse SetSceneItemRender(const RpcRequest&); RpcResponse SetSceneItemPosition(const RpcRequest&); RpcResponse SetSceneItemTransform(const RpcRequest&); diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index abff904f..4b446676 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -14,6 +14,73 @@ void AddSourceHelper(void *_data, obs_scene_t *scene) { obs_sceneitem_set_visible(data->sceneItem, data->setVisible); } +/** +* Get a list of all scene items in a scene. +* +* @param {String} `sceneName` Name of the scene to get the list of scene items from. +* +* @return {Array} `sceneItems` Array of scene items +* @return {int} `sceneItems.*.itemId` Unique item id of the source item +* @return {String} `sceneItems.*.sourceId` ID if the scene item's source. For example `vlc_source` or `image_source` +* @return {String} `sceneItems.*.sourceName` Name of the scene item's source +* @return {String} `sceneItems.*.sourceType` Type of the scene item's source. Either `input` or `scene` +* +* @api requests +* @name GetSceneItemList +* @category scene items +* @since unreleased +*/ +RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) { + if (!request.hasField("sceneName")) { + return request.failed("missing request parameters"); + } + + const char* sceneName = obs_data_get_string(request.parameters(), "sceneName"); + OBSSourceAutoRelease sceneSource = obs_get_source_by_name(sceneName); + OBSScene scene = obs_scene_from_source(sceneSource); + if (!scene) { + return request.failed("requested scene is invalid or doesnt exist"); + } + + OBSDataArrayAutoRelease sceneItemArray = obs_data_array_create(); + + auto sceneItemEnumProc = [](obs_scene_t *, obs_sceneitem_t* item, void* privateData) -> bool { + obs_data_array_t* sceneItemArray = (obs_data_array_t*)privateData; + + OBSDataAutoRelease sceneItemData = obs_data_create(); + obs_data_set_int(sceneItemData, "itemId", obs_sceneitem_get_id(item)); + OBSSource source = obs_sceneitem_get_source(item); + obs_data_set_string(sceneItemData, "sourceId", obs_source_get_id(source)); + obs_data_set_string(sceneItemData, "sourceName", obs_source_get_name(source)); + + QString typeString = ""; + enum obs_source_type sourceType = obs_source_get_type(source); + switch (sourceType) { + case OBS_SOURCE_TYPE_INPUT: + typeString = "input"; + break; + + case OBS_SOURCE_TYPE_SCENE: + typeString = "scene"; + break; + + default: + typeString = "unknown"; + break; + } + obs_data_set_string(sceneItemData, "sourceType", typeString.toUtf8()); + + obs_data_array_push_back(sceneItemArray, sceneItemData); + return true; + }; + obs_scene_enum_items(scene, sceneItemEnumProc, sceneItemArray); + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_array(response, "sceneItems", sceneItemArray); + + return request.success(response); +} + /** * Gets the scene specific properties of the specified source item. * Coordinates are relative to the item's parent (the scene or group it belongs to). From 612bd9960c245cd0ee3e3ec5b4d672c98f2afff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Thu, 4 Jun 2020 10:14:58 +0200 Subject: [PATCH 108/278] readme: add TLS tunneling section --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/README.md b/README.md index 1c210f96..9fd2bb4a 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,50 @@ A web client and frontend made by [t2t2](https://github.com/t2t2/obs-tablet-remo It is **highly recommended** to protect obs-websocket with a password against unauthorized control. To do this, open the "Websocket server settings" dialog under OBS' "Tools" menu. In the settings dialogs, you can enable or disable authentication and set a password for it. +### Connecting over a TLS/secure connection (or remotely) + +**Before doing this, secure the WebSockets server first by enabling authentication with a strong password!** + +If you want to expose the WebSockets server of obs-websocket over a secure TLS connection (or to connect remotely), the easiest approach is to use a localhost tunneling service like [ngrok](https://ngrok.com/) or [pagekite](https://pagekite.net/). + +**Please bear in mind that doing this will expose your OBS instance to the open Internet and the security risks it implies. You've been warned!** + +#### ngrok + +[Install the ngrok CLI tool](https://ngrok.com/download), then start ngrok bound to port 4444 like this: + +```bash +ngrok http 4444 +``` + +The ngrok command will output something like this: + +``` +ngrok by @inconshreveable + +Tunnel Status online +Version 2.0/2.0 +Web Interface http://127.0.0.1:4040 +Forwarding http://TUNNEL_ID.ngrok.io -> localhost:4444 +Forwarding https://TUNNEL_ID.ngrok.io -> localhost:4444 +``` + +Where `TUNNEL_ID` is, as the name implies, the unique name of your ngrok tunnel. You'll get a new one every time you start ngrok. + +Then, use `wss://TUNNEL_ID.ngrok.io` to connect to obs-websocket over TLS. + +See the [ngrok documentation](https://ngrok.com/docs) for more tunneling options and settings. + +#### PageKite + +[Install the PageKite CLI tool](http://pagekite.net/downloads), then start PageKite bound to port 4444 like this (replace NAME with one of your choosing): + +```bash +$ python pagekite.py 4444 NAME.pagekite.me +``` + +Then, use `wss://NAME.pagekite.me` to connect to obs-websocket over TLS. + ### Possible use cases - Remote control OBS from a phone or tablet on the same local network From 1c58727ca973bb20e1d74c1556bf5314821a342c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20B=C3=A4rtschi?= Date: Thu, 4 Jun 2020 15:28:47 +0200 Subject: [PATCH 109/278] Change position datatype from int to double (as it is in OBS) --- src/WSRequestHandler_SceneItems.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index c1fd8522..8b91d4df 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -79,8 +79,8 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request) * @param {String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object). * @param {String (optional)} `item.name` Scene Item name (if the `item` field is an object) * @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object) -* @param {int (optional)} `position.x` The new x position of the source. -* @param {int (optional)} `position.y` The new y position of the source. +* @param {double (optional)} `position.x` The new x position of the source. +* @param {double (optional)} `position.y` The new y position of the source. * @param {int (optional)} `position.alignment` The new alignment of the source. * @param {double (optional)} `rotation` The new clockwise rotation of the item in degrees. * @param {double (optional)} `scale.x` The new x scale of the item. @@ -134,10 +134,10 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) vec2 newPosition = oldPosition; if (obs_data_has_user_value(reqPosition, "x")) { - newPosition.x = obs_data_get_int(reqPosition, "x"); + newPosition.x = obs_data_get_double(reqPosition, "x"); } if (obs_data_has_user_value(reqPosition, "y")) { - newPosition.y = obs_data_get_int(reqPosition, "y"); + newPosition.y = obs_data_get_double(reqPosition, "y"); } if (obs_data_has_user_value(reqPosition, "alignment")) { From 948f6ccd956335253b177ee8a8dd2a9dda2e52e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20B=C3=A4rtschi?= Date: Thu, 4 Jun 2020 15:42:27 +0200 Subject: [PATCH 110/278] Also change all other occurrences and documentation where int was used for transform position --- src/Utils.cpp | 4 ++-- src/WSRequestHandler_SceneItems.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index dd5ad14a..ab301606 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -684,8 +684,8 @@ bool Utils::SetFilenameFormatting(const char* filenameFormatting) { /** * @typedef {Object} `SceneItemTransform` - * @property {int} `position.x` The x position of the scene item from the left. - * @property {int} `position.y` The y position of the scene item from the top. + * @property {double} `position.x` The x position of the scene item from the left. + * @property {double} `position.y` The y position of the scene item from the top. * @property {int} `position.alignment` The point on the scene item that the item is manipulated from. * @property {double} `rotation` The clockwise rotation of the scene item in degrees around the point of alignment. * @property {double} `scale.x` The x-scale factor of the scene item. diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 8b91d4df..8c552431 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -13,8 +13,8 @@ * * @return {String} `name` Scene Item name. * @return {int} `itemId` Scene Item ID. -* @return {int} `position.x` The x position of the source from the left. -* @return {int} `position.y` The y position of the source from the top. +* @return {double} `position.x` The x position of the source from the left. +* @return {double} `position.y` The y position of the source from the top. * @return {int} `position.alignment` The point on the source that the item is manipulated from. * @return {double} `rotation` The clockwise rotation of the item in degrees around the point of alignment. * @return {double} `scale.x` The x-scale factor of the source. From 14d43ac05b946a3830955aada3daa3f5f73e9dd8 Mon Sep 17 00:00:00 2001 From: Curt Grimes Date: Mon, 29 Jun 2020 20:24:11 -0500 Subject: [PATCH 111/278] [Bug Fix] Reenable building with captions on MacOS OBS on MacOS supports closed caption encoding. The Windows-only dependencies [here](https://github.com/obsproject/obs-studio/blob/master/UI/frontend-plugins/frontend-tools/CMakeLists.txt#L73) are only related to audio capture, not closed caption encoding. --- CI/install-build-obs-macos.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/CI/install-build-obs-macos.sh b/CI/install-build-obs-macos.sh index 4af63b49..0dc610aa 100755 --- a/CI/install-build-obs-macos.sh +++ b/CI/install-build-obs-macos.sh @@ -34,6 +34,7 @@ git checkout $OBSLatestTag mkdir build && cd build echo "[obs-websocket] Building obs-studio.." cmake .. \ + -DBUILD_CAPTIONS=true \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \ -DDISABLE_PLUGINS=true \ -DENABLE_SCRIPTING=0 \ From 822a1751a230893c0cbdbc0c8d5926fc7ca7a252 Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Wed, 1 Jul 2020 17:21:52 +1000 Subject: [PATCH 112/278] requests: Add support for audio_active --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_Sources.cpp | 34 ++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 77411461..3ee2cbd8 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -95,6 +95,7 @@ const QHash WSRequestHandler::messageMap { { "ToggleMute", &WSRequestHandler::ToggleMute }, { "SetMute", &WSRequestHandler::SetMute }, { "GetMute", &WSRequestHandler::GetMute }, + { "GetAudioActive", &WSRequestHandler::GetAudioActive }, { "SetSourceName", &WSRequestHandler::SetSourceName }, { "SetSyncOffset", &WSRequestHandler::SetSyncOffset }, { "GetSyncOffset", &WSRequestHandler::GetSyncOffset }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 2a33c8f4..7cdde364 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -112,6 +112,7 @@ class WSRequestHandler { RpcResponse ToggleMute(const RpcRequest&); RpcResponse SetMute(const RpcRequest&); RpcResponse GetMute(const RpcRequest&); + RpcResponse GetAudioActive(const RpcRequest&); RpcResponse SetSourceName(const RpcRequest&); RpcResponse SetSyncOffset(const RpcRequest&); RpcResponse GetSyncOffset(const RpcRequest&); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 3a224205..6b1b2cec 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -341,6 +341,40 @@ RpcResponse WSRequestHandler::ToggleMute(const RpcRequest& request) return request.success(); } +/** +* Get the audio's active status of a specified source. +* +* @param {String} `sourceName` Source name. +* +* @return {boolean} `audioActive` Audio active status of the source. +* +* @api requests +* @name GetAudioActive +* @category sources +* @since 4.9.0 +*/ +RpcResponse WSRequestHandler::GetAudioActive(const RpcRequest& request) +{ + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_bool(response, "audioActive", obs_source_audio_active(source)); + + return request.success(response); +} + /** * Sets (aka rename) the name of a source. Also works with scenes since scenes are technically sources in OBS. * From fdcba2734d12a45bf690c00938f6ddc9a8eb53ab Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sun, 28 Jun 2020 17:02:37 -0700 Subject: [PATCH 113/278] Events, Docs: Refactor media events and improve docs Move duplicated functionality to a helper function and added some docs clarifying the behavior of the events, and fixed a few typos in the request handlers. --- src/WSEvents.cpp | 96 ++++++++++++--------------- src/WSRequestHandler_MediaControl.cpp | 18 ++--- 2 files changed, 51 insertions(+), 63 deletions(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 92e1e1d2..0e20d989 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -425,6 +425,16 @@ QString WSEvents::getRecordingTimecode() { return Utils::nsToTimestamp(getRecordingTime()); } +OBSDataAutoRelease getMediaSourceData(calldata_t* data) { + OBSDataAutoRelease fields = obs_data_create(); + OBSSource source = calldata_get_pointer(data, "source"); + + obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); + obs_data_set_string(fields, "sourceTypeId", obs_source_get_id(source)); + + return fields; +} + /** * Indicates a scene change. * @@ -1359,7 +1369,10 @@ void WSEvents::OnSourceFilterOrderChanged(void* param, calldata_t* data) { /** * A media source has started playing. * + * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + * * @return {String} `sourceName` Source name + * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaPlaying @@ -1369,20 +1382,18 @@ void WSEvents::OnSourceFilterOrderChanged(void* param, calldata_t* data) { void WSEvents::OnMediaPlaying(void* param, calldata_t* data) { auto self = reinterpret_cast(param); - OBSSource source = calldata_get_pointer(data, "source"); - if (!source) { - return; - } + OBSDataAutoRelease fields = getMediaSourceData(data); - OBSDataAutoRelease fields = obs_data_create(); - obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); self->broadcastUpdate("MediaPlaying", fields); } /** * A media source has been paused. * + * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + * * @return {String} `sourceName` Source name + * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaPaused @@ -1392,20 +1403,18 @@ void WSEvents::OnMediaPlaying(void* param, calldata_t* data) { void WSEvents::OnMediaPaused(void* param, calldata_t* data) { auto self = reinterpret_cast(param); - OBSSource source = calldata_get_pointer(data, "source"); - if (!source) { - return; - } + OBSDataAutoRelease fields = getMediaSourceData(data); - OBSDataAutoRelease fields = obs_data_create(); - obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); self->broadcastUpdate("MediaPaused", fields); } /** * A media source has been restarted. * + * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + * * @return {String} `sourceName` Source name + * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaRestarted @@ -1415,20 +1424,18 @@ void WSEvents::OnMediaPaused(void* param, calldata_t* data) { void WSEvents::OnMediaRestarted(void* param, calldata_t* data) { auto self = reinterpret_cast(param); - OBSSource source = calldata_get_pointer(data, "source"); - if (!source) { - return; - } + OBSDataAutoRelease fields = getMediaSourceData(data); - OBSDataAutoRelease fields = obs_data_create(); - obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); self->broadcastUpdate("MediaRestarted", fields); } /** * A media source has been stopped. * + * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + * * @return {String} `sourceName` Source name + * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaStopped @@ -1438,20 +1445,18 @@ void WSEvents::OnMediaRestarted(void* param, calldata_t* data) { void WSEvents::OnMediaStopped(void* param, calldata_t* data) { auto self = reinterpret_cast(param); - OBSSource source = calldata_get_pointer(data, "source"); - if (!source) { - return; - } + OBSDataAutoRelease fields = getMediaSourceData(data); - OBSDataAutoRelease fields = obs_data_create(); - obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); self->broadcastUpdate("MediaStopped", fields); } /** * A media source has gone to the next item in the playlist. * + * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + * * @return {String} `sourceName` Source name + * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaNext @@ -1461,20 +1466,18 @@ void WSEvents::OnMediaStopped(void* param, calldata_t* data) { void WSEvents::OnMediaNext(void* param, calldata_t* data) { auto self = reinterpret_cast(param); - OBSSource source = calldata_get_pointer(data, "source"); - if (!source) { - return; - } + OBSDataAutoRelease fields = getMediaSourceData(data); - OBSDataAutoRelease fields = obs_data_create(); - obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); self->broadcastUpdate("MediaNext", fields); } /** * A media source has gone to the previous item in the playlist. * + * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + * * @return {String} `sourceName` Source name + * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaPrevious @@ -1484,23 +1487,18 @@ void WSEvents::OnMediaNext(void* param, calldata_t* data) { void WSEvents::OnMediaPrevious(void* param, calldata_t* data) { auto self = reinterpret_cast(param); - OBSSource source = calldata_get_pointer(data, "source"); - if (!source) { - return; - } + OBSDataAutoRelease fields = getMediaSourceData(data); - OBSDataAutoRelease fields = obs_data_create(); - obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); self->broadcastUpdate("MediaPrevious", fields); } /** - * A media source has been started. (Does not mean that it is playing. See [`MediaPlaying`](#mediaplaying)) - * - * Note: For VLC, this means that the source and therefore playlist has started. - * For the normal Media Source, this just means that the source has been started. + * A media source has been started. + * + * Note: These events are emitted by the OBS sources themselves. For example when the media file starts playing. The behavior depends on the type of media source being used. * * @return {String} `sourceName` Source name + * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaStarted @@ -1510,23 +1508,18 @@ void WSEvents::OnMediaPrevious(void* param, calldata_t* data) { void WSEvents::OnMediaStarted(void* param, calldata_t* data) { auto self = reinterpret_cast(param); - OBSSource source = calldata_get_pointer(data, "source"); - if (!source) { - return; - } + OBSDataAutoRelease fields = getMediaSourceData(data); - OBSDataAutoRelease fields = obs_data_create(); - obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); self->broadcastUpdate("MediaStarted", fields); } /** * A media source has ended. * - * Note: For VLC, this means that the source and therefore playlist has ended. - * For the normal Media Source, this just means that the source has been stopped/ended. + * Note: These events are emitted by the OBS sources themselves. For example when the media file ends. The behavior depends on the type of media source being used. * * @return {String} `sourceName` Source name + * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaEnded @@ -1536,13 +1529,8 @@ void WSEvents::OnMediaStarted(void* param, calldata_t* data) { void WSEvents::OnMediaEnded(void* param, calldata_t* data) { auto self = reinterpret_cast(param); - OBSSource source = calldata_get_pointer(data, "source"); - if (!source) { - return; - } + OBSDataAutoRelease fields = getMediaSourceData(data); - OBSDataAutoRelease fields = obs_data_create(); - obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); self->broadcastUpdate("MediaEnded", fields); } diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp index ea754c6a..ba61c17f 100644 --- a/src/WSRequestHandler_MediaControl.cpp +++ b/src/WSRequestHandler_MediaControl.cpp @@ -218,7 +218,7 @@ RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) { } OBSDataAutoRelease response = obs_data_create(); - obs_data_set_int(response, "timeStamp", obs_source_media_get_duration(source)); + obs_data_set_int(response, "mediaDuration", obs_source_media_get_duration(source)); return request.success(response); } @@ -227,7 +227,7 @@ RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) { * * @param {String} `sourceName` Source name. * -* @return {int} `timeStamp` The time in milliseconds since the start of the media. +* @return {int} `timestamp` The time in milliseconds since the start of the media. * * @api requests * @name GetMediaTime @@ -250,7 +250,7 @@ RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) { } OBSDataAutoRelease response = obs_data_create(); - obs_data_set_int(response, "timeStamp", obs_source_media_get_time(source)); + obs_data_set_int(response, "timestamp", obs_source_media_get_time(source)); return request.success(response); } @@ -258,7 +258,7 @@ RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) { * Set the timestamp of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) * * @param {String} `sourceName` Source name. -* @param {int} `timeStamp` Milliseconds to set the timestamp to. +* @param {int} `timestamp` Milliseconds to set the timestamp to. * * @api requests * @name SetMediaTime @@ -266,12 +266,12 @@ RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) { * @since 4.9.0 */ RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) { - if (!request.hasField("sourceName") || !request.hasField("timeStamp")) { + if (!request.hasField("sourceName") || !request.hasField("timestamp")) { return request.failed("missing request parameters"); } QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); - int64_t timeStamp = (int64_t)obs_data_get_int(request.parameters(), "timeStamp"); + int64_t timestamp = (int64_t)obs_data_get_int(request.parameters(), "timestamp"); if (sourceName.isEmpty()) { return request.failed("invalid request parameters"); } @@ -281,7 +281,7 @@ RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) { return request.failed("specified source doesn't exist"); } - obs_source_media_set_time(source, timeStamp); + obs_source_media_set_time(source, timestamp); return request.success(); } @@ -374,7 +374,7 @@ RpcResponse WSRequestHandler::GetMediaSourcesList(const RpcRequest& request) auto sourceEnumProc = [](void* privateData, obs_source_t* source) -> bool { obs_data_array_t* sourcesArray = (obs_data_array_t*)privateData; - + QString sourceTypeId = obs_source_get_id(source); if (isMediaSource(sourceTypeId)) { OBSDataAutoRelease sourceData = obs_data_create(); @@ -393,4 +393,4 @@ RpcResponse WSRequestHandler::GetMediaSourcesList(const RpcRequest& request) OBSDataAutoRelease response = obs_data_create(); obs_data_set_array(response, "mediaSources", sourcesArray); return request.success(response); -} \ No newline at end of file +} From 4ead1c3de5b959c6e54e2cfbedc8843c87968e88 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 1 Jul 2020 04:47:28 -0700 Subject: [PATCH 114/278] Requests: Add Replay Buffer and Recording Statuses Adds `GetReplayBufferStatus` and `GetRecordingStatus`. v5.0 Will remove the recording status functionality from `GetStreamingStatus` --- src/WSRequestHandler.cpp | 2 ++ src/WSRequestHandler.h | 2 ++ src/WSRequestHandler_Recording.cpp | 29 +++++++++++++++++++++++++++ src/WSRequestHandler_ReplayBuffer.cpp | 20 ++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 77411461..83cd4041 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -70,11 +70,13 @@ const QHash WSRequestHandler::messageMap { { "StartStreaming", &WSRequestHandler::StartStreaming }, { "StopStreaming", &WSRequestHandler::StopStreaming }, + { "GetRecordingStatus", &WSRequestHandler::GetRecordingStatus }, { "StartRecording", &WSRequestHandler::StartRecording }, { "StopRecording", &WSRequestHandler::StopRecording }, { "PauseRecording", &WSRequestHandler::PauseRecording }, { "ResumeRecording", &WSRequestHandler::ResumeRecording }, + { "GetReplayBufferStatus", &WSRequestHandler::GetReplayBufferStatus }, { "StartStopReplayBuffer", &WSRequestHandler::StartStopReplayBuffer }, { "StartReplayBuffer", &WSRequestHandler::StartReplayBuffer }, { "StopReplayBuffer", &WSRequestHandler::StopReplayBuffer }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 2a33c8f4..d87bc803 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -87,11 +87,13 @@ class WSRequestHandler { RpcResponse StartStreaming(const RpcRequest&); RpcResponse StopStreaming(const RpcRequest&); + RpcResponse GetRecordingStatus(const RpcRequest&); RpcResponse StartRecording(const RpcRequest&); RpcResponse StopRecording(const RpcRequest&); RpcResponse PauseRecording(const RpcRequest&); RpcResponse ResumeRecording(const RpcRequest&); + RpcResponse GetReplayBufferStatus(const RpcRequest&); RpcResponse StartStopReplayBuffer(const RpcRequest&); RpcResponse StartReplayBuffer(const RpcRequest&); RpcResponse StopReplayBuffer(const RpcRequest&); diff --git a/src/WSRequestHandler_Recording.cpp b/src/WSRequestHandler_Recording.cpp index df2ed4a0..d58f60c7 100644 --- a/src/WSRequestHandler_Recording.cpp +++ b/src/WSRequestHandler_Recording.cpp @@ -1,8 +1,10 @@ +#include "obs-websocket.h" #include "WSRequestHandler.h" #include #include #include "Utils.h" +#include "WSEvents.h" RpcResponse ifCanPause(const RpcRequest& request, std::function callback) { @@ -13,6 +15,33 @@ RpcResponse ifCanPause(const RpcRequest& request, std::function c return callback(); } + /** + * Get current recording status. + * + * @return {boolean} `isRecording` Current recording status. + * @return {boolean} `isRecordingPaused` Whether the recording is paused or not. + * @return {String (optional)} `recordTimecode` Time elapsed since recording started (only present if currently recording). + * + * @api requests + * @name GetRecordingStatus + * @category recording + * @since unreleased + */ +RpcResponse WSRequestHandler::GetRecordingStatus(const RpcRequest& request) { + auto events = GetEventsSystem(); + + OBSDataAutoRelease data = obs_data_create(); + obs_data_set_bool(data, "isRecording", obs_frontend_recording_active()); + obs_data_set_bool(data, "isRecordingPaused", obs_frontend_recording_paused()); + + if (obs_frontend_recording_active()) { + QString recordingTimecode = events->getRecordingTimecode(); + obs_data_set_string(data, "recordTimecode", recordingTimecode.toUtf8().constData()); + } + + return request.success(data); +} + /** * Toggle recording on or off. * diff --git a/src/WSRequestHandler_ReplayBuffer.cpp b/src/WSRequestHandler_ReplayBuffer.cpp index 510b8f05..be860584 100644 --- a/src/WSRequestHandler_ReplayBuffer.cpp +++ b/src/WSRequestHandler_ReplayBuffer.cpp @@ -1,7 +1,27 @@ +#include "obs-websocket.h" +#include "WSEvents.h" #include "Utils.h" #include "WSRequestHandler.h" + + /** + * Get the status of the OBS replay buffer. + * + * @return {boolean} `isReplayBufferActive` Current recording status. + * + * @api requests + * @name GetReplayBufferStatus + * @category replay buffer + * @since unreleased + */ +RpcResponse WSRequestHandler::GetReplayBufferStatus(const RpcRequest& request) { + OBSDataAutoRelease data = obs_data_create(); + obs_data_set_bool(data, "isReplayBufferActive", obs_frontend_replay_buffer_active()); + + return request.success(data); +} + /** * Toggle the Replay Buffer on/off. * From bd18aee43c32161881e669c679794c8642c20299 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 28 May 2020 17:00:03 -0700 Subject: [PATCH 115/278] Docs: Update issue template to be **better** --- .github/ISSUE_TEMPLATE.md | 14 +++++++++----- CONTRIBUTING.md | 9 +++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 8c118039..215d0d54 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,16 +1,20 @@ ##### Issue type -Bug report? Feature request? Other? + + + + ##### Description -*Replace this with a description of the bug encountered or feature requested.* + ##### Steps to reproduce and other useful info -*If it's a bug, please describe the steps to reproduce it and PLEASE include an OBS log file. Otherwise, remove this section.* + ##### Technical information - **Operating System** : - **OBS Studio version** : +- **obs-websocket version** : ##### Development Environment -*If you're trying to compile obs-websocket, please describe your compiler type and version (e.g: GCC 4.7, VC2013, ...), and the CMake settings used. -Remove this section if it doesn't apply to your case.* + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1931c0c6..8cd655c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,13 +12,13 @@ Localization happens on [Crowdin](https://crowdin.com/project/obs-websocket) ### Code Formatting Guidelines -- Function and variable names: snake_case for C names, MixedCaps for C++ names +- Function and variable names: snake_case for C names, camelCase for C++ method names - Request and Event names should use MixedCaps names - Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) -- Tabs are 8 columns wide +- Code is indented with Tabs. Assume they are 8 columns wide - 80 columns max code width. (Docs can be larger) @@ -43,6 +43,11 @@ if (!success) { return req->SendOKResponse(); ``` +- Some example common response/request property names are: + - `sceneName` - The name of a scene + - `sourceName` - The name of a source + - `fromScene` - From a scene - scene name + ### Commit Guidelines - Commits follow the 50/72 standard: From d03e94ada84fb2db461abf629ce10f43d02cdd7b Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Wed, 1 Jul 2020 17:22:09 +1000 Subject: [PATCH 116/278] events: Add support for audio_active, fix docs version --- src/WSEvents.cpp | 50 ++++++++++++++++++++++++++++++++ src/WSEvents.h | 2 ++ src/WSRequestHandler_Sources.cpp | 2 +- 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 50bea131..726ab883 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -271,6 +271,8 @@ void WSEvents::connectSourceSignals(obs_source_t* source) { signal_handler_connect(sh, "volume", OnSourceVolumeChange, this); signal_handler_connect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this); signal_handler_connect(sh, "audio_mixers", OnSourceAudioMixersChanged, this); + signal_handler_connect(sh, "audio_activate", OnSourceAudioActivated, this); + signal_handler_connect(sh, "audio_deactivate", OnSourceAudioDeactivated, this); signal_handler_connect(sh, "filter_add", OnSourceFilterAdded, this); signal_handler_connect(sh, "filter_remove", OnSourceFilterRemoved, this); @@ -303,6 +305,8 @@ void WSEvents::disconnectSourceSignals(obs_source_t* source) { signal_handler_disconnect(sh, "volume", OnSourceVolumeChange, this); signal_handler_disconnect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this); signal_handler_disconnect(sh, "audio_mixers", OnSourceAudioMixersChanged, this); + signal_handler_disconnect(sh, "audio_activate", OnSourceAudioActivated, this); + signal_handler_disconnect(sh, "audio_deactivate", OnSourceAudioDeactivated, this); signal_handler_disconnect(sh, "filter_add", OnSourceFilterAdded, this); signal_handler_disconnect(sh, "filter_remove", OnSourceFilterRemoved, this); @@ -1098,6 +1102,52 @@ void WSEvents::OnSourceMuteStateChange(void* param, calldata_t* data) { self->broadcastUpdate("SourceMuteStateChanged", fields); } +/** + * A source has removed audio. + * + * @return {String} `sourceName` Source name + * + * @api events + * @name SourceAudioDeactivated + * @category sources + * @since unreleased + */ +void WSEvents::OnSourceAudioDeactivated(void* param, calldata_t* data) { + auto self = reinterpret_cast(param); + + OBSSource source = calldata_get_pointer(data, "source"); + if (!source) { + return; + } + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); + self->broadcastUpdate("SourceAudioDeactivated", fields); +} + +/** + * A source has added audio. + * + * @return {String} `sourceName` Source name + * + * @api events + * @name SourceAudioActivated + * @category sources + * @since unreleased + */ +void WSEvents::OnSourceAudioActivated(void* param, calldata_t* data) { + auto self = reinterpret_cast(param); + + OBSSource source = calldata_get_pointer(data, "source"); + if (!source) { + return; + } + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); + self->broadcastUpdate("SourceAudioActivated", fields); +} + /** * The audio sync offset of a source has changed. * diff --git a/src/WSEvents.h b/src/WSEvents.h index 06ce7f2c..a7c3d24b 100644 --- a/src/WSEvents.h +++ b/src/WSEvents.h @@ -126,6 +126,8 @@ private: static void OnSourceMuteStateChange(void* param, calldata_t* data); static void OnSourceAudioSyncOffsetChanged(void* param, calldata_t* data); static void OnSourceAudioMixersChanged(void* param, calldata_t* data); + static void OnSourceAudioActivated(void* param, calldata_t* data); + static void OnSourceAudioDeactivated(void* param, calldata_t* data); static void OnSourceRename(void* param, calldata_t* data); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 6b1b2cec..30ba61a9 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -351,7 +351,7 @@ RpcResponse WSRequestHandler::ToggleMute(const RpcRequest& request) * @api requests * @name GetAudioActive * @category sources -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::GetAudioActive(const RpcRequest& request) { From 4f83b7373204167131737f86fa752a40524a57eb Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 3 Jul 2020 06:14:41 -0700 Subject: [PATCH 117/278] Docs: Move ssl tunnelling guide to its own page [skip ci] Having the SSL tunneling instructions on the README caused clutter and was unnecessary. --- README.md | 50 ++++++----------------------------------------- SSL-TUNNELLING.md | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 44 deletions(-) create mode 100644 SSL-TUNNELLING.md diff --git a/README.md b/README.md index 76af15d7..074c30de 100644 --- a/README.md +++ b/README.md @@ -20,50 +20,6 @@ A web client and frontend made by [t2t2](https://github.com/t2t2/obs-tablet-remo It is **highly recommended** to protect obs-websocket with a password against unauthorized control. To do this, open the "Websocket server settings" dialog under OBS' "Tools" menu. In the settings dialogs, you can enable or disable authentication and set a password for it. -### Connecting over a TLS/secure connection (or remotely) - -**Before doing this, secure the WebSockets server first by enabling authentication with a strong password!** - -If you want to expose the WebSockets server of obs-websocket over a secure TLS connection (or to connect remotely), the easiest approach is to use a localhost tunneling service like [ngrok](https://ngrok.com/) or [pagekite](https://pagekite.net/). - -**Please bear in mind that doing this will expose your OBS instance to the open Internet and the security risks it implies. You've been warned!** - -#### ngrok - -[Install the ngrok CLI tool](https://ngrok.com/download), then start ngrok bound to port 4444 like this: - -```bash -ngrok http 4444 -``` - -The ngrok command will output something like this: - -``` -ngrok by @inconshreveable - -Tunnel Status online -Version 2.0/2.0 -Web Interface http://127.0.0.1:4040 -Forwarding http://TUNNEL_ID.ngrok.io -> localhost:4444 -Forwarding https://TUNNEL_ID.ngrok.io -> localhost:4444 -``` - -Where `TUNNEL_ID` is, as the name implies, the unique name of your ngrok tunnel. You'll get a new one every time you start ngrok. - -Then, use `wss://TUNNEL_ID.ngrok.io` to connect to obs-websocket over TLS. - -See the [ngrok documentation](https://ngrok.com/docs) for more tunneling options and settings. - -#### PageKite - -[Install the PageKite CLI tool](http://pagekite.net/downloads), then start PageKite bound to port 4444 like this (replace NAME with one of your choosing): - -```bash -$ python pagekite.py 4444 NAME.pagekite.me -``` - -Then, use `wss://NAME.pagekite.me` to connect to obs-websocket over TLS. - ### Possible use cases - Remote control OBS from a phone or tablet on the same local network @@ -87,6 +43,12 @@ Here's a list of available language APIs for obs-websocket : I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop a message in `#project-showoff` in the [discord server!](https://discord.gg/WBaSQ3A) +### Securing obs-websocket (via TLS/SSL) + +If you are intending to use obs-websocket outside of a LAN environment, it is highly recommended to secure the connection using a tunneling service. + +See the SSL [tunnelling guide](SSL-TUNNELLING.md) for easy instructions on how to encrypt your websocket connection. + ## Compiling obs-websocket See the [build instructions](BUILDING.md). diff --git a/SSL-TUNNELLING.md b/SSL-TUNNELLING.md new file mode 100644 index 00000000..b841e711 --- /dev/null +++ b/SSL-TUNNELLING.md @@ -0,0 +1,45 @@ +# Connecting over a TLS/secure connection (or remotely) + +If you want to expose the WebSocket server of obs-websocket over a secure TLS connection (or to connect remotely), the easiest approach is to use a localhost tunneling service like [ngrok](https://ngrok.com/) or [pagekite](https://pagekite.net/). + +**Before doing this, secure the WebSocket server first by enabling authentication with a strong password!** + +**Please bear in mind that doing this will expose your OBS instance to the open Internet and the security risks it implies. *You've been warned!*** + + +## ngrok + +[Install the ngrok CLI tool](https://ngrok.com/download) on a linux OS, then start ngrok bound to port 4444 like this: + +```bash +ngrok http 4444 +``` + +The ngrok command will output something like this: + +```text +ngrok by @inconshreveable + +Tunnel Status online +Version 2.0/2.0 +Web Interface http://127.0.0.1:4040 +Forwarding http://TUNNEL_ID.ngrok.io -> localhost:4444 +Forwarding https://TUNNEL_ID.ngrok.io -> localhost:4444 +``` + +Where `TUNNEL_ID` is, as the name implies, the unique name of your ngrok tunnel. You'll get a new one every time you start ngrok. + +Then, use `wss://TUNNEL_ID.ngrok.io` to connect to obs-websocket over TLS. + +See the [ngrok documentation](https://ngrok.com/docs) for more tunneling options and settings. + + +## PageKite + +[Install the PageKite CLI tool](http://pagekite.net/downloads), then start PageKite bound to port 4444 like this (replace NAME with one of your choosing): + +```bash +python pagekite.py 4444 NAME.pagekite.me +``` + +Then, use `wss://NAME.pagekite.me` to connect to obs-websocket over TLS. From 802cc3a48f3efe824192feddb70cdec08d031314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 3 Jul 2020 15:48:56 +0200 Subject: [PATCH 118/278] Apply suggestions from code review --- src/WSRequestHandler_MediaControl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp index ba61c17f..bb01ff81 100644 --- a/src/WSRequestHandler_MediaControl.cpp +++ b/src/WSRequestHandler_MediaControl.cpp @@ -360,7 +360,7 @@ RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) { * * @return {Array} `mediaSources` Array of sources * @return {String} `mediaSources.*.sourceName` Unique source name -* @return {String} `mediaSources.*.sourceTypeId` Non-unique source internal type (a.k.a `ffmpeg_source` or `vlc_source`) +* @return {String} `mediaSources.*.sourceKind` Unique source internal type (a.k.a `ffmpeg_source` or `vlc_source`) * @return {String} `mediaSources.*.mediaState` The current state of media for that source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown` * * @api requests @@ -379,7 +379,7 @@ RpcResponse WSRequestHandler::GetMediaSourcesList(const RpcRequest& request) if (isMediaSource(sourceTypeId)) { OBSDataAutoRelease sourceData = obs_data_create(); obs_data_set_string(sourceData, "sourceName", obs_source_get_name(source)); - obs_data_set_string(sourceData, "sourceTypeId", sourceTypeId.toUtf8()); + obs_data_set_string(sourceData, "sourceKind", sourceTypeId.toUtf8()); QString mediaState = getSourceMediaState(source); obs_data_set_string(sourceData, "mediaState", mediaState.toUtf8()); From 38936173d1d005a480846446f056de44708ecf4b Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 3 Jul 2020 07:03:25 -0700 Subject: [PATCH 119/278] Requests/Events: Use sourceKind instead of sourceTypeId Left in some old terms accidentally. This fixes it. --- src/WSEvents.cpp | 18 +++++++++--------- src/WSRequestHandler_MediaControl.cpp | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index c9b701b1..0f467d9c 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -434,7 +434,7 @@ OBSDataAutoRelease getMediaSourceData(calldata_t* data) { OBSSource source = calldata_get_pointer(data, "source"); obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); - obs_data_set_string(fields, "sourceTypeId", obs_source_get_id(source)); + obs_data_set_string(fields, "sourceKind", obs_source_get_id(source)); return fields; } @@ -1422,7 +1422,7 @@ void WSEvents::OnSourceFilterOrderChanged(void* param, calldata_t* data) { * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. * * @return {String} `sourceName` Source name - * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) + * @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaPlaying @@ -1443,7 +1443,7 @@ void WSEvents::OnMediaPlaying(void* param, calldata_t* data) { * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. * * @return {String} `sourceName` Source name - * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) + * @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaPaused @@ -1464,7 +1464,7 @@ void WSEvents::OnMediaPaused(void* param, calldata_t* data) { * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. * * @return {String} `sourceName` Source name - * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) + * @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaRestarted @@ -1485,7 +1485,7 @@ void WSEvents::OnMediaRestarted(void* param, calldata_t* data) { * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. * * @return {String} `sourceName` Source name - * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) + * @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaStopped @@ -1506,7 +1506,7 @@ void WSEvents::OnMediaStopped(void* param, calldata_t* data) { * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. * * @return {String} `sourceName` Source name - * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) + * @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaNext @@ -1527,7 +1527,7 @@ void WSEvents::OnMediaNext(void* param, calldata_t* data) { * Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. * * @return {String} `sourceName` Source name - * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) + * @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaPrevious @@ -1548,7 +1548,7 @@ void WSEvents::OnMediaPrevious(void* param, calldata_t* data) { * Note: These events are emitted by the OBS sources themselves. For example when the media file starts playing. The behavior depends on the type of media source being used. * * @return {String} `sourceName` Source name - * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) + * @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaStarted @@ -1569,7 +1569,7 @@ void WSEvents::OnMediaStarted(void* param, calldata_t* data) { * Note: These events are emitted by the OBS sources themselves. For example when the media file ends. The behavior depends on the type of media source being used. * * @return {String} `sourceName` Source name - * @return {String} `sourceTypeId` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) + * @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) * * @api events * @name MediaEnded diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp index bb01ff81..1b7a73bf 100644 --- a/src/WSRequestHandler_MediaControl.cpp +++ b/src/WSRequestHandler_MediaControl.cpp @@ -375,11 +375,11 @@ RpcResponse WSRequestHandler::GetMediaSourcesList(const RpcRequest& request) auto sourceEnumProc = [](void* privateData, obs_source_t* source) -> bool { obs_data_array_t* sourcesArray = (obs_data_array_t*)privateData; - QString sourceTypeId = obs_source_get_id(source); - if (isMediaSource(sourceTypeId)) { + QString sourceKind = obs_source_get_id(source); + if (isMediaSource(sourceKind)) { OBSDataAutoRelease sourceData = obs_data_create(); obs_data_set_string(sourceData, "sourceName", obs_source_get_name(source)); - obs_data_set_string(sourceData, "sourceKind", sourceTypeId.toUtf8()); + obs_data_set_string(sourceData, "sourceKind", sourceKind.toUtf8()); QString mediaState = getSourceMediaState(source); obs_data_set_string(sourceData, "mediaState", mediaState.toUtf8()); From 513bd1372be20c3a2df3e3f107ce8a36eefc475c Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 3 Jul 2020 07:12:28 -0700 Subject: [PATCH 120/278] Docs, Requests: Fix GetSceneItemList code revisions Replace the `sourceId` term with `sourceKind` to match everything else. --- src/WSRequestHandler_SceneItems.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 01b56789..96ed3f6a 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -21,10 +21,10 @@ void AddSourceHelper(void *_data, obs_scene_t *scene) { * * @return {Array} `sceneItems` Array of scene items * @return {int} `sceneItems.*.itemId` Unique item id of the source item -* @return {String} `sceneItems.*.sourceId` ID if the scene item's source. For example `vlc_source` or `image_source` +* @return {String} `sceneItems.*.sourceKind` ID if the scene item's source. For example `vlc_source` or `image_source` * @return {String} `sceneItems.*.sourceName` Name of the scene item's source -* @return {String} `sceneItems.*.sourceType` Type of the scene item's source. Either `input` or `scene` -* +* @return {String} `sceneItems.*.sourceType` Type of the scene item's source. Either `input`, `group`, or `scene` +* * @api requests * @name GetSceneItemList * @category scene items @@ -50,7 +50,7 @@ RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) { OBSDataAutoRelease sceneItemData = obs_data_create(); obs_data_set_int(sceneItemData, "itemId", obs_sceneitem_get_id(item)); OBSSource source = obs_sceneitem_get_source(item); - obs_data_set_string(sceneItemData, "sourceId", obs_source_get_id(source)); + obs_data_set_string(sceneItemData, "sourceKind", obs_source_get_id(source)); obs_data_set_string(sceneItemData, "sourceName", obs_source_get_name(source)); QString typeString = ""; @@ -654,7 +654,7 @@ RpcResponse WSRequestHandler::AddSceneItem(const RpcRequest& request) { if (!source) { return request.failed("requested source does not exist"); } - + if (source == sceneSource) { return request.failed("you cannot add a scene as a sceneitem to itself"); } @@ -671,7 +671,7 @@ RpcResponse WSRequestHandler::AddSceneItem(const RpcRequest& request) { obs_leave_graphics(); OBSDataAutoRelease responseData = obs_data_create(); - obs_data_set_int(responseData, "itemID", obs_sceneitem_get_id(data.sceneItem)); + obs_data_set_int(responseData, "itemId", obs_sceneitem_get_id(data.sceneItem)); return request.success(responseData); } From bfa6cd0e19530e4d866448c6249ceda4c66a338b Mon Sep 17 00:00:00 2001 From: Juliane Holzt Date: Thu, 9 Jul 2020 00:43:54 +0200 Subject: [PATCH 121/278] TakeSourceScreenshot: default to current scene --- src/WSRequestHandler_Sources.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 6287783f..85471581 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1623,7 +1623,7 @@ RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request) * Clients can specify `width` and `height` parameters to receive scaled pictures. Aspect ratio is * preserved if only one of these two parameters is specified. * -* @param {String} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name. +* @param {String} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name. If an empty string is provided, the currently active scene is used. * @param {String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module) * @param {String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path. * @param {String (optional)} `fileFormat` Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension. @@ -1650,7 +1650,14 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { } const char* sourceName = obs_data_get_string(request.parameters(), "sourceName"); - OBSSourceAutoRelease source = obs_get_source_by_name(sourceName); + + OBSSourceAutoRelease source; + if (strlen(sourceName) == 0) { + source = obs_frontend_get_current_scene(); + } else { + source = obs_get_source_by_name(sourceName); + } + if (!source) { return request.failed("specified source doesn't exist");; } From 31a505f4a455d42dca1f96af009689de78b56cb6 Mon Sep 17 00:00:00 2001 From: Juliane Holzt Date: Thu, 9 Jul 2020 00:50:29 +0200 Subject: [PATCH 122/278] Do it when sourceName is omitted --- src/WSRequestHandler_Sources.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 85471581..d8cfebee 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1623,7 +1623,7 @@ RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request) * Clients can specify `width` and `height` parameters to receive scaled pictures. Aspect ratio is * preserved if only one of these two parameters is specified. * -* @param {String} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name. If an empty string is provided, the currently active scene is used. +* @param {String (optional)} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used. * @param {String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module) * @param {String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path. * @param {String (optional)} `fileFormat` Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension. @@ -1641,25 +1641,19 @@ RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request) * @since 4.6.0 */ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { - if (!request.hasField("sourceName")) { - return request.failed("missing request parameters"); - } - if (!request.hasField("embedPictureFormat") && !request.hasField("saveToFilePath")) { return request.failed("At least 'embedPictureFormat' or 'saveToFilePath' must be specified"); } - const char* sourceName = obs_data_get_string(request.parameters(), "sourceName"); - OBSSourceAutoRelease source; - if (strlen(sourceName) == 0) { + if (!request.hasField("sourceName")) { source = obs_frontend_get_current_scene(); } else { + const char* sourceName = obs_data_get_string(request.parameters(), "sourceName"); source = obs_get_source_by_name(sourceName); - } - - if (!source) { - return request.failed("specified source doesn't exist");; + if (!source) { + return request.failed("specified source doesn't exist");; + } } const uint32_t sourceWidth = obs_source_get_base_width(source); From 9cdb32d56ae93520fdfb66c4ce75209d270b19cb Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 8 Jul 2020 03:19:15 -0700 Subject: [PATCH 123/278] Docs: Various docs improvements I noticed that some parts of the docs were inconsistent/lacking. Decided to go through all of them and update them. --- docs/partials/introduction.md | 4 +++- docs/partials/requestsHeader.md | 2 +- src/WSEvents.cpp | 19 ++++++++++-------- src/WSRequestHandler_General.cpp | 11 ++++++----- src/WSRequestHandler_Outputs.cpp | 8 ++++++-- src/WSRequestHandler_Profiles.cpp | 5 +++-- src/WSRequestHandler_Recording.cpp | 2 +- src/WSRequestHandler_ReplayBuffer.cpp | 2 +- src/WSRequestHandler_SceneCollections.cpp | 3 ++- src/WSRequestHandler_SceneItems.cpp | 1 - src/WSRequestHandler_Scenes.cpp | 24 +++++++++++------------ src/WSRequestHandler_Sources.cpp | 10 +++++----- src/WSRequestHandler_Streaming.cpp | 3 +-- src/WSRequestHandler_StudioMode.cpp | 2 +- 14 files changed, 53 insertions(+), 43 deletions(-) diff --git a/docs/partials/introduction.md b/docs/partials/introduction.md index c63a52cd..496363e7 100644 --- a/docs/partials/introduction.md +++ b/docs/partials/introduction.md @@ -2,7 +2,7 @@ # General Introduction Messages are exchanged between the client and the server as JSON objects. -This protocol is based on the original OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio. +This protocol is based on the original OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio. As of v5.0.0, backwards compatability with the protocol will not be kept. # Authentication `obs-websocket` uses SHA256 to transmit credentials. @@ -32,3 +32,5 @@ auth_response_string = secret + challenge auth_response_hash = binary_sha256(auth_response_string) auth_response = base64_encode(auth_response_hash) ``` + +You can also refer to any of the client libraries listed on the [README](README.md) for examples of how to authenticate. diff --git a/docs/partials/requestsHeader.md b/docs/partials/requestsHeader.md index 457ad61b..b73efc0c 100644 --- a/docs/partials/requestsHeader.md +++ b/docs/partials/requestsHeader.md @@ -6,6 +6,6 @@ Requests are sent by the client and require at least the following two fields: Once a request is sent, the server will return a JSON response with at least the following fields: - `message-id` _String_: The client defined identifier specified in the request. - `status` _String_: Response status, will be one of the following: `ok`, `error` -- `error` _String_: An error message accompanying an `error` status. +- `error` _String (Optional)_: An error message accompanying an `error` status. Additional information may be required/returned depending on the request type. See below for more information. diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 0f467d9c..bb8e354c 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -465,6 +465,8 @@ void WSEvents::OnSceneChange() { * The scene list has been modified. * Scenes have been added, removed, or renamed. * + * Note: This event is not fired when the scenes are reordered. + * * @api events * @name ScenesChanged * @category scenes @@ -756,7 +758,7 @@ void WSEvents::OnExit() { } /** - * Emit every 2 seconds. + * Emitted every 2 seconds when stream is active. * * @return {boolean} `streaming` Current streaming state. * @return {boolean} `recording` Current recording state. @@ -863,6 +865,7 @@ void WSEvents::StreamStatus() { * @api events * @name Heartbeat * @category general + * @since v0.3 */ void WSEvents::Heartbeat() { @@ -1585,7 +1588,7 @@ void WSEvents::OnMediaEnded(void* param, calldata_t* data) { } /** - * Scene items have been reordered. + * Scene items within a scene have been reordered. * * @return {String} `scene-name` Name of the scene where items have been reordered. * @return {Array} `scene-items` Ordered list of scene items @@ -1628,7 +1631,7 @@ void WSEvents::OnSceneReordered(void* param, calldata_t* data) { } /** - * An item has been added to the current scene. + * A scene item has been added to a scene. * * @return {String} `scene-name` Name of the scene. * @return {String} `item-name` Name of the item added to the scene. @@ -1661,7 +1664,7 @@ void WSEvents::OnSceneItemAdd(void* param, calldata_t* data) { } /** - * An item has been removed from the current scene. + * A scene item has been removed from a scene. * * @return {String} `scene-name` Name of the scene. * @return {String} `item-name` Name of the item removed from the scene. @@ -1694,7 +1697,7 @@ void WSEvents::OnSceneItemDelete(void* param, calldata_t* data) { } /** - * An item's visibility has been toggled. + * A scene item's visibility has been toggled. * * @return {String} `scene-name` Name of the scene. * @return {String} `item-name` Name of the item in the scene. @@ -1732,7 +1735,7 @@ void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) { } /** - * An item's locked status has been toggled. + * A scene item's locked status has been toggled. * * @return {String} `scene-name` Name of the scene. * @return {String} `item-name` Name of the item in the scene. @@ -1770,7 +1773,7 @@ void WSEvents::OnSceneItemLockChanged(void* param, calldata_t* data) { } /** - * An item's transform has been changed. + * A scene item's transform has been changed. * * @return {String} `scene-name` Name of the scene. * @return {String} `item-name` Name of the item in the scene. @@ -1921,7 +1924,7 @@ void WSEvents::OnStudioModeSwitched(bool checked) { } /** - * A custom broadcast message was received + * A custom broadcast message, sent by the server, requested by one of the websocket clients. * * @return {String} `realm` Identifier provided by the sender * @return {Object} `data` User-defined data diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index 416ac013..ea1c6a63 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -171,6 +171,7 @@ RpcResponse WSRequestHandler::Authenticate(const RpcRequest& request) { * @name SetHeartbeat * @category general * @since 4.3.0 + * @deprecated Since 4.9.0. Please poll the appropriate data using requests. Will be removed in v5.0.0. */ RpcResponse WSRequestHandler::SetHeartbeat(const RpcRequest& request) { if (!request.hasField("enable")) { @@ -231,7 +232,7 @@ RpcResponse WSRequestHandler::GetFilenameFormatting(const RpcRequest& request) { /** * Get OBS stats (almost the same info as provided in OBS' stats window) * - * @return {OBSStats} `stats` OBS stats + * @return {OBSStats} `stats` [OBS stats](#obsstats) * * @api requests * @name GetStats @@ -319,12 +320,12 @@ RpcResponse WSRequestHandler::GetVideoInfo(const RpcRequest& request) { /** * Open a projector window or create a projector on a monitor. Requires OBS v24.0.4 or newer. - * - * @param {String (Optional)} `type` Type of projector: Preview (default), Source, Scene, StudioProgram, or Multiview (case insensitive). + * + * @param {String (Optional)} `type` Type of projector: `Preview` (default), `Source`, `Scene`, `StudioProgram`, or `Multiview` (case insensitive). * @param {int (Optional)} `monitor` Monitor to open the projector on. If -1 or omitted, opens a window. - * @param {String (Optional)} `geometry` Size and position of the projector window (only if monitor is -1). Encoded in Base64 using Qt's geometry encoding (https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors. + * @param {String (Optional)} `geometry` Size and position of the projector window (only if monitor is -1). Encoded in Base64 using [Qt's geometry encoding](https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors. * @param {String (Optional)} `name` Name of the source or scene to be displayed (ignored for other projector types). - * + * * @api requests * @name OpenProjector * @category general diff --git a/src/WSRequestHandler_Outputs.cpp b/src/WSRequestHandler_Outputs.cpp index dad0bf19..ab8b0cc1 100644 --- a/src/WSRequestHandler_Outputs.cpp +++ b/src/WSRequestHandler_Outputs.cpp @@ -71,7 +71,7 @@ RpcResponse findOutputOrFail(const RpcRequest& request, std::function} `profiles` List of available profiles. + * @return {String} `profiles.*.profile-name` Filter name * * @api requests * @name ListProfiles diff --git a/src/WSRequestHandler_Recording.cpp b/src/WSRequestHandler_Recording.cpp index d58f60c7..24aba87a 100644 --- a/src/WSRequestHandler_Recording.cpp +++ b/src/WSRequestHandler_Recording.cpp @@ -43,7 +43,7 @@ RpcResponse WSRequestHandler::GetRecordingStatus(const RpcRequest& request) { } /** - * Toggle recording on or off. + * Toggle recording on or off (depending on the current recording state). * * @api requests * @name StartStopRecording diff --git a/src/WSRequestHandler_ReplayBuffer.cpp b/src/WSRequestHandler_ReplayBuffer.cpp index be860584..d4c83a27 100644 --- a/src/WSRequestHandler_ReplayBuffer.cpp +++ b/src/WSRequestHandler_ReplayBuffer.cpp @@ -23,7 +23,7 @@ RpcResponse WSRequestHandler::GetReplayBufferStatus(const RpcRequest& request) { } /** -* Toggle the Replay Buffer on/off. +* Toggle the Replay Buffer on/off (depending on the current state of the replay buffer). * * @api requests * @name StartStopReplayBuffer diff --git a/src/WSRequestHandler_SceneCollections.cpp b/src/WSRequestHandler_SceneCollections.cpp index 1f87996b..0359144e 100644 --- a/src/WSRequestHandler_SceneCollections.cpp +++ b/src/WSRequestHandler_SceneCollections.cpp @@ -22,7 +22,7 @@ RpcResponse WSRequestHandler::SetCurrentSceneCollection(const RpcRequest& reques return request.failed("invalid request parameters"); } - // TODO : Check if specified profile exists and if changing is allowed + // TODO : Check if specified collection exists and if changing is allowed obs_frontend_set_current_scene_collection(sceneCollection.toUtf8()); return request.success(); } @@ -51,6 +51,7 @@ RpcResponse WSRequestHandler::GetCurrentSceneCollection(const RpcRequest& reques * List available scene collections * * @return {Array} `scene-collections` Scene collections list + * @return {String} `scene-collections.*.sc-name` Scene collection name * * @api requests * @name ListSceneCollections diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 96ed3f6a..10ca8212 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -408,7 +408,6 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) { * @name SetSceneItemRender * @category scene items * @since 0.3 -* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties. */ RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) { if (!request.hasField("source") || diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp index a6d9d293..18783119 100644 --- a/src/WSRequestHandler_Scenes.cpp +++ b/src/WSRequestHandler_Scenes.cpp @@ -36,7 +36,7 @@ RpcResponse WSRequestHandler::SetCurrentScene(const RpcRequest& request) { /** * Get the current scene's name and source items. - * + * * @return {String} `name` Name of the currently active scene. * @return {Array} `sources` Ordered list of the current scene's source items. * @@ -58,7 +58,7 @@ RpcResponse WSRequestHandler::GetCurrentScene(const RpcRequest& request) { /** * Get a list of scenes in the currently active profile. - * + * * @return {String} `current-scene` Name of the currently active scene. * @return {Array} `scenes` Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information). * @@ -109,8 +109,8 @@ RpcResponse WSRequestHandler::CreateScene(const RpcRequest& request) { * * @param {String (optional)} `scene` Name of the scene to reorder (defaults to current). * @param {Array} `items` Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene -* @param {int (optional)} `items[].id` Id of a specific scene item. Unique on a scene by scene basis. -* @param {String (optional)} `items[].name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene. +* @param {int (optional)} `items.*.id` Id of a specific scene item. Unique on a scene by scene basis. +* @param {String (optional)} `items.*.name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene. * * @api requests * @name ReorderSceneItems @@ -184,7 +184,7 @@ RpcResponse WSRequestHandler::ReorderSceneItems(const RpcRequest& request) { * @api requests * @name SetSceneTransitionOverride * @category scenes - * @since 4.9.0 + * @since unreleased */ RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& request) { if (!request.hasField("sceneName") || !request.hasField("transitionName")) { @@ -196,12 +196,12 @@ RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& reque if (!source) { return request.failed("requested scene does not exist"); } - + enum obs_source_type sourceType = obs_source_get_type(source); if (sourceType != OBS_SOURCE_TYPE_SCENE) { return request.failed("requested scene is invalid"); } - + QString transitionName = obs_data_get_string(request.parameters(), "transitionName"); if (!Utils::GetTransitionFromName(transitionName)) { return request.failed("requested transition does not exist"); @@ -218,7 +218,7 @@ RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& reque obs_frontend_get_transition_duration() ); } - + return request.success(); } @@ -230,7 +230,7 @@ RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& reque * @api requests * @name RemoveSceneTransitionOverride * @category scenes - * @since 4.9.0 + * @since unreleased */ RpcResponse WSRequestHandler::RemoveSceneTransitionOverride(const RpcRequest& request) { if (!request.hasField("sceneName")) { @@ -242,7 +242,7 @@ RpcResponse WSRequestHandler::RemoveSceneTransitionOverride(const RpcRequest& re if (!source) { return request.failed("requested scene does not exist"); } - + enum obs_source_type sourceType = obs_source_get_type(source); if (sourceType != OBS_SOURCE_TYPE_SCENE) { return request.failed("requested scene is invalid"); @@ -266,7 +266,7 @@ RpcResponse WSRequestHandler::RemoveSceneTransitionOverride(const RpcRequest& re * @api requests * @name GetSceneTransitionOverride * @category scenes - * @since 4.9.0 + * @since unreleased */ RpcResponse WSRequestHandler::GetSceneTransitionOverride(const RpcRequest& request) { if (!request.hasField("sceneName")) { @@ -278,7 +278,7 @@ RpcResponse WSRequestHandler::GetSceneTransitionOverride(const RpcRequest& reque if (!source) { return request.failed("requested scene does not exist"); } - + enum obs_source_type sourceType = obs_source_get_type(source); if (sourceType != OBS_SOURCE_TYPE_SCENE) { return request.failed("requested scene is invalid"); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 6287783f..f7d8605e 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -23,7 +23,7 @@ bool isTextFreeType2Source(const QString& sourceKind) * * @return {Array} `sources` Array of sources * @return {String} `sources.*.name` Unique source name -* @return {String} `sources.*.typeId` Non-unique source internal type (a.k.a type id) +* @return {String} `sources.*.typeId` Non-unique source internal type (a.k.a kind) * @return {String} `sources.*.type` Source type. Value is one of the following: "input", "filter", "transition", "scene" or "unknown" * * @api requests @@ -382,7 +382,7 @@ RpcResponse WSRequestHandler::GetAudioActive(const RpcRequest& request) /** * Sets (aka rename) the name of a source. Also works with scenes since scenes are technically sources in OBS. * -* Note: If the new name already exists as a source, OBS will automatically modify the name to not interfere. +* Note: If the new name already exists as a source, obs-websocket will return an error. * * @param {String} `sourceName` Source name. * @param {String} `newName` New source name. @@ -415,7 +415,7 @@ RpcResponse WSRequestHandler::SetSourceName(const RpcRequest& request) return request.success(); } else { - return request.failed("a source with that newSourceName already exists"); + return request.failed("a source with that name already exists"); } } @@ -1013,7 +1013,7 @@ RpcResponse WSRequestHandler::SetTextFreetype2Properties(const RpcRequest& reque * @name GetBrowserSourceProperties * @category sources * @since 4.1.0 - * @deprecated Since 4.8.0. Prefer the use of GetSourceSettings. + * @deprecated Since 4.8.0. Prefer the use of GetSourceSettings. Will be removed in v5.0.0 */ RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& request) { @@ -1055,7 +1055,7 @@ RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& reque * @api requests * @name SetBrowserSourceProperties * @category sources - * @deprecated Since 4.8.0. Prefer the use of SetSourceSettings. + * @deprecated Since 4.8.0. Prefer the use of SetSourceSettings. Will be removed in v5.0.0 * @since 4.1.0 */ RpcResponse WSRequestHandler::SetBrowserSourceProperties(const RpcRequest& request) diff --git a/src/WSRequestHandler_Streaming.cpp b/src/WSRequestHandler_Streaming.cpp index c8f5732e..aa0782c9 100644 --- a/src/WSRequestHandler_Streaming.cpp +++ b/src/WSRequestHandler_Streaming.cpp @@ -43,7 +43,7 @@ RpcResponse WSRequestHandler::GetStreamingStatus(const RpcRequest& request) { } /** - * Toggle streaming on or off. + * Toggle streaming on or off (depending on the current stream state). * * @api requests * @name StartStopStreaming @@ -292,7 +292,6 @@ RpcResponse WSRequestHandler::SaveStreamSettings(const RpcRequest& request) { /** * Send the provided text as embedded CEA-608 caption data. - * As of OBS Studio 23.1, captions are not yet available on Linux. * * @param {String} `text` Captions text * diff --git a/src/WSRequestHandler_StudioMode.cpp b/src/WSRequestHandler_StudioMode.cpp index badae29d..6781f0ad 100644 --- a/src/WSRequestHandler_StudioMode.cpp +++ b/src/WSRequestHandler_StudioMode.cpp @@ -166,7 +166,7 @@ RpcResponse WSRequestHandler::DisableStudioMode(const RpcRequest& request) { } /** - * Toggles Studio Mode. + * Toggles Studio Mode (depending on the current state of studio mode). * * @api requests * @name ToggleStudioMode From 80de8ada5765465d302f6a5bc6ecc8df4946552e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Thu, 9 Jul 2020 12:39:40 +0200 Subject: [PATCH 124/278] ci: fix Azure Pipelines not running on 4.x-current --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7fce0542..bccfedb9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,7 +4,7 @@ variables: trigger: branches: include: - - master + - '4.x-current' tags: include: - '*' From 04c60fd1acb6ca468b35d06dd0def048977f28e5 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Thu, 9 Jul 2020 10:40:21 +0000 Subject: [PATCH 125/278] docs(ci): Update protocol.md - 80de8ad [skip ci] --- docs/generated/comments.json | 5217 +++++++++++++++++++++------------- docs/generated/protocol.md | 2261 +++++++++------ 2 files changed, 4695 insertions(+), 2783 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index fea62b1f..5bfee9f2 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -121,8 +121,8 @@ "subheads": [], "typedef": "{Object} `SceneItemTransform`", "property": [ - "{int} `position.x` The x position of the scene item from the left.", - "{int} `position.y` The y position of the scene item from the top.", + "{double} `position.x` The x position of the scene item from the left.", + "{double} `position.y` The y position of the scene item from the top.", "{int} `position.alignment` The point on the scene item that the item is manipulated from.", "{double} `rotation` The clockwise rotation of the scene item in degrees around the point of alignment.", "{double} `scale.x` The x-scale factor of the scene item.", @@ -146,12 +146,12 @@ ], "properties": [ { - "type": "int", + "type": "double", "name": "position.x", "description": "The x position of the scene item from the left." }, { - "type": "int", + "type": "double", "name": "position.y", "description": "The y position of the scene item from the top." }, @@ -2077,6 +2077,88 @@ "type": "class", "examples": [] }, + { + "subheads": [], + "description": "A source has removed audio.", + "return": "{String} `sourceName` Source name", + "api": "events", + "name": "SourceAudioDeactivated", + "category": "sources", + "since": "unreleased", + "returns": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name" + } + ], + "names": [ + { + "name": "", + "description": "SourceAudioDeactivated" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "SourceAudioDeactivated" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "A source has added audio.", + "return": "{String} `sourceName` Source name", + "api": "events", + "name": "SourceAudioActivated", + "category": "sources", + "since": "unreleased", + "returns": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name" + } + ], + "names": [ + { + "name": "", + "description": "SourceAudioActivated" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "SourceAudioActivated" + }, + "lead": "", + "type": "class", + "examples": [] + }, { "subheads": [], "description": "The audio sync offset of a source has changed.", @@ -2481,6 +2563,400 @@ "examples": [] } ], + "media": [ + { + "subheads": [], + "description": "\n\nNote: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.", + "return": [ + "{String} `sourceName` Source name", + "{String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + ], + "api": "events", + "name": "MediaPlaying", + "category": "media", + "since": "4.9.0", + "returns": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name" + }, + { + "type": "String", + "name": "sourceKind", + "description": "The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + } + ], + "names": [ + { + "name": "", + "description": "MediaPlaying" + } + ], + "categories": [ + { + "name": "", + "description": "media" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "MediaPlaying" + }, + "lead": "A media source has started playing.", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "\n\nNote: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.", + "return": [ + "{String} `sourceName` Source name", + "{String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + ], + "api": "events", + "name": "MediaPaused", + "category": "media", + "since": "4.9.0", + "returns": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name" + }, + { + "type": "String", + "name": "sourceKind", + "description": "The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + } + ], + "names": [ + { + "name": "", + "description": "MediaPaused" + } + ], + "categories": [ + { + "name": "", + "description": "media" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "MediaPaused" + }, + "lead": "A media source has been paused.", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "\n\nNote: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.", + "return": [ + "{String} `sourceName` Source name", + "{String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + ], + "api": "events", + "name": "MediaRestarted", + "category": "media", + "since": "4.9.0", + "returns": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name" + }, + { + "type": "String", + "name": "sourceKind", + "description": "The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + } + ], + "names": [ + { + "name": "", + "description": "MediaRestarted" + } + ], + "categories": [ + { + "name": "", + "description": "media" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "MediaRestarted" + }, + "lead": "A media source has been restarted.", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "\n\nNote: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.", + "return": [ + "{String} `sourceName` Source name", + "{String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + ], + "api": "events", + "name": "MediaStopped", + "category": "media", + "since": "4.9.0", + "returns": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name" + }, + { + "type": "String", + "name": "sourceKind", + "description": "The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + } + ], + "names": [ + { + "name": "", + "description": "MediaStopped" + } + ], + "categories": [ + { + "name": "", + "description": "media" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "MediaStopped" + }, + "lead": "A media source has been stopped.", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "\n\nNote: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.", + "return": [ + "{String} `sourceName` Source name", + "{String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + ], + "api": "events", + "name": "MediaNext", + "category": "media", + "since": "4.9.0", + "returns": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name" + }, + { + "type": "String", + "name": "sourceKind", + "description": "The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + } + ], + "names": [ + { + "name": "", + "description": "MediaNext" + } + ], + "categories": [ + { + "name": "", + "description": "media" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "MediaNext" + }, + "lead": "A media source has gone to the next item in the playlist.", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "\n\nNote: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.", + "return": [ + "{String} `sourceName` Source name", + "{String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + ], + "api": "events", + "name": "MediaPrevious", + "category": "media", + "since": "4.9.0", + "returns": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name" + }, + { + "type": "String", + "name": "sourceKind", + "description": "The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + } + ], + "names": [ + { + "name": "", + "description": "MediaPrevious" + } + ], + "categories": [ + { + "name": "", + "description": "media" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "MediaPrevious" + }, + "lead": "A media source has gone to the previous item in the playlist.", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "\n\nNote: These events are emitted by the OBS sources themselves. For example when the media file starts playing. The behavior depends on the type of media source being used.", + "return": [ + "{String} `sourceName` Source name", + "{String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + ], + "api": "events", + "name": "MediaStarted", + "category": "media", + "since": "4.9.0", + "returns": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name" + }, + { + "type": "String", + "name": "sourceKind", + "description": "The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + } + ], + "names": [ + { + "name": "", + "description": "MediaStarted" + } + ], + "categories": [ + { + "name": "", + "description": "media" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "MediaStarted" + }, + "lead": "A media source has been started.", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "\n\nNote: These events are emitted by the OBS sources themselves. For example when the media file ends. The behavior depends on the type of media source being used.", + "return": [ + "{String} `sourceName` Source name", + "{String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + ], + "api": "events", + "name": "MediaEnded", + "category": "media", + "since": "4.9.0", + "returns": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name" + }, + { + "type": "String", + "name": "sourceKind", + "description": "The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)" + } + ], + "names": [ + { + "name": "", + "description": "MediaEnded" + } + ], + "categories": [ + { + "name": "", + "description": "media" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "MediaEnded" + }, + "lead": "A media source has ended.", + "type": "class", + "examples": [] + } + ], "scene items": [ { "subheads": [], @@ -3571,1942 +4047,40 @@ "examples": [] } ], - "outputs": [ + "media control": [ { "subheads": [], - "description": "List existing outputs", - "return": "{Array} `outputs` Outputs list", - "api": "requests", - "name": "ListOutputs", - "category": "outputs", - "since": "4.7.0", - "returns": [ - { - "type": "Array", - "name": "outputs", - "description": "Outputs list" - } - ], - "names": [ - { - "name": "", - "description": "ListOutputs" - } - ], - "categories": [ - { - "name": "", - "description": "outputs" - } - ], - "sinces": [ - { - "name": "", - "description": "4.7.0" - } - ], - "heading": { - "level": 2, - "text": "ListOutputs" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Get information about a single output", - "param": "{String} `outputName` Output name", - "return": "{Output} `outputInfo` Output info", - "api": "requests", - "name": "GetOutputInfo", - "category": "outputs", - "since": "4.7.0", - "returns": [ - { - "type": "Output", - "name": "outputInfo", - "description": "Output info" - } - ], - "params": [ - { - "type": "String", - "name": "outputName", - "description": "Output name" - } - ], - "names": [ - { - "name": "", - "description": "GetOutputInfo" - } - ], - "categories": [ - { - "name": "", - "description": "outputs" - } - ], - "sinces": [ - { - "name": "", - "description": "4.7.0" - } - ], - "heading": { - "level": 2, - "text": "GetOutputInfo" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Start an output", - "param": "{String} `outputName` Output name", - "api": "requests", - "name": "StartOutput", - "category": "outputs", - "since": "4.7.0", - "params": [ - { - "type": "String", - "name": "outputName", - "description": "Output name" - } - ], - "names": [ - { - "name": "", - "description": "StartOutput" - } - ], - "categories": [ - { - "name": "", - "description": "outputs" - } - ], - "sinces": [ - { - "name": "", - "description": "4.7.0" - } - ], - "heading": { - "level": 2, - "text": "StartOutput" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Stop an output", + "description": "Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)", "param": [ - "{String} `outputName` Output name", - "{boolean (optional)} `force` Force stop (default: false)" + "{String} `sourceName` Source name.", + "{boolean} `playPause` Whether to pause or play the source. `false` for play, `true` for pause." ], "api": "requests", - "name": "StopOutput", - "category": "outputs", - "since": "4.7.0", + "name": "PlayPauseMedia", + "category": "media control", + "since": "4.9.0", "params": [ { "type": "String", - "name": "outputName", - "description": "Output name" - }, - { - "type": "boolean (optional)", - "name": "force", - "description": "Force stop (default: false)" - } - ], - "names": [ - { - "name": "", - "description": "StopOutput" - } - ], - "categories": [ - { - "name": "", - "description": "outputs" - } - ], - "sinces": [ - { - "name": "", - "description": "4.7.0" - } - ], - "heading": { - "level": 2, - "text": "StopOutput" - }, - "lead": "", - "type": "class", - "examples": [] - } - ], - "profiles": [ - { - "subheads": [], - "description": "Set the currently active profile.", - "param": "{String} `profile-name` Name of the desired profile.", - "api": "requests", - "name": "SetCurrentProfile", - "category": "profiles", - "since": "4.0.0", - "params": [ - { - "type": "String", - "name": "profile-name", - "description": "Name of the desired profile." - } - ], - "names": [ - { - "name": "", - "description": "SetCurrentProfile" - } - ], - "categories": [ - { - "name": "", - "description": "profiles" - } - ], - "sinces": [ - { - "name": "", - "description": "4.0.0" - } - ], - "heading": { - "level": 2, - "text": "SetCurrentProfile" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Get the name of the current profile.", - "return": "{String} `profile-name` Name of the currently active profile.", - "api": "requests", - "name": "GetCurrentProfile", - "category": "profiles", - "since": "4.0.0", - "returns": [ - { - "type": "String", - "name": "profile-name", - "description": "Name of the currently active profile." - } - ], - "names": [ - { - "name": "", - "description": "GetCurrentProfile" - } - ], - "categories": [ - { - "name": "", - "description": "profiles" - } - ], - "sinces": [ - { - "name": "", - "description": "4.0.0" - } - ], - "heading": { - "level": 2, - "text": "GetCurrentProfile" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Get a list of available profiles.", - "return": "{Array} `profiles` List of available profiles.", - "api": "requests", - "name": "ListProfiles", - "category": "profiles", - "since": "4.0.0", - "returns": [ - { - "type": "Array", - "name": "profiles", - "description": "List of available profiles." - } - ], - "names": [ - { - "name": "", - "description": "ListProfiles" - } - ], - "categories": [ - { - "name": "", - "description": "profiles" - } - ], - "sinces": [ - { - "name": "", - "description": "4.0.0" - } - ], - "heading": { - "level": 2, - "text": "ListProfiles" - }, - "lead": "", - "type": "class", - "examples": [] - } - ], - "recording": [ - { - "subheads": [], - "description": "Toggle recording on or off.", - "api": "requests", - "name": "StartStopRecording", - "category": "recording", - "since": "0.3", - "names": [ - { - "name": "", - "description": "StartStopRecording" - } - ], - "categories": [ - { - "name": "", - "description": "recording" - } - ], - "sinces": [ - { - "name": "", - "description": "0.3" - } - ], - "heading": { - "level": 2, - "text": "StartStopRecording" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Start recording.\nWill return an `error` if recording is already active.", - "api": "requests", - "name": "StartRecording", - "category": "recording", - "since": "4.1.0", - "names": [ - { - "name": "", - "description": "StartRecording" - } - ], - "categories": [ - { - "name": "", - "description": "recording" - } - ], - "sinces": [ - { - "name": "", - "description": "4.1.0" - } - ], - "heading": { - "level": 2, - "text": "StartRecording" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Stop recording.\nWill return an `error` if recording is not active.", - "api": "requests", - "name": "StopRecording", - "category": "recording", - "since": "4.1.0", - "names": [ - { - "name": "", - "description": "StopRecording" - } - ], - "categories": [ - { - "name": "", - "description": "recording" - } - ], - "sinces": [ - { - "name": "", - "description": "4.1.0" - } - ], - "heading": { - "level": 2, - "text": "StopRecording" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Pause the current recording.\nReturns an error if recording is not active or already paused.", - "api": "requests", - "name": "PauseRecording", - "category": "recording", - "since": "4.7.0", - "names": [ - { - "name": "", - "description": "PauseRecording" - } - ], - "categories": [ - { - "name": "", - "description": "recording" - } - ], - "sinces": [ - { - "name": "", - "description": "4.7.0" - } - ], - "heading": { - "level": 2, - "text": "PauseRecording" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Resume/unpause the current recording (if paused).\nReturns an error if recording is not active or not paused.", - "api": "requests", - "name": "ResumeRecording", - "category": "recording", - "since": "4.7.0", - "names": [ - { - "name": "", - "description": "ResumeRecording" - } - ], - "categories": [ - { - "name": "", - "description": "recording" - } - ], - "sinces": [ - { - "name": "", - "description": "4.7.0" - } - ], - "heading": { - "level": 2, - "text": "ResumeRecording" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "\n\nPlease note: if `SetRecordingFolder` is called while a recording is\nin progress, the change won't be applied immediately and will be\neffective on the next recording.", - "param": "{String} `rec-folder` Path of the recording folder.", - "api": "requests", - "name": "SetRecordingFolder", - "category": "recording", - "since": "4.1.0", - "params": [ - { - "type": "String", - "name": "rec-folder", - "description": "Path of the recording folder." - } - ], - "names": [ - { - "name": "", - "description": "SetRecordingFolder" - } - ], - "categories": [ - { - "name": "", - "description": "recording" - } - ], - "sinces": [ - { - "name": "", - "description": "4.1.0" - } - ], - "heading": { - "level": 2, - "text": "SetRecordingFolder" - }, - "lead": "In the current profile, sets the recording folder of the Simple and Advanced output modes to the specified value.", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Get the path of the current recording folder.", - "return": "{String} `rec-folder` Path of the recording folder.", - "api": "requests", - "name": "GetRecordingFolder", - "category": "recording", - "since": "4.1.0", - "returns": [ - { - "type": "String", - "name": "rec-folder", - "description": "Path of the recording folder." - } - ], - "names": [ - { - "name": "", - "description": "GetRecordingFolder" - } - ], - "categories": [ - { - "name": "", - "description": "recording" - } - ], - "sinces": [ - { - "name": "", - "description": "4.1.0" - } - ], - "heading": { - "level": 2, - "text": "GetRecordingFolder" - }, - "lead": "", - "type": "class", - "examples": [] - } - ], - "replay buffer": [ - { - "subheads": [], - "description": "Toggle the Replay Buffer on/off.", - "api": "requests", - "name": "StartStopReplayBuffer", - "category": "replay buffer", - "since": "4.2.0", - "names": [ - { - "name": "", - "description": "StartStopReplayBuffer" - } - ], - "categories": [ - { - "name": "", - "description": "replay buffer" - } - ], - "sinces": [ - { - "name": "", - "description": "4.2.0" - } - ], - "heading": { - "level": 2, - "text": "StartStopReplayBuffer" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Start recording into the Replay Buffer.\nWill return an `error` if the Replay Buffer is already active or if the\n\"Save Replay Buffer\" hotkey is not set in OBS' settings.\nSetting this hotkey is mandatory, even when triggering saves only\nthrough obs-websocket.", - "api": "requests", - "name": "StartReplayBuffer", - "category": "replay buffer", - "since": "4.2.0", - "names": [ - { - "name": "", - "description": "StartReplayBuffer" - } - ], - "categories": [ - { - "name": "", - "description": "replay buffer" - } - ], - "sinces": [ - { - "name": "", - "description": "4.2.0" - } - ], - "heading": { - "level": 2, - "text": "StartReplayBuffer" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Stop recording into the Replay Buffer.\nWill return an `error` if the Replay Buffer is not active.", - "api": "requests", - "name": "StopReplayBuffer", - "category": "replay buffer", - "since": "4.2.0", - "names": [ - { - "name": "", - "description": "StopReplayBuffer" - } - ], - "categories": [ - { - "name": "", - "description": "replay buffer" - } - ], - "sinces": [ - { - "name": "", - "description": "4.2.0" - } - ], - "heading": { - "level": 2, - "text": "StopReplayBuffer" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Flush and save the contents of the Replay Buffer to disk. This is\nbasically the same as triggering the \"Save Replay Buffer\" hotkey.\nWill return an `error` if the Replay Buffer is not active.", - "api": "requests", - "name": "SaveReplayBuffer", - "category": "replay buffer", - "since": "4.2.0", - "names": [ - { - "name": "", - "description": "SaveReplayBuffer" - } - ], - "categories": [ - { - "name": "", - "description": "replay buffer" - } - ], - "sinces": [ - { - "name": "", - "description": "4.2.0" - } - ], - "heading": { - "level": 2, - "text": "SaveReplayBuffer" - }, - "lead": "", - "type": "class", - "examples": [] - } - ], - "scene collections": [ - { - "subheads": [], - "description": "Change the active scene collection.", - "param": "{String} `sc-name` Name of the desired scene collection.", - "api": "requests", - "name": "SetCurrentSceneCollection", - "category": "scene collections", - "since": "4.0.0", - "params": [ - { - "type": "String", - "name": "sc-name", - "description": "Name of the desired scene collection." - } - ], - "names": [ - { - "name": "", - "description": "SetCurrentSceneCollection" - } - ], - "categories": [ - { - "name": "", - "description": "scene collections" - } - ], - "sinces": [ - { - "name": "", - "description": "4.0.0" - } - ], - "heading": { - "level": 2, - "text": "SetCurrentSceneCollection" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Get the name of the current scene collection.", - "return": "{String} `sc-name` Name of the currently active scene collection.", - "api": "requests", - "name": "GetCurrentSceneCollection", - "category": "scene collections", - "since": "4.0.0", - "returns": [ - { - "type": "String", - "name": "sc-name", - "description": "Name of the currently active scene collection." - } - ], - "names": [ - { - "name": "", - "description": "GetCurrentSceneCollection" - } - ], - "categories": [ - { - "name": "", - "description": "scene collections" - } - ], - "sinces": [ - { - "name": "", - "description": "4.0.0" - } - ], - "heading": { - "level": 2, - "text": "GetCurrentSceneCollection" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "List available scene collections", - "return": "{Array} `scene-collections` Scene collections list", - "api": "requests", - "name": "ListSceneCollections", - "category": "scene collections", - "since": "4.0.0", - "returns": [ - { - "type": "Array", - "name": "scene-collections", - "description": "Scene collections list" - } - ], - "names": [ - { - "name": "", - "description": "ListSceneCollections" - } - ], - "categories": [ - { - "name": "", - "description": "scene collections" - } - ], - "sinces": [ - { - "name": "", - "description": "4.0.0" - } - ], - "heading": { - "level": 2, - "text": "ListSceneCollections" - }, - "lead": "", - "type": "class", - "examples": [] - } - ], - "scene items": [ - { - "subheads": [], - "description": "Gets the scene specific properties of the specified source item.\nCoordinates are relative to the item's parent (the scene or group it belongs to).", - "param": [ - "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", - "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).", - "{String (optional)} `item.name` Scene Item name (if the `item` field is an object)", - "{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)" - ], - "return": [ - "{String} `name` Scene Item name.", - "{int} `itemId` Scene Item ID.", - "{int} `position.x` The x position of the source from the left.", - "{int} `position.y` The y position of the source from the top.", - "{int} `position.alignment` The point on the source that the item is manipulated from.", - "{double} `rotation` The clockwise rotation of the item in degrees around the point of alignment.", - "{double} `scale.x` The x-scale factor of the source.", - "{double} `scale.y` The y-scale factor of the source.", - "{int} `crop.top` The number of pixels cropped off the top of the source before scaling.", - "{int} `crop.right` The number of pixels cropped off the right of the source before scaling.", - "{int} `crop.bottom` The number of pixels cropped off the bottom of the source before scaling.", - "{int} `crop.left` The number of pixels cropped off the left of the source before scaling.", - "{bool} `visible` If the source is visible.", - "{bool} `muted` If the source is muted.", - "{bool} `locked` If the source's transform is locked.", - "{String} `bounds.type` Type of bounding box. Can be \"OBS_BOUNDS_STRETCH\", \"OBS_BOUNDS_SCALE_INNER\", \"OBS_BOUNDS_SCALE_OUTER\", \"OBS_BOUNDS_SCALE_TO_WIDTH\", \"OBS_BOUNDS_SCALE_TO_HEIGHT\", \"OBS_BOUNDS_MAX_ONLY\" or \"OBS_BOUNDS_NONE\".", - "{int} `bounds.alignment` Alignment of the bounding box.", - "{double} `bounds.x` Width of the bounding box.", - "{double} `bounds.y` Height of the bounding box.", - "{int} `sourceWidth` Base width (without scaling) of the source", - "{int} `sourceHeight` Base source (without scaling) of the source", - "{double} `width` Scene item width (base source width multiplied by the horizontal scaling factor)", - "{double} `height` Scene item height (base source height multiplied by the vertical scaling factor)", - "{int} `alignment` The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis.", - "{String (optional)} `parentGroupName` Name of the item's parent (if this item belongs to a group)", - "{Array (optional)} `groupChildren` List of children (if this item is a group)" - ], - "api": "requests", - "name": "GetSceneItemProperties", - "category": "scene items", - "since": "4.3.0", - "returns": [ - { - "type": "String", - "name": "name", - "description": "Scene Item name." - }, - { - "type": "int", - "name": "itemId", - "description": "Scene Item ID." - }, - { - "type": "int", - "name": "position.x", - "description": "The x position of the source from the left." - }, - { - "type": "int", - "name": "position.y", - "description": "The y position of the source from the top." - }, - { - "type": "int", - "name": "position.alignment", - "description": "The point on the source that the item is manipulated from." - }, - { - "type": "double", - "name": "rotation", - "description": "The clockwise rotation of the item in degrees around the point of alignment." - }, - { - "type": "double", - "name": "scale.x", - "description": "The x-scale factor of the source." - }, - { - "type": "double", - "name": "scale.y", - "description": "The y-scale factor of the source." - }, - { - "type": "int", - "name": "crop.top", - "description": "The number of pixels cropped off the top of the source before scaling." - }, - { - "type": "int", - "name": "crop.right", - "description": "The number of pixels cropped off the right of the source before scaling." - }, - { - "type": "int", - "name": "crop.bottom", - "description": "The number of pixels cropped off the bottom of the source before scaling." - }, - { - "type": "int", - "name": "crop.left", - "description": "The number of pixels cropped off the left of the source before scaling." - }, - { - "type": "bool", - "name": "visible", - "description": "If the source is visible." - }, - { - "type": "bool", - "name": "muted", - "description": "If the source is muted." - }, - { - "type": "bool", - "name": "locked", - "description": "If the source's transform is locked." - }, - { - "type": "String", - "name": "bounds.type", - "description": "Type of bounding box. Can be \"OBS_BOUNDS_STRETCH\", \"OBS_BOUNDS_SCALE_INNER\", \"OBS_BOUNDS_SCALE_OUTER\", \"OBS_BOUNDS_SCALE_TO_WIDTH\", \"OBS_BOUNDS_SCALE_TO_HEIGHT\", \"OBS_BOUNDS_MAX_ONLY\" or \"OBS_BOUNDS_NONE\"." - }, - { - "type": "int", - "name": "bounds.alignment", - "description": "Alignment of the bounding box." - }, - { - "type": "double", - "name": "bounds.x", - "description": "Width of the bounding box." - }, - { - "type": "double", - "name": "bounds.y", - "description": "Height of the bounding box." - }, - { - "type": "int", - "name": "sourceWidth", - "description": "Base width (without scaling) of the source" - }, - { - "type": "int", - "name": "sourceHeight", - "description": "Base source (without scaling) of the source" - }, - { - "type": "double", - "name": "width", - "description": "Scene item width (base source width multiplied by the horizontal scaling factor)" - }, - { - "type": "double", - "name": "height", - "description": "Scene item height (base source height multiplied by the vertical scaling factor)" - }, - { - "type": "int", - "name": "alignment", - "description": "The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis." - }, - { - "type": "String (optional)", - "name": "parentGroupName", - "description": "Name of the item's parent (if this item belongs to a group)" - }, - { - "type": "Array (optional)", - "name": "groupChildren", - "description": "List of children (if this item is a group)" - } - ], - "params": [ - { - "type": "String (optional)", - "name": "scene-name", - "description": "Name of the scene the scene item belongs to. Defaults to the current scene." - }, - { - "type": "String | Object", - "name": "item", - "description": "Scene Item name (if this field is a string) or specification (if it is an object)." - }, - { - "type": "String (optional)", - "name": "item.name", - "description": "Scene Item name (if the `item` field is an object)" - }, - { - "type": "int (optional)", - "name": "item.id", - "description": "Scene Item ID (if the `item` field is an object)" - } - ], - "names": [ - { - "name": "", - "description": "GetSceneItemProperties" - } - ], - "categories": [ - { - "name": "", - "description": "scene items" - } - ], - "sinces": [ - { - "name": "", - "description": "4.3.0" - } - ], - "heading": { - "level": 2, - "text": "GetSceneItemProperties" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Sets the scene specific properties of a source. Unspecified properties will remain unchanged.\nCoordinates are relative to the item's parent (the scene or group it belongs to).", - "param": [ - "{String (optional)} `scene-name` Name of the scene the source item belongs to. Defaults to the current scene.", - "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).", - "{String (optional)} `item.name` Scene Item name (if the `item` field is an object)", - "{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)", - "{int (optional)} `position.x` The new x position of the source.", - "{int (optional)} `position.y` The new y position of the source.", - "{int (optional)} `position.alignment` The new alignment of the source.", - "{double (optional)} `rotation` The new clockwise rotation of the item in degrees.", - "{double (optional)} `scale.x` The new x scale of the item.", - "{double (optional)} `scale.y` The new y scale of the item.", - "{int (optional)} `crop.top` The new amount of pixels cropped off the top of the source before scaling.", - "{int (optional)} `crop.bottom` The new amount of pixels cropped off the bottom of the source before scaling.", - "{int (optional)} `crop.left` The new amount of pixels cropped off the left of the source before scaling.", - "{int (optional)} `crop.right` The new amount of pixels cropped off the right of the source before scaling.", - "{bool (optional)} `visible` The new visibility of the source. 'true' shows source, 'false' hides source.", - "{bool (optional)} `locked` The new locked status of the source. 'true' keeps it in its current position, 'false' allows movement.", - "{String (optional)} `bounds.type` The new bounds type of the source. Can be \"OBS_BOUNDS_STRETCH\", \"OBS_BOUNDS_SCALE_INNER\", \"OBS_BOUNDS_SCALE_OUTER\", \"OBS_BOUNDS_SCALE_TO_WIDTH\", \"OBS_BOUNDS_SCALE_TO_HEIGHT\", \"OBS_BOUNDS_MAX_ONLY\" or \"OBS_BOUNDS_NONE\".", - "{int (optional)} `bounds.alignment` The new alignment of the bounding box. (0-2, 4-6, 8-10)", - "{double (optional)} `bounds.x` The new width of the bounding box.", - "{double (optional)} `bounds.y` The new height of the bounding box." - ], - "api": "requests", - "name": "SetSceneItemProperties", - "category": "scene items", - "since": "4.3.0", - "params": [ - { - "type": "String (optional)", - "name": "scene-name", - "description": "Name of the scene the source item belongs to. Defaults to the current scene." - }, - { - "type": "String | Object", - "name": "item", - "description": "Scene Item name (if this field is a string) or specification (if it is an object)." - }, - { - "type": "String (optional)", - "name": "item.name", - "description": "Scene Item name (if the `item` field is an object)" - }, - { - "type": "int (optional)", - "name": "item.id", - "description": "Scene Item ID (if the `item` field is an object)" - }, - { - "type": "int (optional)", - "name": "position.x", - "description": "The new x position of the source." - }, - { - "type": "int (optional)", - "name": "position.y", - "description": "The new y position of the source." - }, - { - "type": "int (optional)", - "name": "position.alignment", - "description": "The new alignment of the source." - }, - { - "type": "double (optional)", - "name": "rotation", - "description": "The new clockwise rotation of the item in degrees." - }, - { - "type": "double (optional)", - "name": "scale.x", - "description": "The new x scale of the item." - }, - { - "type": "double (optional)", - "name": "scale.y", - "description": "The new y scale of the item." - }, - { - "type": "int (optional)", - "name": "crop.top", - "description": "The new amount of pixels cropped off the top of the source before scaling." - }, - { - "type": "int (optional)", - "name": "crop.bottom", - "description": "The new amount of pixels cropped off the bottom of the source before scaling." - }, - { - "type": "int (optional)", - "name": "crop.left", - "description": "The new amount of pixels cropped off the left of the source before scaling." - }, - { - "type": "int (optional)", - "name": "crop.right", - "description": "The new amount of pixels cropped off the right of the source before scaling." - }, - { - "type": "bool (optional)", - "name": "visible", - "description": "The new visibility of the source. 'true' shows source, 'false' hides source." - }, - { - "type": "bool (optional)", - "name": "locked", - "description": "The new locked status of the source. 'true' keeps it in its current position, 'false' allows movement." - }, - { - "type": "String (optional)", - "name": "bounds.type", - "description": "The new bounds type of the source. Can be \"OBS_BOUNDS_STRETCH\", \"OBS_BOUNDS_SCALE_INNER\", \"OBS_BOUNDS_SCALE_OUTER\", \"OBS_BOUNDS_SCALE_TO_WIDTH\", \"OBS_BOUNDS_SCALE_TO_HEIGHT\", \"OBS_BOUNDS_MAX_ONLY\" or \"OBS_BOUNDS_NONE\"." - }, - { - "type": "int (optional)", - "name": "bounds.alignment", - "description": "The new alignment of the bounding box. (0-2, 4-6, 8-10)" - }, - { - "type": "double (optional)", - "name": "bounds.x", - "description": "The new width of the bounding box." - }, - { - "type": "double (optional)", - "name": "bounds.y", - "description": "The new height of the bounding box." - } - ], - "names": [ - { - "name": "", - "description": "SetSceneItemProperties" - } - ], - "categories": [ - { - "name": "", - "description": "scene items" - } - ], - "sinces": [ - { - "name": "", - "description": "4.3.0" - } - ], - "heading": { - "level": 2, - "text": "SetSceneItemProperties" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Reset a scene item.", - "param": [ - "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", - "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).", - "{String (optional)} `item.name` Scene Item name (if the `item` field is an object)", - "{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)" - ], - "api": "requests", - "name": "ResetSceneItem", - "category": "scene items", - "since": "4.2.0", - "params": [ - { - "type": "String (optional)", - "name": "scene-name", - "description": "Name of the scene the scene item belongs to. Defaults to the current scene." - }, - { - "type": "String | Object", - "name": "item", - "description": "Scene Item name (if this field is a string) or specification (if it is an object)." - }, - { - "type": "String (optional)", - "name": "item.name", - "description": "Scene Item name (if the `item` field is an object)" - }, - { - "type": "int (optional)", - "name": "item.id", - "description": "Scene Item ID (if the `item` field is an object)" - } - ], - "names": [ - { - "name": "", - "description": "ResetSceneItem" - } - ], - "categories": [ - { - "name": "", - "description": "scene items" - } - ], - "sinces": [ - { - "name": "", - "description": "4.2.0" - } - ], - "heading": { - "level": 2, - "text": "ResetSceneItem" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Show or hide a specified source item in a specified scene.", - "param": [ - "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the currently active scene.", - "{String} `source` Scene Item name.", - "{boolean} `render` true = shown ; false = hidden" - ], - "api": "requests", - "name": "SetSceneItemRender", - "category": "scene items", - "since": "0.3", - "deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.", - "params": [ - { - "type": "String (optional)", - "name": "scene-name", - "description": "Name of the scene the scene item belongs to. Defaults to the currently active scene." - }, - { - "type": "String", - "name": "source", - "description": "Scene Item name." + "name": "sourceName", + "description": "Source name." }, { "type": "boolean", - "name": "render", - "description": "true = shown ; false = hidden" + "name": "playPause", + "description": "Whether to pause or play the source. `false` for play, `true` for pause." } ], "names": [ { "name": "", - "description": "SetSceneItemRender" + "description": "PlayPauseMedia" } ], "categories": [ { "name": "", - "description": "scene items" - } - ], - "sinces": [ - { - "name": "", - "description": "0.3" - } - ], - "deprecateds": [ - { - "name": "", - "description": "Since 4.3.0. Prefer the use of SetSceneItemProperties." - } - ], - "heading": { - "level": 2, - "text": "SetSceneItemRender" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Sets the coordinates of a specified source item.", - "param": [ - "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", - "{String} `item` Scene Item name.", - "{double} `x` X coordinate.", - "{double} `y` Y coordinate." - ], - "api": "requests", - "name": "SetSceneItemPosition", - "category": "scene items", - "since": "4.0.0", - "deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.", - "params": [ - { - "type": "String (optional)", - "name": "scene-name", - "description": "Name of the scene the scene item belongs to. Defaults to the current scene." - }, - { - "type": "String", - "name": "item", - "description": "Scene Item name." - }, - { - "type": "double", - "name": "x", - "description": "X coordinate." - }, - { - "type": "double", - "name": "y", - "description": "Y coordinate." - } - ], - "names": [ - { - "name": "", - "description": "SetSceneItemPosition" - } - ], - "categories": [ - { - "name": "", - "description": "scene items" - } - ], - "sinces": [ - { - "name": "", - "description": "4.0.0" - } - ], - "deprecateds": [ - { - "name": "", - "description": "Since 4.3.0. Prefer the use of SetSceneItemProperties." - } - ], - "heading": { - "level": 2, - "text": "SetSceneItemPosition" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Set the transform of the specified source item.", - "param": [ - "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", - "{String} `item` Scene Item name.", - "{double} `x-scale` Width scale factor.", - "{double} `y-scale` Height scale factor.", - "{double} `rotation` Source item rotation (in degrees)." - ], - "api": "requests", - "name": "SetSceneItemTransform", - "category": "scene items", - "since": "4.0.0", - "deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.", - "params": [ - { - "type": "String (optional)", - "name": "scene-name", - "description": "Name of the scene the scene item belongs to. Defaults to the current scene." - }, - { - "type": "String", - "name": "item", - "description": "Scene Item name." - }, - { - "type": "double", - "name": "x-scale", - "description": "Width scale factor." - }, - { - "type": "double", - "name": "y-scale", - "description": "Height scale factor." - }, - { - "type": "double", - "name": "rotation", - "description": "Source item rotation (in degrees)." - } - ], - "names": [ - { - "name": "", - "description": "SetSceneItemTransform" - } - ], - "categories": [ - { - "name": "", - "description": "scene items" - } - ], - "sinces": [ - { - "name": "", - "description": "4.0.0" - } - ], - "deprecateds": [ - { - "name": "", - "description": "Since 4.3.0. Prefer the use of SetSceneItemProperties." - } - ], - "heading": { - "level": 2, - "text": "SetSceneItemTransform" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Sets the crop coordinates of the specified source item.", - "param": [ - "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", - "{String} `item` Scene Item name.", - "{int} `top` Pixel position of the top of the source item.", - "{int} `bottom` Pixel position of the bottom of the source item.", - "{int} `left` Pixel position of the left of the source item.", - "{int} `right` Pixel position of the right of the source item." - ], - "api": "requests", - "name": "SetSceneItemCrop", - "category": "scene items", - "since": "4.1.0", - "deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.", - "params": [ - { - "type": "String (optional)", - "name": "scene-name", - "description": "Name of the scene the scene item belongs to. Defaults to the current scene." - }, - { - "type": "String", - "name": "item", - "description": "Scene Item name." - }, - { - "type": "int", - "name": "top", - "description": "Pixel position of the top of the source item." - }, - { - "type": "int", - "name": "bottom", - "description": "Pixel position of the bottom of the source item." - }, - { - "type": "int", - "name": "left", - "description": "Pixel position of the left of the source item." - }, - { - "type": "int", - "name": "right", - "description": "Pixel position of the right of the source item." - } - ], - "names": [ - { - "name": "", - "description": "SetSceneItemCrop" - } - ], - "categories": [ - { - "name": "", - "description": "scene items" - } - ], - "sinces": [ - { - "name": "", - "description": "4.1.0" - } - ], - "deprecateds": [ - { - "name": "", - "description": "Since 4.3.0. Prefer the use of SetSceneItemProperties." - } - ], - "heading": { - "level": 2, - "text": "SetSceneItemCrop" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Deletes a scene item.", - "param": [ - "{String (optional)} `scene` Name of the scene the scene item belongs to. Defaults to the current scene.", - "{Object} `item` Scene item to delete (required)", - "{String} `item.name` Scene Item name (prefer `id`, including both is acceptable).", - "{int} `item.id` Scene Item ID." - ], - "api": "requests", - "name": "DeleteSceneItem", - "category": "scene items", - "since": "4.5.0", - "params": [ - { - "type": "String (optional)", - "name": "scene", - "description": "Name of the scene the scene item belongs to. Defaults to the current scene." - }, - { - "type": "Object", - "name": "item", - "description": "Scene item to delete (required)" - }, - { - "type": "String", - "name": "item.name", - "description": "Scene Item name (prefer `id`, including both is acceptable)." - }, - { - "type": "int", - "name": "item.id", - "description": "Scene Item ID." - } - ], - "names": [ - { - "name": "", - "description": "DeleteSceneItem" - } - ], - "categories": [ - { - "name": "", - "description": "scene items" - } - ], - "sinces": [ - { - "name": "", - "description": "4.5.0" - } - ], - "heading": { - "level": 2, - "text": "DeleteSceneItem" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Duplicates a scene item.", - "param": [ - "{String (optional)} `fromScene` Name of the scene to copy the item from. Defaults to the current scene.", - "{String (optional)} `toScene` Name of the scene to create the item in. Defaults to the current scene.", - "{Object} `item` Scene Item to duplicate from the source scene (required)", - "{String} `item.name` Scene Item name (prefer `id`, including both is acceptable).", - "{int} `item.id` Scene Item ID." - ], - "return": [ - "{String} `scene` Name of the scene where the new item was created", - "{Object} `item` New item info", - "{int} `item.id` New item ID", - "{String} `item.name` New item name" - ], - "api": "requests", - "name": "DuplicateSceneItem", - "category": "scene items", - "since": "4.5.0", - "returns": [ - { - "type": "String", - "name": "scene", - "description": "Name of the scene where the new item was created" - }, - { - "type": "Object", - "name": "item", - "description": "New item info" - }, - { - "type": "int", - "name": "item.id", - "description": "New item ID" - }, - { - "type": "String", - "name": "item.name", - "description": "New item name" - } - ], - "params": [ - { - "type": "String (optional)", - "name": "fromScene", - "description": "Name of the scene to copy the item from. Defaults to the current scene." - }, - { - "type": "String (optional)", - "name": "toScene", - "description": "Name of the scene to create the item in. Defaults to the current scene." - }, - { - "type": "Object", - "name": "item", - "description": "Scene Item to duplicate from the source scene (required)" - }, - { - "type": "String", - "name": "item.name", - "description": "Scene Item name (prefer `id`, including both is acceptable)." - }, - { - "type": "int", - "name": "item.id", - "description": "Scene Item ID." - } - ], - "names": [ - { - "name": "", - "description": "DuplicateSceneItem" - } - ], - "categories": [ - { - "name": "", - "description": "scene items" - } - ], - "sinces": [ - { - "name": "", - "description": "4.5.0" - } - ], - "heading": { - "level": 2, - "text": "DuplicateSceneItem" - }, - "lead": "", - "type": "class", - "examples": [] - } - ], - "scenes": [ - { - "subheads": [], - "description": "Switch to the specified scene.", - "param": "{String} `scene-name` Name of the scene to switch to.", - "api": "requests", - "name": "SetCurrentScene", - "category": "scenes", - "since": "0.3", - "params": [ - { - "type": "String", - "name": "scene-name", - "description": "Name of the scene to switch to." - } - ], - "names": [ - { - "name": "", - "description": "SetCurrentScene" - } - ], - "categories": [ - { - "name": "", - "description": "scenes" - } - ], - "sinces": [ - { - "name": "", - "description": "0.3" - } - ], - "heading": { - "level": 2, - "text": "SetCurrentScene" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Get the current scene's name and source items.", - "return": [ - "{String} `name` Name of the currently active scene.", - "{Array} `sources` Ordered list of the current scene's source items." - ], - "api": "requests", - "name": "GetCurrentScene", - "category": "scenes", - "since": "0.3", - "returns": [ - { - "type": "String", - "name": "name", - "description": "Name of the currently active scene." - }, - { - "type": "Array", - "name": "sources", - "description": "Ordered list of the current scene's source items." - } - ], - "names": [ - { - "name": "", - "description": "GetCurrentScene" - } - ], - "categories": [ - { - "name": "", - "description": "scenes" - } - ], - "sinces": [ - { - "name": "", - "description": "0.3" - } - ], - "heading": { - "level": 2, - "text": "GetCurrentScene" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Get a list of scenes in the currently active profile.", - "return": [ - "{String} `current-scene` Name of the currently active scene.", - "{Array} `scenes` Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information)." - ], - "api": "requests", - "name": "GetSceneList", - "category": "scenes", - "since": "0.3", - "returns": [ - { - "type": "String", - "name": "current-scene", - "description": "Name of the currently active scene." - }, - { - "type": "Array", - "name": "scenes", - "description": "Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information)." - } - ], - "names": [ - { - "name": "", - "description": "GetSceneList" - } - ], - "categories": [ - { - "name": "", - "description": "scenes" - } - ], - "sinces": [ - { - "name": "", - "description": "0.3" - } - ], - "heading": { - "level": 2, - "text": "GetSceneList" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Changes the order of scene items in the requested scene.", - "param": [ - "{String (optional)} `scene` Name of the scene to reorder (defaults to current).", - "{Array} `items` Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene", - "{int (optional)} `items[].id` Id of a specific scene item. Unique on a scene by scene basis.", - "{String (optional)} `items[].name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene." - ], - "api": "requests", - "name": "ReorderSceneItems", - "category": "scenes", - "since": "4.5.0", - "params": [ - { - "type": "String (optional)", - "name": "scene", - "description": "Name of the scene to reorder (defaults to current)." - }, - { - "type": "Array", - "name": "items", - "description": "Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene" - }, - { - "type": "int (optional)", - "name": "items[].id", - "description": "Id of a specific scene item. Unique on a scene by scene basis." - }, - { - "type": "String (optional)", - "name": "items[].name", - "description": "Name of a scene item. Sufficiently unique if no scene items share sources within the scene." - } - ], - "names": [ - { - "name": "", - "description": "ReorderSceneItems" - } - ], - "categories": [ - { - "name": "", - "description": "scenes" - } - ], - "sinces": [ - { - "name": "", - "description": "4.5.0" - } - ], - "heading": { - "level": 2, - "text": "ReorderSceneItems" - }, - "lead": "", - "type": "class", - "examples": [] - }, - { - "subheads": [], - "description": "Set a scene to use a specific transition override.", - "param": [ - "{String} `sceneName` Name of the scene to switch to.", - "{String} `transitionName` Name of the transition to use.", - "{int (Optional)} `transitionDuration` Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given." - ], - "api": "requests", - "name": "SetSceneTransitionOverride", - "category": "scenes", - "since": "4.9.0", - "params": [ - { - "type": "String", - "name": "sceneName", - "description": "Name of the scene to switch to." - }, - { - "type": "String", - "name": "transitionName", - "description": "Name of the transition to use." - }, - { - "type": "int (Optional)", - "name": "transitionDuration", - "description": "Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given." - } - ], - "names": [ - { - "name": "", - "description": "SetSceneTransitionOverride" - } - ], - "categories": [ - { - "name": "", - "description": "scenes" + "description": "media control" } ], "sinces": [ @@ -5517,7 +4091,7 @@ ], "heading": { "level": 2, - "text": "SetSceneTransitionOverride" + "text": "PlayPauseMedia" }, "lead": "", "type": "class", @@ -5525,29 +4099,29 @@ }, { "subheads": [], - "description": "Remove any transition override on a scene.", - "param": "{String} `sceneName` Name of the scene to switch to.", + "description": "Restart a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)", + "param": "{String} `sourceName` Source name.", "api": "requests", - "name": "RemoveSceneTransitionOverride", - "category": "scenes", + "name": "RestartMedia", + "category": "media control", "since": "4.9.0", "params": [ { "type": "String", - "name": "sceneName", - "description": "Name of the scene to switch to." + "name": "sourceName", + "description": "Source name." } ], "names": [ { "name": "", - "description": "RemoveSceneTransitionOverride" + "description": "RestartMedia" } ], "categories": [ { "name": "", - "description": "scenes" + "description": "media control" } ], "sinces": [ @@ -5558,7 +4132,7 @@ ], "heading": { "level": 2, - "text": "RemoveSceneTransitionOverride" + "text": "RestartMedia" }, "lead": "", "type": "class", @@ -5566,45 +4140,356 @@ }, { "subheads": [], - "description": "Get the current scene transition override.", - "param": "{String} `sceneName` Name of the scene to switch to.", - "return": [ - "{String} `transitionName` Name of the current overriding transition. Empty string if no override is set.", - "{int} `transitionDuration` Transition duration. `-1` if no override is set." + "description": "Stop a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)", + "param": "{String} `sourceName` Source name.", + "api": "requests", + "name": "StopMedia", + "category": "media control", + "since": "4.9.0", + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + } + ], + "names": [ + { + "name": "", + "description": "StopMedia" + } + ], + "categories": [ + { + "name": "", + "description": "media control" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "StopMedia" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Skip to the next media item in the playlist. Supports only vlc media source (as of OBS v25.0.8)", + "param": "{String} `sourceName` Source name.", + "api": "requests", + "name": "NextMedia", + "category": "media control", + "since": "4.9.0", + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + } + ], + "names": [ + { + "name": "", + "description": "NextMedia" + } + ], + "categories": [ + { + "name": "", + "description": "media control" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "NextMedia" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Go to the previous media item in the playlist. Supports only vlc media source (as of OBS v25.0.8)", + "param": "{String} `sourceName` Source name.", + "api": "requests", + "name": "PreviousMedia", + "category": "media control", + "since": "4.9.0", + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + } + ], + "names": [ + { + "name": "", + "description": "PreviousMedia" + } + ], + "categories": [ + { + "name": "", + "description": "media control" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "PreviousMedia" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get the length of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)\nNote: For some reason, for the first 5 or so seconds that the media is playing, the total duration can be off by upwards of 50ms.", + "param": "{String} `sourceName` Source name.", + "return": "{int} `mediaDuration` The total length of media in milliseconds..", + "api": "requests", + "name": "GetMediaDuration", + "category": "media control", + "since": "4.9.0", + "returns": [ + { + "type": "int", + "name": "mediaDuration", + "description": "The total length of media in milliseconds.." + } + ], + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + } + ], + "names": [ + { + "name": "", + "description": "GetMediaDuration" + } + ], + "categories": [ + { + "name": "", + "description": "media control" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "GetMediaDuration" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get the current timestamp of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)", + "param": "{String} `sourceName` Source name.", + "return": "{int} `timestamp` The time in milliseconds since the start of the media.", + "api": "requests", + "name": "GetMediaTime", + "category": "media control", + "since": "4.9.0", + "returns": [ + { + "type": "int", + "name": "timestamp", + "description": "The time in milliseconds since the start of the media." + } + ], + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + } + ], + "names": [ + { + "name": "", + "description": "GetMediaTime" + } + ], + "categories": [ + { + "name": "", + "description": "media control" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "GetMediaTime" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Set the timestamp of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)", + "param": [ + "{String} `sourceName` Source name.", + "{int} `timestamp` Milliseconds to set the timestamp to." ], "api": "requests", - "name": "GetSceneTransitionOverride", - "category": "scenes", + "name": "SetMediaTime", + "category": "media control", + "since": "4.9.0", + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + }, + { + "type": "int", + "name": "timestamp", + "description": "Milliseconds to set the timestamp to." + } + ], + "names": [ + { + "name": "", + "description": "SetMediaTime" + } + ], + "categories": [ + { + "name": "", + "description": "media control" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "SetMediaTime" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Scrub media using a supplied offset. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)\nNote: Due to processing/network delays, this request is not perfect. The processing rate of this request has also not been tested.", + "param": [ + "{String} `sourceName` Source name.", + "{int} `timeOffset` Millisecond offset (positive or negative) to offset the current media position." + ], + "api": "requests", + "name": "ScrubMedia", + "category": "media control", + "since": "4.9.0", + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + }, + { + "type": "int", + "name": "timeOffset", + "description": "Millisecond offset (positive or negative) to offset the current media position." + } + ], + "names": [ + { + "name": "", + "description": "ScrubMedia" + } + ], + "categories": [ + { + "name": "", + "description": "media control" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "ScrubMedia" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get the current playing state of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)", + "param": "{String} `sourceName` Source name.", + "return": "{String} `mediaState` The media state of the provided source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown`", + "api": "requests", + "name": "GetMediaState", + "category": "media control", "since": "4.9.0", "returns": [ { "type": "String", - "name": "transitionName", - "description": "Name of the current overriding transition. Empty string if no override is set." - }, - { - "type": "int", - "name": "transitionDuration", - "description": "Transition duration. `-1` if no override is set." + "name": "mediaState", + "description": "The media state of the provided source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown`" } ], "params": [ { "type": "String", - "name": "sceneName", - "description": "Name of the scene to switch to." + "name": "sourceName", + "description": "Source name." } ], "names": [ { "name": "", - "description": "GetSceneTransitionOverride" + "description": "GetMediaState" } ], "categories": [ { "name": "", - "description": "scenes" + "description": "media control" } ], "sinces": [ @@ -5615,7 +4500,7 @@ ], "heading": { "level": 2, - "text": "GetSceneTransitionOverride" + "text": "GetMediaState" }, "lead": "", "type": "class", @@ -5623,6 +4508,67 @@ } ], "sources": [ + { + "subheads": [], + "description": "List the media state of all media sources (vlc and media source)", + "return": [ + "{Array} `mediaSources` Array of sources", + "{String} `mediaSources.*.sourceName` Unique source name", + "{String} `mediaSources.*.sourceKind` Unique source internal type (a.k.a `ffmpeg_source` or `vlc_source`)", + "{String} `mediaSources.*.mediaState` The current state of media for that source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown`" + ], + "api": "requests", + "name": "GetMediaSourcesList", + "category": "sources", + "since": "4.9.0", + "returns": [ + { + "type": "Array", + "name": "mediaSources", + "description": "Array of sources" + }, + { + "type": "String", + "name": "mediaSources.*.sourceName", + "description": "Unique source name" + }, + { + "type": "String", + "name": "mediaSources.*.sourceKind", + "description": "Unique source internal type (a.k.a `ffmpeg_source` or `vlc_source`)" + }, + { + "type": "String", + "name": "mediaSources.*.mediaState", + "description": "The current state of media for that source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown`" + } + ], + "names": [ + { + "name": "", + "description": "GetMediaSourcesList" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "GetMediaSourcesList" + }, + "lead": "", + "type": "class", + "examples": [] + }, { "subheads": [], "description": "List all sources available in the running OBS instance", @@ -6072,6 +5018,55 @@ "type": "class", "examples": [] }, + { + "subheads": [], + "description": "Get the audio's active status of a specified source.", + "param": "{String} `sourceName` Source name.", + "return": "{boolean} `audioActive` Audio active status of the source.", + "api": "requests", + "name": "GetAudioActive", + "category": "sources", + "since": "unreleased", + "returns": [ + { + "type": "boolean", + "name": "audioActive", + "description": "Audio active status of the source." + } + ], + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + } + ], + "names": [ + { + "name": "", + "description": "GetAudioActive" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "GetAudioActive" + }, + "lead": "", + "type": "class", + "examples": [] + }, { "subheads": [], "description": "\n\nNote: If the new name already exists as a source, OBS will automatically modify the name to not interfere.", @@ -8032,6 +7027,2332 @@ "examples": [] } ], + "outputs": [ + { + "subheads": [], + "description": "List existing outputs", + "return": "{Array} `outputs` Outputs list", + "api": "requests", + "name": "ListOutputs", + "category": "outputs", + "since": "4.7.0", + "returns": [ + { + "type": "Array", + "name": "outputs", + "description": "Outputs list" + } + ], + "names": [ + { + "name": "", + "description": "ListOutputs" + } + ], + "categories": [ + { + "name": "", + "description": "outputs" + } + ], + "sinces": [ + { + "name": "", + "description": "4.7.0" + } + ], + "heading": { + "level": 2, + "text": "ListOutputs" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get information about a single output", + "param": "{String} `outputName` Output name", + "return": "{Output} `outputInfo` Output info", + "api": "requests", + "name": "GetOutputInfo", + "category": "outputs", + "since": "4.7.0", + "returns": [ + { + "type": "Output", + "name": "outputInfo", + "description": "Output info" + } + ], + "params": [ + { + "type": "String", + "name": "outputName", + "description": "Output name" + } + ], + "names": [ + { + "name": "", + "description": "GetOutputInfo" + } + ], + "categories": [ + { + "name": "", + "description": "outputs" + } + ], + "sinces": [ + { + "name": "", + "description": "4.7.0" + } + ], + "heading": { + "level": 2, + "text": "GetOutputInfo" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Start an output", + "param": "{String} `outputName` Output name", + "api": "requests", + "name": "StartOutput", + "category": "outputs", + "since": "4.7.0", + "params": [ + { + "type": "String", + "name": "outputName", + "description": "Output name" + } + ], + "names": [ + { + "name": "", + "description": "StartOutput" + } + ], + "categories": [ + { + "name": "", + "description": "outputs" + } + ], + "sinces": [ + { + "name": "", + "description": "4.7.0" + } + ], + "heading": { + "level": 2, + "text": "StartOutput" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Stop an output", + "param": [ + "{String} `outputName` Output name", + "{boolean (optional)} `force` Force stop (default: false)" + ], + "api": "requests", + "name": "StopOutput", + "category": "outputs", + "since": "4.7.0", + "params": [ + { + "type": "String", + "name": "outputName", + "description": "Output name" + }, + { + "type": "boolean (optional)", + "name": "force", + "description": "Force stop (default: false)" + } + ], + "names": [ + { + "name": "", + "description": "StopOutput" + } + ], + "categories": [ + { + "name": "", + "description": "outputs" + } + ], + "sinces": [ + { + "name": "", + "description": "4.7.0" + } + ], + "heading": { + "level": 2, + "text": "StopOutput" + }, + "lead": "", + "type": "class", + "examples": [] + } + ], + "profiles": [ + { + "subheads": [], + "description": "Set the currently active profile.", + "param": "{String} `profile-name` Name of the desired profile.", + "api": "requests", + "name": "SetCurrentProfile", + "category": "profiles", + "since": "4.0.0", + "params": [ + { + "type": "String", + "name": "profile-name", + "description": "Name of the desired profile." + } + ], + "names": [ + { + "name": "", + "description": "SetCurrentProfile" + } + ], + "categories": [ + { + "name": "", + "description": "profiles" + } + ], + "sinces": [ + { + "name": "", + "description": "4.0.0" + } + ], + "heading": { + "level": 2, + "text": "SetCurrentProfile" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get the name of the current profile.", + "return": "{String} `profile-name` Name of the currently active profile.", + "api": "requests", + "name": "GetCurrentProfile", + "category": "profiles", + "since": "4.0.0", + "returns": [ + { + "type": "String", + "name": "profile-name", + "description": "Name of the currently active profile." + } + ], + "names": [ + { + "name": "", + "description": "GetCurrentProfile" + } + ], + "categories": [ + { + "name": "", + "description": "profiles" + } + ], + "sinces": [ + { + "name": "", + "description": "4.0.0" + } + ], + "heading": { + "level": 2, + "text": "GetCurrentProfile" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get a list of available profiles.", + "return": "{Array} `profiles` List of available profiles.", + "api": "requests", + "name": "ListProfiles", + "category": "profiles", + "since": "4.0.0", + "returns": [ + { + "type": "Array", + "name": "profiles", + "description": "List of available profiles." + } + ], + "names": [ + { + "name": "", + "description": "ListProfiles" + } + ], + "categories": [ + { + "name": "", + "description": "profiles" + } + ], + "sinces": [ + { + "name": "", + "description": "4.0.0" + } + ], + "heading": { + "level": 2, + "text": "ListProfiles" + }, + "lead": "", + "type": "class", + "examples": [] + } + ], + "recording": [ + { + "subheads": [], + "description": "Get current recording status.", + "return": [ + "{boolean} `isRecording` Current recording status.", + "{boolean} `isRecordingPaused` Whether the recording is paused or not.", + "{String (optional)} `recordTimecode` Time elapsed since recording started (only present if currently recording)." + ], + "api": "requests", + "name": "GetRecordingStatus", + "category": "recording", + "since": "unreleased", + "returns": [ + { + "type": "boolean", + "name": "isRecording", + "description": "Current recording status." + }, + { + "type": "boolean", + "name": "isRecordingPaused", + "description": "Whether the recording is paused or not." + }, + { + "type": "String (optional)", + "name": "recordTimecode", + "description": "Time elapsed since recording started (only present if currently recording)." + } + ], + "names": [ + { + "name": "", + "description": "GetRecordingStatus" + } + ], + "categories": [ + { + "name": "", + "description": "recording" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "GetRecordingStatus" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Toggle recording on or off.", + "api": "requests", + "name": "StartStopRecording", + "category": "recording", + "since": "0.3", + "names": [ + { + "name": "", + "description": "StartStopRecording" + } + ], + "categories": [ + { + "name": "", + "description": "recording" + } + ], + "sinces": [ + { + "name": "", + "description": "0.3" + } + ], + "heading": { + "level": 2, + "text": "StartStopRecording" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Start recording.\nWill return an `error` if recording is already active.", + "api": "requests", + "name": "StartRecording", + "category": "recording", + "since": "4.1.0", + "names": [ + { + "name": "", + "description": "StartRecording" + } + ], + "categories": [ + { + "name": "", + "description": "recording" + } + ], + "sinces": [ + { + "name": "", + "description": "4.1.0" + } + ], + "heading": { + "level": 2, + "text": "StartRecording" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Stop recording.\nWill return an `error` if recording is not active.", + "api": "requests", + "name": "StopRecording", + "category": "recording", + "since": "4.1.0", + "names": [ + { + "name": "", + "description": "StopRecording" + } + ], + "categories": [ + { + "name": "", + "description": "recording" + } + ], + "sinces": [ + { + "name": "", + "description": "4.1.0" + } + ], + "heading": { + "level": 2, + "text": "StopRecording" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Pause the current recording.\nReturns an error if recording is not active or already paused.", + "api": "requests", + "name": "PauseRecording", + "category": "recording", + "since": "4.7.0", + "names": [ + { + "name": "", + "description": "PauseRecording" + } + ], + "categories": [ + { + "name": "", + "description": "recording" + } + ], + "sinces": [ + { + "name": "", + "description": "4.7.0" + } + ], + "heading": { + "level": 2, + "text": "PauseRecording" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Resume/unpause the current recording (if paused).\nReturns an error if recording is not active or not paused.", + "api": "requests", + "name": "ResumeRecording", + "category": "recording", + "since": "4.7.0", + "names": [ + { + "name": "", + "description": "ResumeRecording" + } + ], + "categories": [ + { + "name": "", + "description": "recording" + } + ], + "sinces": [ + { + "name": "", + "description": "4.7.0" + } + ], + "heading": { + "level": 2, + "text": "ResumeRecording" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "\n\nPlease note: if `SetRecordingFolder` is called while a recording is\nin progress, the change won't be applied immediately and will be\neffective on the next recording.", + "param": "{String} `rec-folder` Path of the recording folder.", + "api": "requests", + "name": "SetRecordingFolder", + "category": "recording", + "since": "4.1.0", + "params": [ + { + "type": "String", + "name": "rec-folder", + "description": "Path of the recording folder." + } + ], + "names": [ + { + "name": "", + "description": "SetRecordingFolder" + } + ], + "categories": [ + { + "name": "", + "description": "recording" + } + ], + "sinces": [ + { + "name": "", + "description": "4.1.0" + } + ], + "heading": { + "level": 2, + "text": "SetRecordingFolder" + }, + "lead": "In the current profile, sets the recording folder of the Simple and Advanced output modes to the specified value.", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get the path of the current recording folder.", + "return": "{String} `rec-folder` Path of the recording folder.", + "api": "requests", + "name": "GetRecordingFolder", + "category": "recording", + "since": "4.1.0", + "returns": [ + { + "type": "String", + "name": "rec-folder", + "description": "Path of the recording folder." + } + ], + "names": [ + { + "name": "", + "description": "GetRecordingFolder" + } + ], + "categories": [ + { + "name": "", + "description": "recording" + } + ], + "sinces": [ + { + "name": "", + "description": "4.1.0" + } + ], + "heading": { + "level": 2, + "text": "GetRecordingFolder" + }, + "lead": "", + "type": "class", + "examples": [] + } + ], + "replay buffer": [ + { + "subheads": [], + "description": "Get the status of the OBS replay buffer.", + "return": "{boolean} `isReplayBufferActive` Current recording status.", + "api": "requests", + "name": "GetReplayBufferStatus", + "category": "replay buffer", + "since": "unreleased", + "returns": [ + { + "type": "boolean", + "name": "isReplayBufferActive", + "description": "Current recording status." + } + ], + "names": [ + { + "name": "", + "description": "GetReplayBufferStatus" + } + ], + "categories": [ + { + "name": "", + "description": "replay buffer" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "GetReplayBufferStatus" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Toggle the Replay Buffer on/off.", + "api": "requests", + "name": "StartStopReplayBuffer", + "category": "replay buffer", + "since": "4.2.0", + "names": [ + { + "name": "", + "description": "StartStopReplayBuffer" + } + ], + "categories": [ + { + "name": "", + "description": "replay buffer" + } + ], + "sinces": [ + { + "name": "", + "description": "4.2.0" + } + ], + "heading": { + "level": 2, + "text": "StartStopReplayBuffer" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Start recording into the Replay Buffer.\nWill return an `error` if the Replay Buffer is already active or if the\n\"Save Replay Buffer\" hotkey is not set in OBS' settings.\nSetting this hotkey is mandatory, even when triggering saves only\nthrough obs-websocket.", + "api": "requests", + "name": "StartReplayBuffer", + "category": "replay buffer", + "since": "4.2.0", + "names": [ + { + "name": "", + "description": "StartReplayBuffer" + } + ], + "categories": [ + { + "name": "", + "description": "replay buffer" + } + ], + "sinces": [ + { + "name": "", + "description": "4.2.0" + } + ], + "heading": { + "level": 2, + "text": "StartReplayBuffer" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Stop recording into the Replay Buffer.\nWill return an `error` if the Replay Buffer is not active.", + "api": "requests", + "name": "StopReplayBuffer", + "category": "replay buffer", + "since": "4.2.0", + "names": [ + { + "name": "", + "description": "StopReplayBuffer" + } + ], + "categories": [ + { + "name": "", + "description": "replay buffer" + } + ], + "sinces": [ + { + "name": "", + "description": "4.2.0" + } + ], + "heading": { + "level": 2, + "text": "StopReplayBuffer" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Flush and save the contents of the Replay Buffer to disk. This is\nbasically the same as triggering the \"Save Replay Buffer\" hotkey.\nWill return an `error` if the Replay Buffer is not active.", + "api": "requests", + "name": "SaveReplayBuffer", + "category": "replay buffer", + "since": "4.2.0", + "names": [ + { + "name": "", + "description": "SaveReplayBuffer" + } + ], + "categories": [ + { + "name": "", + "description": "replay buffer" + } + ], + "sinces": [ + { + "name": "", + "description": "4.2.0" + } + ], + "heading": { + "level": 2, + "text": "SaveReplayBuffer" + }, + "lead": "", + "type": "class", + "examples": [] + } + ], + "scene collections": [ + { + "subheads": [], + "description": "Change the active scene collection.", + "param": "{String} `sc-name` Name of the desired scene collection.", + "api": "requests", + "name": "SetCurrentSceneCollection", + "category": "scene collections", + "since": "4.0.0", + "params": [ + { + "type": "String", + "name": "sc-name", + "description": "Name of the desired scene collection." + } + ], + "names": [ + { + "name": "", + "description": "SetCurrentSceneCollection" + } + ], + "categories": [ + { + "name": "", + "description": "scene collections" + } + ], + "sinces": [ + { + "name": "", + "description": "4.0.0" + } + ], + "heading": { + "level": 2, + "text": "SetCurrentSceneCollection" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get the name of the current scene collection.", + "return": "{String} `sc-name` Name of the currently active scene collection.", + "api": "requests", + "name": "GetCurrentSceneCollection", + "category": "scene collections", + "since": "4.0.0", + "returns": [ + { + "type": "String", + "name": "sc-name", + "description": "Name of the currently active scene collection." + } + ], + "names": [ + { + "name": "", + "description": "GetCurrentSceneCollection" + } + ], + "categories": [ + { + "name": "", + "description": "scene collections" + } + ], + "sinces": [ + { + "name": "", + "description": "4.0.0" + } + ], + "heading": { + "level": 2, + "text": "GetCurrentSceneCollection" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "List available scene collections", + "return": "{Array} `scene-collections` Scene collections list", + "api": "requests", + "name": "ListSceneCollections", + "category": "scene collections", + "since": "4.0.0", + "returns": [ + { + "type": "Array", + "name": "scene-collections", + "description": "Scene collections list" + } + ], + "names": [ + { + "name": "", + "description": "ListSceneCollections" + } + ], + "categories": [ + { + "name": "", + "description": "scene collections" + } + ], + "sinces": [ + { + "name": "", + "description": "4.0.0" + } + ], + "heading": { + "level": 2, + "text": "ListSceneCollections" + }, + "lead": "", + "type": "class", + "examples": [] + } + ], + "scene items": [ + { + "subheads": [], + "description": "Get a list of all scene items in a scene.", + "param": "{String} `sceneName` Name of the scene to get the list of scene items from.", + "return": [ + "{Array} `sceneItems` Array of scene items", + "{int} `sceneItems.*.itemId` Unique item id of the source item", + "{String} `sceneItems.*.sourceKind` ID if the scene item's source. For example `vlc_source` or `image_source`", + "{String} `sceneItems.*.sourceName` Name of the scene item's source", + "{String} `sceneItems.*.sourceType` Type of the scene item's source. Either `input`, `group`, or `scene`" + ], + "api": "requests", + "name": "GetSceneItemList", + "category": "scene items", + "since": "unreleased", + "returns": [ + { + "type": "Array", + "name": "sceneItems", + "description": "Array of scene items" + }, + { + "type": "int", + "name": "sceneItems.*.itemId", + "description": "Unique item id of the source item" + }, + { + "type": "String", + "name": "sceneItems.*.sourceKind", + "description": "ID if the scene item's source. For example `vlc_source` or `image_source`" + }, + { + "type": "String", + "name": "sceneItems.*.sourceName", + "description": "Name of the scene item's source" + }, + { + "type": "String", + "name": "sceneItems.*.sourceType", + "description": "Type of the scene item's source. Either `input`, `group`, or `scene`" + } + ], + "params": [ + { + "type": "String", + "name": "sceneName", + "description": "Name of the scene to get the list of scene items from." + } + ], + "names": [ + { + "name": "", + "description": "GetSceneItemList" + } + ], + "categories": [ + { + "name": "", + "description": "scene items" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "GetSceneItemList" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Gets the scene specific properties of the specified source item.\nCoordinates are relative to the item's parent (the scene or group it belongs to).", + "param": [ + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).", + "{String (optional)} `item.name` Scene Item name (if the `item` field is an object)", + "{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)" + ], + "return": [ + "{String} `name` Scene Item name.", + "{int} `itemId` Scene Item ID.", + "{double} `position.x` The x position of the source from the left.", + "{double} `position.y` The y position of the source from the top.", + "{int} `position.alignment` The point on the source that the item is manipulated from.", + "{double} `rotation` The clockwise rotation of the item in degrees around the point of alignment.", + "{double} `scale.x` The x-scale factor of the source.", + "{double} `scale.y` The y-scale factor of the source.", + "{int} `crop.top` The number of pixels cropped off the top of the source before scaling.", + "{int} `crop.right` The number of pixels cropped off the right of the source before scaling.", + "{int} `crop.bottom` The number of pixels cropped off the bottom of the source before scaling.", + "{int} `crop.left` The number of pixels cropped off the left of the source before scaling.", + "{bool} `visible` If the source is visible.", + "{bool} `muted` If the source is muted.", + "{bool} `locked` If the source's transform is locked.", + "{String} `bounds.type` Type of bounding box. Can be \"OBS_BOUNDS_STRETCH\", \"OBS_BOUNDS_SCALE_INNER\", \"OBS_BOUNDS_SCALE_OUTER\", \"OBS_BOUNDS_SCALE_TO_WIDTH\", \"OBS_BOUNDS_SCALE_TO_HEIGHT\", \"OBS_BOUNDS_MAX_ONLY\" or \"OBS_BOUNDS_NONE\".", + "{int} `bounds.alignment` Alignment of the bounding box.", + "{double} `bounds.x` Width of the bounding box.", + "{double} `bounds.y` Height of the bounding box.", + "{int} `sourceWidth` Base width (without scaling) of the source", + "{int} `sourceHeight` Base source (without scaling) of the source", + "{double} `width` Scene item width (base source width multiplied by the horizontal scaling factor)", + "{double} `height` Scene item height (base source height multiplied by the vertical scaling factor)", + "{int} `alignment` The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis.", + "{String (optional)} `parentGroupName` Name of the item's parent (if this item belongs to a group)", + "{Array (optional)} `groupChildren` List of children (if this item is a group)" + ], + "api": "requests", + "name": "GetSceneItemProperties", + "category": "scene items", + "since": "4.3.0", + "returns": [ + { + "type": "String", + "name": "name", + "description": "Scene Item name." + }, + { + "type": "int", + "name": "itemId", + "description": "Scene Item ID." + }, + { + "type": "double", + "name": "position.x", + "description": "The x position of the source from the left." + }, + { + "type": "double", + "name": "position.y", + "description": "The y position of the source from the top." + }, + { + "type": "int", + "name": "position.alignment", + "description": "The point on the source that the item is manipulated from." + }, + { + "type": "double", + "name": "rotation", + "description": "The clockwise rotation of the item in degrees around the point of alignment." + }, + { + "type": "double", + "name": "scale.x", + "description": "The x-scale factor of the source." + }, + { + "type": "double", + "name": "scale.y", + "description": "The y-scale factor of the source." + }, + { + "type": "int", + "name": "crop.top", + "description": "The number of pixels cropped off the top of the source before scaling." + }, + { + "type": "int", + "name": "crop.right", + "description": "The number of pixels cropped off the right of the source before scaling." + }, + { + "type": "int", + "name": "crop.bottom", + "description": "The number of pixels cropped off the bottom of the source before scaling." + }, + { + "type": "int", + "name": "crop.left", + "description": "The number of pixels cropped off the left of the source before scaling." + }, + { + "type": "bool", + "name": "visible", + "description": "If the source is visible." + }, + { + "type": "bool", + "name": "muted", + "description": "If the source is muted." + }, + { + "type": "bool", + "name": "locked", + "description": "If the source's transform is locked." + }, + { + "type": "String", + "name": "bounds.type", + "description": "Type of bounding box. Can be \"OBS_BOUNDS_STRETCH\", \"OBS_BOUNDS_SCALE_INNER\", \"OBS_BOUNDS_SCALE_OUTER\", \"OBS_BOUNDS_SCALE_TO_WIDTH\", \"OBS_BOUNDS_SCALE_TO_HEIGHT\", \"OBS_BOUNDS_MAX_ONLY\" or \"OBS_BOUNDS_NONE\"." + }, + { + "type": "int", + "name": "bounds.alignment", + "description": "Alignment of the bounding box." + }, + { + "type": "double", + "name": "bounds.x", + "description": "Width of the bounding box." + }, + { + "type": "double", + "name": "bounds.y", + "description": "Height of the bounding box." + }, + { + "type": "int", + "name": "sourceWidth", + "description": "Base width (without scaling) of the source" + }, + { + "type": "int", + "name": "sourceHeight", + "description": "Base source (without scaling) of the source" + }, + { + "type": "double", + "name": "width", + "description": "Scene item width (base source width multiplied by the horizontal scaling factor)" + }, + { + "type": "double", + "name": "height", + "description": "Scene item height (base source height multiplied by the vertical scaling factor)" + }, + { + "type": "int", + "name": "alignment", + "description": "The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis." + }, + { + "type": "String (optional)", + "name": "parentGroupName", + "description": "Name of the item's parent (if this item belongs to a group)" + }, + { + "type": "Array (optional)", + "name": "groupChildren", + "description": "List of children (if this item is a group)" + } + ], + "params": [ + { + "type": "String (optional)", + "name": "scene-name", + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." + }, + { + "type": "String | Object", + "name": "item", + "description": "Scene Item name (if this field is a string) or specification (if it is an object)." + }, + { + "type": "String (optional)", + "name": "item.name", + "description": "Scene Item name (if the `item` field is an object)" + }, + { + "type": "int (optional)", + "name": "item.id", + "description": "Scene Item ID (if the `item` field is an object)" + } + ], + "names": [ + { + "name": "", + "description": "GetSceneItemProperties" + } + ], + "categories": [ + { + "name": "", + "description": "scene items" + } + ], + "sinces": [ + { + "name": "", + "description": "4.3.0" + } + ], + "heading": { + "level": 2, + "text": "GetSceneItemProperties" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Sets the scene specific properties of a source. Unspecified properties will remain unchanged.\nCoordinates are relative to the item's parent (the scene or group it belongs to).", + "param": [ + "{String (optional)} `scene-name` Name of the scene the source item belongs to. Defaults to the current scene.", + "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).", + "{String (optional)} `item.name` Scene Item name (if the `item` field is an object)", + "{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)", + "{double (optional)} `position.x` The new x position of the source.", + "{double (optional)} `position.y` The new y position of the source.", + "{int (optional)} `position.alignment` The new alignment of the source.", + "{double (optional)} `rotation` The new clockwise rotation of the item in degrees.", + "{double (optional)} `scale.x` The new x scale of the item.", + "{double (optional)} `scale.y` The new y scale of the item.", + "{int (optional)} `crop.top` The new amount of pixels cropped off the top of the source before scaling.", + "{int (optional)} `crop.bottom` The new amount of pixels cropped off the bottom of the source before scaling.", + "{int (optional)} `crop.left` The new amount of pixels cropped off the left of the source before scaling.", + "{int (optional)} `crop.right` The new amount of pixels cropped off the right of the source before scaling.", + "{bool (optional)} `visible` The new visibility of the source. 'true' shows source, 'false' hides source.", + "{bool (optional)} `locked` The new locked status of the source. 'true' keeps it in its current position, 'false' allows movement.", + "{String (optional)} `bounds.type` The new bounds type of the source. Can be \"OBS_BOUNDS_STRETCH\", \"OBS_BOUNDS_SCALE_INNER\", \"OBS_BOUNDS_SCALE_OUTER\", \"OBS_BOUNDS_SCALE_TO_WIDTH\", \"OBS_BOUNDS_SCALE_TO_HEIGHT\", \"OBS_BOUNDS_MAX_ONLY\" or \"OBS_BOUNDS_NONE\".", + "{int (optional)} `bounds.alignment` The new alignment of the bounding box. (0-2, 4-6, 8-10)", + "{double (optional)} `bounds.x` The new width of the bounding box.", + "{double (optional)} `bounds.y` The new height of the bounding box." + ], + "api": "requests", + "name": "SetSceneItemProperties", + "category": "scene items", + "since": "4.3.0", + "params": [ + { + "type": "String (optional)", + "name": "scene-name", + "description": "Name of the scene the source item belongs to. Defaults to the current scene." + }, + { + "type": "String | Object", + "name": "item", + "description": "Scene Item name (if this field is a string) or specification (if it is an object)." + }, + { + "type": "String (optional)", + "name": "item.name", + "description": "Scene Item name (if the `item` field is an object)" + }, + { + "type": "int (optional)", + "name": "item.id", + "description": "Scene Item ID (if the `item` field is an object)" + }, + { + "type": "double (optional)", + "name": "position.x", + "description": "The new x position of the source." + }, + { + "type": "double (optional)", + "name": "position.y", + "description": "The new y position of the source." + }, + { + "type": "int (optional)", + "name": "position.alignment", + "description": "The new alignment of the source." + }, + { + "type": "double (optional)", + "name": "rotation", + "description": "The new clockwise rotation of the item in degrees." + }, + { + "type": "double (optional)", + "name": "scale.x", + "description": "The new x scale of the item." + }, + { + "type": "double (optional)", + "name": "scale.y", + "description": "The new y scale of the item." + }, + { + "type": "int (optional)", + "name": "crop.top", + "description": "The new amount of pixels cropped off the top of the source before scaling." + }, + { + "type": "int (optional)", + "name": "crop.bottom", + "description": "The new amount of pixels cropped off the bottom of the source before scaling." + }, + { + "type": "int (optional)", + "name": "crop.left", + "description": "The new amount of pixels cropped off the left of the source before scaling." + }, + { + "type": "int (optional)", + "name": "crop.right", + "description": "The new amount of pixels cropped off the right of the source before scaling." + }, + { + "type": "bool (optional)", + "name": "visible", + "description": "The new visibility of the source. 'true' shows source, 'false' hides source." + }, + { + "type": "bool (optional)", + "name": "locked", + "description": "The new locked status of the source. 'true' keeps it in its current position, 'false' allows movement." + }, + { + "type": "String (optional)", + "name": "bounds.type", + "description": "The new bounds type of the source. Can be \"OBS_BOUNDS_STRETCH\", \"OBS_BOUNDS_SCALE_INNER\", \"OBS_BOUNDS_SCALE_OUTER\", \"OBS_BOUNDS_SCALE_TO_WIDTH\", \"OBS_BOUNDS_SCALE_TO_HEIGHT\", \"OBS_BOUNDS_MAX_ONLY\" or \"OBS_BOUNDS_NONE\"." + }, + { + "type": "int (optional)", + "name": "bounds.alignment", + "description": "The new alignment of the bounding box. (0-2, 4-6, 8-10)" + }, + { + "type": "double (optional)", + "name": "bounds.x", + "description": "The new width of the bounding box." + }, + { + "type": "double (optional)", + "name": "bounds.y", + "description": "The new height of the bounding box." + } + ], + "names": [ + { + "name": "", + "description": "SetSceneItemProperties" + } + ], + "categories": [ + { + "name": "", + "description": "scene items" + } + ], + "sinces": [ + { + "name": "", + "description": "4.3.0" + } + ], + "heading": { + "level": 2, + "text": "SetSceneItemProperties" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Reset a scene item.", + "param": [ + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).", + "{String (optional)} `item.name` Scene Item name (if the `item` field is an object)", + "{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)" + ], + "api": "requests", + "name": "ResetSceneItem", + "category": "scene items", + "since": "4.2.0", + "params": [ + { + "type": "String (optional)", + "name": "scene-name", + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." + }, + { + "type": "String | Object", + "name": "item", + "description": "Scene Item name (if this field is a string) or specification (if it is an object)." + }, + { + "type": "String (optional)", + "name": "item.name", + "description": "Scene Item name (if the `item` field is an object)" + }, + { + "type": "int (optional)", + "name": "item.id", + "description": "Scene Item ID (if the `item` field is an object)" + } + ], + "names": [ + { + "name": "", + "description": "ResetSceneItem" + } + ], + "categories": [ + { + "name": "", + "description": "scene items" + } + ], + "sinces": [ + { + "name": "", + "description": "4.2.0" + } + ], + "heading": { + "level": 2, + "text": "ResetSceneItem" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Show or hide a specified source item in a specified scene.", + "param": [ + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the currently active scene.", + "{String} `source` Scene Item name.", + "{boolean} `render` true = shown ; false = hidden" + ], + "api": "requests", + "name": "SetSceneItemRender", + "category": "scene items", + "since": "0.3", + "deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.", + "params": [ + { + "type": "String (optional)", + "name": "scene-name", + "description": "Name of the scene the scene item belongs to. Defaults to the currently active scene." + }, + { + "type": "String", + "name": "source", + "description": "Scene Item name." + }, + { + "type": "boolean", + "name": "render", + "description": "true = shown ; false = hidden" + } + ], + "names": [ + { + "name": "", + "description": "SetSceneItemRender" + } + ], + "categories": [ + { + "name": "", + "description": "scene items" + } + ], + "sinces": [ + { + "name": "", + "description": "0.3" + } + ], + "deprecateds": [ + { + "name": "", + "description": "Since 4.3.0. Prefer the use of SetSceneItemProperties." + } + ], + "heading": { + "level": 2, + "text": "SetSceneItemRender" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Sets the coordinates of a specified source item.", + "param": [ + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{String} `item` Scene Item name.", + "{double} `x` X coordinate.", + "{double} `y` Y coordinate." + ], + "api": "requests", + "name": "SetSceneItemPosition", + "category": "scene items", + "since": "4.0.0", + "deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.", + "params": [ + { + "type": "String (optional)", + "name": "scene-name", + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." + }, + { + "type": "String", + "name": "item", + "description": "Scene Item name." + }, + { + "type": "double", + "name": "x", + "description": "X coordinate." + }, + { + "type": "double", + "name": "y", + "description": "Y coordinate." + } + ], + "names": [ + { + "name": "", + "description": "SetSceneItemPosition" + } + ], + "categories": [ + { + "name": "", + "description": "scene items" + } + ], + "sinces": [ + { + "name": "", + "description": "4.0.0" + } + ], + "deprecateds": [ + { + "name": "", + "description": "Since 4.3.0. Prefer the use of SetSceneItemProperties." + } + ], + "heading": { + "level": 2, + "text": "SetSceneItemPosition" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Set the transform of the specified source item.", + "param": [ + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{String} `item` Scene Item name.", + "{double} `x-scale` Width scale factor.", + "{double} `y-scale` Height scale factor.", + "{double} `rotation` Source item rotation (in degrees)." + ], + "api": "requests", + "name": "SetSceneItemTransform", + "category": "scene items", + "since": "4.0.0", + "deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.", + "params": [ + { + "type": "String (optional)", + "name": "scene-name", + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." + }, + { + "type": "String", + "name": "item", + "description": "Scene Item name." + }, + { + "type": "double", + "name": "x-scale", + "description": "Width scale factor." + }, + { + "type": "double", + "name": "y-scale", + "description": "Height scale factor." + }, + { + "type": "double", + "name": "rotation", + "description": "Source item rotation (in degrees)." + } + ], + "names": [ + { + "name": "", + "description": "SetSceneItemTransform" + } + ], + "categories": [ + { + "name": "", + "description": "scene items" + } + ], + "sinces": [ + { + "name": "", + "description": "4.0.0" + } + ], + "deprecateds": [ + { + "name": "", + "description": "Since 4.3.0. Prefer the use of SetSceneItemProperties." + } + ], + "heading": { + "level": 2, + "text": "SetSceneItemTransform" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Sets the crop coordinates of the specified source item.", + "param": [ + "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{String} `item` Scene Item name.", + "{int} `top` Pixel position of the top of the source item.", + "{int} `bottom` Pixel position of the bottom of the source item.", + "{int} `left` Pixel position of the left of the source item.", + "{int} `right` Pixel position of the right of the source item." + ], + "api": "requests", + "name": "SetSceneItemCrop", + "category": "scene items", + "since": "4.1.0", + "deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.", + "params": [ + { + "type": "String (optional)", + "name": "scene-name", + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." + }, + { + "type": "String", + "name": "item", + "description": "Scene Item name." + }, + { + "type": "int", + "name": "top", + "description": "Pixel position of the top of the source item." + }, + { + "type": "int", + "name": "bottom", + "description": "Pixel position of the bottom of the source item." + }, + { + "type": "int", + "name": "left", + "description": "Pixel position of the left of the source item." + }, + { + "type": "int", + "name": "right", + "description": "Pixel position of the right of the source item." + } + ], + "names": [ + { + "name": "", + "description": "SetSceneItemCrop" + } + ], + "categories": [ + { + "name": "", + "description": "scene items" + } + ], + "sinces": [ + { + "name": "", + "description": "4.1.0" + } + ], + "deprecateds": [ + { + "name": "", + "description": "Since 4.3.0. Prefer the use of SetSceneItemProperties." + } + ], + "heading": { + "level": 2, + "text": "SetSceneItemCrop" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Deletes a scene item.", + "param": [ + "{String (optional)} `scene` Name of the scene the scene item belongs to. Defaults to the current scene.", + "{Object} `item` Scene item to delete (required)", + "{String} `item.name` Scene Item name (prefer `id`, including both is acceptable).", + "{int} `item.id` Scene Item ID." + ], + "api": "requests", + "name": "DeleteSceneItem", + "category": "scene items", + "since": "4.5.0", + "params": [ + { + "type": "String (optional)", + "name": "scene", + "description": "Name of the scene the scene item belongs to. Defaults to the current scene." + }, + { + "type": "Object", + "name": "item", + "description": "Scene item to delete (required)" + }, + { + "type": "String", + "name": "item.name", + "description": "Scene Item name (prefer `id`, including both is acceptable)." + }, + { + "type": "int", + "name": "item.id", + "description": "Scene Item ID." + } + ], + "names": [ + { + "name": "", + "description": "DeleteSceneItem" + } + ], + "categories": [ + { + "name": "", + "description": "scene items" + } + ], + "sinces": [ + { + "name": "", + "description": "4.5.0" + } + ], + "heading": { + "level": 2, + "text": "DeleteSceneItem" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Creates a scene item in a scene. In other words, this is how you add a source into a scene.", + "param": [ + "{String} `sceneName` Name of the scene to create the scene item in", + "{String} `sourceName` Name of the source to be added", + "{boolean} `setVisible` Whether to make the sceneitem visible on creation or not. Default `true`" + ], + "return": "{int} `itemId` Numerical ID of the created scene item", + "api": "requests", + "name": "AddSceneItem", + "category": "scene items", + "since": "unreleased", + "returns": [ + { + "type": "int", + "name": "itemId", + "description": "Numerical ID of the created scene item" + } + ], + "params": [ + { + "type": "String", + "name": "sceneName", + "description": "Name of the scene to create the scene item in" + }, + { + "type": "String", + "name": "sourceName", + "description": "Name of the source to be added" + }, + { + "type": "boolean", + "name": "setVisible", + "description": "Whether to make the sceneitem visible on creation or not. Default `true`" + } + ], + "names": [ + { + "name": "", + "description": "AddSceneItem" + } + ], + "categories": [ + { + "name": "", + "description": "scene items" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "AddSceneItem" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Duplicates a scene item.", + "param": [ + "{String (optional)} `fromScene` Name of the scene to copy the item from. Defaults to the current scene.", + "{String (optional)} `toScene` Name of the scene to create the item in. Defaults to the current scene.", + "{Object} `item` Scene Item to duplicate from the source scene (required)", + "{String} `item.name` Scene Item name (prefer `id`, including both is acceptable).", + "{int} `item.id` Scene Item ID." + ], + "return": [ + "{String} `scene` Name of the scene where the new item was created", + "{Object} `item` New item info", + "{int} `item.id` New item ID", + "{String} `item.name` New item name" + ], + "api": "requests", + "name": "DuplicateSceneItem", + "category": "scene items", + "since": "4.5.0", + "returns": [ + { + "type": "String", + "name": "scene", + "description": "Name of the scene where the new item was created" + }, + { + "type": "Object", + "name": "item", + "description": "New item info" + }, + { + "type": "int", + "name": "item.id", + "description": "New item ID" + }, + { + "type": "String", + "name": "item.name", + "description": "New item name" + } + ], + "params": [ + { + "type": "String (optional)", + "name": "fromScene", + "description": "Name of the scene to copy the item from. Defaults to the current scene." + }, + { + "type": "String (optional)", + "name": "toScene", + "description": "Name of the scene to create the item in. Defaults to the current scene." + }, + { + "type": "Object", + "name": "item", + "description": "Scene Item to duplicate from the source scene (required)" + }, + { + "type": "String", + "name": "item.name", + "description": "Scene Item name (prefer `id`, including both is acceptable)." + }, + { + "type": "int", + "name": "item.id", + "description": "Scene Item ID." + } + ], + "names": [ + { + "name": "", + "description": "DuplicateSceneItem" + } + ], + "categories": [ + { + "name": "", + "description": "scene items" + } + ], + "sinces": [ + { + "name": "", + "description": "4.5.0" + } + ], + "heading": { + "level": 2, + "text": "DuplicateSceneItem" + }, + "lead": "", + "type": "class", + "examples": [] + } + ], + "scenes": [ + { + "subheads": [], + "description": "Switch to the specified scene.", + "param": "{String} `scene-name` Name of the scene to switch to.", + "api": "requests", + "name": "SetCurrentScene", + "category": "scenes", + "since": "0.3", + "params": [ + { + "type": "String", + "name": "scene-name", + "description": "Name of the scene to switch to." + } + ], + "names": [ + { + "name": "", + "description": "SetCurrentScene" + } + ], + "categories": [ + { + "name": "", + "description": "scenes" + } + ], + "sinces": [ + { + "name": "", + "description": "0.3" + } + ], + "heading": { + "level": 2, + "text": "SetCurrentScene" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get the current scene's name and source items.", + "return": [ + "{String} `name` Name of the currently active scene.", + "{Array} `sources` Ordered list of the current scene's source items." + ], + "api": "requests", + "name": "GetCurrentScene", + "category": "scenes", + "since": "0.3", + "returns": [ + { + "type": "String", + "name": "name", + "description": "Name of the currently active scene." + }, + { + "type": "Array", + "name": "sources", + "description": "Ordered list of the current scene's source items." + } + ], + "names": [ + { + "name": "", + "description": "GetCurrentScene" + } + ], + "categories": [ + { + "name": "", + "description": "scenes" + } + ], + "sinces": [ + { + "name": "", + "description": "0.3" + } + ], + "heading": { + "level": 2, + "text": "GetCurrentScene" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get a list of scenes in the currently active profile.", + "return": [ + "{String} `current-scene` Name of the currently active scene.", + "{Array} `scenes` Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information)." + ], + "api": "requests", + "name": "GetSceneList", + "category": "scenes", + "since": "0.3", + "returns": [ + { + "type": "String", + "name": "current-scene", + "description": "Name of the currently active scene." + }, + { + "type": "Array", + "name": "scenes", + "description": "Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information)." + } + ], + "names": [ + { + "name": "", + "description": "GetSceneList" + } + ], + "categories": [ + { + "name": "", + "description": "scenes" + } + ], + "sinces": [ + { + "name": "", + "description": "0.3" + } + ], + "heading": { + "level": 2, + "text": "GetSceneList" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Create a new scene scene.", + "param": "{String} `sceneName` Name of the scene to create.", + "api": "requests", + "name": "CreateScene", + "category": "scenes", + "since": "4.8.0", + "params": [ + { + "type": "String", + "name": "sceneName", + "description": "Name of the scene to create." + } + ], + "names": [ + { + "name": "", + "description": "CreateScene" + } + ], + "categories": [ + { + "name": "", + "description": "scenes" + } + ], + "sinces": [ + { + "name": "", + "description": "4.8.0" + } + ], + "heading": { + "level": 2, + "text": "CreateScene" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Changes the order of scene items in the requested scene.", + "param": [ + "{String (optional)} `scene` Name of the scene to reorder (defaults to current).", + "{Array} `items` Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene", + "{int (optional)} `items[].id` Id of a specific scene item. Unique on a scene by scene basis.", + "{String (optional)} `items[].name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene." + ], + "api": "requests", + "name": "ReorderSceneItems", + "category": "scenes", + "since": "4.5.0", + "params": [ + { + "type": "String (optional)", + "name": "scene", + "description": "Name of the scene to reorder (defaults to current)." + }, + { + "type": "Array", + "name": "items", + "description": "Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene" + }, + { + "type": "int (optional)", + "name": "items[].id", + "description": "Id of a specific scene item. Unique on a scene by scene basis." + }, + { + "type": "String (optional)", + "name": "items[].name", + "description": "Name of a scene item. Sufficiently unique if no scene items share sources within the scene." + } + ], + "names": [ + { + "name": "", + "description": "ReorderSceneItems" + } + ], + "categories": [ + { + "name": "", + "description": "scenes" + } + ], + "sinces": [ + { + "name": "", + "description": "4.5.0" + } + ], + "heading": { + "level": 2, + "text": "ReorderSceneItems" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Set a scene to use a specific transition override.", + "param": [ + "{String} `sceneName` Name of the scene to switch to.", + "{String} `transitionName` Name of the transition to use.", + "{int (Optional)} `transitionDuration` Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given." + ], + "api": "requests", + "name": "SetSceneTransitionOverride", + "category": "scenes", + "since": "4.9.0", + "params": [ + { + "type": "String", + "name": "sceneName", + "description": "Name of the scene to switch to." + }, + { + "type": "String", + "name": "transitionName", + "description": "Name of the transition to use." + }, + { + "type": "int (Optional)", + "name": "transitionDuration", + "description": "Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given." + } + ], + "names": [ + { + "name": "", + "description": "SetSceneTransitionOverride" + } + ], + "categories": [ + { + "name": "", + "description": "scenes" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "SetSceneTransitionOverride" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Remove any transition override on a scene.", + "param": "{String} `sceneName` Name of the scene to switch to.", + "api": "requests", + "name": "RemoveSceneTransitionOverride", + "category": "scenes", + "since": "4.9.0", + "params": [ + { + "type": "String", + "name": "sceneName", + "description": "Name of the scene to switch to." + } + ], + "names": [ + { + "name": "", + "description": "RemoveSceneTransitionOverride" + } + ], + "categories": [ + { + "name": "", + "description": "scenes" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "RemoveSceneTransitionOverride" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Get the current scene transition override.", + "param": "{String} `sceneName` Name of the scene to switch to.", + "return": [ + "{String} `transitionName` Name of the current overriding transition. Empty string if no override is set.", + "{int} `transitionDuration` Transition duration. `-1` if no override is set." + ], + "api": "requests", + "name": "GetSceneTransitionOverride", + "category": "scenes", + "since": "4.9.0", + "returns": [ + { + "type": "String", + "name": "transitionName", + "description": "Name of the current overriding transition. Empty string if no override is set." + }, + { + "type": "int", + "name": "transitionDuration", + "description": "Transition duration. `-1` if no override is set." + } + ], + "params": [ + { + "type": "String", + "name": "sceneName", + "description": "Name of the scene to switch to." + } + ], + "names": [ + { + "name": "", + "description": "GetSceneTransitionOverride" + } + ], + "categories": [ + { + "name": "", + "description": "scenes" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "GetSceneTransitionOverride" + }, + "lead": "", + "type": "class", + "examples": [] + } + ], "streaming": [ { "subheads": [], diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index f7b999a2..b2c5a84e 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -92,6 +92,8 @@ auth_response = base64_encode(auth_response_hash) + [SourceDestroyed](#sourcedestroyed) + [SourceVolumeChanged](#sourcevolumechanged) + [SourceMuteStateChanged](#sourcemutestatechanged) + + [SourceAudioDeactivated](#sourceaudiodeactivated) + + [SourceAudioActivated](#sourceaudioactivated) + [SourceAudioSyncOffsetChanged](#sourceaudiosyncoffsetchanged) + [SourceAudioMixersChanged](#sourceaudiomixerschanged) + [SourceRenamed](#sourcerenamed) @@ -99,6 +101,15 @@ auth_response = base64_encode(auth_response_hash) + [SourceFilterRemoved](#sourcefilterremoved) + [SourceFilterVisibilityChanged](#sourcefiltervisibilitychanged) + [SourceFiltersReordered](#sourcefiltersreordered) + * [Media](#media) + + [MediaPlaying](#mediaplaying) + + [MediaPaused](#mediapaused) + + [MediaRestarted](#mediarestarted) + + [MediaStopped](#mediastopped) + + [MediaNext](#medianext) + + [MediaPrevious](#mediaprevious) + + [MediaStarted](#mediastarted) + + [MediaEnded](#mediaended) * [Scene Items](#scene-items) + [SourceOrderChanged](#sourceorderchanged) + [SceneItemAdded](#sceneitemadded) @@ -123,51 +134,19 @@ auth_response = base64_encode(auth_response_hash) + [BroadcastCustomMessage](#broadcastcustommessage-1) + [GetVideoInfo](#getvideoinfo) + [OpenProjector](#openprojector) - * [Outputs](#outputs) - + [ListOutputs](#listoutputs) - + [GetOutputInfo](#getoutputinfo) - + [StartOutput](#startoutput) - + [StopOutput](#stopoutput) - * [Profiles](#profiles-1) - + [SetCurrentProfile](#setcurrentprofile) - + [GetCurrentProfile](#getcurrentprofile) - + [ListProfiles](#listprofiles) - * [Recording](#recording-1) - + [StartStopRecording](#startstoprecording) - + [StartRecording](#startrecording) - + [StopRecording](#stoprecording) - + [PauseRecording](#pauserecording) - + [ResumeRecording](#resumerecording) - + [SetRecordingFolder](#setrecordingfolder) - + [GetRecordingFolder](#getrecordingfolder) - * [Replay Buffer](#replay-buffer-1) - + [StartStopReplayBuffer](#startstopreplaybuffer) - + [StartReplayBuffer](#startreplaybuffer) - + [StopReplayBuffer](#stopreplaybuffer) - + [SaveReplayBuffer](#savereplaybuffer) - * [Scene Collections](#scene-collections) - + [SetCurrentSceneCollection](#setcurrentscenecollection) - + [GetCurrentSceneCollection](#getcurrentscenecollection) - + [ListSceneCollections](#listscenecollections) - * [Scene Items](#scene-items-1) - + [GetSceneItemProperties](#getsceneitemproperties) - + [SetSceneItemProperties](#setsceneitemproperties) - + [ResetSceneItem](#resetsceneitem) - + [SetSceneItemRender](#setsceneitemrender) - + [SetSceneItemPosition](#setsceneitemposition) - + [SetSceneItemTransform](#setsceneitemtransform) - + [SetSceneItemCrop](#setsceneitemcrop) - + [DeleteSceneItem](#deletesceneitem) - + [DuplicateSceneItem](#duplicatesceneitem) - * [Scenes](#scenes-1) - + [SetCurrentScene](#setcurrentscene) - + [GetCurrentScene](#getcurrentscene) - + [GetSceneList](#getscenelist) - + [ReorderSceneItems](#reordersceneitems) - + [SetSceneTransitionOverride](#setscenetransitionoverride) - + [RemoveSceneTransitionOverride](#removescenetransitionoverride) - + [GetSceneTransitionOverride](#getscenetransitionoverride) + * [Media Control](#media-control) + + [PlayPauseMedia](#playpausemedia) + + [RestartMedia](#restartmedia) + + [StopMedia](#stopmedia) + + [NextMedia](#nextmedia) + + [PreviousMedia](#previousmedia) + + [GetMediaDuration](#getmediaduration) + + [GetMediaTime](#getmediatime) + + [SetMediaTime](#setmediatime) + + [ScrubMedia](#scrubmedia) + + [GetMediaState](#getmediastate) * [Sources](#sources-1) + + [GetMediaSourcesList](#getmediasourceslist) + [GetSourcesList](#getsourceslist) + [GetSourceTypesList](#getsourcetypeslist) + [GetVolume](#getvolume) @@ -175,6 +154,7 @@ auth_response = base64_encode(auth_response_hash) + [GetMute](#getmute) + [SetMute](#setmute) + [ToggleMute](#togglemute) + + [GetAudioActive](#getaudioactive) + [SetSourceName](#setsourcename) + [SetSyncOffset](#setsyncoffset) + [GetSyncOffset](#getsyncoffset) @@ -198,6 +178,55 @@ auth_response = base64_encode(auth_response_hash) + [GetAudioMonitorType](#getaudiomonitortype) + [SetAudioMonitorType](#setaudiomonitortype) + [TakeSourceScreenshot](#takesourcescreenshot) + * [Outputs](#outputs) + + [ListOutputs](#listoutputs) + + [GetOutputInfo](#getoutputinfo) + + [StartOutput](#startoutput) + + [StopOutput](#stopoutput) + * [Profiles](#profiles-1) + + [SetCurrentProfile](#setcurrentprofile) + + [GetCurrentProfile](#getcurrentprofile) + + [ListProfiles](#listprofiles) + * [Recording](#recording-1) + + [GetRecordingStatus](#getrecordingstatus) + + [StartStopRecording](#startstoprecording) + + [StartRecording](#startrecording) + + [StopRecording](#stoprecording) + + [PauseRecording](#pauserecording) + + [ResumeRecording](#resumerecording) + + [SetRecordingFolder](#setrecordingfolder) + + [GetRecordingFolder](#getrecordingfolder) + * [Replay Buffer](#replay-buffer-1) + + [GetReplayBufferStatus](#getreplaybufferstatus) + + [StartStopReplayBuffer](#startstopreplaybuffer) + + [StartReplayBuffer](#startreplaybuffer) + + [StopReplayBuffer](#stopreplaybuffer) + + [SaveReplayBuffer](#savereplaybuffer) + * [Scene Collections](#scene-collections) + + [SetCurrentSceneCollection](#setcurrentscenecollection) + + [GetCurrentSceneCollection](#getcurrentscenecollection) + + [ListSceneCollections](#listscenecollections) + * [Scene Items](#scene-items-1) + + [GetSceneItemList](#getsceneitemlist) + + [GetSceneItemProperties](#getsceneitemproperties) + + [SetSceneItemProperties](#setsceneitemproperties) + + [ResetSceneItem](#resetsceneitem) + + [SetSceneItemRender](#setsceneitemrender) + + [SetSceneItemPosition](#setsceneitemposition) + + [SetSceneItemTransform](#setsceneitemtransform) + + [SetSceneItemCrop](#setsceneitemcrop) + + [DeleteSceneItem](#deletesceneitem) + + [AddSceneItem](#addsceneitem) + + [DuplicateSceneItem](#duplicatesceneitem) + * [Scenes](#scenes-1) + + [SetCurrentScene](#setcurrentscene) + + [GetCurrentScene](#getcurrentscene) + + [GetSceneList](#getscenelist) + + [CreateScene](#createscene) + + [ReorderSceneItems](#reordersceneitems) + + [SetSceneTransitionOverride](#setscenetransitionoverride) + + [RemoveSceneTransitionOverride](#removescenetransitionoverride) + + [GetSceneTransitionOverride](#getscenetransitionoverride) * [Streaming](#streaming-1) + [GetStreamingStatus](#getstreamingstatus) + [StartStopStreaming](#startstopstreaming) @@ -251,8 +280,8 @@ These are complex types, such as `Source` and `Scene`, which are used as argumen ## SceneItemTransform | Name | Type | Description | | ---- | :---: | ------------| -| `position.x` | _int_ | The x position of the scene item from the left. | -| `position.y` | _int_ | The y position of the scene item from the top. | +| `position.x` | _double_ | The x position of the scene item from the left. | +| `position.y` | _double_ | The y position of the scene item from the top. | | `position.alignment` | _int_ | The point on the scene item that the item is manipulated from. | | `rotation` | _double_ | The clockwise rotation of the scene item in degrees around the point of alignment. | | `scale.x` | _double_ | The x-scale factor of the scene item. | @@ -882,6 +911,38 @@ A source has been muted or unmuted. | `muted` | _boolean_ | Mute status of the source | +--- + +### SourceAudioDeactivated + + +- Unreleased + +A source has removed audio. + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name | + + +--- + +### SourceAudioActivated + + +- Unreleased + +A source has added audio. + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name | + + --- ### SourceAudioSyncOffsetChanged @@ -1011,6 +1072,160 @@ Filters in a source have been reordered. | `filters.*.type` | _String_ | Filter type | +--- + +## Media + +### MediaPlaying + + +- Added in v4.9.0 + + + +Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name | +| `sourceKind` | _String_ | The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) | + + +--- + +### MediaPaused + + +- Added in v4.9.0 + + + +Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name | +| `sourceKind` | _String_ | The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) | + + +--- + +### MediaRestarted + + +- Added in v4.9.0 + + + +Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name | +| `sourceKind` | _String_ | The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) | + + +--- + +### MediaStopped + + +- Added in v4.9.0 + + + +Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name | +| `sourceKind` | _String_ | The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) | + + +--- + +### MediaNext + + +- Added in v4.9.0 + + + +Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name | +| `sourceKind` | _String_ | The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) | + + +--- + +### MediaPrevious + + +- Added in v4.9.0 + + + +Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally. + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name | +| `sourceKind` | _String_ | The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) | + + +--- + +### MediaStarted + + +- Added in v4.9.0 + + + +Note: These events are emitted by the OBS sources themselves. For example when the media file starts playing. The behavior depends on the type of media source being used. + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name | +| `sourceKind` | _String_ | The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) | + + +--- + +### MediaEnded + + +- Added in v4.9.0 + + + +Note: These events are emitted by the OBS sources themselves. For example when the media file ends. The behavior depends on the type of media source being used. + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name | +| `sourceKind` | _String_ | The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`) | + + --- ## Scene Items @@ -1435,797 +1650,21 @@ _No additional response items._ --- -## Outputs +## Media Control -### ListOutputs - - -- Added in v4.7.0 - -List existing outputs - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `outputs` | _Array<Output>_ | Outputs list | - - ---- - -### GetOutputInfo - - -- Added in v4.7.0 - -Get information about a single output - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `outputName` | _String_ | Output name | - - -**Response Items:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `outputInfo` | _Output_ | Output info | - - ---- - -### StartOutput - - -- Added in v4.7.0 - -Start an output - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `outputName` | _String_ | Output name | - - -**Response Items:** - -_No additional response items._ - ---- - -### StopOutput - - -- Added in v4.7.0 - -Stop an output - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `outputName` | _String_ | Output name | -| `force` | _boolean (optional)_ | Force stop (default: false) | - - -**Response Items:** - -_No additional response items._ - ---- - -## Profiles - -### SetCurrentProfile - - -- Added in v4.0.0 - -Set the currently active profile. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `profile-name` | _String_ | Name of the desired profile. | - - -**Response Items:** - -_No additional response items._ - ---- - -### GetCurrentProfile - - -- Added in v4.0.0 - -Get the name of the current profile. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `profile-name` | _String_ | Name of the currently active profile. | - - ---- - -### ListProfiles - - -- Added in v4.0.0 - -Get a list of available profiles. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `profiles` | _Array<Object>_ | List of available profiles. | - - ---- - -## Recording - -### StartStopRecording - - -- Added in v0.3 - -Toggle recording on or off. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -_No additional response items._ - ---- - -### StartRecording - - -- Added in v4.1.0 - -Start recording. -Will return an `error` if recording is already active. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -_No additional response items._ - ---- - -### StopRecording - - -- Added in v4.1.0 - -Stop recording. -Will return an `error` if recording is not active. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -_No additional response items._ - ---- - -### PauseRecording - - -- Added in v4.7.0 - -Pause the current recording. -Returns an error if recording is not active or already paused. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -_No additional response items._ - ---- - -### ResumeRecording - - -- Added in v4.7.0 - -Resume/unpause the current recording (if paused). -Returns an error if recording is not active or not paused. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -_No additional response items._ - ---- - -### SetRecordingFolder - - -- Added in v4.1.0 - - - -Please note: if `SetRecordingFolder` is called while a recording is -in progress, the change won't be applied immediately and will be -effective on the next recording. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `rec-folder` | _String_ | Path of the recording folder. | - - -**Response Items:** - -_No additional response items._ - ---- - -### GetRecordingFolder - - -- Added in v4.1.0 - -Get the path of the current recording folder. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `rec-folder` | _String_ | Path of the recording folder. | - - ---- - -## Replay Buffer - -### StartStopReplayBuffer - - -- Added in v4.2.0 - -Toggle the Replay Buffer on/off. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -_No additional response items._ - ---- - -### StartReplayBuffer - - -- Added in v4.2.0 - -Start recording into the Replay Buffer. -Will return an `error` if the Replay Buffer is already active or if the -"Save Replay Buffer" hotkey is not set in OBS' settings. -Setting this hotkey is mandatory, even when triggering saves only -through obs-websocket. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -_No additional response items._ - ---- - -### StopReplayBuffer - - -- Added in v4.2.0 - -Stop recording into the Replay Buffer. -Will return an `error` if the Replay Buffer is not active. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -_No additional response items._ - ---- - -### SaveReplayBuffer - - -- Added in v4.2.0 - -Flush and save the contents of the Replay Buffer to disk. This is -basically the same as triggering the "Save Replay Buffer" hotkey. -Will return an `error` if the Replay Buffer is not active. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -_No additional response items._ - ---- - -## Scene Collections - -### SetCurrentSceneCollection - - -- Added in v4.0.0 - -Change the active scene collection. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `sc-name` | _String_ | Name of the desired scene collection. | - - -**Response Items:** - -_No additional response items._ - ---- - -### GetCurrentSceneCollection - - -- Added in v4.0.0 - -Get the name of the current scene collection. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `sc-name` | _String_ | Name of the currently active scene collection. | - - ---- - -### ListSceneCollections - - -- Added in v4.0.0 - -List available scene collections - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene-collections` | _Array<String>_ | Scene collections list | - - ---- - -## Scene Items - -### GetSceneItemProperties - - -- Added in v4.3.0 - -Gets the scene specific properties of the specified source item. -Coordinates are relative to the item's parent (the scene or group it belongs to). - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | -| `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). | -| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) | -| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) | - - -**Response Items:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `name` | _String_ | Scene Item name. | -| `itemId` | _int_ | Scene Item ID. | -| `position.x` | _int_ | The x position of the source from the left. | -| `position.y` | _int_ | The y position of the source from the top. | -| `position.alignment` | _int_ | The point on the source that the item is manipulated from. | -| `rotation` | _double_ | The clockwise rotation of the item in degrees around the point of alignment. | -| `scale.x` | _double_ | The x-scale factor of the source. | -| `scale.y` | _double_ | The y-scale factor of the source. | -| `crop.top` | _int_ | The number of pixels cropped off the top of the source before scaling. | -| `crop.right` | _int_ | The number of pixels cropped off the right of the source before scaling. | -| `crop.bottom` | _int_ | The number of pixels cropped off the bottom of the source before scaling. | -| `crop.left` | _int_ | The number of pixels cropped off the left of the source before scaling. | -| `visible` | _bool_ | If the source is visible. | -| `muted` | _bool_ | If the source is muted. | -| `locked` | _bool_ | If the source's transform is locked. | -| `bounds.type` | _String_ | Type of bounding box. Can be "OBS_BOUNDS_STRETCH", "OBS_BOUNDS_SCALE_INNER", "OBS_BOUNDS_SCALE_OUTER", "OBS_BOUNDS_SCALE_TO_WIDTH", "OBS_BOUNDS_SCALE_TO_HEIGHT", "OBS_BOUNDS_MAX_ONLY" or "OBS_BOUNDS_NONE". | -| `bounds.alignment` | _int_ | Alignment of the bounding box. | -| `bounds.x` | _double_ | Width of the bounding box. | -| `bounds.y` | _double_ | Height of the bounding box. | -| `sourceWidth` | _int_ | Base width (without scaling) of the source | -| `sourceHeight` | _int_ | Base source (without scaling) of the source | -| `width` | _double_ | Scene item width (base source width multiplied by the horizontal scaling factor) | -| `height` | _double_ | Scene item height (base source height multiplied by the vertical scaling factor) | -| `alignment` | _int_ | The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis. | -| `parentGroupName` | _String (optional)_ | Name of the item's parent (if this item belongs to a group) | -| `groupChildren` | _Array<SceneItemTransform> (optional)_ | List of children (if this item is a group) | - - ---- - -### SetSceneItemProperties - - -- Added in v4.3.0 - -Sets the scene specific properties of a source. Unspecified properties will remain unchanged. -Coordinates are relative to the item's parent (the scene or group it belongs to). - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | Name of the scene the source item belongs to. Defaults to the current scene. | -| `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). | -| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) | -| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) | -| `position.x` | _int (optional)_ | The new x position of the source. | -| `position.y` | _int (optional)_ | The new y position of the source. | -| `position.alignment` | _int (optional)_ | The new alignment of the source. | -| `rotation` | _double (optional)_ | The new clockwise rotation of the item in degrees. | -| `scale.x` | _double (optional)_ | The new x scale of the item. | -| `scale.y` | _double (optional)_ | The new y scale of the item. | -| `crop.top` | _int (optional)_ | The new amount of pixels cropped off the top of the source before scaling. | -| `crop.bottom` | _int (optional)_ | The new amount of pixels cropped off the bottom of the source before scaling. | -| `crop.left` | _int (optional)_ | The new amount of pixels cropped off the left of the source before scaling. | -| `crop.right` | _int (optional)_ | The new amount of pixels cropped off the right of the source before scaling. | -| `visible` | _bool (optional)_ | The new visibility of the source. 'true' shows source, 'false' hides source. | -| `locked` | _bool (optional)_ | The new locked status of the source. 'true' keeps it in its current position, 'false' allows movement. | -| `bounds.type` | _String (optional)_ | The new bounds type of the source. Can be "OBS_BOUNDS_STRETCH", "OBS_BOUNDS_SCALE_INNER", "OBS_BOUNDS_SCALE_OUTER", "OBS_BOUNDS_SCALE_TO_WIDTH", "OBS_BOUNDS_SCALE_TO_HEIGHT", "OBS_BOUNDS_MAX_ONLY" or "OBS_BOUNDS_NONE". | -| `bounds.alignment` | _int (optional)_ | The new alignment of the bounding box. (0-2, 4-6, 8-10) | -| `bounds.x` | _double (optional)_ | The new width of the bounding box. | -| `bounds.y` | _double (optional)_ | The new height of the bounding box. | - - -**Response Items:** - -_No additional response items._ - ---- - -### ResetSceneItem - - -- Added in v4.2.0 - -Reset a scene item. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | -| `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). | -| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) | -| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) | - - -**Response Items:** - -_No additional response items._ - ---- - -### SetSceneItemRender - -- **⚠️ Deprecated. Since 4.3.0. Prefer the use of SetSceneItemProperties. ⚠️** - -- Added in v0.3 - -Show or hide a specified source item in a specified scene. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the currently active scene. | -| `source` | _String_ | Scene Item name. | -| `render` | _boolean_ | true = shown ; false = hidden | - - -**Response Items:** - -_No additional response items._ - ---- - -### SetSceneItemPosition - -- **⚠️ Deprecated. Since 4.3.0. Prefer the use of SetSceneItemProperties. ⚠️** - -- Added in v4.0.0 - -Sets the coordinates of a specified source item. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | -| `item` | _String_ | Scene Item name. | -| `x` | _double_ | X coordinate. | -| `y` | _double_ | Y coordinate. | - - -**Response Items:** - -_No additional response items._ - ---- - -### SetSceneItemTransform - -- **⚠️ Deprecated. Since 4.3.0. Prefer the use of SetSceneItemProperties. ⚠️** - -- Added in v4.0.0 - -Set the transform of the specified source item. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | -| `item` | _String_ | Scene Item name. | -| `x-scale` | _double_ | Width scale factor. | -| `y-scale` | _double_ | Height scale factor. | -| `rotation` | _double_ | Source item rotation (in degrees). | - - -**Response Items:** - -_No additional response items._ - ---- - -### SetSceneItemCrop - -- **⚠️ Deprecated. Since 4.3.0. Prefer the use of SetSceneItemProperties. ⚠️** - -- Added in v4.1.0 - -Sets the crop coordinates of the specified source item. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | -| `item` | _String_ | Scene Item name. | -| `top` | _int_ | Pixel position of the top of the source item. | -| `bottom` | _int_ | Pixel position of the bottom of the source item. | -| `left` | _int_ | Pixel position of the left of the source item. | -| `right` | _int_ | Pixel position of the right of the source item. | - - -**Response Items:** - -_No additional response items._ - ---- - -### DeleteSceneItem - - -- Added in v4.5.0 - -Deletes a scene item. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | -| `item` | _Object_ | Scene item to delete (required) | -| `item.name` | _String_ | Scene Item name (prefer `id`, including both is acceptable). | -| `item.id` | _int_ | Scene Item ID. | - - -**Response Items:** - -_No additional response items._ - ---- - -### DuplicateSceneItem - - -- Added in v4.5.0 - -Duplicates a scene item. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `fromScene` | _String (optional)_ | Name of the scene to copy the item from. Defaults to the current scene. | -| `toScene` | _String (optional)_ | Name of the scene to create the item in. Defaults to the current scene. | -| `item` | _Object_ | Scene Item to duplicate from the source scene (required) | -| `item.name` | _String_ | Scene Item name (prefer `id`, including both is acceptable). | -| `item.id` | _int_ | Scene Item ID. | - - -**Response Items:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene` | _String_ | Name of the scene where the new item was created | -| `item` | _Object_ | New item info | -| `item.id` | _int_ | New item ID | -| `item.name` | _String_ | New item name | - - ---- - -## Scenes - -### SetCurrentScene - - -- Added in v0.3 - -Switch to the specified scene. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene-name` | _String_ | Name of the scene to switch to. | - - -**Response Items:** - -_No additional response items._ - ---- - -### GetCurrentScene - - -- Added in v0.3 - -Get the current scene's name and source items. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `name` | _String_ | Name of the currently active scene. | -| `sources` | _Array<SceneItem>_ | Ordered list of the current scene's source items. | - - ---- - -### GetSceneList - - -- Added in v0.3 - -Get a list of scenes in the currently active profile. - -**Request Fields:** - -_No specified parameters._ - -**Response Items:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `current-scene` | _String_ | Name of the currently active scene. | -| `scenes` | _Array<Scene>_ | Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information). | - - ---- - -### ReorderSceneItems - - -- Added in v4.5.0 - -Changes the order of scene items in the requested scene. - -**Request Fields:** - -| Name | Type | Description | -| ---- | :---: | ------------| -| `scene` | _String (optional)_ | Name of the scene to reorder (defaults to current). | -| `items` | _Array<Scene>_ | Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene | -| `items[].id` | _int (optional)_ | Id of a specific scene item. Unique on a scene by scene basis. | -| `items[].name` | _String (optional)_ | Name of a scene item. Sufficiently unique if no scene items share sources within the scene. | - - -**Response Items:** - -_No additional response items._ - ---- - -### SetSceneTransitionOverride +### PlayPauseMedia - Added in v4.9.0 -Set a scene to use a specific transition override. +Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) **Request Fields:** | Name | Type | Description | | ---- | :---: | ------------| -| `sceneName` | _String_ | Name of the scene to switch to. | -| `transitionName` | _String_ | Name of the transition to use. | -| `transitionDuration` | _int (Optional)_ | Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given. | +| `sourceName` | _String_ | Source name. | +| `playPause` | _boolean_ | Whether to pause or play the source. `false` for play, `true` for pause. | **Response Items:** @@ -2234,18 +1673,18 @@ _No additional response items._ --- -### RemoveSceneTransitionOverride +### RestartMedia - Added in v4.9.0 -Remove any transition override on a scene. +Restart a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) **Request Fields:** | Name | Type | Description | | ---- | :---: | ------------| -| `sceneName` | _String_ | Name of the scene to switch to. | +| `sourceName` | _String_ | Source name. | **Response Items:** @@ -2254,32 +1693,204 @@ _No additional response items._ --- -### GetSceneTransitionOverride +### StopMedia - Added in v4.9.0 -Get the current scene transition override. +Stop a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) **Request Fields:** | Name | Type | Description | | ---- | :---: | ------------| -| `sceneName` | _String_ | Name of the scene to switch to. | +| `sourceName` | _String_ | Source name. | + + +**Response Items:** + +_No additional response items._ + +--- + +### NextMedia + + +- Added in v4.9.0 + +Skip to the next media item in the playlist. Supports only vlc media source (as of OBS v25.0.8) + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | + + +**Response Items:** + +_No additional response items._ + +--- + +### PreviousMedia + + +- Added in v4.9.0 + +Go to the previous media item in the playlist. Supports only vlc media source (as of OBS v25.0.8) + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | + + +**Response Items:** + +_No additional response items._ + +--- + +### GetMediaDuration + + +- Added in v4.9.0 + +Get the length of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +Note: For some reason, for the first 5 or so seconds that the media is playing, the total duration can be off by upwards of 50ms. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | **Response Items:** | Name | Type | Description | | ---- | :---: | ------------| -| `transitionName` | _String_ | Name of the current overriding transition. Empty string if no override is set. | -| `transitionDuration` | _int_ | Transition duration. `-1` if no override is set. | +| `mediaDuration` | _int_ | The total length of media in milliseconds.. | + + +--- + +### GetMediaTime + + +- Added in v4.9.0 + +Get the current timestamp of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `timestamp` | _int_ | The time in milliseconds since the start of the media. | + + +--- + +### SetMediaTime + + +- Added in v4.9.0 + +Set the timestamp of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | +| `timestamp` | _int_ | Milliseconds to set the timestamp to. | + + +**Response Items:** + +_No additional response items._ + +--- + +### ScrubMedia + + +- Added in v4.9.0 + +Scrub media using a supplied offset. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +Note: Due to processing/network delays, this request is not perfect. The processing rate of this request has also not been tested. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | +| `timeOffset` | _int_ | Millisecond offset (positive or negative) to offset the current media position. | + + +**Response Items:** + +_No additional response items._ + +--- + +### GetMediaState + + +- Added in v4.9.0 + +Get the current playing state of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `mediaState` | _String_ | The media state of the provided source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown` | --- ## Sources +### GetMediaSourcesList + + +- Added in v4.9.0 + +List the media state of all media sources (vlc and media source) + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `mediaSources` | _Array<Object>_ | Array of sources | +| `mediaSources.*.sourceName` | _String_ | Unique source name | +| `mediaSources.*.sourceKind` | _String_ | Unique source internal type (a.k.a `ffmpeg_source` or `vlc_source`) | +| `mediaSources.*.mediaState` | _String_ | The current state of media for that source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown` | + + +--- + ### GetSourcesList @@ -2446,6 +2057,29 @@ Inverts the mute status of a specified source. _No additional response items._ +--- + +### GetAudioActive + + +- Unreleased + +Get the audio's active status of a specified source. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `audioActive` | _boolean_ | Audio active status of the source. | + + --- ### SetSourceName @@ -3090,6 +2724,963 @@ preserved if only one of these two parameters is specified. | `imageFile` | _String_ | Absolute path to the saved image file (if `saveToFilePath` was specified in the request) | +--- + +## Outputs + +### ListOutputs + + +- Added in v4.7.0 + +List existing outputs + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `outputs` | _Array<Output>_ | Outputs list | + + +--- + +### GetOutputInfo + + +- Added in v4.7.0 + +Get information about a single output + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `outputName` | _String_ | Output name | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `outputInfo` | _Output_ | Output info | + + +--- + +### StartOutput + + +- Added in v4.7.0 + +Start an output + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `outputName` | _String_ | Output name | + + +**Response Items:** + +_No additional response items._ + +--- + +### StopOutput + + +- Added in v4.7.0 + +Stop an output + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `outputName` | _String_ | Output name | +| `force` | _boolean (optional)_ | Force stop (default: false) | + + +**Response Items:** + +_No additional response items._ + +--- + +## Profiles + +### SetCurrentProfile + + +- Added in v4.0.0 + +Set the currently active profile. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `profile-name` | _String_ | Name of the desired profile. | + + +**Response Items:** + +_No additional response items._ + +--- + +### GetCurrentProfile + + +- Added in v4.0.0 + +Get the name of the current profile. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `profile-name` | _String_ | Name of the currently active profile. | + + +--- + +### ListProfiles + + +- Added in v4.0.0 + +Get a list of available profiles. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `profiles` | _Array<Object>_ | List of available profiles. | + + +--- + +## Recording + +### GetRecordingStatus + + +- Unreleased + +Get current recording status. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `isRecording` | _boolean_ | Current recording status. | +| `isRecordingPaused` | _boolean_ | Whether the recording is paused or not. | +| `recordTimecode` | _String (optional)_ | Time elapsed since recording started (only present if currently recording). | + + +--- + +### StartStopRecording + + +- Added in v0.3 + +Toggle recording on or off. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +_No additional response items._ + +--- + +### StartRecording + + +- Added in v4.1.0 + +Start recording. +Will return an `error` if recording is already active. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +_No additional response items._ + +--- + +### StopRecording + + +- Added in v4.1.0 + +Stop recording. +Will return an `error` if recording is not active. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +_No additional response items._ + +--- + +### PauseRecording + + +- Added in v4.7.0 + +Pause the current recording. +Returns an error if recording is not active or already paused. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +_No additional response items._ + +--- + +### ResumeRecording + + +- Added in v4.7.0 + +Resume/unpause the current recording (if paused). +Returns an error if recording is not active or not paused. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +_No additional response items._ + +--- + +### SetRecordingFolder + + +- Added in v4.1.0 + + + +Please note: if `SetRecordingFolder` is called while a recording is +in progress, the change won't be applied immediately and will be +effective on the next recording. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `rec-folder` | _String_ | Path of the recording folder. | + + +**Response Items:** + +_No additional response items._ + +--- + +### GetRecordingFolder + + +- Added in v4.1.0 + +Get the path of the current recording folder. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `rec-folder` | _String_ | Path of the recording folder. | + + +--- + +## Replay Buffer + +### GetReplayBufferStatus + + +- Unreleased + +Get the status of the OBS replay buffer. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `isReplayBufferActive` | _boolean_ | Current recording status. | + + +--- + +### StartStopReplayBuffer + + +- Added in v4.2.0 + +Toggle the Replay Buffer on/off. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +_No additional response items._ + +--- + +### StartReplayBuffer + + +- Added in v4.2.0 + +Start recording into the Replay Buffer. +Will return an `error` if the Replay Buffer is already active or if the +"Save Replay Buffer" hotkey is not set in OBS' settings. +Setting this hotkey is mandatory, even when triggering saves only +through obs-websocket. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +_No additional response items._ + +--- + +### StopReplayBuffer + + +- Added in v4.2.0 + +Stop recording into the Replay Buffer. +Will return an `error` if the Replay Buffer is not active. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +_No additional response items._ + +--- + +### SaveReplayBuffer + + +- Added in v4.2.0 + +Flush and save the contents of the Replay Buffer to disk. This is +basically the same as triggering the "Save Replay Buffer" hotkey. +Will return an `error` if the Replay Buffer is not active. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +_No additional response items._ + +--- + +## Scene Collections + +### SetCurrentSceneCollection + + +- Added in v4.0.0 + +Change the active scene collection. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sc-name` | _String_ | Name of the desired scene collection. | + + +**Response Items:** + +_No additional response items._ + +--- + +### GetCurrentSceneCollection + + +- Added in v4.0.0 + +Get the name of the current scene collection. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sc-name` | _String_ | Name of the currently active scene collection. | + + +--- + +### ListSceneCollections + + +- Added in v4.0.0 + +List available scene collections + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene-collections` | _Array<String>_ | Scene collections list | + + +--- + +## Scene Items + +### GetSceneItemList + + +- Unreleased + +Get a list of all scene items in a scene. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneName` | _String_ | Name of the scene to get the list of scene items from. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneItems` | _Array<Object>_ | Array of scene items | +| `sceneItems.*.itemId` | _int_ | Unique item id of the source item | +| `sceneItems.*.sourceKind` | _String_ | ID if the scene item's source. For example `vlc_source` or `image_source` | +| `sceneItems.*.sourceName` | _String_ | Name of the scene item's source | +| `sceneItems.*.sourceType` | _String_ | Type of the scene item's source. Either `input`, `group`, or `scene` | + + +--- + +### GetSceneItemProperties + + +- Added in v4.3.0 + +Gets the scene specific properties of the specified source item. +Coordinates are relative to the item's parent (the scene or group it belongs to). + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). | +| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) | +| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `name` | _String_ | Scene Item name. | +| `itemId` | _int_ | Scene Item ID. | +| `position.x` | _double_ | The x position of the source from the left. | +| `position.y` | _double_ | The y position of the source from the top. | +| `position.alignment` | _int_ | The point on the source that the item is manipulated from. | +| `rotation` | _double_ | The clockwise rotation of the item in degrees around the point of alignment. | +| `scale.x` | _double_ | The x-scale factor of the source. | +| `scale.y` | _double_ | The y-scale factor of the source. | +| `crop.top` | _int_ | The number of pixels cropped off the top of the source before scaling. | +| `crop.right` | _int_ | The number of pixels cropped off the right of the source before scaling. | +| `crop.bottom` | _int_ | The number of pixels cropped off the bottom of the source before scaling. | +| `crop.left` | _int_ | The number of pixels cropped off the left of the source before scaling. | +| `visible` | _bool_ | If the source is visible. | +| `muted` | _bool_ | If the source is muted. | +| `locked` | _bool_ | If the source's transform is locked. | +| `bounds.type` | _String_ | Type of bounding box. Can be "OBS_BOUNDS_STRETCH", "OBS_BOUNDS_SCALE_INNER", "OBS_BOUNDS_SCALE_OUTER", "OBS_BOUNDS_SCALE_TO_WIDTH", "OBS_BOUNDS_SCALE_TO_HEIGHT", "OBS_BOUNDS_MAX_ONLY" or "OBS_BOUNDS_NONE". | +| `bounds.alignment` | _int_ | Alignment of the bounding box. | +| `bounds.x` | _double_ | Width of the bounding box. | +| `bounds.y` | _double_ | Height of the bounding box. | +| `sourceWidth` | _int_ | Base width (without scaling) of the source | +| `sourceHeight` | _int_ | Base source (without scaling) of the source | +| `width` | _double_ | Scene item width (base source width multiplied by the horizontal scaling factor) | +| `height` | _double_ | Scene item height (base source height multiplied by the vertical scaling factor) | +| `alignment` | _int_ | The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis. | +| `parentGroupName` | _String (optional)_ | Name of the item's parent (if this item belongs to a group) | +| `groupChildren` | _Array<SceneItemTransform> (optional)_ | List of children (if this item is a group) | + + +--- + +### SetSceneItemProperties + + +- Added in v4.3.0 + +Sets the scene specific properties of a source. Unspecified properties will remain unchanged. +Coordinates are relative to the item's parent (the scene or group it belongs to). + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene-name` | _String (optional)_ | Name of the scene the source item belongs to. Defaults to the current scene. | +| `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). | +| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) | +| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) | +| `position.x` | _double (optional)_ | The new x position of the source. | +| `position.y` | _double (optional)_ | The new y position of the source. | +| `position.alignment` | _int (optional)_ | The new alignment of the source. | +| `rotation` | _double (optional)_ | The new clockwise rotation of the item in degrees. | +| `scale.x` | _double (optional)_ | The new x scale of the item. | +| `scale.y` | _double (optional)_ | The new y scale of the item. | +| `crop.top` | _int (optional)_ | The new amount of pixels cropped off the top of the source before scaling. | +| `crop.bottom` | _int (optional)_ | The new amount of pixels cropped off the bottom of the source before scaling. | +| `crop.left` | _int (optional)_ | The new amount of pixels cropped off the left of the source before scaling. | +| `crop.right` | _int (optional)_ | The new amount of pixels cropped off the right of the source before scaling. | +| `visible` | _bool (optional)_ | The new visibility of the source. 'true' shows source, 'false' hides source. | +| `locked` | _bool (optional)_ | The new locked status of the source. 'true' keeps it in its current position, 'false' allows movement. | +| `bounds.type` | _String (optional)_ | The new bounds type of the source. Can be "OBS_BOUNDS_STRETCH", "OBS_BOUNDS_SCALE_INNER", "OBS_BOUNDS_SCALE_OUTER", "OBS_BOUNDS_SCALE_TO_WIDTH", "OBS_BOUNDS_SCALE_TO_HEIGHT", "OBS_BOUNDS_MAX_ONLY" or "OBS_BOUNDS_NONE". | +| `bounds.alignment` | _int (optional)_ | The new alignment of the bounding box. (0-2, 4-6, 8-10) | +| `bounds.x` | _double (optional)_ | The new width of the bounding box. | +| `bounds.y` | _double (optional)_ | The new height of the bounding box. | + + +**Response Items:** + +_No additional response items._ + +--- + +### ResetSceneItem + + +- Added in v4.2.0 + +Reset a scene item. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). | +| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) | +| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) | + + +**Response Items:** + +_No additional response items._ + +--- + +### SetSceneItemRender + +- **⚠️ Deprecated. Since 4.3.0. Prefer the use of SetSceneItemProperties. ⚠️** + +- Added in v0.3 + +Show or hide a specified source item in a specified scene. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the currently active scene. | +| `source` | _String_ | Scene Item name. | +| `render` | _boolean_ | true = shown ; false = hidden | + + +**Response Items:** + +_No additional response items._ + +--- + +### SetSceneItemPosition + +- **⚠️ Deprecated. Since 4.3.0. Prefer the use of SetSceneItemProperties. ⚠️** + +- Added in v4.0.0 + +Sets the coordinates of a specified source item. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _String_ | Scene Item name. | +| `x` | _double_ | X coordinate. | +| `y` | _double_ | Y coordinate. | + + +**Response Items:** + +_No additional response items._ + +--- + +### SetSceneItemTransform + +- **⚠️ Deprecated. Since 4.3.0. Prefer the use of SetSceneItemProperties. ⚠️** + +- Added in v4.0.0 + +Set the transform of the specified source item. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _String_ | Scene Item name. | +| `x-scale` | _double_ | Width scale factor. | +| `y-scale` | _double_ | Height scale factor. | +| `rotation` | _double_ | Source item rotation (in degrees). | + + +**Response Items:** + +_No additional response items._ + +--- + +### SetSceneItemCrop + +- **⚠️ Deprecated. Since 4.3.0. Prefer the use of SetSceneItemProperties. ⚠️** + +- Added in v4.1.0 + +Sets the crop coordinates of the specified source item. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _String_ | Scene Item name. | +| `top` | _int_ | Pixel position of the top of the source item. | +| `bottom` | _int_ | Pixel position of the bottom of the source item. | +| `left` | _int_ | Pixel position of the left of the source item. | +| `right` | _int_ | Pixel position of the right of the source item. | + + +**Response Items:** + +_No additional response items._ + +--- + +### DeleteSceneItem + + +- Added in v4.5.0 + +Deletes a scene item. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. | +| `item` | _Object_ | Scene item to delete (required) | +| `item.name` | _String_ | Scene Item name (prefer `id`, including both is acceptable). | +| `item.id` | _int_ | Scene Item ID. | + + +**Response Items:** + +_No additional response items._ + +--- + +### AddSceneItem + + +- Unreleased + +Creates a scene item in a scene. In other words, this is how you add a source into a scene. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneName` | _String_ | Name of the scene to create the scene item in | +| `sourceName` | _String_ | Name of the source to be added | +| `setVisible` | _boolean_ | Whether to make the sceneitem visible on creation or not. Default `true` | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `itemId` | _int_ | Numerical ID of the created scene item | + + +--- + +### DuplicateSceneItem + + +- Added in v4.5.0 + +Duplicates a scene item. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `fromScene` | _String (optional)_ | Name of the scene to copy the item from. Defaults to the current scene. | +| `toScene` | _String (optional)_ | Name of the scene to create the item in. Defaults to the current scene. | +| `item` | _Object_ | Scene Item to duplicate from the source scene (required) | +| `item.name` | _String_ | Scene Item name (prefer `id`, including both is acceptable). | +| `item.id` | _int_ | Scene Item ID. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene` | _String_ | Name of the scene where the new item was created | +| `item` | _Object_ | New item info | +| `item.id` | _int_ | New item ID | +| `item.name` | _String_ | New item name | + + +--- + +## Scenes + +### SetCurrentScene + + +- Added in v0.3 + +Switch to the specified scene. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene-name` | _String_ | Name of the scene to switch to. | + + +**Response Items:** + +_No additional response items._ + +--- + +### GetCurrentScene + + +- Added in v0.3 + +Get the current scene's name and source items. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `name` | _String_ | Name of the currently active scene. | +| `sources` | _Array<SceneItem>_ | Ordered list of the current scene's source items. | + + +--- + +### GetSceneList + + +- Added in v0.3 + +Get a list of scenes in the currently active profile. + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `current-scene` | _String_ | Name of the currently active scene. | +| `scenes` | _Array<Scene>_ | Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information). | + + +--- + +### CreateScene + + +- Added in v4.8.0 + +Create a new scene scene. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneName` | _String_ | Name of the scene to create. | + + +**Response Items:** + +_No additional response items._ + +--- + +### ReorderSceneItems + + +- Added in v4.5.0 + +Changes the order of scene items in the requested scene. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `scene` | _String (optional)_ | Name of the scene to reorder (defaults to current). | +| `items` | _Array<Scene>_ | Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene | +| `items[].id` | _int (optional)_ | Id of a specific scene item. Unique on a scene by scene basis. | +| `items[].name` | _String (optional)_ | Name of a scene item. Sufficiently unique if no scene items share sources within the scene. | + + +**Response Items:** + +_No additional response items._ + +--- + +### SetSceneTransitionOverride + + +- Added in v4.9.0 + +Set a scene to use a specific transition override. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneName` | _String_ | Name of the scene to switch to. | +| `transitionName` | _String_ | Name of the transition to use. | +| `transitionDuration` | _int (Optional)_ | Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given. | + + +**Response Items:** + +_No additional response items._ + +--- + +### RemoveSceneTransitionOverride + + +- Added in v4.9.0 + +Remove any transition override on a scene. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneName` | _String_ | Name of the scene to switch to. | + + +**Response Items:** + +_No additional response items._ + +--- + +### GetSceneTransitionOverride + + +- Added in v4.9.0 + +Get the current scene transition override. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneName` | _String_ | Name of the scene to switch to. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `transitionName` | _String_ | Name of the current overriding transition. Empty string if no override is set. | +| `transitionDuration` | _int_ | Transition duration. `-1` if no override is set. | + + --- ## Streaming From eb00294bbb8bad3f61ba0d9ba2c68af0f566bee3 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 9 Jul 2020 18:37:06 -0700 Subject: [PATCH 126/278] Settings: Add option to lock binding to IPv4 WebsocketPP defaults to binding to an IPv6 interface. Since IPv6 is broken on so many networks, this adds an option to force WebsocketPP to use the default IPv4 interface to bind to. --- data/locale/en-US.ini | 1 + src/Config.cpp | 18 ++- src/Config.h | 1 + src/WSServer.cpp | 15 +- src/WSServer.h | 3 +- src/forms/settings-dialog.cpp | 4 +- src/forms/settings-dialog.ui | 291 +++++++++++++++++----------------- src/obs-websocket.cpp | 2 +- 8 files changed, 184 insertions(+), 151 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 607ab2f2..69fbed16 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -3,6 +3,7 @@ OBSWebsocket.Settings.ServerEnable="Enable WebSockets server" OBSWebsocket.Settings.ServerPort="Server Port" OBSWebsocket.Settings.AuthRequired="Enable authentication" OBSWebsocket.Settings.Password="Password" +OBSWebsocket.Settings.LockToIPv4="Lock server to only using IPv4" OBSWebsocket.Settings.DebugEnable="Enable debug logging" OBSWebsocket.Settings.AlertsEnable="Enable System Tray Alerts" OBSWebsocket.NotifyConnect.Title="New WebSocket connection" diff --git a/src/Config.cpp b/src/Config.cpp index 4ed30608..1a17065f 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -25,6 +25,7 @@ with this program. If not, see #define SECTION_NAME "WebsocketAPI" #define PARAM_ENABLE "ServerEnabled" #define PARAM_PORT "ServerPort" +#define PARAM_LOCKTOIPV4 "LockToIPv4" #define PARAM_DEBUG "DebugEnabled" #define PARAM_ALERT "AlertsEnabled" #define PARAM_AUTHREQUIRED "AuthRequired" @@ -41,6 +42,7 @@ with this program. If not, see Config::Config() : ServerEnabled(true), ServerPort(4444), + LockToIPv4(false), DebugEnabled(false), AlertsEnabled(true), AuthRequired(false), @@ -67,6 +69,7 @@ void Config::Load() ServerEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ENABLE); ServerPort = config_get_uint(obsConfig, SECTION_NAME, PARAM_PORT); + LockToIPv4 = config_get_bool(obsConfig, SECTION_NAME, PARAM_LOCKTOIPV4); DebugEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_DEBUG); AlertsEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ALERT); @@ -82,6 +85,7 @@ void Config::Save() config_set_bool(obsConfig, SECTION_NAME, PARAM_ENABLE, ServerEnabled); config_set_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort); + config_set_bool(obsConfig, SECTION_NAME, PARAM_LOCKTOIPV4, LockToIPv4); config_set_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled); config_set_bool(obsConfig, SECTION_NAME, PARAM_ALERT, AlertsEnabled); @@ -104,6 +108,8 @@ void Config::SetDefaults() SECTION_NAME, PARAM_ENABLE, ServerEnabled); config_set_default_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort); + config_set_default_bool(obsConfig, + SECTION_NAME, PARAM_LOCKTOIPV4, LockToIPv4); config_set_default_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled); @@ -205,16 +211,17 @@ void Config::OnFrontendEvent(enum obs_frontend_event event, void* param) bool previousEnabled = config->ServerEnabled; uint64_t previousPort = config->ServerPort; + bool previousLock = config->LockToIPv4; config->SetDefaults(); config->Load(); - if (config->ServerEnabled != previousEnabled || config->ServerPort != previousPort) { + if (config->ServerEnabled != previousEnabled || config->ServerPort != previousPort || config->LockToIPv4 != previousLock) { auto server = GetServer(); server->stop(); if (config->ServerEnabled) { - server->start(config->ServerPort); + server->start(config->ServerPort, config->LockToIPv4); if (previousEnabled != config->ServerEnabled) { Utils::SysTrayNotify(startMessage, QSystemTrayIcon::MessageIcon::Information); @@ -246,6 +253,13 @@ void Config::MigrateFromGlobalSettings() config_remove_value(source, SECTION_NAME, PARAM_PORT); } + + if(config_has_user_value(source, SECTION_NAME, PARAM_LOCKTOIPV4)) { + bool value = config_get_bool(source, SECTION_NAME, PARAM_LOCKTOIPV4); + config_set_bool(destination, SECTION_NAME, PARAM_LOCKTOIPV4, value); + + config_remove_value(source, SECTION_NAME, PARAM_LOCKTOIPV4); + } if(config_has_user_value(source, SECTION_NAME, PARAM_DEBUG)) { bool value = config_get_bool(source, SECTION_NAME, PARAM_DEBUG); diff --git a/src/Config.h b/src/Config.h index 0845f874..f21e396a 100644 --- a/src/Config.h +++ b/src/Config.h @@ -42,6 +42,7 @@ class Config { bool ServerEnabled; uint64_t ServerPort; + bool LockToIPv4; bool DebugEnabled; bool AlertsEnabled; diff --git a/src/WSServer.cpp b/src/WSServer.cpp index 33f1bcbb..add0e1e1 100644 --- a/src/WSServer.cpp +++ b/src/WSServer.cpp @@ -60,10 +60,10 @@ WSServer::~WSServer() stop(); } -void WSServer::start(quint16 port) +void WSServer::start(quint16 port, bool lockToIPv4) { - if (_server.is_listening() && port == _serverPort) { - blog(LOG_INFO, "WSServer::start: server already on this port. no restart needed"); + if (_server.is_listening() && (port == _serverPort && _lockToIPv4 == lockToIPv4)) { + blog(LOG_INFO, "WSServer::start: server already on this port and protocol mode. no restart needed"); return; } @@ -74,9 +74,16 @@ void WSServer::start(quint16 port) _server.reset(); _serverPort = port; + _lockToIPv4 = lockToIPv4; websocketpp::lib::error_code errorCode; - _server.listen(_serverPort, errorCode); + if (lockToIPv4) { + blog(LOG_INFO, "WSServer::start: Locked to IPv4 bindings"); + _server.listen(websocketpp::lib::asio::ip::tcp::v4(), _serverPort, errorCode); + } else { + blog(LOG_INFO, "WSServer::start: Not locked to IPv4 bindings"); + _server.listen(_serverPort, errorCode); + } if (errorCode) { std::string errorCodeMessage = errorCode.message(); diff --git a/src/WSServer.h b/src/WSServer.h index 723f51af..1ca0734b 100644 --- a/src/WSServer.h +++ b/src/WSServer.h @@ -44,7 +44,7 @@ Q_OBJECT public: explicit WSServer(); virtual ~WSServer(); - void start(quint16 port); + void start(quint16 port, bool lockToIPv4); void stop(); void broadcast(const RpcEvent& event); QThreadPool* threadPool() { @@ -62,6 +62,7 @@ private: server _server; quint16 _serverPort; + bool _lockToIPv4; std::set> _connections; std::map> _connectionProperties; QMutex _clMutex; diff --git a/src/forms/settings-dialog.cpp b/src/forms/settings-dialog.cpp index 6aa2d4f1..ca686dfe 100644 --- a/src/forms/settings-dialog.cpp +++ b/src/forms/settings-dialog.cpp @@ -45,6 +45,7 @@ void SettingsDialog::showEvent(QShowEvent* event) { ui->serverEnabled->setChecked(conf->ServerEnabled); ui->serverPort->setValue(conf->ServerPort); + ui->lockToIPv4->setChecked(conf->LockToIPv4); ui->debugEnabled->setChecked(conf->DebugEnabled); ui->alertsEnabled->setChecked(conf->AlertsEnabled); @@ -72,6 +73,7 @@ void SettingsDialog::FormAccepted() { conf->ServerEnabled = ui->serverEnabled->isChecked(); conf->ServerPort = ui->serverPort->value(); + conf->LockToIPv4 = ui->lockToIPv4->isChecked(); conf->DebugEnabled = ui->debugEnabled->isChecked(); conf->AlertsEnabled = ui->alertsEnabled->isChecked(); @@ -95,7 +97,7 @@ void SettingsDialog::FormAccepted() { auto server = GetServer(); if (conf->ServerEnabled) { - server->start(conf->ServerPort); + server->start(conf->ServerPort, conf->LockToIPv4); } else { server->stop(); } diff --git a/src/forms/settings-dialog.ui b/src/forms/settings-dialog.ui index 6964a153..237fb932 100644 --- a/src/forms/settings-dialog.ui +++ b/src/forms/settings-dialog.ui @@ -2,150 +2,157 @@ SettingsDialog - - - 0 - 0 - 407 - 195 - - - - - 0 - 0 - - - - OBSWebsocket.Settings.DialogTitle - - - false - - - - QLayout::SetDefaultConstraint - - - - - - - OBSWebsocket.Settings.AuthRequired - - - - - - - OBSWebsocket.Settings.Password - - - - - - - QLineEdit::Password - - - - - - - OBSWebsocket.Settings.ServerEnable - - - true - - - - - - - OBSWebsocket.Settings.ServerPort - - - - - - - 1024 - - - 65535 - - - 4444 - - - - - - - OBSWebsocket.Settings.AlertsEnable - - - true - - - - - - - OBSWebsocket.Settings.DebugEnable - - - false - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - + + + 0 + 0 + 407 + 216 + + + + + 0 + 0 + + + + OBSWebsocket.Settings.DialogTitle + + + false + + + + QLayout::SetDefaultConstraint + + + + + + + OBSWebsocket.Settings.AuthRequired + + + + + + + OBSWebsocket.Settings.Password + + + + + + + QLineEdit::Password + + + + + + + OBSWebsocket.Settings.ServerEnable + + + true + + + + + + + OBSWebsocket.Settings.ServerPort + + + + + + + 1024 + + + 65535 + + + 4444 + + + + + + + OBSWebsocket.Settings.AlertsEnable + + + true + + + + + + + OBSWebsocket.Settings.DebugEnable + + + false + + + + + + + OBSWebsocket.Settings.LockToIPv4 + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + - - buttonBox - accepted() - SettingsDialog - accept() - - - 248 - 294 - - - 157 - 314 - - - - - buttonBox - rejected() - SettingsDialog - reject() - - - 316 - 300 - - - 286 - 314 - - - + + buttonBox + accepted() + SettingsDialog + accept() + + + 248 + 294 + + + 157 + 314 + + + + + buttonBox + rejected() + SettingsDialog + reject() + + + 316 + 300 + + + 286 + 314 + + + diff --git a/src/obs-websocket.cpp b/src/obs-websocket.cpp index 9f19ab9e..b198ebb7 100644 --- a/src/obs-websocket.cpp +++ b/src/obs-websocket.cpp @@ -81,7 +81,7 @@ bool obs_module_load(void) { auto eventCallback = [](enum obs_frontend_event event, void *param) { if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) { if (_config->ServerEnabled) { - _server->start(_config->ServerPort); + _server->start(_config->ServerPort, _config->LockToIPv4); } obs_frontend_remove_event_callback((obs_frontend_event_cb)param, nullptr); } From 9c4bd7a48705871bc37c7873cb9bfa9d8f877930 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 11 Jul 2020 05:00:10 -0700 Subject: [PATCH 127/278] Requests: Scene collection and profile name checking For `SetCurrentProfile` and `SetCurrentSceneCollection`, check if it exists before attempting to switch to it, and return an error if it does not exist. --- src/Utils.cpp | 20 ++++++++++++++++++++ src/Utils.h | 1 + src/WSRequestHandler_Profiles.cpp | 8 +++++++- src/WSRequestHandler_SceneCollections.cpp | 8 +++++++- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index ab301606..4ac3577d 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -51,6 +51,26 @@ obs_bounds_type getBoundsTypeFromName(QString name) { return boundTypeNames.key(name); } +bool Utils::StringInStringList(char** strings, const char* string) { + size_t index = 0; + char* value = nullptr; + + while (1) { + value = strings[index]; + + if (value == nullptr) { + return false; + } + + if (strcmp(value, string) == 0) { + return true; + } + + index++; + } + return false; +} + obs_data_array_t* Utils::StringListToArray(char** strings, const char* key) { obs_data_array_t* list = obs_data_array_create(); diff --git a/src/Utils.h b/src/Utils.h index dbe2481f..69b86fc5 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -35,6 +35,7 @@ typedef void(*PauseRecordingFunction)(bool); typedef bool(*RecordingPausedFunction)(); namespace Utils { + bool StringInStringList(char** strings, const char* string); obs_data_array_t* StringListToArray(char** strings, const char* key); obs_data_array_t* GetSceneItems(obs_source_t* source); obs_data_t* GetSceneItemData(obs_sceneitem_t* item); diff --git a/src/WSRequestHandler_Profiles.cpp b/src/WSRequestHandler_Profiles.cpp index 78e3ce2d..12728021 100644 --- a/src/WSRequestHandler_Profiles.cpp +++ b/src/WSRequestHandler_Profiles.cpp @@ -22,7 +22,13 @@ RpcResponse WSRequestHandler::SetCurrentProfile(const RpcRequest& request) { return request.failed("invalid request parameters"); } - // TODO : check if profile exists + char** profiles = obs_frontend_get_profiles(); + bool profileExists = Utils::StringInStringList(profiles, profileName.toUtf8()); + bfree(profiles); + if (!profileExists) { + return request.failed("profile does not exist"); + } + obs_frontend_set_current_profile(profileName.toUtf8()); return request.success(); } diff --git a/src/WSRequestHandler_SceneCollections.cpp b/src/WSRequestHandler_SceneCollections.cpp index 1f87996b..50d6417c 100644 --- a/src/WSRequestHandler_SceneCollections.cpp +++ b/src/WSRequestHandler_SceneCollections.cpp @@ -22,7 +22,13 @@ RpcResponse WSRequestHandler::SetCurrentSceneCollection(const RpcRequest& reques return request.failed("invalid request parameters"); } - // TODO : Check if specified profile exists and if changing is allowed + char** collections = obs_frontend_get_profiles(); + bool collectionExists = Utils::StringInStringList(collections, sceneCollection.toUtf8()); + bfree(collections); + if (!collectionExists) { + return request.failed("collection does not exist"); + } + obs_frontend_set_current_scene_collection(sceneCollection.toUtf8()); return request.success(); } From aa8fa008119818b81e9973390ddfb1a3a0db7fa5 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 9 Jul 2020 03:50:50 -0700 Subject: [PATCH 128/278] Docs: More updates Changes the media stuff's `@since 4.9.0` to `@since unreleased` to reduce potential confusion on the docs. As a part of the final 4.9.0 commits, I will be changing it back to 4.9.0. We also fix the contributing document since it was all messed up. --- CONTRIBUTING.md | 52 +++++++++++++-------------- src/WSEvents.cpp | 16 ++++----- src/WSRequestHandler_MediaControl.cpp | 22 ++++++------ 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8cd655c1..f94782b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,22 +12,22 @@ Localization happens on [Crowdin](https://crowdin.com/project/obs-websocket) ### Code Formatting Guidelines -- Function and variable names: snake_case for C names, camelCase for C++ method names +* Function and variable names: snake_case for C names, camelCase for C++ method names -- Request and Event names should use MixedCaps names +* Request and Event names should use MixedCaps names -- Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) +* Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml) -- Code is indented with Tabs. Assume they are 8 columns wide +* Code is indented with Tabs. Assume they are 8 columns wide -- 80 columns max code width. (Docs can be larger) +* 80 columns max code width. (Docs can be larger) -- New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples). - These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). +* New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples). +These are required to automatically generate the [protocol specification document](docs/generated/protocol.md). ### Code Best-Practices -- Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: +* Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this: ```cpp if (success) { return req->SendOKResponse(); @@ -43,23 +43,23 @@ if (!success) { return req->SendOKResponse(); ``` -- Some example common response/request property names are: - - `sceneName` - The name of a scene - - `sourceName` - The name of a source - - `fromScene` - From a scene - scene name +* Some example common response/request property names are: + * `sceneName` - The name of a scene + * `sourceName` - The name of a source + * `fromScene` - From a scene - scene name ### Commit Guidelines -- Commits follow the 50/72 standard: - - 50 characters max for the commit title (excluding scope name) - - One empty line after the title - - Description wrapped to 72 columns max width per line. +* Commits follow the 50/72 standard: + * 50 characters max for the commit title (excluding scope name) + * One empty line after the title + * Description wrapped to 72 columns max width per line. -- Commit titles: - - Use present tense - - Prefix the title with a "scope" name - - e.g: "CI: fix wrong behaviour when packaging for OS X" - - Typical scopes: CI, General, Requests, Events, Server +* Commit titles: + * Use present tense + * Prefix the title with a "scope" name + * e.g: "CI: fix wrong behaviour when packaging for OS X" + * Typical scopes: CI, General, Requests, Events, Server **Example commit:** @@ -73,10 +73,10 @@ transitions. ### Pull Requests -- Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). - - Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) +* Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). + * Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...) -- Only open a pull request if you are ready to show off your work. +* Only open a pull request if you are ready to show off your work. -- If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers), - create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). +* If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers), + create it as a Draft Pull Request (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request"). \ No newline at end of file diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index bb8e354c..0818fada 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -1430,7 +1430,7 @@ void WSEvents::OnSourceFilterOrderChanged(void* param, calldata_t* data) { * @api events * @name MediaPlaying * @category media - * @since 4.9.0 + * @since unreleased */ void WSEvents::OnMediaPlaying(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1451,7 +1451,7 @@ void WSEvents::OnMediaPlaying(void* param, calldata_t* data) { * @api events * @name MediaPaused * @category media - * @since 4.9.0 + * @since unreleased */ void WSEvents::OnMediaPaused(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1472,7 +1472,7 @@ void WSEvents::OnMediaPaused(void* param, calldata_t* data) { * @api events * @name MediaRestarted * @category media - * @since 4.9.0 + * @since unreleased */ void WSEvents::OnMediaRestarted(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1493,7 +1493,7 @@ void WSEvents::OnMediaRestarted(void* param, calldata_t* data) { * @api events * @name MediaStopped * @category media - * @since 4.9.0 + * @since unreleased */ void WSEvents::OnMediaStopped(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1514,7 +1514,7 @@ void WSEvents::OnMediaStopped(void* param, calldata_t* data) { * @api events * @name MediaNext * @category media - * @since 4.9.0 + * @since unreleased */ void WSEvents::OnMediaNext(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1535,7 +1535,7 @@ void WSEvents::OnMediaNext(void* param, calldata_t* data) { * @api events * @name MediaPrevious * @category media - * @since 4.9.0 + * @since unreleased */ void WSEvents::OnMediaPrevious(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1556,7 +1556,7 @@ void WSEvents::OnMediaPrevious(void* param, calldata_t* data) { * @api events * @name MediaStarted * @category media - * @since 4.9.0 + * @since unreleased */ void WSEvents::OnMediaStarted(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1577,7 +1577,7 @@ void WSEvents::OnMediaStarted(void* param, calldata_t* data) { * @api events * @name MediaEnded * @category media - * @since 4.9.0 + * @since unreleased */ void WSEvents::OnMediaEnded(void* param, calldata_t* data) { auto self = reinterpret_cast(param); diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp index 1b7a73bf..225c40c5 100644 --- a/src/WSRequestHandler_MediaControl.cpp +++ b/src/WSRequestHandler_MediaControl.cpp @@ -51,7 +51,7 @@ QString getSourceMediaState(obs_source_t *source) * @api requests * @name PlayPauseMedia * @category media control -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) { if ((!request.hasField("sourceName")) || (!request.hasField("playPause"))) { @@ -81,7 +81,7 @@ RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) { * @api requests * @name RestartMedia * @category media control -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::RestartMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -110,7 +110,7 @@ RpcResponse WSRequestHandler::RestartMedia(const RpcRequest& request) { * @api requests * @name StopMedia * @category media control -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::StopMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -139,7 +139,7 @@ RpcResponse WSRequestHandler::StopMedia(const RpcRequest& request) { * @api requests * @name NextMedia * @category media control -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::NextMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -168,7 +168,7 @@ RpcResponse WSRequestHandler::NextMedia(const RpcRequest& request) { * @api requests * @name PreviousMedia * @category media control -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::PreviousMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -200,7 +200,7 @@ RpcResponse WSRequestHandler::PreviousMedia(const RpcRequest& request) { * @api requests * @name GetMediaDuration * @category media control -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -232,7 +232,7 @@ RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) { * @api requests * @name GetMediaTime * @category media control -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -263,7 +263,7 @@ RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) { * @api requests * @name SetMediaTime * @category media control -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) { if (!request.hasField("sourceName") || !request.hasField("timestamp")) { @@ -295,7 +295,7 @@ RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) { * @api requests * @name ScrubMedia * @category media control -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) { if (!request.hasField("sourceName") || !request.hasField("timeOffset")) { @@ -332,7 +332,7 @@ RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) { * @api requests * @name GetMediaState * @category media control -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -366,7 +366,7 @@ RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) { * @api requests * @name GetMediaSourcesList * @category sources -* @since 4.9.0 +* @since unreleased */ RpcResponse WSRequestHandler::GetMediaSourcesList(const RpcRequest& request) { From ac9c6b4a1f70b09e202f6e407b5227d59c43bd2f Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sun, 12 Jul 2020 02:22:26 -0700 Subject: [PATCH 129/278] Docs: Fix GDIPlus requests documentation `bk-color` and `bk-opacity` are actually `bk_color` and `bk_opacity` respectively. Issue reported in #561 --- src/WSRequestHandler_Sources.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index f7d8605e..036b41c1 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -596,8 +596,8 @@ RpcResponse WSRequestHandler::SetSourceSettings(const RpcRequest& request) * * @return {String} `source` Source name. * @return {String} `align` Text Alignment ("left", "center", "right"). - * @return {int} `bk-color` Background color. - * @return {int} `bk-opacity` Background opacity (0-100). + * @return {int} `bk_color` Background color. + * @return {int} `bk_opacity` Background opacity (0-100). * @return {boolean} `chatlog` Chat log. * @return {int} `chatlog_lines` Chat log lines. * @return {int} `color` Text color. @@ -656,8 +656,8 @@ RpcResponse WSRequestHandler::GetTextGDIPlusProperties(const RpcRequest& request * * @param {String} `source` Name of the source. * @param {String (optional)} `align` Text Alignment ("left", "center", "right"). - * @param {int (optional)} `bk-color` Background color. - * @param {int (optional)} `bk-opacity` Background opacity (0-100). + * @param {int (optional)} `bk_color` Background color. + * @param {int (optional)} `bk_opacity` Background opacity (0-100). * @param {boolean (optional)} `chatlog` Chat log. * @param {int (optional)} `chatlog_lines` Chat log lines. * @param {int (optional)} `color` Text color. From c368696ddce80ac3b459db55b853e18d92d5fe48 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Wed, 15 Jul 2020 13:00:59 +0000 Subject: [PATCH 130/278] docs(ci): Update protocol.md - 94269c0 [skip ci] --- docs/generated/comments.json | 217 +++++++++++++++++++---------------- docs/generated/protocol.md | 120 ++++++++++--------- 2 files changed, 184 insertions(+), 153 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 5bfee9f2..5447e5f5 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -561,7 +561,7 @@ }, { "subheads": [], - "description": "The scene list has been modified.\nScenes have been added, removed, or renamed.", + "description": "\n\nNote: This event is not fired when the scenes are reordered.", "api": "events", "name": "ScenesChanged", "category": "scenes", @@ -588,7 +588,7 @@ "level": 2, "text": "ScenesChanged" }, - "lead": "", + "lead": "The scene list has been modified. Scenes have been added, removed, or renamed.", "type": "class", "examples": [] }, @@ -1190,7 +1190,7 @@ }, { "subheads": [], - "description": "Emit every 2 seconds.", + "description": "Emitted every 2 seconds when stream is active.", "return": [ "{boolean} `streaming` Current streaming state.", "{boolean} `recording` Current recording state.", @@ -1730,6 +1730,7 @@ "api": "events", "name": "Heartbeat", "category": "general", + "since": "v0.3", "returns": [ { "type": "boolean", @@ -1804,6 +1805,12 @@ "description": "general" } ], + "sinces": [ + { + "name": "", + "description": "v0.3" + } + ], "heading": { "level": 2, "text": "Heartbeat" @@ -1814,7 +1821,7 @@ }, { "subheads": [], - "description": "A custom broadcast message was received", + "description": "A custom broadcast message, sent by the server, requested by one of the websocket clients.", "return": [ "{String} `realm` Identifier provided by the sender", "{Object} `data` User-defined data" @@ -2574,7 +2581,7 @@ "api": "events", "name": "MediaPlaying", "category": "media", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "String", @@ -2602,7 +2609,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -2623,7 +2630,7 @@ "api": "events", "name": "MediaPaused", "category": "media", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "String", @@ -2651,7 +2658,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -2672,7 +2679,7 @@ "api": "events", "name": "MediaRestarted", "category": "media", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "String", @@ -2700,7 +2707,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -2721,7 +2728,7 @@ "api": "events", "name": "MediaStopped", "category": "media", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "String", @@ -2749,7 +2756,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -2770,7 +2777,7 @@ "api": "events", "name": "MediaNext", "category": "media", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "String", @@ -2798,7 +2805,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -2819,7 +2826,7 @@ "api": "events", "name": "MediaPrevious", "category": "media", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "String", @@ -2847,7 +2854,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -2868,7 +2875,7 @@ "api": "events", "name": "MediaStarted", "category": "media", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "String", @@ -2896,7 +2903,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -2917,7 +2924,7 @@ "api": "events", "name": "MediaEnded", "category": "media", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "String", @@ -2945,7 +2952,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -2960,7 +2967,7 @@ "scene items": [ { "subheads": [], - "description": "Scene items have been reordered.", + "description": "Scene items within a scene have been reordered.", "return": [ "{String} `scene-name` Name of the scene where items have been reordered.", "{Array} `scene-items` Ordered list of scene items", @@ -3021,7 +3028,7 @@ }, { "subheads": [], - "description": "An item has been added to the current scene.", + "description": "A scene item has been added to a scene.", "return": [ "{String} `scene-name` Name of the scene.", "{String} `item-name` Name of the item added to the scene.", @@ -3076,7 +3083,7 @@ }, { "subheads": [], - "description": "An item has been removed from the current scene.", + "description": "A scene item has been removed from a scene.", "return": [ "{String} `scene-name` Name of the scene.", "{String} `item-name` Name of the item removed from the scene.", @@ -3131,7 +3138,7 @@ }, { "subheads": [], - "description": "An item's visibility has been toggled.", + "description": "A scene item's visibility has been toggled.", "return": [ "{String} `scene-name` Name of the scene.", "{String} `item-name` Name of the item in the scene.", @@ -3192,7 +3199,7 @@ }, { "subheads": [], - "description": "An item's locked status has been toggled.", + "description": "A scene item's locked status has been toggled.", "return": [ "{String} `scene-name` Name of the scene.", "{String} `item-name` Name of the item in the scene.", @@ -3253,7 +3260,7 @@ }, { "subheads": [], - "description": "An item's transform has been changed.", + "description": "A scene item's transform has been changed.", "return": [ "{String} `scene-name` Name of the scene.", "{String} `item-name` Name of the item in the scene.", @@ -3689,6 +3696,7 @@ "name": "SetHeartbeat", "category": "general", "since": "4.3.0", + "deprecated": "Since 4.9.0. Please poll the appropriate data using requests. Will be removed in v5.0.0.", "params": [ { "type": "boolean", @@ -3714,6 +3722,12 @@ "description": "4.3.0" } ], + "deprecateds": [ + { + "name": "", + "description": "Since 4.9.0. Please poll the appropriate data using requests. Will be removed in v5.0.0." + } + ], "heading": { "level": 2, "text": "SetHeartbeat" @@ -3807,7 +3821,7 @@ { "subheads": [], "description": "Get OBS stats (almost the same info as provided in OBS' stats window)", - "return": "{OBSStats} `stats` OBS stats", + "return": "{OBSStats} `stats` [OBS stats](#obsstats)", "api": "requests", "name": "GetStats", "category": "general", @@ -3816,7 +3830,7 @@ { "type": "OBSStats", "name": "stats", - "description": "OBS stats" + "description": "[OBS stats](#obsstats)" } ], "names": [ @@ -3989,9 +4003,9 @@ "subheads": [], "description": "Open a projector window or create a projector on a monitor. Requires OBS v24.0.4 or newer.", "param": [ - "{String (Optional)} `type` Type of projector: Preview (default), Source, Scene, StudioProgram, or Multiview (case insensitive).", + "{String (Optional)} `type` Type of projector: `Preview` (default), `Source`, `Scene`, `StudioProgram`, or `Multiview` (case insensitive).", "{int (Optional)} `monitor` Monitor to open the projector on. If -1 or omitted, opens a window.", - "{String (Optional)} `geometry` Size and position of the projector window (only if monitor is -1). Encoded in Base64 using Qt's geometry encoding (https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors.", + "{String (Optional)} `geometry` Size and position of the projector window (only if monitor is -1). Encoded in Base64 using [Qt's geometry encoding](https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors.", "{String (Optional)} `name` Name of the source or scene to be displayed (ignored for other projector types)." ], "api": "requests", @@ -4002,7 +4016,7 @@ { "type": "String (Optional)", "name": "type", - "description": "Type of projector: Preview (default), Source, Scene, StudioProgram, or Multiview (case insensitive)." + "description": "Type of projector: `Preview` (default), `Source`, `Scene`, `StudioProgram`, or `Multiview` (case insensitive)." }, { "type": "int (Optional)", @@ -4012,7 +4026,7 @@ { "type": "String (Optional)", "name": "geometry", - "description": "Size and position of the projector window (only if monitor is -1). Encoded in Base64 using Qt's geometry encoding (https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors." + "description": "Size and position of the projector window (only if monitor is -1). Encoded in Base64 using [Qt's geometry encoding](https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors." }, { "type": "String (Optional)", @@ -4058,7 +4072,7 @@ "api": "requests", "name": "PlayPauseMedia", "category": "media control", - "since": "4.9.0", + "since": "unreleased", "params": [ { "type": "String", @@ -4086,7 +4100,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -4104,7 +4118,7 @@ "api": "requests", "name": "RestartMedia", "category": "media control", - "since": "4.9.0", + "since": "unreleased", "params": [ { "type": "String", @@ -4127,7 +4141,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -4145,7 +4159,7 @@ "api": "requests", "name": "StopMedia", "category": "media control", - "since": "4.9.0", + "since": "unreleased", "params": [ { "type": "String", @@ -4168,7 +4182,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -4186,7 +4200,7 @@ "api": "requests", "name": "NextMedia", "category": "media control", - "since": "4.9.0", + "since": "unreleased", "params": [ { "type": "String", @@ -4209,7 +4223,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -4227,7 +4241,7 @@ "api": "requests", "name": "PreviousMedia", "category": "media control", - "since": "4.9.0", + "since": "unreleased", "params": [ { "type": "String", @@ -4250,7 +4264,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -4269,7 +4283,7 @@ "api": "requests", "name": "GetMediaDuration", "category": "media control", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "int", @@ -4299,7 +4313,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -4318,7 +4332,7 @@ "api": "requests", "name": "GetMediaTime", "category": "media control", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "int", @@ -4348,7 +4362,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -4369,7 +4383,7 @@ "api": "requests", "name": "SetMediaTime", "category": "media control", - "since": "4.9.0", + "since": "unreleased", "params": [ { "type": "String", @@ -4397,7 +4411,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -4418,7 +4432,7 @@ "api": "requests", "name": "ScrubMedia", "category": "media control", - "since": "4.9.0", + "since": "unreleased", "params": [ { "type": "String", @@ -4446,7 +4460,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -4465,7 +4479,7 @@ "api": "requests", "name": "GetMediaState", "category": "media control", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "String", @@ -4495,7 +4509,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -4520,7 +4534,7 @@ "api": "requests", "name": "GetMediaSourcesList", "category": "sources", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "Array", @@ -4558,7 +4572,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -4575,7 +4589,7 @@ "return": [ "{Array} `sources` Array of sources", "{String} `sources.*.name` Unique source name", - "{String} `sources.*.typeId` Non-unique source internal type (a.k.a type id)", + "{String} `sources.*.typeId` Non-unique source internal type (a.k.a kind)", "{String} `sources.*.type` Source type. Value is one of the following: \"input\", \"filter\", \"transition\", \"scene\" or \"unknown\"" ], "api": "requests", @@ -4596,7 +4610,7 @@ { "type": "String", "name": "sources.*.typeId", - "description": "Non-unique source internal type (a.k.a type id)" + "description": "Non-unique source internal type (a.k.a kind)" }, { "type": "String", @@ -5069,7 +5083,7 @@ }, { "subheads": [], - "description": "\n\nNote: If the new name already exists as a source, OBS will automatically modify the name to not interfere.", + "description": "\n\nNote: If the new name already exists as a source, obs-websocket will return an error.", "param": [ "{String} `sourceName` Source name.", "{String} `newName` New source name." @@ -5377,8 +5391,8 @@ "return": [ "{String} `source` Source name.", "{String} `align` Text Alignment (\"left\", \"center\", \"right\").", - "{int} `bk-color` Background color.", - "{int} `bk-opacity` Background opacity (0-100).", + "{int} `bk_color` Background color.", + "{int} `bk_opacity` Background opacity (0-100).", "{boolean} `chatlog` Chat log.", "{int} `chatlog_lines` Chat log lines.", "{int} `color` Text color.", @@ -5421,12 +5435,12 @@ }, { "type": "int", - "name": "bk-color", + "name": "bk_color", "description": "Background color." }, { "type": "int", - "name": "bk-opacity", + "name": "bk_opacity", "description": "Background opacity (0-100)." }, { @@ -5589,8 +5603,8 @@ "param": [ "{String} `source` Name of the source.", "{String (optional)} `align` Text Alignment (\"left\", \"center\", \"right\").", - "{int (optional)} `bk-color` Background color.", - "{int (optional)} `bk-opacity` Background opacity (0-100).", + "{int (optional)} `bk_color` Background color.", + "{int (optional)} `bk_opacity` Background opacity (0-100).", "{boolean (optional)} `chatlog` Chat log.", "{int (optional)} `chatlog_lines` Chat log lines.", "{int (optional)} `color` Text color.", @@ -5634,12 +5648,12 @@ }, { "type": "int (optional)", - "name": "bk-color", + "name": "bk_color", "description": "Background color." }, { "type": "int (optional)", - "name": "bk-opacity", + "name": "bk_opacity", "description": "Background opacity (0-100)." }, { @@ -6087,7 +6101,7 @@ "name": "GetBrowserSourceProperties", "category": "sources", "since": "4.1.0", - "deprecated": "Since 4.8.0. Prefer the use of GetSourceSettings.", + "deprecated": "Since 4.8.0. Prefer the use of GetSourceSettings. Will be removed in v5.0.0", "returns": [ { "type": "String", @@ -6163,7 +6177,7 @@ "deprecateds": [ { "name": "", - "description": "Since 4.8.0. Prefer the use of GetSourceSettings." + "description": "Since 4.8.0. Prefer the use of GetSourceSettings. Will be removed in v5.0.0" } ], "heading": { @@ -6192,7 +6206,7 @@ "api": "requests", "name": "SetBrowserSourceProperties", "category": "sources", - "deprecated": "Since 4.8.0. Prefer the use of SetSourceSettings.", + "deprecated": "Since 4.8.0. Prefer the use of SetSourceSettings. Will be removed in v5.0.0", "since": "4.1.0", "params": [ { @@ -6261,7 +6275,7 @@ "deprecateds": [ { "name": "", - "description": "Since 4.8.0. Prefer the use of SetSourceSettings." + "description": "Since 4.8.0. Prefer the use of SetSourceSettings. Will be removed in v5.0.0" } ], "sinces": [ @@ -7120,7 +7134,7 @@ }, { "subheads": [], - "description": "Start an output", + "description": "\n\nNote: Controlling outputs is an experimental feature of obs-websocket. Some plugins which add outputs to OBS may not function properly when they are controlled in this way.", "param": "{String} `outputName` Output name", "api": "requests", "name": "StartOutput", @@ -7155,13 +7169,13 @@ "level": 2, "text": "StartOutput" }, - "lead": "", + "lead": "Start an output", "type": "class", "examples": [] }, { "subheads": [], - "description": "Stop an output", + "description": "\n\nNote: Controlling outputs is an experimental feature of obs-websocket. Some plugins which add outputs to OBS may not function properly when they are controlled in this way.", "param": [ "{String} `outputName` Output name", "{boolean (optional)} `force` Force stop (default: false)" @@ -7204,7 +7218,7 @@ "level": 2, "text": "StopOutput" }, - "lead": "", + "lead": "Stop an output", "type": "class", "examples": [] } @@ -7295,7 +7309,10 @@ { "subheads": [], "description": "Get a list of available profiles.", - "return": "{Array} `profiles` List of available profiles.", + "return": [ + "{Array} `profiles` List of available profiles.", + "{String} `profiles.*.profile-name` Filter name" + ], "api": "requests", "name": "ListProfiles", "category": "profiles", @@ -7305,6 +7322,11 @@ "type": "Array", "name": "profiles", "description": "List of available profiles." + }, + { + "type": "String", + "name": "profiles.*.profile-name", + "description": "Filter name" } ], "names": [ @@ -7392,7 +7414,7 @@ }, { "subheads": [], - "description": "Toggle recording on or off.", + "description": "Toggle recording on or off (depending on the current recording state).", "api": "requests", "name": "StartStopRecording", "category": "recording", @@ -7682,7 +7704,7 @@ }, { "subheads": [], - "description": "Toggle the Replay Buffer on/off.", + "description": "Toggle the Replay Buffer on/off (depending on the current state of the replay buffer).", "api": "requests", "name": "StartStopReplayBuffer", "category": "replay buffer", @@ -7899,7 +7921,10 @@ { "subheads": [], "description": "List available scene collections", - "return": "{Array} `scene-collections` Scene collections list", + "return": [ + "{Array} `scene-collections` Scene collections list", + "{String} `scene-collections.*.sc-name` Scene collection name" + ], "api": "requests", "name": "ListSceneCollections", "category": "scene collections", @@ -7909,6 +7934,11 @@ "type": "Array", "name": "scene-collections", "description": "Scene collections list" + }, + { + "type": "String", + "name": "scene-collections.*.sc-name", + "description": "Scene collection name" } ], "names": [ @@ -8465,7 +8495,6 @@ "name": "SetSceneItemRender", "category": "scene items", "since": "0.3", - "deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.", "params": [ { "type": "String (optional)", @@ -8501,12 +8530,6 @@ "description": "0.3" } ], - "deprecateds": [ - { - "name": "", - "description": "Since 4.3.0. Prefer the use of SetSceneItemProperties." - } - ], "heading": { "level": 2, "text": "SetSceneItemRender" @@ -9144,8 +9167,8 @@ "param": [ "{String (optional)} `scene` Name of the scene to reorder (defaults to current).", "{Array} `items` Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene", - "{int (optional)} `items[].id` Id of a specific scene item. Unique on a scene by scene basis.", - "{String (optional)} `items[].name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene." + "{int (optional)} `items.*.id` Id of a specific scene item. Unique on a scene by scene basis.", + "{String (optional)} `items.*.name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene." ], "api": "requests", "name": "ReorderSceneItems", @@ -9164,12 +9187,12 @@ }, { "type": "int (optional)", - "name": "items[].id", + "name": "items.*.id", "description": "Id of a specific scene item. Unique on a scene by scene basis." }, { "type": "String (optional)", - "name": "items[].name", + "name": "items.*.name", "description": "Name of a scene item. Sufficiently unique if no scene items share sources within the scene." } ], @@ -9210,7 +9233,7 @@ "api": "requests", "name": "SetSceneTransitionOverride", "category": "scenes", - "since": "4.9.0", + "since": "unreleased", "params": [ { "type": "String", @@ -9243,7 +9266,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -9261,7 +9284,7 @@ "api": "requests", "name": "RemoveSceneTransitionOverride", "category": "scenes", - "since": "4.9.0", + "since": "unreleased", "params": [ { "type": "String", @@ -9284,7 +9307,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -9306,7 +9329,7 @@ "api": "requests", "name": "GetSceneTransitionOverride", "category": "scenes", - "since": "4.9.0", + "since": "unreleased", "returns": [ { "type": "String", @@ -9341,7 +9364,7 @@ "sinces": [ { "name": "", - "description": "4.9.0" + "description": "unreleased" } ], "heading": { @@ -9423,7 +9446,7 @@ }, { "subheads": [], - "description": "Toggle streaming on or off.", + "description": "Toggle streaming on or off (depending on the current stream state).", "api": "requests", "name": "StartStopStreaming", "category": "streaming", @@ -9777,7 +9800,7 @@ }, { "subheads": [], - "description": "Send the provided text as embedded CEA-608 caption data.\nAs of OBS Studio 23.1, captions are not yet available on Linux.", + "description": "Send the provided text as embedded CEA-608 caption data.", "param": "{String} `text` Captions text", "api": "requests", "name": "SendCaptions", @@ -10072,7 +10095,7 @@ }, { "subheads": [], - "description": "Toggles Studio Mode.", + "description": "Toggles Studio Mode (depending on the current state of studio mode).", "api": "requests", "name": "ToggleStudioMode", "category": "studio mode", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index b2c5a84e..a5d3a57f 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -4,7 +4,7 @@ # General Introduction Messages are exchanged between the client and the server as JSON objects. -This protocol is based on the original OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio. +This protocol is based on the original OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio. As of v5.0.0, backwards compatability with the protocol will not be kept. # Authentication `obs-websocket` uses SHA256 to transmit credentials. @@ -35,6 +35,8 @@ auth_response_hash = binary_sha256(auth_response_string) auth_response = base64_encode(auth_response_hash) ``` +You can also refer to any of the client libraries listed on the [README](README.md) for examples of how to authenticate. + @@ -380,8 +382,9 @@ Indicates a scene change. - Added in v0.3 -The scene list has been modified. -Scenes have been added, removed, or renamed. + + +Note: This event is not fired when the scenes are reordered. **Response Items:** @@ -616,7 +619,7 @@ _No additional response items._ - Added in v0.3 -Emit every 2 seconds. +Emitted every 2 seconds when stream is active. **Response Items:** @@ -799,7 +802,7 @@ _No additional response items._ ### Heartbeat -- Added in v +- Added in vv0.3 Emitted every 2 seconds after enabling it by calling SetHeartbeat. @@ -828,7 +831,7 @@ Emitted every 2 seconds after enabling it by calling SetHeartbeat. - Added in v4.7.0 -A custom broadcast message was received +A custom broadcast message, sent by the server, requested by one of the websocket clients. **Response Items:** @@ -1079,7 +1082,7 @@ Filters in a source have been reordered. ### MediaPlaying -- Added in v4.9.0 +- Unreleased @@ -1098,7 +1101,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaPaused -- Added in v4.9.0 +- Unreleased @@ -1117,7 +1120,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaRestarted -- Added in v4.9.0 +- Unreleased @@ -1136,7 +1139,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaStopped -- Added in v4.9.0 +- Unreleased @@ -1155,7 +1158,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaNext -- Added in v4.9.0 +- Unreleased @@ -1174,7 +1177,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaPrevious -- Added in v4.9.0 +- Unreleased @@ -1193,7 +1196,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaStarted -- Added in v4.9.0 +- Unreleased @@ -1212,7 +1215,7 @@ Note: These events are emitted by the OBS sources themselves. For example when t ### MediaEnded -- Added in v4.9.0 +- Unreleased @@ -1235,7 +1238,7 @@ Note: These events are emitted by the OBS sources themselves. For example when t - Added in v4.0.0 -Scene items have been reordered. +Scene items within a scene have been reordered. **Response Items:** @@ -1254,7 +1257,7 @@ Scene items have been reordered. - Added in v4.0.0 -An item has been added to the current scene. +A scene item has been added to a scene. **Response Items:** @@ -1272,7 +1275,7 @@ An item has been added to the current scene. - Added in v4.0.0 -An item has been removed from the current scene. +A scene item has been removed from a scene. **Response Items:** @@ -1290,7 +1293,7 @@ An item has been removed from the current scene. - Added in v4.0.0 -An item's visibility has been toggled. +A scene item's visibility has been toggled. **Response Items:** @@ -1309,7 +1312,7 @@ An item's visibility has been toggled. - Added in v4.8.0 -An item's locked status has been toggled. +A scene item's locked status has been toggled. **Response Items:** @@ -1328,7 +1331,7 @@ An item's locked status has been toggled. - Added in v4.6.0 -An item's transform has been changed. +A scene item's transform has been changed. **Response Items:** @@ -1424,7 +1427,7 @@ Requests are sent by the client and require at least the following two fields: Once a request is sent, the server will return a JSON response with at least the following fields: - `message-id` _String_: The client defined identifier specified in the request. - `status` _String_: Response status, will be one of the following: `ok`, `error` -- `error` _String_: An error message accompanying an `error` status. +- `error` _String (Optional)_: An error message accompanying an `error` status. Additional information may be required/returned depending on the request type. See below for more information. @@ -1500,6 +1503,7 @@ _No additional response items._ ### SetHeartbeat +- **⚠️ Deprecated. Since 4.9.0. Please poll the appropriate data using requests. Will be removed in v5.0.0. ⚠️** - Added in v4.3.0 @@ -1573,7 +1577,7 @@ _No specified parameters._ | Name | Type | Description | | ---- | :---: | ------------| -| `stats` | _OBSStats_ | OBS stats | +| `stats` | _OBSStats_ | [OBS stats](#obsstats) | --- @@ -1638,9 +1642,9 @@ Open a projector window or create a projector on a monitor. Requires OBS v24.0.4 | Name | Type | Description | | ---- | :---: | ------------| -| `type` | _String (Optional)_ | Type of projector: Preview (default), Source, Scene, StudioProgram, or Multiview (case insensitive). | +| `type` | _String (Optional)_ | Type of projector: `Preview` (default), `Source`, `Scene`, `StudioProgram`, or `Multiview` (case insensitive). | | `monitor` | _int (Optional)_ | Monitor to open the projector on. If -1 or omitted, opens a window. | -| `geometry` | _String (Optional)_ | Size and position of the projector window (only if monitor is -1). Encoded in Base64 using Qt's geometry encoding (https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors. | +| `geometry` | _String (Optional)_ | Size and position of the projector window (only if monitor is -1). Encoded in Base64 using [Qt's geometry encoding](https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors. | | `name` | _String (Optional)_ | Name of the source or scene to be displayed (ignored for other projector types). | @@ -1655,7 +1659,7 @@ _No additional response items._ ### PlayPauseMedia -- Added in v4.9.0 +- Unreleased Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -1676,7 +1680,7 @@ _No additional response items._ ### RestartMedia -- Added in v4.9.0 +- Unreleased Restart a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -1696,7 +1700,7 @@ _No additional response items._ ### StopMedia -- Added in v4.9.0 +- Unreleased Stop a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -1716,7 +1720,7 @@ _No additional response items._ ### NextMedia -- Added in v4.9.0 +- Unreleased Skip to the next media item in the playlist. Supports only vlc media source (as of OBS v25.0.8) @@ -1736,7 +1740,7 @@ _No additional response items._ ### PreviousMedia -- Added in v4.9.0 +- Unreleased Go to the previous media item in the playlist. Supports only vlc media source (as of OBS v25.0.8) @@ -1756,7 +1760,7 @@ _No additional response items._ ### GetMediaDuration -- Added in v4.9.0 +- Unreleased Get the length of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) Note: For some reason, for the first 5 or so seconds that the media is playing, the total duration can be off by upwards of 50ms. @@ -1780,7 +1784,7 @@ Note: For some reason, for the first 5 or so seconds that the media is playing, ### GetMediaTime -- Added in v4.9.0 +- Unreleased Get the current timestamp of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -1803,7 +1807,7 @@ Get the current timestamp of media in milliseconds. Supports ffmpeg and vlc medi ### SetMediaTime -- Added in v4.9.0 +- Unreleased Set the timestamp of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -1824,7 +1828,7 @@ _No additional response items._ ### ScrubMedia -- Added in v4.9.0 +- Unreleased Scrub media using a supplied offset. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) Note: Due to processing/network delays, this request is not perfect. The processing rate of this request has also not been tested. @@ -1846,7 +1850,7 @@ _No additional response items._ ### GetMediaState -- Added in v4.9.0 +- Unreleased Get the current playing state of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -1871,7 +1875,7 @@ Get the current playing state of a media source. Supports ffmpeg and vlc media s ### GetMediaSourcesList -- Added in v4.9.0 +- Unreleased List the media state of all media sources (vlc and media source) @@ -1908,7 +1912,7 @@ _No specified parameters._ | ---- | :---: | ------------| | `sources` | _Array<Object>_ | Array of sources | | `sources.*.name` | _String_ | Unique source name | -| `sources.*.typeId` | _String_ | Non-unique source internal type (a.k.a type id) | +| `sources.*.typeId` | _String_ | Non-unique source internal type (a.k.a kind) | | `sources.*.type` | _String_ | Source type. Value is one of the following: "input", "filter", "transition", "scene" or "unknown" | @@ -2089,7 +2093,7 @@ Get the audio's active status of a specified source. -Note: If the new name already exists as a source, OBS will automatically modify the name to not interfere. +Note: If the new name already exists as a source, obs-websocket will return an error. **Request Fields:** @@ -2223,8 +2227,8 @@ Get the current properties of a Text GDI Plus source. | ---- | :---: | ------------| | `source` | _String_ | Source name. | | `align` | _String_ | Text Alignment ("left", "center", "right"). | -| `bk-color` | _int_ | Background color. | -| `bk-opacity` | _int_ | Background opacity (0-100). | +| `bk_color` | _int_ | Background color. | +| `bk_opacity` | _int_ | Background opacity (0-100). | | `chatlog` | _boolean_ | Chat log. | | `chatlog_lines` | _int_ | Chat log lines. | | `color` | _int_ | Text color. | @@ -2266,8 +2270,8 @@ Set the current properties of a Text GDI Plus source. | ---- | :---: | ------------| | `source` | _String_ | Name of the source. | | `align` | _String (optional)_ | Text Alignment ("left", "center", "right"). | -| `bk-color` | _int (optional)_ | Background color. | -| `bk-opacity` | _int (optional)_ | Background opacity (0-100). | +| `bk_color` | _int (optional)_ | Background color. | +| `bk_opacity` | _int (optional)_ | Background opacity (0-100). | | `chatlog` | _boolean (optional)_ | Chat log. | | `chatlog_lines` | _int (optional)_ | Chat log lines. | | `color` | _int (optional)_ | Text color. | @@ -2376,7 +2380,7 @@ _No additional response items._ ### GetBrowserSourceProperties -- **⚠️ Deprecated. Since 4.8.0. Prefer the use of GetSourceSettings. ⚠️** +- **⚠️ Deprecated. Since 4.8.0. Prefer the use of GetSourceSettings. Will be removed in v5.0.0 ⚠️** - Added in v4.1.0 @@ -2408,7 +2412,7 @@ Get current properties for a Browser Source. ### SetBrowserSourceProperties -- **⚠️ Deprecated. Since 4.8.0. Prefer the use of SetSourceSettings. ⚠️** +- **⚠️ Deprecated. Since 4.8.0. Prefer the use of SetSourceSettings. Will be removed in v5.0.0 ⚠️** - Added in v4.1.0 @@ -2776,7 +2780,9 @@ Get information about a single output - Added in v4.7.0 -Start an output + + +Note: Controlling outputs is an experimental feature of obs-websocket. Some plugins which add outputs to OBS may not function properly when they are controlled in this way. **Request Fields:** @@ -2796,7 +2802,9 @@ _No additional response items._ - Added in v4.7.0 -Stop an output + + +Note: Controlling outputs is an experimental feature of obs-websocket. Some plugins which add outputs to OBS may not function properly when they are controlled in this way. **Request Fields:** @@ -2870,6 +2878,7 @@ _No specified parameters._ | Name | Type | Description | | ---- | :---: | ------------| | `profiles` | _Array<Object>_ | List of available profiles. | +| `profiles.*.profile-name` | _String_ | Filter name | --- @@ -2903,7 +2912,7 @@ _No specified parameters._ - Added in v0.3 -Toggle recording on or off. +Toggle recording on or off (depending on the current recording state). **Request Fields:** @@ -3058,7 +3067,7 @@ _No specified parameters._ - Added in v4.2.0 -Toggle the Replay Buffer on/off. +Toggle the Replay Buffer on/off (depending on the current state of the replay buffer). **Request Fields:** @@ -3186,6 +3195,7 @@ _No specified parameters._ | Name | Type | Description | | ---- | :---: | ------------| | `scene-collections` | _Array<String>_ | Scene collections list | +| `scene-collections.*.sc-name` | _String_ | Scene collection name | --- @@ -3336,7 +3346,6 @@ _No additional response items._ ### SetSceneItemRender -- **⚠️ Deprecated. Since 4.3.0. Prefer the use of SetSceneItemProperties. ⚠️** - Added in v0.3 @@ -3607,8 +3616,8 @@ Changes the order of scene items in the requested scene. | ---- | :---: | ------------| | `scene` | _String (optional)_ | Name of the scene to reorder (defaults to current). | | `items` | _Array<Scene>_ | Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene | -| `items[].id` | _int (optional)_ | Id of a specific scene item. Unique on a scene by scene basis. | -| `items[].name` | _String (optional)_ | Name of a scene item. Sufficiently unique if no scene items share sources within the scene. | +| `items.*.id` | _int (optional)_ | Id of a specific scene item. Unique on a scene by scene basis. | +| `items.*.name` | _String (optional)_ | Name of a scene item. Sufficiently unique if no scene items share sources within the scene. | **Response Items:** @@ -3620,7 +3629,7 @@ _No additional response items._ ### SetSceneTransitionOverride -- Added in v4.9.0 +- Unreleased Set a scene to use a specific transition override. @@ -3642,7 +3651,7 @@ _No additional response items._ ### RemoveSceneTransitionOverride -- Added in v4.9.0 +- Unreleased Remove any transition override on a scene. @@ -3662,7 +3671,7 @@ _No additional response items._ ### GetSceneTransitionOverride -- Added in v4.9.0 +- Unreleased Get the current scene transition override. @@ -3714,7 +3723,7 @@ _No specified parameters._ - Added in v0.3 -Toggle streaming on or off. +Toggle streaming on or off (depending on the current stream state). **Request Fields:** @@ -3849,7 +3858,6 @@ _No additional response items._ - Added in v4.6.0 Send the provided text as embedded CEA-608 caption data. -As of OBS Studio 23.1, captions are not yet available on Linux. **Request Fields:** @@ -3991,7 +3999,7 @@ _No additional response items._ - Added in v4.1.0 -Toggles Studio Mode. +Toggles Studio Mode (depending on the current state of studio mode). **Request Fields:** From a651a1a69fd94022abb65f89756a8b64aa29e2c0 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Wed, 15 Jul 2020 13:01:23 +0000 Subject: [PATCH 131/278] docs(ci): Update protocol.md - c368696 [skip ci] --- docs/generated/comments.json | 6 +++--- docs/generated/protocol.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 5447e5f5..6635e97e 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -6943,7 +6943,7 @@ "subheads": [], "description": "\n\nAt least `embedPictureFormat` or `saveToFilePath` must be specified.\n\nClients can specify `width` and `height` parameters to receive scaled pictures. Aspect ratio is\npreserved if only one of these two parameters is specified.", "param": [ - "{String} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name.", + "{String (optional)} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used.", "{String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be \"png\", \"jpg\", \"jpeg\" or \"bmp\" (or any other value supported by Qt's Image module)", "{String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path.", "{String (optional)} `fileFormat` Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension.", @@ -6979,9 +6979,9 @@ ], "params": [ { - "type": "String", + "type": "String (optional)", "name": "sourceName", - "description": "Source name. Note that, since scenes are also sources, you can also provide a scene name." + "description": "Source name. Note that, since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used." }, { "type": "String (optional)", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index a5d3a57f..d2f4f324 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -2710,7 +2710,7 @@ preserved if only one of these two parameters is specified. | Name | Type | Description | | ---- | :---: | ------------| -| `sourceName` | _String_ | Source name. Note that, since scenes are also sources, you can also provide a scene name. | +| `sourceName` | _String (optional)_ | Source name. Note that, since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used. | | `embedPictureFormat` | _String (optional)_ | Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module) | | `saveToFilePath` | _String (optional)_ | Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path. | | `fileFormat` | _String (optional)_ | Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension. | From 58448f363b078339e8861715f26a28dee63cf62d Mon Sep 17 00:00:00 2001 From: venepe Date: Wed, 15 Jul 2020 21:23:51 -0500 Subject: [PATCH 132/278] CMakeLists: Compile for arm targets --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9230d197..261d8c78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) add_definitions(-DASIO_STANDALONE) +if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(CMAKE_CXX_FLAGS "-mfpu=neon") +endif() + if (WIN32 OR APPLE) include(external/FindLibObs.cmake) endif() From d91b3f8dd9261ed9f912e4dd33202f20494942ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 28 Jul 2020 02:23:31 +0200 Subject: [PATCH 133/278] Utils(GetTrayIcon): refactor with frontend API function --- src/Utils.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index ab301606..da0b7fe6 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -485,11 +485,8 @@ QString Utils::OBSVersionString() { } QSystemTrayIcon* Utils::GetTrayIcon() { - QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); - if (!main) return nullptr; - - QList trays = main->findChildren(); - return trays.isEmpty() ? nullptr : trays.first(); + void* systemTray = obs_frontend_get_system_tray(); + return reinterpret_cast(systemTray); } void Utils::SysTrayNotify(QString text, From 1dd96fa7144d80791af9694270c4d0fe61b8d7cd Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 31 Jul 2020 22:08:07 -0700 Subject: [PATCH 134/278] Docs: Add `enabled` field to `SourceFiltersReordered` Was noticed in Discord that this was missing from the docs --- src/WSEvents.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 0818fada..56d24c8a 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -1397,6 +1397,7 @@ void WSEvents::OnSourceFilterVisibilityChanged(void* param, calldata_t* data) { * @return {Array} `filters` Ordered Filters list * @return {String} `filters.*.name` Filter name * @return {String} `filters.*.type` Filter type + * @return {boolean} `filters.*.enabled` Filter visibility status * * @api events * @name SourceFiltersReordered From a51ad1383bc2dd2333a16df20dc8584a65ce3340 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 12 Aug 2020 16:54:03 -0700 Subject: [PATCH 135/278] Docs: Fix requests marked as "unreleased" --- src/WSRequestHandler_Scenes.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp index 18783119..9c317cdf 100644 --- a/src/WSRequestHandler_Scenes.cpp +++ b/src/WSRequestHandler_Scenes.cpp @@ -184,7 +184,7 @@ RpcResponse WSRequestHandler::ReorderSceneItems(const RpcRequest& request) { * @api requests * @name SetSceneTransitionOverride * @category scenes - * @since unreleased + * @since 4.8.0 */ RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& request) { if (!request.hasField("sceneName") || !request.hasField("transitionName")) { @@ -230,7 +230,7 @@ RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& reque * @api requests * @name RemoveSceneTransitionOverride * @category scenes - * @since unreleased + * @since 4.8.0 */ RpcResponse WSRequestHandler::RemoveSceneTransitionOverride(const RpcRequest& request) { if (!request.hasField("sceneName")) { @@ -266,7 +266,7 @@ RpcResponse WSRequestHandler::RemoveSceneTransitionOverride(const RpcRequest& re * @api requests * @name GetSceneTransitionOverride * @category scenes - * @since unreleased + * @since 4.8.0 */ RpcResponse WSRequestHandler::GetSceneTransitionOverride(const RpcRequest& request) { if (!request.hasField("sceneName")) { From 9679de097fad250a60d506ee582f2b153c1012d5 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 21 Aug 2020 22:43:42 -0700 Subject: [PATCH 136/278] Installer: Don't show warning when obs directory is not empty More info here: https://jrsoftware.org/ishelp/index.php?topic=setup_direxistswarning --- installer/installer.iss | 1 + 1 file changed, 1 insertion(+) diff --git a/installer/installer.iss b/installer/installer.iss index 243f3a31..e94ff579 100644 --- a/installer/installer.iss +++ b/installer/installer.iss @@ -23,6 +23,7 @@ DefaultGroupName={#MyAppName} OutputBaseFilename=obs-websocket-Windows-Installer Compression=lzma SolidCompression=yes +DirExistsWarning=no [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" From 588487f8607fad3ef92cc0b27ad2c995bdcf5ff4 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Tue, 15 Sep 2020 09:36:27 +0000 Subject: [PATCH 137/278] docs(ci): Update protocol.md - f3ab210 [skip ci] --- docs/generated/comments.json | 20 +++++++++++++------- docs/generated/protocol.md | 7 ++++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 6635e97e..a4fb874f 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -2515,7 +2515,8 @@ "{String} `sourceName` Source name", "{Array} `filters` Ordered Filters list", "{String} `filters.*.name` Filter name", - "{String} `filters.*.type` Filter type" + "{String} `filters.*.type` Filter type", + "{boolean} `filters.*.enabled` Filter visibility status" ], "api": "events", "name": "SourceFiltersReordered", @@ -2541,6 +2542,11 @@ "type": "String", "name": "filters.*.type", "description": "Filter type" + }, + { + "type": "boolean", + "name": "filters.*.enabled", + "description": "Filter visibility status" } ], "names": [ @@ -9233,7 +9239,7 @@ "api": "requests", "name": "SetSceneTransitionOverride", "category": "scenes", - "since": "unreleased", + "since": "4.8.0", "params": [ { "type": "String", @@ -9266,7 +9272,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.8.0" } ], "heading": { @@ -9284,7 +9290,7 @@ "api": "requests", "name": "RemoveSceneTransitionOverride", "category": "scenes", - "since": "unreleased", + "since": "4.8.0", "params": [ { "type": "String", @@ -9307,7 +9313,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.8.0" } ], "heading": { @@ -9329,7 +9335,7 @@ "api": "requests", "name": "GetSceneTransitionOverride", "category": "scenes", - "since": "unreleased", + "since": "4.8.0", "returns": [ { "type": "String", @@ -9364,7 +9370,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.8.0" } ], "heading": { diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index d2f4f324..ec8d7760 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -1073,6 +1073,7 @@ Filters in a source have been reordered. | `filters` | _Array<Object>_ | Ordered Filters list | | `filters.*.name` | _String_ | Filter name | | `filters.*.type` | _String_ | Filter type | +| `filters.*.enabled` | _boolean_ | Filter visibility status | --- @@ -3629,7 +3630,7 @@ _No additional response items._ ### SetSceneTransitionOverride -- Unreleased +- Added in v4.8.0 Set a scene to use a specific transition override. @@ -3651,7 +3652,7 @@ _No additional response items._ ### RemoveSceneTransitionOverride -- Unreleased +- Added in v4.8.0 Remove any transition override on a scene. @@ -3671,7 +3672,7 @@ _No additional response items._ ### GetSceneTransitionOverride -- Unreleased +- Added in v4.8.0 Get the current scene transition override. From 6b254e0ad5b86a6695ae78e2919a6c020bb1d0ba Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 15 Sep 2020 02:49:01 -0700 Subject: [PATCH 138/278] Requests: Add CreateSource and clean up code --- src/Utils.cpp | 7 +++ src/Utils.h | 6 +++ src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_SceneItems.cpp | 16 +----- src/WSRequestHandler_Sources.cpp | 75 +++++++++++++++++++++++++++++ 6 files changed, 92 insertions(+), 14 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index ab301606..148a0b11 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -859,3 +859,10 @@ QString Utils::nsToTimestamp(uint64_t ns) return QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart); } + +void Utils::AddSourceHelper(void *_data, obs_scene_t *scene) +{ + auto *data = reinterpret_cast(_data); + data->sceneItem = obs_scene_add(scene, data->source); + obs_sceneitem_set_visible(data->sceneItem, data->setVisible); +} diff --git a/src/Utils.h b/src/Utils.h index dbe2481f..de1d7f7a 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -85,4 +85,10 @@ namespace Utils { bool SetFilenameFormatting(const char* filenameFormatting); QString nsToTimestamp(uint64_t ns); + struct AddSourceData { + obs_source_t *source; + obs_sceneitem_t *sceneItem; + bool setVisible; + }; + void AddSourceHelper(void *_data, obs_scene_t *scene); }; diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 56c4cdaa..fd9e1be0 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -94,6 +94,7 @@ const QHash WSRequestHandler::messageMap { { "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration }, { "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition }, + { "CreateSource", &WSRequestHandler::CreateSource }, { "SetVolume", &WSRequestHandler::SetVolume }, { "GetVolume", &WSRequestHandler::GetVolume }, { "ToggleMute", &WSRequestHandler::ToggleMute }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index e409db50..b7404527 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -111,6 +111,7 @@ class WSRequestHandler { RpcResponse GetTransitionDuration(const RpcRequest&); RpcResponse GetTransitionPosition(const RpcRequest&); + RpcResponse CreateSource(const RpcRequest&); RpcResponse SetVolume(const RpcRequest&); RpcResponse GetVolume(const RpcRequest&); RpcResponse ToggleMute(const RpcRequest&); diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 10ca8212..38771d22 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -2,18 +2,6 @@ #include "WSRequestHandler.h" -struct AddSourceData { - obs_source_t *source; - obs_sceneitem_t *sceneItem; - bool setVisible; -}; - -void AddSourceHelper(void *_data, obs_scene_t *scene) { - auto *data = reinterpret_cast(_data); - data->sceneItem = obs_scene_add(scene, data->source); - obs_sceneitem_set_visible(data->sceneItem, data->setVisible); -} - /** * Get a list of all scene items in a scene. * @@ -658,7 +646,7 @@ RpcResponse WSRequestHandler::AddSceneItem(const RpcRequest& request) { return request.failed("you cannot add a scene as a sceneitem to itself"); } - AddSourceData data; + Utils::AddSourceData data; data.source = source; data.setVisible = true; if (request.hasField("setVisible")) { @@ -666,7 +654,7 @@ RpcResponse WSRequestHandler::AddSceneItem(const RpcRequest& request) { } obs_enter_graphics(); - obs_scene_atomic_update(scene, AddSourceHelper, &data); + obs_scene_atomic_update(scene, Utils::AddSourceHelper, &data); obs_leave_graphics(); OBSDataAutoRelease responseData = obs_data_create(); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index c6125e5b..28039323 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -18,6 +18,81 @@ bool isTextFreeType2Source(const QString& sourceKind) return (sourceKind == "text_ft2_source" || sourceKind == "text_ft2_source_v2"); } +/** + * Create a source and add it as a sceneitem to a scene. + * + * @param {String} `sourceName` Source name. + * @param {String} `sourceKind` Source kind, Eg. `vlc_source`. + * @param {String} `sceneName` Scene to add the new source to. + * @param {Object (optional)} `sourceSettings` Source settings data. + * @param {boolean (optional)} `setVisible` Set the created SceneItem as visible or not. + * + * @return {int} `itemId` ID of the SceneItem in the scene. + * + * @api requests + * @name CreateSource + * @category sources + * @since unreleased + */ +RpcResponse WSRequestHandler::CreateSource(const RpcRequest& request) +{ + if (!request.hasField("sourceName") || !request.hasField("sourceKind") || !request.hasField("sceneName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + QString sourceKind = obs_data_get_string(request.parameters(), "sourceKind"); + if (sourceName.isEmpty() || sourceKind.isEmpty()) { + return request.failed("empty sourceKind or sourceName parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (source) { + return request.failed("a source with that name already exists"); + } + + const char* sceneName = obs_data_get_string(request.parameters(), "sceneName"); + OBSSourceAutoRelease sceneSource = obs_get_source_by_name(sceneName); + OBSScene scene = obs_scene_from_source(sceneSource); + if (!scene) { + return request.failed("requested scene is invalid or doesnt exist"); + } + + obs_source_t *newSource = obs_source_create(sourceKind.toUtf8(), sourceName.toUtf8(), nullptr, nullptr); + + if (!newSource) { + return request.failed("failed to create the source"); + } + obs_source_set_enabled(newSource, true); + + if (request.hasField("sourceSettings")) { // We apply the settings after source creation because otherwise we get a bunch of memory leaks. + OBSDataAutoRelease currentSettings = obs_source_get_settings(newSource); + OBSDataAutoRelease newSettings = obs_data_get_obj(request.parameters(), "sourceSettings"); + OBSDataAutoRelease sourceSettings = obs_data_create(); + obs_data_apply(sourceSettings, currentSettings); + obs_data_apply(sourceSettings, newSettings); + obs_source_update(newSource, sourceSettings); + obs_source_update_properties(newSource); + } + + Utils::AddSourceData data; + data.source = newSource; + data.setVisible = true; + if (request.hasField("setVisible")) { + data.setVisible = obs_data_get_bool(request.parameters(), "setVisible"); + } + + obs_enter_graphics(); + obs_scene_atomic_update(scene, Utils::AddSourceHelper, &data); + obs_leave_graphics(); + + OBSDataAutoRelease responseData = obs_data_create(); + obs_data_set_int(responseData, "itemId", obs_sceneitem_get_id(data.sceneItem)); + + obs_source_release(newSource); + return request.success(responseData); +} + /** * List all sources available in the running OBS instance * From 7f1c4a1c4c254679436bd5cd8cbfd0341fa86310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 15 Sep 2020 17:12:34 +0200 Subject: [PATCH 139/278] Utils: simplified string list lookup --- src/Utils.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index 4ac3577d..1abb27ab 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -52,15 +52,13 @@ obs_bounds_type getBoundsTypeFromName(QString name) { } bool Utils::StringInStringList(char** strings, const char* string) { + if (!strings) { + return false; + } + size_t index = 0; - char* value = nullptr; - - while (1) { - value = strings[index]; - - if (value == nullptr) { - return false; - } + while (strings[index] != NULL) { + char* value = strings[index]; if (strcmp(value, string) == 0) { return true; @@ -68,6 +66,7 @@ bool Utils::StringInStringList(char** strings, const char* string) { index++; } + return false; } From 539e636939432fbe2bb3cace0ee6ec37fd73ac26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 15 Sep 2020 17:14:53 +0200 Subject: [PATCH 140/278] requests(SceneCollections): fix typo (profiles instead of scene collections) --- src/WSRequestHandler_SceneCollections.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WSRequestHandler_SceneCollections.cpp b/src/WSRequestHandler_SceneCollections.cpp index 1df49673..40ac5fb7 100644 --- a/src/WSRequestHandler_SceneCollections.cpp +++ b/src/WSRequestHandler_SceneCollections.cpp @@ -22,11 +22,11 @@ RpcResponse WSRequestHandler::SetCurrentSceneCollection(const RpcRequest& reques return request.failed("invalid request parameters"); } - char** collections = obs_frontend_get_profiles(); + char** collections = obs_frontend_get_scene_collections(); bool collectionExists = Utils::StringInStringList(collections, sceneCollection.toUtf8()); bfree(collections); if (!collectionExists) { - return request.failed("collection does not exist"); + return request.failed("scene collection does not exist"); } obs_frontend_set_current_scene_collection(sceneCollection.toUtf8()); From 0dd0d01e8f385e092f502f2b13c3b02f90a14b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 15 Sep 2020 18:17:45 +0200 Subject: [PATCH 141/278] requests(Profiles + SceneCollections): don't use QString where it is not needed --- src/WSRequestHandler_Profiles.cpp | 8 ++++---- src/WSRequestHandler_SceneCollections.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/WSRequestHandler_Profiles.cpp b/src/WSRequestHandler_Profiles.cpp index b3e0bd8a..17cae4a3 100644 --- a/src/WSRequestHandler_Profiles.cpp +++ b/src/WSRequestHandler_Profiles.cpp @@ -17,19 +17,19 @@ RpcResponse WSRequestHandler::SetCurrentProfile(const RpcRequest& request) { return request.failed("missing request parameters"); } - QString profileName = obs_data_get_string(request.parameters(), "profile-name"); - if (profileName.isEmpty()) { + const char* profileName = obs_data_get_string(request.parameters(), "profile-name"); + if (!profileName) { return request.failed("invalid request parameters"); } char** profiles = obs_frontend_get_profiles(); - bool profileExists = Utils::StringInStringList(profiles, profileName.toUtf8()); + bool profileExists = Utils::StringInStringList(profiles, profileName); bfree(profiles); if (!profileExists) { return request.failed("profile does not exist"); } - obs_frontend_set_current_profile(profileName.toUtf8()); + obs_frontend_set_current_profile(profileName); return request.success(); } diff --git a/src/WSRequestHandler_SceneCollections.cpp b/src/WSRequestHandler_SceneCollections.cpp index 40ac5fb7..faf6299b 100644 --- a/src/WSRequestHandler_SceneCollections.cpp +++ b/src/WSRequestHandler_SceneCollections.cpp @@ -17,19 +17,19 @@ RpcResponse WSRequestHandler::SetCurrentSceneCollection(const RpcRequest& reques return request.failed("missing request parameters"); } - QString sceneCollection = obs_data_get_string(request.parameters(), "sc-name"); - if (sceneCollection.isEmpty()) { + const char* sceneCollection = obs_data_get_string(request.parameters(), "sc-name"); + if (!sceneCollection) { return request.failed("invalid request parameters"); } char** collections = obs_frontend_get_scene_collections(); - bool collectionExists = Utils::StringInStringList(collections, sceneCollection.toUtf8()); + bool collectionExists = Utils::StringInStringList(collections, sceneCollection); bfree(collections); if (!collectionExists) { return request.failed("scene collection does not exist"); } - obs_frontend_set_current_scene_collection(sceneCollection.toUtf8()); + obs_frontend_set_current_scene_collection(sceneCollection); return request.success(); } From a148f7fd7cce52bcf778ec6a48f8dd96194ed718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 16 Sep 2020 21:45:31 +0200 Subject: [PATCH 142/278] requests(Profiles + SceneCollections): fix crash on change --- src/WSRequestHandler_Profiles.cpp | 5 ++++- src/WSRequestHandler_SceneCollections.cpp | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/WSRequestHandler_Profiles.cpp b/src/WSRequestHandler_Profiles.cpp index 17cae4a3..55d39b83 100644 --- a/src/WSRequestHandler_Profiles.cpp +++ b/src/WSRequestHandler_Profiles.cpp @@ -29,7 +29,10 @@ RpcResponse WSRequestHandler::SetCurrentProfile(const RpcRequest& request) { return request.failed("profile does not exist"); } - obs_frontend_set_current_profile(profileName); + obs_queue_task(OBS_TASK_UI, [](void* param) { + obs_frontend_set_current_profile(reinterpret_cast(param)); + }, (void*)profileName, true); + return request.success(); } diff --git a/src/WSRequestHandler_SceneCollections.cpp b/src/WSRequestHandler_SceneCollections.cpp index faf6299b..77ce5d0a 100644 --- a/src/WSRequestHandler_SceneCollections.cpp +++ b/src/WSRequestHandler_SceneCollections.cpp @@ -29,7 +29,10 @@ RpcResponse WSRequestHandler::SetCurrentSceneCollection(const RpcRequest& reques return request.failed("scene collection does not exist"); } - obs_frontend_set_current_scene_collection(sceneCollection); + obs_queue_task(OBS_TASK_UI, [](void* param) { + obs_frontend_set_current_scene_collection(reinterpret_cast(param)); + }, (void*)sceneCollection, true); + return request.success(); } From fbc6e02ff66c74e176362423536cee6d4337de1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gra=C3=A7a?= Date: Thu, 24 Sep 2020 13:16:57 +0100 Subject: [PATCH 143/278] =?UTF-8?q?docs:=20=F0=9F=93=9D=20list=20multiple?= =?UTF-8?q?=20available=20web=20clients?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 074c30de..741ca5cf 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,10 @@ Binaries for Windows, MacOS, and Linux are available in the [Releases](https://g ## Using obs-websocket -A web client and frontend made by [t2t2](https://github.com/t2t2/obs-tablet-remote) (compatible with tablets and other touch interfaces) is available here : http://t2t2.github.io/obs-tablet-remote/ +Here is a list of available web clients: (compatible with tablets and other touch interfaces) + +- [Niek/obs-web](https://github.com/Niek/obs-web) +- [t2t2/obs-tablet-remote](https://github.com/t2t2/obs-tablet-remote) It is **highly recommended** to protect obs-websocket with a password against unauthorized control. To do this, open the "Websocket server settings" dialog under OBS' "Tools" menu. In the settings dialogs, you can enable or disable authentication and set a password for it. From bac5b1551b229c016916fbfb36900fb4181fb2f1 Mon Sep 17 00:00:00 2001 From: LorenaGdL Date: Tue, 29 Sep 2020 13:38:32 +0000 Subject: [PATCH 144/278] Requests: Add ProcessHotkeyByName Adds a new request called `ProcessHotkeyByName` which calls the routine associated with a given hotkey. The hotkey is identified by the unique name set while registering it. Works with both OBS default hotkeys and hotkeys added by plugins/scripts. --- src/WSRequestHandler.cpp | 2 ++ src/WSRequestHandler.h | 2 ++ src/WSRequestHandler_General.cpp | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 56c4cdaa..2c92305a 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -43,6 +43,8 @@ const QHash WSRequestHandler::messageMap { { "BroadcastCustomMessage", &WSRequestHandler::BroadcastCustomMessage }, + { "ProcessHotkeyByName", &WSRequestHandler::ProcessHotkeyByName }, + { "SetCurrentScene", &WSRequestHandler::SetCurrentScene }, { "GetCurrentScene", &WSRequestHandler::GetCurrentScene }, { "GetSceneList", &WSRequestHandler::GetSceneList }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index e409db50..7a4e64ff 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -61,6 +61,8 @@ class WSRequestHandler { RpcResponse BroadcastCustomMessage(const RpcRequest&); + RpcResponse ProcessHotkeyByName(const RpcRequest&); + RpcResponse SetCurrentScene(const RpcRequest&); RpcResponse GetCurrentScene(const RpcRequest&); RpcResponse GetSceneList(const RpcRequest&); diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index ea1c6a63..54675a84 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -345,3 +345,24 @@ RpcResponse WSRequestHandler::OpenProjector(const RpcRequest& request) { obs_frontend_open_projector(type, monitor, geometry, name); return request.success(); } + +/** +* Executes hotkey routine, identified by hotkey unique name +* +* @param {String} `name` Unique name of the hotkey, as defined when registering the hotkey (e.g. "ReplayBuffer.Save") +* +* @api requests +* @name ProcessHotkeyByName +* @category general +* @since unreleased +*/ +RpcResponse WSRequestHandler::ProcessHotkeyByName(const RpcRequest& request) { + const char* name = obs_data_get_string(request.parameters(), "name"); + + obs_hotkey_t* hk = Utils::FindHotkeyByName(name); + if (!hk) { + return request.failed("Hotkey not found"); + } + obs_hotkey_trigger_routed_callback(obs_hotkey_get_id(hk), true); + return request.success(); +} From 9daae857de24753f1b85461f05acaab23516cee8 Mon Sep 17 00:00:00 2001 From: LorenaGdL Date: Tue, 29 Sep 2020 13:43:00 +0000 Subject: [PATCH 145/278] Requests: Add ProcessHotkeyByCombination Adds a new request called `ProcessHotkeyByCombination` which calls the routine associated with a given hotkey. The hotkey is identified by the combination of keys set by the user to trigger it. Multiple routines are called if the same combination is bound to different hotkeys. --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_General.cpp | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 2c92305a..9b62546c 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -44,6 +44,7 @@ const QHash WSRequestHandler::messageMap { { "BroadcastCustomMessage", &WSRequestHandler::BroadcastCustomMessage }, { "ProcessHotkeyByName", &WSRequestHandler::ProcessHotkeyByName }, + { "ProcessHotkeyByCombination", &WSRequestHandler::ProcessHotkeyByCombination }, { "SetCurrentScene", &WSRequestHandler::SetCurrentScene }, { "GetCurrentScene", &WSRequestHandler::GetCurrentScene }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 7a4e64ff..f8849e84 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -62,6 +62,7 @@ class WSRequestHandler { RpcResponse BroadcastCustomMessage(const RpcRequest&); RpcResponse ProcessHotkeyByName(const RpcRequest&); + RpcResponse ProcessHotkeyByCombination(const RpcRequest&); RpcResponse SetCurrentScene(const RpcRequest&); RpcResponse GetCurrentScene(const RpcRequest&); diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index 54675a84..0fc5d619 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -366,3 +366,48 @@ RpcResponse WSRequestHandler::ProcessHotkeyByName(const RpcRequest& request) { obs_hotkey_trigger_routed_callback(obs_hotkey_get_id(hk), true); return request.success(); } + +/** +* Executes hotkey routine, identified by bound combination of keys. A single key combination might trigger multiple hotkey routines depending on user settings +* +* @param {String} `key` Main key identifier (e.g. `OBS_KEY_A` for key "A"). Available identifiers [here](https://github.com/obsproject/obs-studio/blob/master/libobs/obs-hotkeys.h) +* @param {Object (Optional)} `modifiers` Optional key modifiers object: `{"shift":true/false, "alt":true/false, "control":true/false, "command":true/false}`. False entries can be ommitted +* +* @api requests +* @name ProcessHotkeyByCombination +* @category general +* @since unreleased +*/ +RpcResponse WSRequestHandler::ProcessHotkeyByCombination(const RpcRequest& request) { + if (!request.hasField("key")) { + return request.failed("Missing request key parameter"); + } + + OBSDataAutoRelease data = obs_data_get_obj(request.parameters(), "modifiers"); + + obs_key_combination_t combo = {0}; + uint32_t modifiers = 0; + if (obs_data_get_bool(data, "shift")) + modifiers |= INTERACT_SHIFT_KEY; + if (obs_data_get_bool(data, "control")) + modifiers |= INTERACT_CONTROL_KEY; + if (obs_data_get_bool(data, "alt")) + modifiers |= INTERACT_ALT_KEY; + if (obs_data_get_bool(data, "command")) + modifiers |= INTERACT_COMMAND_KEY; + + combo.modifiers = modifiers; + combo.key = obs_key_from_name(obs_data_get_string(request.parameters(), "key")); + + if (!modifiers + && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) { + return request.failed("Invalid key-modifier combination"); + } + + // Inject hotkey press-release sequence + obs_hotkey_inject_event(combo, false); + obs_hotkey_inject_event(combo, true); + obs_hotkey_inject_event(combo, false); + + return request.success(); +} From 46f624e3b993f35c9a3429b84cd13f61f18707f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 29 Sep 2020 19:37:28 +0200 Subject: [PATCH 146/278] requests(GetSceneItemList): fallback to current scene if sceneName is not specified --- src/WSRequestHandler_SceneItems.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 10ca8212..01b6a29d 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -31,12 +31,15 @@ void AddSourceHelper(void *_data, obs_scene_t *scene) { * @since unreleased */ RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) { - if (!request.hasField("sceneName")) { - return request.failed("missing request parameters"); + const char* sceneName = obs_data_get_string(request.parameters(), "sceneName"); + + OBSSourceAutoRelease sceneSource; + if (sceneName) { + sceneSource = obs_get_source_by_name(sceneName); + } else { + sceneSource = obs_frontend_get_current_scene(); } - const char* sceneName = obs_data_get_string(request.parameters(), "sceneName"); - OBSSourceAutoRelease sceneSource = obs_get_source_by_name(sceneName); OBSScene scene = obs_scene_from_source(sceneSource); if (!scene) { return request.failed("requested scene is invalid or doesnt exist"); From 32960afc1b64883f1577f848c8cc138203b184c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 29 Sep 2020 19:45:07 +0200 Subject: [PATCH 147/278] requests(GetSceneItemList): update docs --- src/WSRequestHandler_SceneItems.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 01b6a29d..d7c08899 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -17,7 +17,7 @@ void AddSourceHelper(void *_data, obs_scene_t *scene) { /** * Get a list of all scene items in a scene. * -* @param {String} `sceneName` Name of the scene to get the list of scene items from. +* @param {String (optional)} `sceneName` Name of the scene to get the list of scene items from. Defaults to the current scene if not specified. * * @return {Array} `sceneItems` Array of scene items * @return {int} `sceneItems.*.itemId` Unique item id of the source item From 4491da0350f5567193d2738717e10db2fa3282c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 29 Sep 2020 23:48:17 +0200 Subject: [PATCH 148/278] requests(GetSceneItemList): fix empty/non-present parameter detection --- src/WSRequestHandler_SceneItems.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index d7c08899..1d4393f6 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -34,7 +34,7 @@ RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) { const char* sceneName = obs_data_get_string(request.parameters(), "sceneName"); OBSSourceAutoRelease sceneSource; - if (sceneName) { + if (sceneName && strcmp(sceneName, "") != 0) { sceneSource = obs_get_source_by_name(sceneName); } else { sceneSource = obs_frontend_get_current_scene(); From 4a4c97aac46f0232488c8f8766f9f13396b13101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 29 Sep 2020 23:48:35 +0200 Subject: [PATCH 149/278] request(GetSceneItemList): provide scene name in response --- src/WSRequestHandler_SceneItems.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 1d4393f6..2d09780d 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -19,6 +19,7 @@ void AddSourceHelper(void *_data, obs_scene_t *scene) { * * @param {String (optional)} `sceneName` Name of the scene to get the list of scene items from. Defaults to the current scene if not specified. * +* @return {String} `sceneName` Name of the requested (or current) scene * @return {Array} `sceneItems` Array of scene items * @return {int} `sceneItems.*.itemId` Unique item id of the source item * @return {String} `sceneItems.*.sourceKind` ID if the scene item's source. For example `vlc_source` or `image_source` @@ -79,6 +80,7 @@ RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) { obs_scene_enum_items(scene, sceneItemEnumProc, sceneItemArray); OBSDataAutoRelease response = obs_data_create(); + obs_data_set_string(response, "sceneName", obs_source_get_name(sceneSource)); obs_data_set_array(response, "sceneItems", sceneItemArray); return request.success(response); From ae4ee0332c7b11a9de1cdd7bf6b5e8772c3fd1f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 30 Sep 2020 12:19:37 +0200 Subject: [PATCH 150/278] Fix macOS CI (#597) * ci(macos): fix attempt #1 * Revert "ci(macos): fix attempt #1" This reverts commit b846490136973872ac3b724637cef303e3255083. * ci(macos): provide QTDIR when building OBS * Revert "ci(macos): provide QTDIR when building OBS" This reverts commit 74a882fcf4b9e502b1125508d796b29b69ec0e73. * ci(macos): use local qt formula * ci(macos): update qt.rb --- CI/install-dependencies-macos.sh | 7 +- CI/macos/qt.rb | 262 ++++++++++++++----------------- 2 files changed, 123 insertions(+), 146 deletions(-) diff --git a/CI/install-dependencies-macos.sh b/CI/install-dependencies-macos.sh index 0adf464b..267fa49a 100755 --- a/CI/install-dependencies-macos.sh +++ b/CI/install-dependencies-macos.sh @@ -35,13 +35,8 @@ done # qtwebsockets deps echo "[obs-websocket] Installing obs-websocket dependency 'QT 5.10.1'.." -# =!= NOTICE =!= -# When building QT5 from sources on macOS 10.13+, use local qt5 formula: -# brew install ./CI/macos/qt.rb -# Pouring from the bottle is much quicker though, so use bottle for now. -# =!= NOTICE =!= -brew install https://gist.githubusercontent.com/DDRBoxman/b3956fab6073335a4bf151db0dcbd4ad/raw/ed1342a8a86793ea8c10d8b4d712a654da121ace/qt.rb +brew install ./CI/macos/qt.rb # Pin this version of QT5 to avoid `brew upgrade` # upgrading it to incompatible version diff --git a/CI/macos/qt.rb b/CI/macos/qt.rb index 50ff4f57..4405a240 100644 --- a/CI/macos/qt.rb +++ b/CI/macos/qt.rb @@ -1,163 +1,145 @@ # Patches for Qt must be at the very least submitted to Qt's Gerrit codereview # rather than their bug-report Jira. The latter is rarely reviewed by Qt. class Qt < Formula - desc "Cross-platform application and UI framework" - homepage "https://www.qt.io/" - url "https://download.qt.io/archive/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz" - mirror "https://mirrorservice.org/sites/download.qt-project.org/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz" - sha256 "05ffba7b811b854ed558abf2be2ddbd3bb6ddd0b60ea4b5da75d277ac15e740a" - head "https://code.qt.io/qt/qt5.git", :branch => "5.10.1", :shallow => false + desc "Cross-platform application and UI framework" + homepage "https://www.qt.io/" + url "https://download.qt.io/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz" + mirror "https://www.mirrorservice.org/sites/download.qt-project.org/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz" + sha256 "05ffba7b811b854ed558abf2be2ddbd3bb6ddd0b60ea4b5da75d277ac15e740a" + head "https://code.qt.io/qt/qt5.git", :branch => "5.10", :shallow => false - bottle do - sha256 "8b4bad005596a5f8790150fe455db998ac2406f4e0f04140d6656205d844d266" => :high_sierra - sha256 "9c488554935fb573554a4e36d36d3c81e47245b7fefc4b61edef894e67ba1740" => :sierra - sha256 "c0407afba5951df6cc4c6f6c1c315972bd41c99cecb4e029919c4c15ab6f7bdc" => :el_capitan - end + bottle do + sha256 "8b4bad005596a5f8790150fe455db998ac2406f4e0f04140d6656205d844d266" => :high_sierra + sha256 "9c488554935fb573554a4e36d36d3c81e47245b7fefc4b61edef894e67ba1740" => :sierra + sha256 "c0407afba5951df6cc4c6f6c1c315972bd41c99cecb4e029919c4c15ab6f7bdc" => :el_capitan + end - keg_only "Qt 5 has CMake issues when linked" + keg_only "Qt 5 has CMake issues when linked" - option "with-docs", "Build documentation" - option "with-examples", "Build examples" + option "with-docs", "Build documentation" + option "with-examples", "Build examples" + option "without-proprietary-codecs", "Don't build with proprietary codecs (e.g. mp3)" - deprecated_option "with-mysql" => "with-mysql-client" + # OS X 10.7 Lion is still supported in Qt 5.5, but is no longer a reference + # configuration and thus untested in practice. Builds on OS X 10.7 have been + # reported to fail: . + # depends_on :macos => :mountain_lion - # OS X 10.7 Lion is still supported in Qt 5.5, but is no longer a reference - # configuration and thus untested in practice. Builds on OS X 10.7 have been - # reported to fail: . - depends_on :macos => :mountain_lion + depends_on "pkg-config" => :build + depends_on :xcode => :build + depends_on "mysql" => :optional + depends_on "postgresql" => :optional - depends_on "pkg-config" => :build - depends_on :xcode => :build - depends_on "mysql-client" => :optional - depends_on "postgresql" => :optional + # Restore `.pc` files for framework-based build of Qt 5 on OS X. This + # partially reverts merged + # between the 5.5.1 and 5.6.0 releases. (Remove this as soon as feasible!) + # + # Core formulae known to fail without this patch (as of 2016-10-15): + # * gnuplot (with `--with-qt` option) + # * mkvtoolnix (with `--with-qt` option, silent build failure) + # * poppler (with `--with-qt` option) + patch do + url "https://raw.githubusercontent.com/Homebrew/formula-patches/e8fe6567/qt5/restore-pc-files.patch" + sha256 "48ff18be2f4050de7288bddbae7f47e949512ac4bcd126c2f504be2ac701158b" + end - # Restore `.pc` files for framework-based build of Qt 5 on OS X. This - # partially reverts merged - # between the 5.5.1 and 5.6.0 releases. (Remove this as soon as feasible!) - # - # Core formulae known to fail without this patch (as of 2016-10-15): - # * gnuplot (with `--with-qt` option) - # * mkvtoolnix (with `--with-qt` option, silent build failure) - # * poppler (with `--with-qt` option) - patch do - url "https://raw.githubusercontent.com/Homebrew/formula-patches/e8fe6567/qt5/restore-pc-files.patch" - sha256 "48ff18be2f4050de7288bddbae7f47e949512ac4bcd126c2f504be2ac701158b" - end + def install + args = %W[ + -verbose + -prefix #{prefix} + -release + -opensource -confirm-license + -system-zlib + -qt-libpng + -qt-libjpeg + -qt-freetype + -qt-pcre + -nomake tests + -no-rpath + -pkg-config + -dbus-runtime + ] - # Fix compile error on macOS 10.13 around QFixed: - # https://github.com/Homebrew/homebrew-core/issues/27095 - # https://bugreports.qt.io/browse/QTBUG-67545 - patch do - url "https://raw.githubusercontent.com/z00m1n/formula-patches/0de0e229/qt/QTBUG-67545.patch" - sha256 "4a115097c7582c7dce4207f5500d13feb8c990eb8a05a43f41953985976ebe6c" - end + args << "-nomake" << "examples" if build.without? "examples" - # Fix compile error on macOS 10.13 caused by qtlocation dependency - # mapbox-gl-native using Boost 1.62.0 does not build with C++ 17: - # https://github.com/Homebrew/homebrew-core/issues/27095 - # https://bugreports.qt.io/browse/QTBUG-67810 - patch do - url "https://raw.githubusercontent.com/z00m1n/formula-patches/a1a1f0dd/qt/QTBUG-67810.patch" - sha256 "8ee0bf71df1043f08ebae3aa35036be29c4d9ebff8a27e3b0411a6bd635e9382" - end + if build.with? "mysql" + args << "-plugin-sql-mysql" + (buildpath/"brew_shim/mysql_config").write <<~EOS + #!/bin/sh + if [ x"$1" = x"--libs" ]; then + mysql_config --libs | sed "s/-lssl -lcrypto//" + else + exec mysql_config "$@" + fi + EOS + chmod 0755, "brew_shim/mysql_config" + args << "-mysql_config" << buildpath/"brew_shim/mysql_config" + end - def install - args = %W[ - -verbose - -prefix #{prefix} - -release - -opensource -confirm-license - -system-zlib - -qt-libpng - -qt-libjpeg - -qt-freetype - -qt-pcre - -nomake tests - -no-rpath - -pkg-config - -dbus-runtime - -no-assimp - ] + args << "-plugin-sql-psql" if build.with? "postgresql" + args << "-proprietary-codecs" if build.with? "proprietary-codecs" - args << "-nomake" << "examples" if build.without? "examples" + system "./configure", *args + system "make" + ENV.deparallelize + system "make", "install" - if build.with? "mysql-client" - args << "-plugin-sql-mysql" - (buildpath/"brew_shim/mysql_config").write <<~EOS - #!/bin/sh - if [ x"$1" = x"--libs" ]; then - mysql_config --libs | sed "s/-lssl -lcrypto//" - else - exec mysql_config "$@" - fi - EOS - chmod 0755, "brew_shim/mysql_config" - args << "-mysql_config" << buildpath/"brew_shim/mysql_config" - end + if build.with? "docs" + system "make", "docs" + system "make", "install_docs" + end - args << "-plugin-sql-psql" if build.with? "postgresql" + # Some config scripts will only find Qt in a "Frameworks" folder + frameworks.install_symlink Dir["#{lib}/*.framework"] - system "./configure", *args - system "make" - ENV.deparallelize - system "make", "install" + # The pkg-config files installed suggest that headers can be found in the + # `include` directory. Make this so by creating symlinks from `include` to + # the Frameworks' Headers folders. + Pathname.glob("#{lib}/*.framework/Headers") do |path| + include.install_symlink path => path.parent.basename(".framework") + end - if build.with? "docs" - system "make", "docs" - system "make", "install_docs" - end + # Move `*.app` bundles into `libexec` to expose them to `brew linkapps` and + # because we don't like having them in `bin`. + # (Note: This move breaks invocation of Assistant via the Help menu + # of both Designer and Linguist as that relies on Assistant being in `bin`.) + libexec.mkpath + Pathname.glob("#{bin}/*.app") { |app| mv app, libexec } + end - # Some config scripts will only find Qt in a "Frameworks" folder - frameworks.install_symlink Dir["#{lib}/*.framework"] + def caveats; <<~EOS + We agreed to the Qt opensource license for you. + If this is unacceptable you should uninstall. + EOS + end - # The pkg-config files installed suggest that headers can be found in the - # `include` directory. Make this so by creating symlinks from `include` to - # the Frameworks' Headers folders. - Pathname.glob("#{lib}/*.framework/Headers") do |path| - include.install_symlink path => path.parent.basename(".framework") - end + test do + (testpath/"hello.pro").write <<~EOS + QT += core + QT -= gui + TARGET = hello + CONFIG += console + CONFIG -= app_bundle + TEMPLATE = app + SOURCES += main.cpp + EOS - # Move `*.app` bundles into `libexec` to expose them to `brew linkapps` and - # because we don't like having them in `bin`. - # (Note: This move breaks invocation of Assistant via the Help menu - # of both Designer and Linguist as that relies on Assistant being in `bin`.) - libexec.mkpath - Pathname.glob("#{bin}/*.app") { |app| mv app, libexec } - end + (testpath/"main.cpp").write <<~EOS + #include + #include - def caveats; <<~EOS - We agreed to the Qt opensource license for you. - If this is unacceptable you should uninstall. - EOS - end + int main(int argc, char *argv[]) + { + QCoreApplication a(argc, argv); + qDebug() << "Hello World!"; + return 0; + } + EOS - test do - (testpath/"hello.pro").write <<~EOS - QT += core - QT -= gui - TARGET = hello - CONFIG += console - CONFIG -= app_bundle - TEMPLATE = app - SOURCES += main.cpp - EOS - - (testpath/"main.cpp").write <<~EOS - #include - #include - - int main(int argc, char *argv[]) - { - QCoreApplication a(argc, argv); - qDebug() << "Hello World!"; - return 0; - } - EOS - - system bin/"qmake", testpath/"hello.pro" - system "make" - assert_predicate testpath/"hello", :exist? - assert_predicate testpath/"main.o", :exist? - system "./hello" - end -end + system bin/"qmake", testpath/"hello.pro" + system "make" + assert_predicate testpath/"hello", :exist? + assert_predicate testpath/"main.o", :exist? + system "./hello" + end +end \ No newline at end of file From 449ad6d13c1fcf92a81466939a5186b37a540bf6 Mon Sep 17 00:00:00 2001 From: LorenaGdL Date: Wed, 30 Sep 2020 19:15:23 +0000 Subject: [PATCH 151/278] PR review changes: Modify requests names Changes requests names to "TriggerHotkeyByName" and "TriggerHotkeyBySequence" --- src/WSRequestHandler.cpp | 4 ++-- src/WSRequestHandler.h | 4 ++-- src/WSRequestHandler_General.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 9b62546c..b4ab8137 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -43,8 +43,8 @@ const QHash WSRequestHandler::messageMap { { "BroadcastCustomMessage", &WSRequestHandler::BroadcastCustomMessage }, - { "ProcessHotkeyByName", &WSRequestHandler::ProcessHotkeyByName }, - { "ProcessHotkeyByCombination", &WSRequestHandler::ProcessHotkeyByCombination }, + { "TriggerHotkeyByName", &WSRequestHandler::TriggerHotkeyByName }, + { "TriggerHotkeyBySequence", &WSRequestHandler::TriggerHotkeyBySequence }, { "SetCurrentScene", &WSRequestHandler::SetCurrentScene }, { "GetCurrentScene", &WSRequestHandler::GetCurrentScene }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index f8849e84..c3190668 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -61,8 +61,8 @@ class WSRequestHandler { RpcResponse BroadcastCustomMessage(const RpcRequest&); - RpcResponse ProcessHotkeyByName(const RpcRequest&); - RpcResponse ProcessHotkeyByCombination(const RpcRequest&); + RpcResponse TriggerHotkeyByName(const RpcRequest&); + RpcResponse TriggerHotkeyBySequence(const RpcRequest&); RpcResponse SetCurrentScene(const RpcRequest&); RpcResponse GetCurrentScene(const RpcRequest&); diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index 0fc5d619..6f5cea2a 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -356,7 +356,7 @@ RpcResponse WSRequestHandler::OpenProjector(const RpcRequest& request) { * @category general * @since unreleased */ -RpcResponse WSRequestHandler::ProcessHotkeyByName(const RpcRequest& request) { +RpcResponse WSRequestHandler::TriggerHotkeyByName(const RpcRequest& request) { const char* name = obs_data_get_string(request.parameters(), "name"); obs_hotkey_t* hk = Utils::FindHotkeyByName(name); @@ -378,7 +378,7 @@ RpcResponse WSRequestHandler::ProcessHotkeyByName(const RpcRequest& request) { * @category general * @since unreleased */ -RpcResponse WSRequestHandler::ProcessHotkeyByCombination(const RpcRequest& request) { +RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request) { if (!request.hasField("key")) { return request.failed("Missing request key parameter"); } From e7c8c1d6b60926e8ba520d524dff4ede876144c8 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 30 Sep 2020 15:02:59 -0700 Subject: [PATCH 152/278] Requests: Update hotkey docs and rename some variables --- src/WSRequestHandler_General.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index 6f5cea2a..a5f19143 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -349,19 +349,19 @@ RpcResponse WSRequestHandler::OpenProjector(const RpcRequest& request) { /** * Executes hotkey routine, identified by hotkey unique name * -* @param {String} `name` Unique name of the hotkey, as defined when registering the hotkey (e.g. "ReplayBuffer.Save") +* @param {String} `hotkeyName` Unique name of the hotkey, as defined when registering the hotkey (e.g. "ReplayBuffer.Save") * * @api requests -* @name ProcessHotkeyByName +* @name TriggerHotkeyByName * @category general * @since unreleased */ RpcResponse WSRequestHandler::TriggerHotkeyByName(const RpcRequest& request) { - const char* name = obs_data_get_string(request.parameters(), "name"); + const char* name = obs_data_get_string(request.parameters(), "hotkeyName"); obs_hotkey_t* hk = Utils::FindHotkeyByName(name); if (!hk) { - return request.failed("Hotkey not found"); + return request.failed("hotkey not found"); } obs_hotkey_trigger_routed_callback(obs_hotkey_get_id(hk), true); return request.success(); @@ -370,20 +370,24 @@ RpcResponse WSRequestHandler::TriggerHotkeyByName(const RpcRequest& request) { /** * Executes hotkey routine, identified by bound combination of keys. A single key combination might trigger multiple hotkey routines depending on user settings * -* @param {String} `key` Main key identifier (e.g. `OBS_KEY_A` for key "A"). Available identifiers [here](https://github.com/obsproject/obs-studio/blob/master/libobs/obs-hotkeys.h) -* @param {Object (Optional)} `modifiers` Optional key modifiers object: `{"shift":true/false, "alt":true/false, "control":true/false, "command":true/false}`. False entries can be ommitted +* @param {String} `keyId` Main key identifier (e.g. `OBS_KEY_A` for key "A"). Available identifiers [here](https://github.com/obsproject/obs-studio/blob/master/libobs/obs-hotkeys.h) +* @param {Object (Optional)} `keyModifiers` Optional key modifiers object. False entries can be ommitted +* @param {boolean} `keyModifiers.shift` Trigger Shift Key +* @param {boolean} `keyModifiers.alt` Trigger Alt Key +* @param {boolean} `keyModifiers.control` Trigger Control (Ctrl) Key +* @param {boolean} `keyModifiers.command` Trigger Command Key (Mac) * * @api requests -* @name ProcessHotkeyByCombination +* @name TriggerHotkeyByCombination * @category general * @since unreleased */ RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request) { - if (!request.hasField("key")) { - return request.failed("Missing request key parameter"); + if (!request.hasField("keyId")) { + return request.failed("missing request keyId parameter"); } - OBSDataAutoRelease data = obs_data_get_obj(request.parameters(), "modifiers"); + OBSDataAutoRelease data = obs_data_get_obj(request.parameters(), "keyModifiers"); obs_key_combination_t combo = {0}; uint32_t modifiers = 0; @@ -397,11 +401,11 @@ RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request) modifiers |= INTERACT_COMMAND_KEY; combo.modifiers = modifiers; - combo.key = obs_key_from_name(obs_data_get_string(request.parameters(), "key")); + combo.key = obs_key_from_name(obs_data_get_string(request.parameters(), "keyId")); if (!modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) { - return request.failed("Invalid key-modifier combination"); + return request.failed("invalid key-modifier combination"); } // Inject hotkey press-release sequence From 8a90051ab5f4ce08bc00626faf49775b9e1b58b5 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Wed, 30 Sep 2020 22:22:57 +0000 Subject: [PATCH 153/278] docs(ci): Update protocol.md - 02c7a7f [skip ci] --- docs/generated/comments.json | 114 +++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 47 +++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index a4fb874f..9f1bf005 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -4065,6 +4065,120 @@ "lead": "", "type": "class", "examples": [] + }, + { + "subheads": [], + "description": "Executes hotkey routine, identified by hotkey unique name", + "param": "{String} `hotkeyName` Unique name of the hotkey, as defined when registering the hotkey (e.g. \"ReplayBuffer.Save\")", + "api": "requests", + "name": "TriggerHotkeyByName", + "category": "general", + "since": "unreleased", + "params": [ + { + "type": "String", + "name": "hotkeyName", + "description": "Unique name of the hotkey, as defined when registering the hotkey (e.g. \"ReplayBuffer.Save\")" + } + ], + "names": [ + { + "name": "", + "description": "TriggerHotkeyByName" + } + ], + "categories": [ + { + "name": "", + "description": "general" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "TriggerHotkeyByName" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Executes hotkey routine, identified by bound combination of keys. A single key combination might trigger multiple hotkey routines depending on user settings", + "param": [ + "{String} `keyId` Main key identifier (e.g. `OBS_KEY_A` for key \"A\"). Available identifiers [here](https://github.com/obsproject/obs-studio/blob/master/libobs/obs-hotkeys.h)", + "{Object (Optional)} `keyModifiers` Optional key modifiers object. False entries can be ommitted", + "{boolean} `keyModifiers.shift` Trigger Shift Key", + "{boolean} `keyModifiers.alt` Trigger Alt Key", + "{boolean} `keyModifiers.control` Trigger Control (Ctrl) Key", + "{boolean} `keyModifiers.command` Trigger Command Key (Mac)" + ], + "api": "requests", + "name": "TriggerHotkeyByCombination", + "category": "general", + "since": "unreleased", + "params": [ + { + "type": "String", + "name": "keyId", + "description": "Main key identifier (e.g. `OBS_KEY_A` for key \"A\"). Available identifiers [here](https://github.com/obsproject/obs-studio/blob/master/libobs/obs-hotkeys.h)" + }, + { + "type": "Object (Optional)", + "name": "keyModifiers", + "description": "Optional key modifiers object. False entries can be ommitted" + }, + { + "type": "boolean", + "name": "keyModifiers.shift", + "description": "Trigger Shift Key" + }, + { + "type": "boolean", + "name": "keyModifiers.alt", + "description": "Trigger Alt Key" + }, + { + "type": "boolean", + "name": "keyModifiers.control", + "description": "Trigger Control (Ctrl) Key" + }, + { + "type": "boolean", + "name": "keyModifiers.command", + "description": "Trigger Command Key (Mac)" + } + ], + "names": [ + { + "name": "", + "description": "TriggerHotkeyByCombination" + } + ], + "categories": [ + { + "name": "", + "description": "general" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "TriggerHotkeyByCombination" + }, + "lead": "", + "type": "class", + "examples": [] } ], "media control": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index ec8d7760..78bfda94 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -136,6 +136,8 @@ You can also refer to any of the client libraries listed on the [README](README. + [BroadcastCustomMessage](#broadcastcustommessage-1) + [GetVideoInfo](#getvideoinfo) + [OpenProjector](#openprojector) + + [TriggerHotkeyByName](#triggerhotkeybyname) + + [TriggerHotkeyByCombination](#triggerhotkeybycombination) * [Media Control](#media-control) + [PlayPauseMedia](#playpausemedia) + [RestartMedia](#restartmedia) @@ -1649,6 +1651,51 @@ Open a projector window or create a projector on a monitor. Requires OBS v24.0.4 | `name` | _String (Optional)_ | Name of the source or scene to be displayed (ignored for other projector types). | +**Response Items:** + +_No additional response items._ + +--- + +### TriggerHotkeyByName + + +- Unreleased + +Executes hotkey routine, identified by hotkey unique name + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `hotkeyName` | _String_ | Unique name of the hotkey, as defined when registering the hotkey (e.g. "ReplayBuffer.Save") | + + +**Response Items:** + +_No additional response items._ + +--- + +### TriggerHotkeyByCombination + + +- Unreleased + +Executes hotkey routine, identified by bound combination of keys. A single key combination might trigger multiple hotkey routines depending on user settings + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `keyId` | _String_ | Main key identifier (e.g. `OBS_KEY_A` for key "A"). Available identifiers [here](https://github.com/obsproject/obs-studio/blob/master/libobs/obs-hotkeys.h) | +| `keyModifiers` | _Object (Optional)_ | Optional key modifiers object. False entries can be ommitted | +| `keyModifiers.shift` | _boolean_ | Trigger Shift Key | +| `keyModifiers.alt` | _boolean_ | Trigger Alt Key | +| `keyModifiers.control` | _boolean_ | Trigger Control (Ctrl) Key | +| `keyModifiers.command` | _boolean_ | Trigger Command Key (Mac) | + + **Response Items:** _No additional response items._ From 0be5564ce7fa124f35d5a8efaec6749655ce4549 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 30 Sep 2020 15:57:13 -0700 Subject: [PATCH 154/278] Docs: Fix typo --- src/WSRequestHandler_General.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index a5f19143..d42513bc 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -378,7 +378,7 @@ RpcResponse WSRequestHandler::TriggerHotkeyByName(const RpcRequest& request) { * @param {boolean} `keyModifiers.command` Trigger Command Key (Mac) * * @api requests -* @name TriggerHotkeyByCombination +* @name TriggerHotkeyBySequence * @category general * @since unreleased */ From d9f35a855dbdc4cc64ca2c08612480d5a6bbfe34 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Thu, 1 Oct 2020 07:56:26 +0000 Subject: [PATCH 155/278] docs(ci): Update protocol.md - 9678d59 [skip ci] --- docs/generated/comments.json | 6 +++--- docs/generated/protocol.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 9f1bf005..9670bee5 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -4119,7 +4119,7 @@ "{boolean} `keyModifiers.command` Trigger Command Key (Mac)" ], "api": "requests", - "name": "TriggerHotkeyByCombination", + "name": "TriggerHotkeyBySequence", "category": "general", "since": "unreleased", "params": [ @@ -4157,7 +4157,7 @@ "names": [ { "name": "", - "description": "TriggerHotkeyByCombination" + "description": "TriggerHotkeyBySequence" } ], "categories": [ @@ -4174,7 +4174,7 @@ ], "heading": { "level": 2, - "text": "TriggerHotkeyByCombination" + "text": "TriggerHotkeyBySequence" }, "lead": "", "type": "class", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 78bfda94..76aad352 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -137,7 +137,7 @@ You can also refer to any of the client libraries listed on the [README](README. + [GetVideoInfo](#getvideoinfo) + [OpenProjector](#openprojector) + [TriggerHotkeyByName](#triggerhotkeybyname) - + [TriggerHotkeyByCombination](#triggerhotkeybycombination) + + [TriggerHotkeyBySequence](#triggerhotkeybysequence) * [Media Control](#media-control) + [PlayPauseMedia](#playpausemedia) + [RestartMedia](#restartmedia) @@ -1677,7 +1677,7 @@ _No additional response items._ --- -### TriggerHotkeyByCombination +### TriggerHotkeyBySequence - Unreleased From 7a31e88ed6dada1555fb7ce28d076404e6ae483c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 25 Oct 2020 19:25:26 +0100 Subject: [PATCH 156/278] requests: add GetTransitionSettings and SetTransitionSettings --- src/WSRequestHandler.cpp | 4 +- src/WSRequestHandler.h | 2 + src/WSRequestHandler_Transitions.cpp | 65 ++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index b4ab8137..8d8e7776 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -28,7 +28,7 @@ using namespace std::placeholders; -const QHash WSRequestHandler::messageMap { +const QHash WSRequestHandler::messageMap{ { "GetVersion", &WSRequestHandler::GetVersion }, { "GetAuthRequired", &WSRequestHandler::GetAuthRequired }, { "Authenticate", &WSRequestHandler::Authenticate }, @@ -96,6 +96,8 @@ const QHash WSRequestHandler::messageMap { { "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration }, { "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration }, { "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition }, + { "GetTransitionSettings", &WSRequestHandler::GetTransitionSettings }, + { "SetTransitionSettings", &WSRequestHandler::SetTransitionSettings }, { "SetVolume", &WSRequestHandler::SetVolume }, { "GetVolume", &WSRequestHandler::GetVolume }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index c3190668..03281a5e 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -113,6 +113,8 @@ class WSRequestHandler { RpcResponse SetTransitionDuration(const RpcRequest&); RpcResponse GetTransitionDuration(const RpcRequest&); RpcResponse GetTransitionPosition(const RpcRequest&); + RpcResponse GetTransitionSettings(const RpcRequest&); + RpcResponse SetTransitionSettings(const RpcRequest&); RpcResponse SetVolume(const RpcRequest&); RpcResponse GetVolume(const RpcRequest&); diff --git a/src/WSRequestHandler_Transitions.cpp b/src/WSRequestHandler_Transitions.cpp index 9bcee450..9875b625 100644 --- a/src/WSRequestHandler_Transitions.cpp +++ b/src/WSRequestHandler_Transitions.cpp @@ -139,3 +139,68 @@ RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) { return request.success(response); } + +/** + * Get the current settings of a transition + * + * @param {String} `transitionName` Transition name + * + * @return {Object} `transitionSettings` Current transition settings + * + * @api requests + * @name GetTransitionSettings + * @category transitions + * @since unreleased + */ +RpcResponse WSRequestHandler::GetTransitionSettings(const RpcRequest& request) { + if (!request.hasField("transitionName")) { + return request.failed("missing request parameters"); + } + + const char* transitionName = obs_data_get_string(request.parameters(), "transitionName"); + OBSSourceAutoRelease transition = Utils::GetTransitionFromName(transitionName); + if (!transition) { + return request.failed("specified transition doesn't exist"); + } + + OBSDataAutoRelease transitionSettings = obs_source_get_settings(transition); + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_obj(response, "transitionSettings", transitionSettings); + return request.success(response); +} + +/** + * Change the current settings of a transition + * + * @param {String} `transitionName` Transition name + * @param {Object} `transitionSettings` Transition settings (they can be partial) + * + * @return {Object} `transitionSettings` Updated transition settings + * + * @api requests + * @name SetTransitionSettings + * @category transitions + * @since unreleased + */ +RpcResponse WSRequestHandler::SetTransitionSettings(const RpcRequest& request) { + if (!request.hasField("transitionName") || !request.hasField("transitionSettings")) { + return request.failed("missing request parameters"); + } + + const char* transitionName = obs_data_get_string(request.parameters(), "transitionName"); + OBSSourceAutoRelease transition = Utils::GetTransitionFromName(transitionName); + if (!transition) { + return request.failed("specified transition doesn't exist"); + } + + OBSDataAutoRelease newSettings = obs_data_get_obj(request.parameters(), "transitionSettings"); + obs_source_update(transition, newSettings); + obs_source_update_properties(transition); + + OBSDataAutoRelease updatedSettings = obs_source_get_settings(transition); + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_obj(response, "transitionSettings", updatedSettings); // TODO + return request.success(response); +} From 5d9d5e074676631a808eec9deecc910a66e4664b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 25 Oct 2020 19:25:48 +0100 Subject: [PATCH 157/278] request(SetSourceSettings): remove redundant obs_data_apply step --- src/WSRequestHandler_Sources.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index c6125e5b..ba98ecdb 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -511,6 +511,7 @@ RpcResponse WSRequestHandler::GetSourceSettings(const RpcRequest& request) const char* sourceName = obs_data_get_string(request.parameters(), "sourceName"); OBSSourceAutoRelease source = obs_get_source_by_name(sourceName); + if (!source) { return request.failed("specified source doesn't exist"); } @@ -571,21 +572,17 @@ RpcResponse WSRequestHandler::SetSourceSettings(const RpcRequest& request) } } - OBSDataAutoRelease currentSettings = obs_source_get_settings(source); OBSDataAutoRelease newSettings = obs_data_get_obj(request.parameters(), "sourceSettings"); - OBSDataAutoRelease sourceSettings = obs_data_create(); - obs_data_apply(sourceSettings, currentSettings); - obs_data_apply(sourceSettings, newSettings); - - obs_source_update(source, sourceSettings); + obs_source_update(source, newSettings); obs_source_update_properties(source); + OBSDataAutoRelease updatedSettings = obs_source_get_settings(source); + OBSDataAutoRelease response = obs_data_create(); obs_data_set_string(response, "sourceName", obs_source_get_name(source)); obs_data_set_string(response, "sourceType", obs_source_get_id(source)); - obs_data_set_obj(response, "sourceSettings", sourceSettings); - + obs_data_set_obj(response, "sourceSettings", updatedSettings); return request.success(response); } From 8ba441da7fbeecffd90f971e97128b08dfba3c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 25 Oct 2020 19:33:42 +0100 Subject: [PATCH 158/278] requests(transitions): remove TODO --- src/WSRequestHandler_Transitions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_Transitions.cpp b/src/WSRequestHandler_Transitions.cpp index 9875b625..ef03352d 100644 --- a/src/WSRequestHandler_Transitions.cpp +++ b/src/WSRequestHandler_Transitions.cpp @@ -201,6 +201,6 @@ RpcResponse WSRequestHandler::SetTransitionSettings(const RpcRequest& request) { OBSDataAutoRelease updatedSettings = obs_source_get_settings(transition); OBSDataAutoRelease response = obs_data_create(); - obs_data_set_obj(response, "transitionSettings", updatedSettings); // TODO + obs_data_set_obj(response, "transitionSettings", updatedSettings); return request.success(response); } From 11326274e06d0b3c33d4c1f75115cf5a25cd9170 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Sun, 25 Oct 2020 18:34:53 +0000 Subject: [PATCH 159/278] docs(ci): Update protocol.md - acd3940 [skip ci] --- docs/generated/comments.json | 12 +++++++++--- docs/generated/protocol.md | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 9670bee5..9754b0c1 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -8092,8 +8092,9 @@ { "subheads": [], "description": "Get a list of all scene items in a scene.", - "param": "{String} `sceneName` Name of the scene to get the list of scene items from.", + "param": "{String (optional)} `sceneName` Name of the scene to get the list of scene items from. Defaults to the current scene if not specified.", "return": [ + "{String} `sceneName` Name of the requested (or current) scene", "{Array} `sceneItems` Array of scene items", "{int} `sceneItems.*.itemId` Unique item id of the source item", "{String} `sceneItems.*.sourceKind` ID if the scene item's source. For example `vlc_source` or `image_source`", @@ -8105,6 +8106,11 @@ "category": "scene items", "since": "unreleased", "returns": [ + { + "type": "String", + "name": "sceneName", + "description": "Name of the requested (or current) scene" + }, { "type": "Array", "name": "sceneItems", @@ -8133,9 +8139,9 @@ ], "params": [ { - "type": "String", + "type": "String (optional)", "name": "sceneName", - "description": "Name of the scene to get the list of scene items from." + "description": "Name of the scene to get the list of scene items from. Defaults to the current scene if not specified." } ], "names": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 76aad352..487c732e 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -3261,13 +3261,14 @@ Get a list of all scene items in a scene. | Name | Type | Description | | ---- | :---: | ------------| -| `sceneName` | _String_ | Name of the scene to get the list of scene items from. | +| `sceneName` | _String (optional)_ | Name of the scene to get the list of scene items from. Defaults to the current scene if not specified. | **Response Items:** | Name | Type | Description | | ---- | :---: | ------------| +| `sceneName` | _String_ | Name of the requested (or current) scene | | `sceneItems` | _Array<Object>_ | Array of scene items | | `sceneItems.*.itemId` | _int_ | Unique item id of the source item | | `sceneItems.*.sourceKind` | _String_ | ID if the scene item's source. For example `vlc_source` or `image_source` | From fabf68b6350b53a31a65dadfde07a16fc80dd967 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Mon, 2 Nov 2020 23:13:33 -0800 Subject: [PATCH 160/278] CI: Remove BUILD_CAPTIONS flag OBS has removed the `BUILD_CAPTIONS` flag from their repo. No reason to keep it --- .github/workflows/pr_push.yml | 8 ++++---- .github/workflows/tag_release.yml | 8 ++++---- CI/install-build-obs-macos.sh | 1 - CI/prepare-obs-windows.cmd | 6 +++--- src/WSRequestHandler.cpp | 2 -- src/WSRequestHandler.h | 2 -- src/WSRequestHandler_Streaming.cpp | 2 -- 7 files changed, 11 insertions(+), 18 deletions(-) diff --git a/.github/workflows/pr_push.yml b/.github/workflows/pr_push.yml index 62836706..cad2023f 100644 --- a/.github/workflows/pr_push.yml +++ b/.github/workflows/pr_push.yml @@ -82,7 +82,7 @@ jobs: run: | mkdir .\build32 cd .\build32 - cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. + cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. - name: 'Build OBS-Studio 32-bit' if: steps.build-cache-obs-32.outputs.cache-hit != 'true' working-directory: ${{ github.workspace }}/obs-studio @@ -105,7 +105,7 @@ jobs: run: | mkdir .\build64 cd .\build64 - cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. + cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. - name: 'Build OBS-Studio 64-bit' if: steps.build-cache-obs-64.outputs.cache-hit != 'true' working-directory: ${{ github.workspace }}/obs-studio @@ -242,7 +242,7 @@ jobs: run: | mkdir ./build cd ./build - cmake -DBUILD_CAPTIONS=YES -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr .. + cmake -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr .. - name: 'Build OBS-Studio' working-directory: ${{ github.workspace }}/obs-studio shell: bash @@ -354,7 +354,7 @@ jobs: run: | mkdir build cd build - cmake -DBUILD_CAPTIONS=YES -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DENABLE_SCRIPTING=NO -DDepsPath=/tmp/obsdeps -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/${{ env.QT_VERSION }}/lib/cmake .. + cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DENABLE_SCRIPTING=NO -DDepsPath=/tmp/obsdeps -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/${{ env.QT_VERSION }}/lib/cmake .. - name: 'Build OBS Studio libraries' working-directory: ${{ github.workspace }}/obs-studio shell: bash diff --git a/.github/workflows/tag_release.yml b/.github/workflows/tag_release.yml index 698ce49a..f499e0bb 100644 --- a/.github/workflows/tag_release.yml +++ b/.github/workflows/tag_release.yml @@ -76,7 +76,7 @@ jobs: run: | mkdir .\build32 cd .\build32 - cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. + cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. - name: 'Build OBS-Studio 32-bit' if: steps.build-cache-obs-32.outputs.cache-hit != 'true' working-directory: ${{ github.workspace }}/obs-studio @@ -99,7 +99,7 @@ jobs: run: | mkdir .\build64 cd .\build64 - cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DBUILD_CAPTIONS=YES -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. + cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES .. - name: 'Build OBS-Studio 64-bit' if: steps.build-cache-obs-64.outputs.cache-hit != 'true' working-directory: ${{ github.workspace }}/obs-studio @@ -235,7 +235,7 @@ jobs: run: | mkdir ./build cd ./build - cmake -DBUILD_CAPTIONS=YES -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr .. + cmake -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr .. - name: 'Build OBS-Studio' working-directory: ${{ github.workspace }}/obs-studio shell: bash @@ -346,7 +346,7 @@ jobs: run: | mkdir build cd build - cmake -DBUILD_CAPTIONS=YES -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DENABLE_SCRIPTING=NO -DDepsPath=/tmp/obsdeps -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/${{ env.QT_VERSION }}/lib/cmake .. + cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DENABLE_SCRIPTING=NO -DDepsPath=/tmp/obsdeps -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/${{ env.QT_VERSION }}/lib/cmake .. - name: 'Build OBS Studio libraries' working-directory: ${{ github.workspace }}/obs-studio shell: bash diff --git a/CI/install-build-obs-macos.sh b/CI/install-build-obs-macos.sh index 0dc610aa..4af63b49 100755 --- a/CI/install-build-obs-macos.sh +++ b/CI/install-build-obs-macos.sh @@ -34,7 +34,6 @@ git checkout $OBSLatestTag mkdir build && cd build echo "[obs-websocket] Building obs-studio.." cmake .. \ - -DBUILD_CAPTIONS=true \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \ -DDISABLE_PLUGINS=true \ -DENABLE_SCRIPTING=0 \ diff --git a/CI/prepare-obs-windows.cmd b/CI/prepare-obs-windows.cmd index 961fbe17..43fc1c32 100644 --- a/CI/prepare-obs-windows.cmd +++ b/CI/prepare-obs-windows.cmd @@ -24,14 +24,14 @@ if not exist build64 mkdir build64 echo Running cmake for obs-studio %OBSLatestTag% 32-bit... cd build32 -cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DepsPath32%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. +cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DepsPath32%" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. echo: echo: echo Running cmake for obs-studio %OBSLatestTag% 64-bit... cd ..\build64 -cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DepsPath64%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. +cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DepsPath64%" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. echo: echo: -dir "%OBSPath%\libobs" \ No newline at end of file +dir "%OBSPath%\libobs" diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index b4ab8137..e4f0a907 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -135,9 +135,7 @@ const QHash WSRequestHandler::messageMap { { "SetStreamSettings", &WSRequestHandler::SetStreamSettings }, { "GetStreamSettings", &WSRequestHandler::GetStreamSettings }, { "SaveStreamSettings", &WSRequestHandler::SaveStreamSettings }, -#if BUILD_CAPTIONS { "SendCaptions", &WSRequestHandler::SendCaptions }, -#endif { "GetStudioModeStatus", &WSRequestHandler::GetStudioModeStatus }, { "GetPreviewScene", &WSRequestHandler::GetPreviewScene }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index c3190668..fcde500c 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -152,9 +152,7 @@ class WSRequestHandler { RpcResponse SetStreamSettings(const RpcRequest&); RpcResponse GetStreamSettings(const RpcRequest&); RpcResponse SaveStreamSettings(const RpcRequest&); -#if BUILD_CAPTIONS RpcResponse SendCaptions(const RpcRequest&); -#endif RpcResponse GetStudioModeStatus(const RpcRequest&); RpcResponse GetPreviewScene(const RpcRequest&); diff --git a/src/WSRequestHandler_Streaming.cpp b/src/WSRequestHandler_Streaming.cpp index aa0782c9..94f5b5c3 100644 --- a/src/WSRequestHandler_Streaming.cpp +++ b/src/WSRequestHandler_Streaming.cpp @@ -300,7 +300,6 @@ RpcResponse WSRequestHandler::SaveStreamSettings(const RpcRequest& request) { * @category streaming * @since 4.6.0 */ -#if BUILD_CAPTIONS RpcResponse WSRequestHandler::SendCaptions(const RpcRequest& request) { if (!request.hasField("text")) { return request.failed("missing request parameters"); @@ -315,5 +314,4 @@ RpcResponse WSRequestHandler::SendCaptions(const RpcRequest& request) { return request.success(); } -#endif From b9cbd0ecd155ae18920a520b7df374427a37368f Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Fri, 27 Nov 2020 11:19:36 +0000 Subject: [PATCH 161/278] docs(ci): Update protocol.md - 3d7511c [skip ci] --- docs/generated/comments.json | 106 +++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 49 ++++++++++++++++ 2 files changed, 155 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 9754b0c1..a703f6d3 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -10521,6 +10521,112 @@ "lead": "", "type": "class", "examples": [] + }, + { + "subheads": [], + "description": "Get the current settings of a transition", + "param": "{String} `transitionName` Transition name", + "return": "{Object} `transitionSettings` Current transition settings", + "api": "requests", + "name": "GetTransitionSettings", + "category": "transitions", + "since": "unreleased", + "returns": [ + { + "type": "Object", + "name": "transitionSettings", + "description": "Current transition settings" + } + ], + "params": [ + { + "type": "String", + "name": "transitionName", + "description": "Transition name" + } + ], + "names": [ + { + "name": "", + "description": "GetTransitionSettings" + } + ], + "categories": [ + { + "name": "", + "description": "transitions" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "GetTransitionSettings" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Change the current settings of a transition", + "param": [ + "{String} `transitionName` Transition name", + "{Object} `transitionSettings` Transition settings (they can be partial)" + ], + "return": "{Object} `transitionSettings` Updated transition settings", + "api": "requests", + "name": "SetTransitionSettings", + "category": "transitions", + "since": "unreleased", + "returns": [ + { + "type": "Object", + "name": "transitionSettings", + "description": "Updated transition settings" + } + ], + "params": [ + { + "type": "String", + "name": "transitionName", + "description": "Transition name" + }, + { + "type": "Object", + "name": "transitionSettings", + "description": "Transition settings (they can be partial)" + } + ], + "names": [ + { + "name": "", + "description": "SetTransitionSettings" + } + ], + "categories": [ + { + "name": "", + "description": "transitions" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "SetTransitionSettings" + }, + "lead": "", + "type": "class", + "examples": [] } ] } diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 487c732e..7d3f7610 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -255,6 +255,8 @@ You can also refer to any of the client libraries listed on the [README](README. + [SetTransitionDuration](#settransitionduration) + [GetTransitionDuration](#gettransitionduration) + [GetTransitionPosition](#gettransitionposition) + + [GetTransitionSettings](#gettransitionsettings) + + [SetTransitionSettings](#settransitionsettings) @@ -4185,3 +4187,50 @@ _No specified parameters._ --- +### GetTransitionSettings + + +- Unreleased + +Get the current settings of a transition + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `transitionName` | _String_ | Transition name | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `transitionSettings` | _Object_ | Current transition settings | + + +--- + +### SetTransitionSettings + + +- Unreleased + +Change the current settings of a transition + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `transitionName` | _String_ | Transition name | +| `transitionSettings` | _Object_ | Transition settings (they can be partial) | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `transitionSettings` | _Object_ | Updated transition settings | + + +--- + From eddd4abe76dee7d7dc94444551ad660b27e012e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 12:45:57 +0100 Subject: [PATCH 162/278] ci(linux): update frontend api headers to v26 --- CI/install-dependencies-ubuntu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/install-dependencies-ubuntu.sh b/CI/install-dependencies-ubuntu.sh index d0e16f53..4ed044da 100755 --- a/CI/install-dependencies-ubuntu.sh +++ b/CI/install-dependencies-ubuntu.sh @@ -14,6 +14,6 @@ sudo apt-get install -y \ qtbase5-dev # Dirty hack -sudo wget -O /usr/include/obs/obs-frontend-api.h https://raw.githubusercontent.com/obsproject/obs-studio/25.0.0/UI/obs-frontend-api/obs-frontend-api.h +sudo wget -O /usr/include/obs/obs-frontend-api.h https://raw.githubusercontent.com/obsproject/obs-studio/26.0.0/UI/obs-frontend-api/obs-frontend-api.h sudo ldconfig From 6b03efed42ac4180c9fbe18ed0b08cc1cfaa6537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 15:33:32 +0100 Subject: [PATCH 163/278] requests(outputs): fix corrupted parameters object on MSVC when using findOutputOrFail --- src/WSRequestHandler_Outputs.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/WSRequestHandler_Outputs.cpp b/src/WSRequestHandler_Outputs.cpp index ab8b0cc1..c30f9ef5 100644 --- a/src/WSRequestHandler_Outputs.cpp +++ b/src/WSRequestHandler_Outputs.cpp @@ -59,7 +59,7 @@ obs_data_t* getOutputInfo(obs_output_t* output) return data; } -RpcResponse findOutputOrFail(const RpcRequest& request, std::function callback) +RpcResponse findOutputOrFail(const RpcRequest& request, std::function callback) { if (!request.hasField("outputName")) { return request.failed("missing request parameters"); @@ -71,7 +71,7 @@ RpcResponse findOutputOrFail(const RpcRequest& request, std::function Date: Fri, 27 Nov 2020 16:03:21 +0100 Subject: [PATCH 164/278] requests(transitions): documentation fixes --- src/WSRequestHandler_Transitions.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/WSRequestHandler_Transitions.cpp b/src/WSRequestHandler_Transitions.cpp index 78d7ee30..fbdef0cf 100644 --- a/src/WSRequestHandler_Transitions.cpp +++ b/src/WSRequestHandler_Transitions.cpp @@ -129,7 +129,7 @@ RpcResponse WSRequestHandler::GetTransitionDuration(const RpcRequest& request) { * @api requests * @name GetTransitionPosition * @category transitions - * @since 4.8.0 + * @since unreleased */ RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) { OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); @@ -206,12 +206,12 @@ RpcResponse WSRequestHandler::SetTransitionSettings(const RpcRequest& request) { } /** - * Release the T-Bar. YOU MUST CALL THIS IF YOU SPECIFY `release = false` IN `SetTBarPosition`. + * Release the T-Bar. *YOU MUST CALL THIS if you called `SetTBarPosition` with the `release` parameter set to `false`.* * * @api requests * @name ReleaseTBar * @category transitions - * @since 4.8.0 + * @since unreleased */ RpcResponse WSRequestHandler::ReleaseTBar(const RpcRequest& request) { if (!obs_frontend_preview_program_mode_active()) { @@ -231,12 +231,13 @@ RpcResponse WSRequestHandler::ReleaseTBar(const RpcRequest& request) { * Set the manual position of the T-Bar (in Studio Mode) to the specified value. Will return an error if OBS is not in studio mode * or if the current transition doesn't support T-Bar control. * - * @param {double} `position` T-Bar position. This value must be between 0.0 and 1.0. + * @param {double} `position` T-Bar position. This value must be between 0.0 and 1024.0. + * @param {boolean (optional)} `release` Whether or not the T-Bar gets released after setting its new position. Call `ReleaseTBar` manually if you set `release` to false. Defaults to true * * @api requests * @name SetTBarPosition * @category transitions - * @since 4.8.0 + * @since unreleased */ RpcResponse WSRequestHandler::SetTBarPosition(const RpcRequest& request) { if (!obs_frontend_preview_program_mode_active()) { From 98187e2bd7a91a942c65e01933df7b783b9bb547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 16:07:20 +0100 Subject: [PATCH 165/278] requests(transitions): documentation fixes cont. --- src/WSRequestHandler_Transitions.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/WSRequestHandler_Transitions.cpp b/src/WSRequestHandler_Transitions.cpp index fbdef0cf..0c7f3f9c 100644 --- a/src/WSRequestHandler_Transitions.cpp +++ b/src/WSRequestHandler_Transitions.cpp @@ -206,7 +206,8 @@ RpcResponse WSRequestHandler::SetTransitionSettings(const RpcRequest& request) { } /** - * Release the T-Bar. *YOU MUST CALL THIS if you called `SetTBarPosition` with the `release` parameter set to `false`.* + * Release the T-Bar (like a user releasing their mouse button after moving it). + * *YOU MUST CALL THIS if you called `SetTBarPosition` with the `release` parameter set to `false`.* * * @api requests * @name ReleaseTBar @@ -231,8 +232,8 @@ RpcResponse WSRequestHandler::ReleaseTBar(const RpcRequest& request) { * Set the manual position of the T-Bar (in Studio Mode) to the specified value. Will return an error if OBS is not in studio mode * or if the current transition doesn't support T-Bar control. * - * @param {double} `position` T-Bar position. This value must be between 0.0 and 1024.0. - * @param {boolean (optional)} `release` Whether or not the T-Bar gets released after setting its new position. Call `ReleaseTBar` manually if you set `release` to false. Defaults to true + * @param {double} `position` T-Bar position. This value must be between 0.0 and 1.0. + * @param {boolean (optional)} `release` Whether or not the T-Bar gets released automatically after setting its new position (like a user releasing their mouse button after moving the T-Bar). Call `ReleaseTBar` manually if you set `release` to false. Defaults to true. * * @api requests * @name SetTBarPosition From a53df39e46f3897c21c93db4bc7c185b5b6c63e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 16:15:03 +0100 Subject: [PATCH 166/278] requests(transitions): yet another doc fix --- src/WSRequestHandler_Transitions.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/WSRequestHandler_Transitions.cpp b/src/WSRequestHandler_Transitions.cpp index 0c7f3f9c..7fd15349 100644 --- a/src/WSRequestHandler_Transitions.cpp +++ b/src/WSRequestHandler_Transitions.cpp @@ -231,6 +231,8 @@ RpcResponse WSRequestHandler::ReleaseTBar(const RpcRequest& request) { /** * Set the manual position of the T-Bar (in Studio Mode) to the specified value. Will return an error if OBS is not in studio mode * or if the current transition doesn't support T-Bar control. + * + * If your code needs to perform multiple successive T-Bar moves (e.g. : in an animation, or in response to a user moving a T-Bar control in your User Interface), set `release` to false and call `ReleaseTBar` later once the animation/interaction is over. * * @param {double} `position` T-Bar position. This value must be between 0.0 and 1.0. * @param {boolean (optional)} `release` Whether or not the T-Bar gets released automatically after setting its new position (like a user releasing their mouse button after moving the T-Bar). Call `ReleaseTBar` manually if you set `release` to false. Defaults to true. From f7616ade1fc6dc3fed3a19b28cd960d5bcea8ea8 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Fri, 27 Nov 2020 15:41:27 +0000 Subject: [PATCH 167/278] docs(ci): Update protocol.md - f14379a [skip ci] --- docs/generated/comments.json | 86 +++++++++++++++++++++++++++++++++++- docs/generated/protocol.md | 45 ++++++++++++++++++- 2 files changed, 128 insertions(+), 3 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index a703f6d3..ba652f2f 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -10488,7 +10488,7 @@ "api": "requests", "name": "GetTransitionPosition", "category": "transitions", - "since": "4.8.0", + "since": "unreleased", "returns": [ { "type": "double", @@ -10511,7 +10511,7 @@ "sinces": [ { "name": "", - "description": "4.8.0" + "description": "unreleased" } ], "heading": { @@ -10627,6 +10627,88 @@ "lead": "", "type": "class", "examples": [] + }, + { + "subheads": [], + "description": "Release the T-Bar (like a user releasing their mouse button after moving it).\n*YOU MUST CALL THIS if you called `SetTBarPosition` with the `release` parameter set to `false`.*", + "api": "requests", + "name": "ReleaseTBar", + "category": "transitions", + "since": "unreleased", + "names": [ + { + "name": "", + "description": "ReleaseTBar" + } + ], + "categories": [ + { + "name": "", + "description": "transitions" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "ReleaseTBar" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "\n\nIf your code needs to perform multiple successive T-Bar moves (e.g. : in an animation, or in response to a user moving a T-Bar control in your User Interface), set `release` to false and call `ReleaseTBar` later once the animation/interaction is over.", + "param": [ + "{double} `position` T-Bar position. This value must be between 0.0 and 1.0.", + "{boolean (optional)} `release` Whether or not the T-Bar gets released automatically after setting its new position (like a user releasing their mouse button after moving the T-Bar). Call `ReleaseTBar` manually if you set `release` to false. Defaults to true." + ], + "api": "requests", + "name": "SetTBarPosition", + "category": "transitions", + "since": "unreleased", + "params": [ + { + "type": "double", + "name": "position", + "description": "T-Bar position. This value must be between 0.0 and 1.0." + }, + { + "type": "boolean (optional)", + "name": "release", + "description": "Whether or not the T-Bar gets released automatically after setting its new position (like a user releasing their mouse button after moving the T-Bar). Call `ReleaseTBar` manually if you set `release` to false. Defaults to true." + } + ], + "names": [ + { + "name": "", + "description": "SetTBarPosition" + } + ], + "categories": [ + { + "name": "", + "description": "transitions" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "SetTBarPosition" + }, + "lead": "Set the manual position of the T-Bar (in Studio Mode) to the specified value. Will return an error if OBS is not in studio mode or if the current transition doesn't support T-Bar control.", + "type": "class", + "examples": [] } ] } diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 7d3f7610..2bb187d1 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -257,6 +257,8 @@ You can also refer to any of the client libraries listed on the [README](README. + [GetTransitionPosition](#gettransitionposition) + [GetTransitionSettings](#gettransitionsettings) + [SetTransitionSettings](#settransitionsettings) + + [ReleaseTBar](#releasetbar) + + [SetTBarPosition](#settbarposition) @@ -4170,7 +4172,7 @@ _No specified parameters._ ### GetTransitionPosition -- Added in v4.8.0 +- Unreleased Get the position of the current transition. @@ -4234,3 +4236,44 @@ Change the current settings of a transition --- +### ReleaseTBar + + +- Unreleased + +Release the T-Bar (like a user releasing their mouse button after moving it). +*YOU MUST CALL THIS if you called `SetTBarPosition` with the `release` parameter set to `false`.* + +**Request Fields:** + +_No specified parameters._ + +**Response Items:** + +_No additional response items._ + +--- + +### SetTBarPosition + + +- Unreleased + + + +If your code needs to perform multiple successive T-Bar moves (e.g. : in an animation, or in response to a user moving a T-Bar control in your User Interface), set `release` to false and call `ReleaseTBar` later once the animation/interaction is over. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `position` | _double_ | T-Bar position. This value must be between 0.0 and 1.0. | +| `release` | _boolean (optional)_ | Whether or not the T-Bar gets released automatically after setting its new position (like a user releasing their mouse button after moving the T-Bar). Call `ReleaseTBar` manually if you set `release` to false. Defaults to true. | + + +**Response Items:** + +_No additional response items._ + +--- + From d87a7e896bbe980023b79dee8b4486cdfc91ef51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 16:46:33 +0100 Subject: [PATCH 168/278] events(SceneCollectionChanged): add new scene collection name --- src/WSEvents.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 56d24c8a..c2e9d0bc 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -479,13 +479,17 @@ void WSEvents::OnSceneListChange() { /** * Triggered when switching to another scene collection or when renaming the current scene collection. * + * @return {String} `sceneCollection` Name of the new current scene collection. + * * @api events * @name SceneCollectionChanged * @category scenes * @since 4.0.0 */ void WSEvents::OnSceneCollectionChange() { - broadcastUpdate("SceneCollectionChanged"); + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "sceneCollection", obs_frontend_get_current_scene_collection()); + broadcastUpdate("SceneCollectionChanged", fields); OnTransitionListChange(); OnTransitionChange(); From fe52cd8db15cfa95c1a67999b505480822b46b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 16:47:10 +0100 Subject: [PATCH 169/278] events(ProfileChanged): add new profile name --- src/WSEvents.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index c2e9d0bc..d04b2880 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -546,13 +546,17 @@ void WSEvents::OnTransitionListChange() { /** * Triggered when switching to another profile or when renaming the current profile. * + * @return {String} `profile` Name of the new current profile. + * * @api events * @name ProfileChanged * @category profiles * @since 4.0.0 */ void WSEvents::OnProfileChange() { - broadcastUpdate("ProfileChanged"); + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_string(fields, "profile", obs_frontend_get_current_profile()); + broadcastUpdate("ProfileChanged", fields); } /** From 0dc8e070ff07b691d8d0b0fa45d57901bae85bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 16:57:47 +0100 Subject: [PATCH 170/278] events(SceneCollectionListChanged): add scene collections list property --- src/WSEvents.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index d04b2880..705a3184 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -501,13 +501,23 @@ void WSEvents::OnSceneCollectionChange() { /** * Triggered when a scene collection is created, added, renamed, or removed. * + * @return {Array} `sceneCollections` Scene collections list. + * @return {String} `sceneCollections.*.name` Scene collection name. + * * @api events * @name SceneCollectionListChanged * @category scenes * @since 4.0.0 */ void WSEvents::OnSceneCollectionListChange() { - broadcastUpdate("SceneCollectionListChanged"); + char** sceneCollections = obs_frontend_get_scene_collections(); + OBSDataArrayAutoRelease sceneCollectionsList = + Utils::StringListToArray(sceneCollections, "name"); + bfree(sceneCollections); + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_array(fields, "sceneCollections", sceneCollectionsList); + broadcastUpdate("SceneCollectionListChanged", fields); } /** @@ -568,7 +578,9 @@ void WSEvents::OnProfileChange() { * @since 4.0.0 */ void WSEvents::OnProfileListChange() { - broadcastUpdate("ProfileListChanged"); + OBSDataAutoRelease fields = obs_data_create(); + // TODO provide new profile list + broadcastUpdate("ProfileListChanged", fields); } /** From 542761e411871e6f86e69ea42d21234f71a1b5e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 16:58:34 +0100 Subject: [PATCH 171/278] events(ProfileListChanged): add profile list property --- src/WSEvents.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 705a3184..351b30ad 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -572,14 +572,21 @@ void WSEvents::OnProfileChange() { /** * Triggered when a profile is created, added, renamed, or removed. * + * @return {Array} `profiles` Profiles list. + * @return {String} `profiles.*.name` Profile name. + * * @api events * @name ProfileListChanged * @category profiles * @since 4.0.0 */ void WSEvents::OnProfileListChange() { + char** profiles = obs_frontend_get_profiles(); + OBSDataArrayAutoRelease profilesList = Utils::StringListToArray(profiles, "name"); + bfree(profiles); + OBSDataAutoRelease fields = obs_data_create(); - // TODO provide new profile list + obs_data_set_array(fields, "profiles", profilesList); broadcastUpdate("ProfileListChanged", fields); } From 0cdea68567d11bb07506a5b966a96823013f665a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 17:07:28 +0100 Subject: [PATCH 172/278] events(ScenesChanged): provide scenes list property --- src/WSEvents.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 351b30ad..ad765e26 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -467,13 +467,19 @@ void WSEvents::OnSceneChange() { * * Note: This event is not fired when the scenes are reordered. * + * @return {Array} `scenes` Scenes list. + * * @api events * @name ScenesChanged * @category scenes * @since 0.3 */ void WSEvents::OnSceneListChange() { - broadcastUpdate("ScenesChanged"); + OBSDataArrayAutoRelease scenes = Utils::GetScenes(); + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_array(fields, "scenes", scenes); + broadcastUpdate("ScenesChanged", fields); } /** From 14409dec4f933748d4aa9177110c6fd979df8777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 17:09:07 +0100 Subject: [PATCH 173/278] events(TransitionListChanged): provide transitions list property --- src/WSEvents.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index ad765e26..af94214e 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -550,13 +550,30 @@ void WSEvents::OnTransitionChange() { * The list of available transitions has been modified. * Transitions have been added, removed, or renamed. * + * @return {Array} `transitions` Transitions list. + * * @api events * @name TransitionListChanged * @category transitions * @since 4.0.0 */ void WSEvents::OnTransitionListChange() { - broadcastUpdate("TransitionListChanged"); + obs_frontend_source_list transitionList = {}; + obs_frontend_get_transitions(&transitionList); + + OBSDataArrayAutoRelease transitions = obs_data_array_create(); + for (size_t i = 0; i < transitionList.sources.num; i++) { + OBSSource transition = transitionList.sources.array[i]; + + OBSDataAutoRelease obj = obs_data_create(); + obs_data_set_string(obj, "name", obs_source_get_name(transition)); + obs_data_array_push_back(transitions, obj); + } + obs_frontend_source_list_free(&transitionList); + + OBSDataAutoRelease fields = obs_data_create(); + obs_data_set_array(fields, "transitions", transitions); + broadcastUpdate("TransitionListChanged", fields); } /** From ae9ea8510c8439f3188e8f90f51a34e4876ecb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 17:10:27 +0100 Subject: [PATCH 174/278] events: fix docs --- src/WSEvents.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index af94214e..ce27798d 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -507,7 +507,7 @@ void WSEvents::OnSceneCollectionChange() { /** * Triggered when a scene collection is created, added, renamed, or removed. * - * @return {Array} `sceneCollections` Scene collections list. + * @return {Array} `sceneCollections` Scene collections list. * @return {String} `sceneCollections.*.name` Scene collection name. * * @api events @@ -550,7 +550,8 @@ void WSEvents::OnTransitionChange() { * The list of available transitions has been modified. * Transitions have been added, removed, or renamed. * - * @return {Array} `transitions` Transitions list. + * @return {Array} `transitions` Transitions list. + * @return {String} `transitions.*.name` Transition name. * * @api events * @name TransitionListChanged @@ -595,7 +596,7 @@ void WSEvents::OnProfileChange() { /** * Triggered when a profile is created, added, renamed, or removed. * - * @return {Array} `profiles` Profiles list. + * @return {Array} `profiles` Profiles list. * @return {String} `profiles.*.name` Profile name. * * @api events From ef0e90701431d571e35cd3b0d37269e8c80a814c Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Fri, 27 Nov 2020 16:24:32 +0000 Subject: [PATCH 175/278] docs(ci): Update protocol.md - 9fb0f56 [skip ci] --- docs/generated/comments.json | 72 ++++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 33 ++++++++++++++--- 2 files changed, 99 insertions(+), 6 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index ba652f2f..cffcd1d5 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -562,10 +562,18 @@ { "subheads": [], "description": "\n\nNote: This event is not fired when the scenes are reordered.", + "return": "{Array} `scenes` Scenes list.", "api": "events", "name": "ScenesChanged", "category": "scenes", "since": "0.3", + "returns": [ + { + "type": "Array", + "name": "scenes", + "description": "Scenes list." + } + ], "names": [ { "name": "", @@ -595,10 +603,18 @@ { "subheads": [], "description": "Triggered when switching to another scene collection or when renaming the current scene collection.", + "return": "{String} `sceneCollection` Name of the new current scene collection.", "api": "events", "name": "SceneCollectionChanged", "category": "scenes", "since": "4.0.0", + "returns": [ + { + "type": "String", + "name": "sceneCollection", + "description": "Name of the new current scene collection." + } + ], "names": [ { "name": "", @@ -628,10 +644,26 @@ { "subheads": [], "description": "Triggered when a scene collection is created, added, renamed, or removed.", + "return": [ + "{Array} `sceneCollections` Scene collections list.", + "{String} `sceneCollections.*.name` Scene collection name." + ], "api": "events", "name": "SceneCollectionListChanged", "category": "scenes", "since": "4.0.0", + "returns": [ + { + "type": "Array", + "name": "sceneCollections", + "description": "Scene collections list." + }, + { + "type": "String", + "name": "sceneCollections.*.name", + "description": "Scene collection name." + } + ], "names": [ { "name": "", @@ -704,10 +736,26 @@ { "subheads": [], "description": "The list of available transitions has been modified.\nTransitions have been added, removed, or renamed.", + "return": [ + "{Array} `transitions` Transitions list.", + "{String} `transitions.*.name` Transition name." + ], "api": "events", "name": "TransitionListChanged", "category": "transitions", "since": "4.0.0", + "returns": [ + { + "type": "Array", + "name": "transitions", + "description": "Transitions list." + }, + { + "type": "String", + "name": "transitions.*.name", + "description": "Transition name." + } + ], "names": [ { "name": "", @@ -975,10 +1023,18 @@ { "subheads": [], "description": "Triggered when switching to another profile or when renaming the current profile.", + "return": "{String} `profile` Name of the new current profile.", "api": "events", "name": "ProfileChanged", "category": "profiles", "since": "4.0.0", + "returns": [ + { + "type": "String", + "name": "profile", + "description": "Name of the new current profile." + } + ], "names": [ { "name": "", @@ -1008,10 +1064,26 @@ { "subheads": [], "description": "Triggered when a profile is created, added, renamed, or removed.", + "return": [ + "{Array} `profiles` Profiles list.", + "{String} `profiles.*.name` Profile name." + ], "api": "events", "name": "ProfileListChanged", "category": "profiles", "since": "4.0.0", + "returns": [ + { + "type": "Array", + "name": "profiles", + "description": "Profiles list." + }, + { + "type": "String", + "name": "profiles.*.name", + "description": "Profile name." + } + ], "names": [ { "name": "", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 2bb187d1..6ecdc51d 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -394,7 +394,10 @@ Note: This event is not fired when the scenes are reordered. **Response Items:** -_No additional response items._ +| Name | Type | Description | +| ---- | :---: | ------------| +| `scenes` | _Array<Scene>_ | Scenes list. | + --- @@ -407,7 +410,10 @@ Triggered when switching to another scene collection or when renaming the curren **Response Items:** -_No additional response items._ +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneCollection` | _String_ | Name of the new current scene collection. | + --- @@ -420,7 +426,11 @@ Triggered when a scene collection is created, added, renamed, or removed. **Response Items:** -_No additional response items._ +| Name | Type | Description | +| ---- | :---: | ------------| +| `sceneCollections` | _Array<Object>_ | Scene collections list. | +| `sceneCollections.*.name` | _String_ | Scene collection name. | + --- @@ -452,7 +462,11 @@ Transitions have been added, removed, or renamed. **Response Items:** -_No additional response items._ +| Name | Type | Description | +| ---- | :---: | ------------| +| `transitions` | _Array<Object>_ | Transitions list. | +| `transitions.*.name` | _String_ | Transition name. | + --- @@ -543,7 +557,10 @@ Triggered when switching to another profile or when renaming the current profile **Response Items:** -_No additional response items._ +| Name | Type | Description | +| ---- | :---: | ------------| +| `profile` | _String_ | Name of the new current profile. | + --- @@ -556,7 +573,11 @@ Triggered when a profile is created, added, renamed, or removed. **Response Items:** -_No additional response items._ +| Name | Type | Description | +| ---- | :---: | ------------| +| `profiles` | _Array<Object>_ | Profiles list. | +| `profiles.*.name` | _String_ | Profile name. | + --- From eaf34f3c3a3ee0fe6473a771301be38d61a795b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 19:38:01 +0100 Subject: [PATCH 176/278] requests(CreateSource): simplified settings apply obs_source_update already does that internally --- src/WSRequestHandler_Sources.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 4968d238..abd5b21c 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -66,12 +66,8 @@ RpcResponse WSRequestHandler::CreateSource(const RpcRequest& request) obs_source_set_enabled(newSource, true); if (request.hasField("sourceSettings")) { // We apply the settings after source creation because otherwise we get a bunch of memory leaks. - OBSDataAutoRelease currentSettings = obs_source_get_settings(newSource); OBSDataAutoRelease newSettings = obs_data_get_obj(request.parameters(), "sourceSettings"); - OBSDataAutoRelease sourceSettings = obs_data_create(); - obs_data_apply(sourceSettings, currentSettings); - obs_data_apply(sourceSettings, newSettings); - obs_source_update(newSource, sourceSettings); + obs_source_update(newSource, newSettings); obs_source_update_properties(newSource); } From 60ce25c689a33302cfb7828c659e6c46d8a7fbaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 19:38:55 +0100 Subject: [PATCH 177/278] requests(CreateSource): fix docs --- src/WSRequestHandler_Sources.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index abd5b21c..5214b408 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -25,7 +25,7 @@ bool isTextFreeType2Source(const QString& sourceKind) * @param {String} `sourceKind` Source kind, Eg. `vlc_source`. * @param {String} `sceneName` Scene to add the new source to. * @param {Object (optional)} `sourceSettings` Source settings data. - * @param {boolean (optional)} `setVisible` Set the created SceneItem as visible or not. + * @param {boolean (optional)} `setVisible` Set the created SceneItem as visible or not. Defaults to true * * @return {int} `itemId` ID of the SceneItem in the scene. * From 08178b93542e474f7d4028e0c6ce24aabe1e74fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 19:40:02 +0100 Subject: [PATCH 178/278] requests(CreateSource): use the OBSSourceAutoRelease container instead of manipulating refs manually --- src/WSRequestHandler_Sources.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 5214b408..3b662fdd 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -58,7 +58,7 @@ RpcResponse WSRequestHandler::CreateSource(const RpcRequest& request) return request.failed("requested scene is invalid or doesnt exist"); } - obs_source_t *newSource = obs_source_create(sourceKind.toUtf8(), sourceName.toUtf8(), nullptr, nullptr); + OBSSourceAutoRelease newSource = obs_source_create(sourceKind.toUtf8(), sourceName.toUtf8(), nullptr, nullptr); if (!newSource) { return request.failed("failed to create the source"); @@ -85,7 +85,6 @@ RpcResponse WSRequestHandler::CreateSource(const RpcRequest& request) OBSDataAutoRelease responseData = obs_data_create(); obs_data_set_int(responseData, "itemId", obs_sceneitem_get_id(data.sceneItem)); - obs_source_release(newSource); return request.success(responseData); } From 80d21ce80d029658207df9d7142c602ebb99cb1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 27 Nov 2020 23:52:55 +0100 Subject: [PATCH 179/278] WSRequestHandler: fix indent --- src/WSRequestHandler.cpp | 2 +- src/WSRequestHandler.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index bb897b44..e68f250e 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -99,7 +99,7 @@ const QHash WSRequestHandler::messageMap{ { "GetTransitionSettings", &WSRequestHandler::GetTransitionSettings }, { "SetTransitionSettings", &WSRequestHandler::SetTransitionSettings }, - { "CreateSource", &WSRequestHandler::CreateSource }, + { "CreateSource", &WSRequestHandler::CreateSource }, { "SetVolume", &WSRequestHandler::SetVolume }, { "GetVolume", &WSRequestHandler::GetVolume }, { "ToggleMute", &WSRequestHandler::ToggleMute }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 047734a5..0dfbdbb5 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -116,7 +116,7 @@ class WSRequestHandler { RpcResponse GetTransitionSettings(const RpcRequest&); RpcResponse SetTransitionSettings(const RpcRequest&); - RpcResponse CreateSource(const RpcRequest&); + RpcResponse CreateSource(const RpcRequest&); RpcResponse SetVolume(const RpcRequest&); RpcResponse GetVolume(const RpcRequest&); RpcResponse ToggleMute(const RpcRequest&); From a1fcc35fd6f97d60804495185b201d703db4965f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sat, 28 Nov 2020 00:19:27 +0100 Subject: [PATCH 180/278] requests(CreateSource): pass settings on source creation --- src/WSRequestHandler_Sources.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 3b662fdd..9aa5c260 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -58,19 +58,18 @@ RpcResponse WSRequestHandler::CreateSource(const RpcRequest& request) return request.failed("requested scene is invalid or doesnt exist"); } - OBSSourceAutoRelease newSource = obs_source_create(sourceKind.toUtf8(), sourceName.toUtf8(), nullptr, nullptr); + OBSDataAutoRelease sourceSettings = nullptr; + if (request.hasField("sourceSettings")) { + sourceSettings = obs_data_get_obj(request.parameters(), "sourceSettings"); + } + + OBSSourceAutoRelease newSource = obs_source_create(sourceKind.toUtf8(), sourceName.toUtf8(), sourceSettings, nullptr); if (!newSource) { return request.failed("failed to create the source"); } obs_source_set_enabled(newSource, true); - if (request.hasField("sourceSettings")) { // We apply the settings after source creation because otherwise we get a bunch of memory leaks. - OBSDataAutoRelease newSettings = obs_data_get_obj(request.parameters(), "sourceSettings"); - obs_source_update(newSource, newSettings); - obs_source_update_properties(newSource); - } - Utils::AddSourceData data; data.source = newSource; data.setVisible = true; From 569e9681e5ab5e2d2a3c11f4b7bece776f25646d Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Fri, 27 Nov 2020 23:39:48 +0000 Subject: [PATCH 181/278] docs(ci): Update protocol.md - 9edc3ea [skip ci] --- docs/generated/comments.json | 75 ++++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 28 ++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index cffcd1d5..d87fc089 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -4775,6 +4775,81 @@ "type": "class", "examples": [] }, + { + "subheads": [], + "description": "Create a source and add it as a sceneitem to a scene.", + "param": [ + "{String} `sourceName` Source name.", + "{String} `sourceKind` Source kind, Eg. `vlc_source`.", + "{String} `sceneName` Scene to add the new source to.", + "{Object (optional)} `sourceSettings` Source settings data.", + "{boolean (optional)} `setVisible` Set the created SceneItem as visible or not. Defaults to true" + ], + "return": "{int} `itemId` ID of the SceneItem in the scene.", + "api": "requests", + "name": "CreateSource", + "category": "sources", + "since": "unreleased", + "returns": [ + { + "type": "int", + "name": "itemId", + "description": "ID of the SceneItem in the scene." + } + ], + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + }, + { + "type": "String", + "name": "sourceKind", + "description": "Source kind, Eg. `vlc_source`." + }, + { + "type": "String", + "name": "sceneName", + "description": "Scene to add the new source to." + }, + { + "type": "Object (optional)", + "name": "sourceSettings", + "description": "Source settings data." + }, + { + "type": "boolean (optional)", + "name": "setVisible", + "description": "Set the created SceneItem as visible or not. Defaults to true" + } + ], + "names": [ + { + "name": "", + "description": "CreateSource" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "CreateSource" + }, + "lead": "", + "type": "class", + "examples": [] + }, { "subheads": [], "description": "List all sources available in the running OBS instance", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 6ecdc51d..82296c31 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -151,6 +151,7 @@ You can also refer to any of the client libraries listed on the [README](README. + [GetMediaState](#getmediastate) * [Sources](#sources-1) + [GetMediaSourcesList](#getmediasourceslist) + + [CreateSource](#createsource) + [GetSourcesList](#getsourceslist) + [GetSourceTypesList](#getsourcetypeslist) + [GetVolume](#getvolume) @@ -1966,6 +1967,33 @@ _No specified parameters._ | `mediaSources.*.mediaState` | _String_ | The current state of media for that source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown` | +--- + +### CreateSource + + +- Unreleased + +Create a source and add it as a sceneitem to a scene. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | +| `sourceKind` | _String_ | Source kind, Eg. `vlc_source`. | +| `sceneName` | _String_ | Scene to add the new source to. | +| `sourceSettings` | _Object (optional)_ | Source settings data. | +| `setVisible` | _boolean (optional)_ | Set the created SceneItem as visible or not. Defaults to true | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `itemId` | _int_ | ID of the SceneItem in the scene. | + + --- ### GetSourcesList From 9999b30d1aefbd7824b55601046ba0430ac51fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sat, 28 Nov 2020 05:49:09 +0100 Subject: [PATCH 182/278] docs(scene items): fix documentation for GetSceneItemProperties Fixes #607 --- src/WSRequestHandler_SceneItems.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index b5334d89..659a8b9b 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -87,7 +87,7 @@ RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) { * @return {int} `itemId` Scene Item ID. * @return {double} `position.x` The x position of the source from the left. * @return {double} `position.y` The y position of the source from the top. -* @return {int} `position.alignment` The point on the source that the item is manipulated from. +* @return {int} `position.alignment` The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis. * @return {double} `rotation` The clockwise rotation of the item in degrees around the point of alignment. * @return {double} `scale.x` The x-scale factor of the source. * @return {double} `scale.y` The y-scale factor of the source. @@ -106,7 +106,6 @@ RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) { * @return {int} `sourceHeight` Base source (without scaling) of the source * @return {double} `width` Scene item width (base source width multiplied by the horizontal scaling factor) * @return {double} `height` Scene item height (base source height multiplied by the vertical scaling factor) -* @return {int} `alignment` The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis. * @return {String (optional)} `parentGroupName` Name of the item's parent (if this item belongs to a group) * @return {Array (optional)} `groupChildren` List of children (if this item is a group) * From e794762f72def6df75a63481fa021c0bab58cbca Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Sat, 28 Nov 2020 04:50:08 +0000 Subject: [PATCH 183/278] docs(ci): Update protocol.md - 9999b30 [skip ci] --- docs/generated/comments.json | 10 ++-------- docs/generated/protocol.md | 3 +-- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index d87fc089..a06b3cb8 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -8331,7 +8331,7 @@ "{int} `itemId` Scene Item ID.", "{double} `position.x` The x position of the source from the left.", "{double} `position.y` The y position of the source from the top.", - "{int} `position.alignment` The point on the source that the item is manipulated from.", + "{int} `position.alignment` The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis.", "{double} `rotation` The clockwise rotation of the item in degrees around the point of alignment.", "{double} `scale.x` The x-scale factor of the source.", "{double} `scale.y` The y-scale factor of the source.", @@ -8350,7 +8350,6 @@ "{int} `sourceHeight` Base source (without scaling) of the source", "{double} `width` Scene item width (base source width multiplied by the horizontal scaling factor)", "{double} `height` Scene item height (base source height multiplied by the vertical scaling factor)", - "{int} `alignment` The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis.", "{String (optional)} `parentGroupName` Name of the item's parent (if this item belongs to a group)", "{Array (optional)} `groupChildren` List of children (if this item is a group)" ], @@ -8382,7 +8381,7 @@ { "type": "int", "name": "position.alignment", - "description": "The point on the source that the item is manipulated from." + "description": "The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis." }, { "type": "double", @@ -8474,11 +8473,6 @@ "name": "height", "description": "Scene item height (base source height multiplied by the vertical scaling factor)" }, - { - "type": "int", - "name": "alignment", - "description": "The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis." - }, { "type": "String (optional)", "name": "parentGroupName", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 82296c31..2ace2cde 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -3357,7 +3357,7 @@ Coordinates are relative to the item's parent (the scene or group it belongs to) | `itemId` | _int_ | Scene Item ID. | | `position.x` | _double_ | The x position of the source from the left. | | `position.y` | _double_ | The y position of the source from the top. | -| `position.alignment` | _int_ | The point on the source that the item is manipulated from. | +| `position.alignment` | _int_ | The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis. | | `rotation` | _double_ | The clockwise rotation of the item in degrees around the point of alignment. | | `scale.x` | _double_ | The x-scale factor of the source. | | `scale.y` | _double_ | The y-scale factor of the source. | @@ -3376,7 +3376,6 @@ Coordinates are relative to the item's parent (the scene or group it belongs to) | `sourceHeight` | _int_ | Base source (without scaling) of the source | | `width` | _double_ | Scene item width (base source width multiplied by the horizontal scaling factor) | | `height` | _double_ | Scene item height (base source height multiplied by the vertical scaling factor) | -| `alignment` | _int_ | The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis. | | `parentGroupName` | _String (optional)_ | Name of the item's parent (if this item belongs to a group) | | `groupChildren` | _Array<SceneItemTransform> (optional)_ | List of children (if this item is a group) | From 96bd4141fdd4fc4e0c8d06f9c7174dc99a8f863e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Nov 2020 12:22:21 +0100 Subject: [PATCH 184/278] events: add recordingFilename property to RecordingStarted, RecordingStopping and RecordingStopped --- src/Utils.cpp | 23 +++++++++++++++++++++++ src/Utils.h | 2 ++ src/WSEvents.cpp | 23 +++++++++++++++++++---- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index cd197519..5140a7ae 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -696,6 +696,29 @@ bool Utils::SetFilenameFormatting(const char* filenameFormatting) { return true; } +const char* Utils::GetCurrentRecordingFilename() +{ + OBSOutputAutoRelease recordingOutput = obs_frontend_get_recording_output(); + if (!recordingOutput) { + return nullptr; + } + + OBSDataAutoRelease settings = obs_output_get_settings(recordingOutput); + + // mimicks the behavior of BasicOutputHandler::GetRecordingFilename : + // try to fetch the path from the "url" property, then try "path" if the first one + // didn't yield any result + OBSDataItemAutoRelease item = obs_data_item_byname(settings, "url"); + if (!item) { + item = obs_data_item_byname(settings, "path"); + if (!item) { + return nullptr; + } + } + + return obs_data_item_get_string(item); +} + // Transform properties copy-pasted from WSRequestHandler_SceneItems.cpp because typedefs can't be extended yet /** diff --git a/src/Utils.h b/src/Utils.h index 0851a8ea..47a19f2d 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -85,6 +85,8 @@ namespace Utils { const char* GetFilenameFormatting(); bool SetFilenameFormatting(const char* filenameFormatting); + const char* GetCurrentRecordingFilename(); + QString nsToTimestamp(uint64_t ns); struct AddSourceData { obs_source_t *source; diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index ce27798d..80b565f7 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -678,7 +678,10 @@ void WSEvents::OnStreamStopped() { /** * A request to start recording has been issued. - * + * + * Note: `recordingFilename` is not provided in this event because this information + * is not available at the time this event is emitted. + * * @api events * @name RecordingStarting * @category recording @@ -691,37 +694,49 @@ void WSEvents::OnRecordingStarting() { /** * Recording started successfully. * + * @return {String} `recordingFilename` Absolute path to the file of the current recording. + * * @api events * @name RecordingStarted * @category recording * @since 0.3 */ void WSEvents::OnRecordingStarted() { - broadcastUpdate("RecordingStarted"); + OBSDataAutoRelease data = obs_data_create(); + obs_data_set_string(data, "recordingFilename", Utils::GetCurrentRecordingFilename()); + broadcastUpdate("RecordingStarted", data); } /** * A request to stop recording has been issued. * + * @return {String} `recordingFilename` Absolute path to the file of the current recording. + * * @api events * @name RecordingStopping * @category recording * @since 0.3 */ void WSEvents::OnRecordingStopping() { - broadcastUpdate("RecordingStopping"); + OBSDataAutoRelease data = obs_data_create(); + obs_data_set_string(data, "recordingFilename", Utils::GetCurrentRecordingFilename()); + broadcastUpdate("RecordingStopping", data); } /** * Recording stopped successfully. * + * @return {String} `recordingFilename` Absolute path to the file of the current recording. + * * @api events * @name RecordingStopped * @category recording * @since 0.3 */ void WSEvents::OnRecordingStopped() { - broadcastUpdate("RecordingStopped"); + OBSDataAutoRelease data = obs_data_create(); + obs_data_set_string(data, "recordingFilename", Utils::GetCurrentRecordingFilename()); + broadcastUpdate("RecordingStopped", data); } /** From a137ccd8ba2b396940a0066ffcb58b4fafbc35cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 29 Nov 2020 12:24:49 +0100 Subject: [PATCH 185/278] requests(GetRecordingStatus): add recordingFilename property --- src/WSRequestHandler_Recording.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/WSRequestHandler_Recording.cpp b/src/WSRequestHandler_Recording.cpp index 24aba87a..db0c529d 100644 --- a/src/WSRequestHandler_Recording.cpp +++ b/src/WSRequestHandler_Recording.cpp @@ -21,6 +21,7 @@ RpcResponse ifCanPause(const RpcRequest& request, std::function c * @return {boolean} `isRecording` Current recording status. * @return {boolean} `isRecordingPaused` Whether the recording is paused or not. * @return {String (optional)} `recordTimecode` Time elapsed since recording started (only present if currently recording). + * @return {String (optional)} `recordingFilename` Absolute path to the recording file (only present if currently recording). * * @api requests * @name GetRecordingStatus @@ -37,6 +38,7 @@ RpcResponse WSRequestHandler::GetRecordingStatus(const RpcRequest& request) { if (obs_frontend_recording_active()) { QString recordingTimecode = events->getRecordingTimecode(); obs_data_set_string(data, "recordTimecode", recordingTimecode.toUtf8().constData()); + obs_data_set_string(data, "recordingFilename", Utils::GetCurrentRecordingFilename()); } return request.success(data); From a93c4dfbbd106e3383373858191eec08a0016885 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Sun, 29 Nov 2020 11:40:39 +0000 Subject: [PATCH 186/278] docs(ci): Update protocol.md - aceb437 [skip ci] --- docs/generated/comments.json | 36 +++++++++++++++++++++++++++++++++--- docs/generated/protocol.md | 21 +++++++++++++++++---- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index a06b3cb8..c432f672 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -1415,7 +1415,7 @@ "recording": [ { "subheads": [], - "description": "A request to start recording has been issued.", + "description": "\n\nNote: `recordingFilename` is not provided in this event because this information\nis not available at the time this event is emitted.", "api": "events", "name": "RecordingStarting", "category": "recording", @@ -1442,17 +1442,25 @@ "level": 2, "text": "RecordingStarting" }, - "lead": "", + "lead": "A request to start recording has been issued.", "type": "class", "examples": [] }, { "subheads": [], "description": "Recording started successfully.", + "return": "{String} `recordingFilename` Absolute path to the file of the current recording.", "api": "events", "name": "RecordingStarted", "category": "recording", "since": "0.3", + "returns": [ + { + "type": "String", + "name": "recordingFilename", + "description": "Absolute path to the file of the current recording." + } + ], "names": [ { "name": "", @@ -1482,10 +1490,18 @@ { "subheads": [], "description": "A request to stop recording has been issued.", + "return": "{String} `recordingFilename` Absolute path to the file of the current recording.", "api": "events", "name": "RecordingStopping", "category": "recording", "since": "0.3", + "returns": [ + { + "type": "String", + "name": "recordingFilename", + "description": "Absolute path to the file of the current recording." + } + ], "names": [ { "name": "", @@ -1515,10 +1531,18 @@ { "subheads": [], "description": "Recording stopped successfully.", + "return": "{String} `recordingFilename` Absolute path to the file of the current recording.", "api": "events", "name": "RecordingStopped", "category": "recording", "since": "0.3", + "returns": [ + { + "type": "String", + "name": "recordingFilename", + "description": "Absolute path to the file of the current recording." + } + ], "names": [ { "name": "", @@ -7630,7 +7654,8 @@ "return": [ "{boolean} `isRecording` Current recording status.", "{boolean} `isRecordingPaused` Whether the recording is paused or not.", - "{String (optional)} `recordTimecode` Time elapsed since recording started (only present if currently recording)." + "{String (optional)} `recordTimecode` Time elapsed since recording started (only present if currently recording).", + "{String (optional)} `recordingFilename` Absolute path to the recording file (only present if currently recording)." ], "api": "requests", "name": "GetRecordingStatus", @@ -7651,6 +7676,11 @@ "type": "String (optional)", "name": "recordTimecode", "description": "Time elapsed since recording started (only present if currently recording)." + }, + { + "type": "String (optional)", + "name": "recordingFilename", + "description": "Absolute path to the recording file (only present if currently recording)." } ], "names": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 2ace2cde..6a1e542f 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -683,7 +683,10 @@ Emitted every 2 seconds when stream is active. - Added in v0.3 -A request to start recording has been issued. + + +Note: `recordingFilename` is not provided in this event because this information +is not available at the time this event is emitted. **Response Items:** @@ -700,7 +703,10 @@ Recording started successfully. **Response Items:** -_No additional response items._ +| Name | Type | Description | +| ---- | :---: | ------------| +| `recordingFilename` | _String_ | Absolute path to the file of the current recording. | + --- @@ -713,7 +719,10 @@ A request to stop recording has been issued. **Response Items:** -_No additional response items._ +| Name | Type | Description | +| ---- | :---: | ------------| +| `recordingFilename` | _String_ | Absolute path to the file of the current recording. | + --- @@ -726,7 +735,10 @@ Recording stopped successfully. **Response Items:** -_No additional response items._ +| Name | Type | Description | +| ---- | :---: | ------------| +| `recordingFilename` | _String_ | Absolute path to the file of the current recording. | + --- @@ -3004,6 +3016,7 @@ _No specified parameters._ | `isRecording` | _boolean_ | Current recording status. | | `isRecordingPaused` | _boolean_ | Whether the recording is paused or not. | | `recordTimecode` | _String (optional)_ | Time elapsed since recording started (only present if currently recording). | +| `recordingFilename` | _String (optional)_ | Absolute path to the recording file (only present if currently recording). | --- From 3e55b3d7bc04ddbda97ed3dc260d4ec3735a1747 Mon Sep 17 00:00:00 2001 From: Gavin Hamill Date: Tue, 8 Dec 2020 17:11:20 +0000 Subject: [PATCH 187/278] Request: add ID support for SetSceneItemRender --- src/WSRequestHandler_SceneItems.cpp | 30 ++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 659a8b9b..65af35d4 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -393,7 +393,8 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) { * Show or hide a specified source item in a specified scene. * * @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the currently active scene. -* @param {String} `source` Scene Item name. +* @param {String (optional)} `source` Scene Item name. +* @param {int (optional)} `item` Scene Item id * @param {boolean} `render` true = shown ; false = hidden * * @api requests @@ -402,16 +403,18 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) { * @since 0.3 */ RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) { - if (!request.hasField("source") || - !request.hasField("render")) + if (!request.hasField("render") || + (!request.hasField("source") && !request.hasField("item")) + ) { return request.failed("missing request parameters"); } const char* itemName = obs_data_get_string(request.parameters(), "source"); + int64_t itemId = obs_data_get_int(request.parameters(), "item"); bool isVisible = obs_data_get_bool(request.parameters(), "render"); - if (!itemName) { + if (!itemName && !itemId) { return request.failed("invalid request parameters"); } @@ -421,12 +424,21 @@ RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) { return request.failed("requested scene doesn't exist"); } - OBSSceneItemAutoRelease sceneItem = - Utils::GetSceneItemFromName(scene, itemName); - if (!sceneItem) { - return request.failed("specified scene item doesn't exist"); - } + OBSSceneItemAutoRelease sceneItem; + if (strlen(itemName)) { + OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "source"); + sceneItem = Utils::GetSceneItemFromName(scene, itemName); + if (!sceneItem) { + return request.failed("specified scene item name doesn't exist"); + } + } else { + OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item"); + sceneItem = Utils::GetSceneItemFromId(scene, itemId); + if (!sceneItem) { + return request.failed("specified scene item ID doesn't exist"); + } + } obs_sceneitem_set_visible(sceneItem, isVisible); return request.success(); } From c8f0d5a3e45a712d69e242e1b68ae62665ac5f92 Mon Sep 17 00:00:00 2001 From: Leandro Facchinetti Date: Wed, 16 Dec 2020 21:57:23 +0000 Subject: [PATCH 188/278] Add link to obs-cli --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 741ca5cf..d1c2ff9e 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Here's a list of available language APIs for obs-websocket : - Java 8+: [obs-websocket-java](https://github.com/Twasi/websocket-obs-java) by TwasiNET - Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf - HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468 +- CLI: [obs-cli](https://github.com/leafac/obs-cli) by leafac I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop a message in `#project-showoff` in the [discord server!](https://discord.gg/WBaSQ3A) From 3dea5fd4f43c2b023d4038a4ea89936c9e5b6eac Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 19 Dec 2020 21:51:25 -0800 Subject: [PATCH 189/278] README: Add codefactor badge They give us an A and I think that's pretty cool so might as well display it --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d1c2ff9e..df8d28e8 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,10 @@ WebSockets API for OBS Studio. -[![Discord](https://img.shields.io/discord/715691013825364120.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/WBaSQ3A) [![Build Status](https://dev.azure.com/Palakis/obs-websocket/_apis/build/status/Palakis.obs-websocket?branchName=4.x-current)](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current) +[![CodeFactor](https://www.codefactor.io/repository/github/palakis/obs-websocket/badge)](https://www.codefactor.io/repository/github/palakis/obs-websocket) [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fold_left.svg?style=social&label=Follow%20%40LePalakis)](https://twitter.com/LePalakis) +[![Discord](https://img.shields.io/discord/715691013825364120.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/WBaSQ3A) ## Downloads From adc46a80f9811e6adadc940d1b242372fa48e0cd Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Wed, 9 Dec 2020 21:43:41 +0900 Subject: [PATCH 190/278] Requests: SetVolume: allow volumes > 1.0 Allow volumes > 1.0 / > 0dB, which is legal in OBS (you can do this in Advanced Audio Properties). OBS allows up to +26dB gain, so we do the same. This corresponds to approximately 20x linear gain (actually 19.9526231497, but 20 is easier to explain and OBS doesn't care). --- src/WSRequestHandler_Sources.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 9aa5c260..94e9a06c 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -231,7 +231,7 @@ RpcResponse WSRequestHandler::GetSourceTypesList(const RpcRequest& request) * @param {boolean (optional)} `useDecibel` Output volume in decibels of attenuation instead of amplitude/mul. * * @return {String} `name` Source name. -* @return {double} `volume` Volume of the source. Between `0.0` and `1.0` if using mul, under `0.0` if using dB (since it is attenuating). +* @return {double} `volume` Volume of the source. Between `0.0` and `20.0` if using mul, under `26.0` if using dB. * @return {boolean} `muted` Indicates whether the source is muted. * * @api requests @@ -277,7 +277,7 @@ RpcResponse WSRequestHandler::GetVolume(const RpcRequest& request) * Set the volume of the specified source. Default request format uses mul, NOT SLIDER PERCENTAGE. * * @param {String} `source` Source name. -* @param {double} `volume` Desired volume. Must be between `0.0` and `1.0` for mul, and under 0.0 for dB. Note: OBS will interpret dB values under -100.0 as Inf. +* @param {double} `volume` Desired volume. Must be between `0.0` and `20.0` for mul, and under 26.0 for dB. OBS will interpret dB values under -100.0 as Inf. Note: The OBS volume sliders only reach a maximum of 1.0mul/0.0dB, however OBS actually supports larger values. * @param {boolean (optional)} `useDecibel` Interperet `volume` data as decibels instead of amplitude/mul. * * @api requests @@ -296,8 +296,8 @@ RpcResponse WSRequestHandler::SetVolume(const RpcRequest& request) QString sourceName = obs_data_get_string(request.parameters(), "source"); float sourceVolume = obs_data_get_double(request.parameters(), "volume"); - bool isNotValidDecibel = (useDecibel && sourceVolume > 0.0); - bool isNotValidMul = (!useDecibel && (sourceVolume < 0.0 || sourceVolume > 1.0)); + bool isNotValidDecibel = (useDecibel && sourceVolume > 26.0); + bool isNotValidMul = (!useDecibel && (sourceVolume < 0.0 || sourceVolume > 20.0)); if (sourceName.isEmpty() || isNotValidDecibel || isNotValidMul) { return request.failed("invalid request parameters"); } From d77e4ab10d42e4b4ca137a5e0f2e6568d73746a6 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Sun, 20 Dec 2020 06:20:47 +0000 Subject: [PATCH 191/278] docs(ci): Update protocol.md - 8a8ea92 [skip ci] --- docs/generated/comments.json | 8 ++++---- docs/generated/protocol.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index c432f672..cc02464d 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -5059,7 +5059,7 @@ ], "return": [ "{String} `name` Source name.", - "{double} `volume` Volume of the source. Between `0.0` and `1.0` if using mul, under `0.0` if using dB (since it is attenuating).", + "{double} `volume` Volume of the source. Between `0.0` and `20.0` if using mul, under `26.0` if using dB.", "{boolean} `muted` Indicates whether the source is muted." ], "api": "requests", @@ -5075,7 +5075,7 @@ { "type": "double", "name": "volume", - "description": "Volume of the source. Between `0.0` and `1.0` if using mul, under `0.0` if using dB (since it is attenuating)." + "description": "Volume of the source. Between `0.0` and `20.0` if using mul, under `26.0` if using dB." }, { "type": "boolean", @@ -5126,7 +5126,7 @@ "description": "Set the volume of the specified source. Default request format uses mul, NOT SLIDER PERCENTAGE.", "param": [ "{String} `source` Source name.", - "{double} `volume` Desired volume. Must be between `0.0` and `1.0` for mul, and under 0.0 for dB. Note: OBS will interpret dB values under -100.0 as Inf.", + "{double} `volume` Desired volume. Must be between `0.0` and `20.0` for mul, and under 26.0 for dB. OBS will interpret dB values under -100.0 as Inf. Note: The OBS volume sliders only reach a maximum of 1.0mul/0.0dB, however OBS actually supports larger values.", "{boolean (optional)} `useDecibel` Interperet `volume` data as decibels instead of amplitude/mul." ], "api": "requests", @@ -5142,7 +5142,7 @@ { "type": "double", "name": "volume", - "description": "Desired volume. Must be between `0.0` and `1.0` for mul, and under 0.0 for dB. Note: OBS will interpret dB values under -100.0 as Inf." + "description": "Desired volume. Must be between `0.0` and `20.0` for mul, and under 26.0 for dB. OBS will interpret dB values under -100.0 as Inf. Note: The OBS volume sliders only reach a maximum of 1.0mul/0.0dB, however OBS actually supports larger values." }, { "type": "boolean (optional)", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 6a1e542f..9d9bd8a7 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -2083,7 +2083,7 @@ Get the volume of the specified source. Default response uses mul format, NOT SL | Name | Type | Description | | ---- | :---: | ------------| | `name` | _String_ | Source name. | -| `volume` | _double_ | Volume of the source. Between `0.0` and `1.0` if using mul, under `0.0` if using dB (since it is attenuating). | +| `volume` | _double_ | Volume of the source. Between `0.0` and `20.0` if using mul, under `26.0` if using dB. | | `muted` | _boolean_ | Indicates whether the source is muted. | @@ -2101,7 +2101,7 @@ Set the volume of the specified source. Default request format uses mul, NOT SLI | Name | Type | Description | | ---- | :---: | ------------| | `source` | _String_ | Source name. | -| `volume` | _double_ | Desired volume. Must be between `0.0` and `1.0` for mul, and under 0.0 for dB. Note: OBS will interpret dB values under -100.0 as Inf. | +| `volume` | _double_ | Desired volume. Must be between `0.0` and `20.0` for mul, and under 26.0 for dB. OBS will interpret dB values under -100.0 as Inf. Note: The OBS volume sliders only reach a maximum of 1.0mul/0.0dB, however OBS actually supports larger values. | | `useDecibel` | _boolean (optional)_ | Interperet `volume` data as decibels instead of amplitude/mul. | From 501e0f63f588e67dec5d818a7b7777e3f0f2787d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Mon, 7 Dec 2020 10:50:13 +0100 Subject: [PATCH 192/278] ci(linux): fix deb package file permissions --- CMakeLists.txt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 261d8c78..6b6f2550 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,12 +192,17 @@ if(UNIX AND NOT APPLE) file(GLOB locale_files data/locale/*.ini) + set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + if(${USE_UBUNTU_FIX}) - install(TARGETS obs-websocket - LIBRARY DESTINATION "/usr/lib/obs-plugins") + install(TARGETS obs-websocket LIBRARY + DESTINATION "/usr/lib/obs-plugins" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) endif() - install(TARGETS obs-websocket - LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins") + install(TARGETS obs-websocket LIBRARY + DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) install(FILES ${locale_files} DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/obs/obs-plugins/obs-websocket/locale") From 31c717eb400fcf36980280bedee75e1078516639 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sun, 20 Dec 2020 00:26:35 -0800 Subject: [PATCH 193/278] Docs: Add recording-paused to documentation [skip ci] It wasn't included for some reason. --- src/WSRequestHandler_Streaming.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/WSRequestHandler_Streaming.cpp b/src/WSRequestHandler_Streaming.cpp index aa0782c9..a77cffd1 100644 --- a/src/WSRequestHandler_Streaming.cpp +++ b/src/WSRequestHandler_Streaming.cpp @@ -11,9 +11,10 @@ * * @return {boolean} `streaming` Current streaming status. * @return {boolean} `recording` Current recording status. + * @return {boolean} `recording-paused` If recording is paused. + * @return {boolean} `preview-only` Always false. Retrocompatibility with OBSRemote. * @return {String (optional)} `stream-timecode` Time elapsed since streaming started (only present if currently streaming). * @return {String (optional)} `rec-timecode` Time elapsed since recording started (only present if currently recording). - * @return {boolean} `preview-only` Always false. Retrocompatibility with OBSRemote. * * @api requests * @name GetStreamingStatus From ffb34c3fd4c6b029a1911177871cc5c6e3da221a Mon Sep 17 00:00:00 2001 From: tt2468 Date: Mon, 21 Dec 2020 00:05:48 -0800 Subject: [PATCH 194/278] Requests: Add GetSourceDefaultSettings Adds a request, mainly for development purposes, which returns an object of default settings for a given sourceKind (eg. `vlc_source`) --- src/Utils.cpp | 50 ++++++++++++++++++++++++++++++++ src/Utils.h | 2 ++ src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_Sources.cpp | 34 ++++++++++++++++++++++ 5 files changed, 88 insertions(+) diff --git a/src/Utils.cpp b/src/Utils.cpp index 5140a7ae..2752b8ab 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -24,6 +24,7 @@ with this program. If not, see #include #include #include +#include #include "obs-websocket.h" @@ -905,3 +906,52 @@ void Utils::AddSourceHelper(void *_data, obs_scene_t *scene) data->sceneItem = obs_scene_add(scene, data->source); obs_sceneitem_set_visible(data->sceneItem, data->setVisible); } + +struct obs_data_item { // Used for OBSDataGetDefaults + volatile long ref; + struct obs_data *parent; + struct obs_data_item *next; + enum obs_data_type type; + size_t name_len; + size_t data_len; + size_t data_size; + size_t default_len; + size_t default_size; + size_t autoselect_size; + size_t capacity; +}; + +obs_data_t *Utils::OBSDataGetDefaults(obs_data_t *data) +{ + obs_data_t *returnData = obs_data_create(); + obs_data_item_t *item = NULL; + + for (item = obs_data_first(data); item; obs_data_item_next(&item)) { + enum obs_data_type type = obs_data_item_gettype(item); + const char *name = (char *)item + sizeof(struct obs_data_item); + + if (type == OBS_DATA_STRING) { + const char *val = obs_data_item_get_string(item); + obs_data_set_string(returnData, name, val); + } else if (type == OBS_DATA_NUMBER) { + enum obs_data_number_type type = obs_data_item_numtype(item); + if (type == OBS_DATA_NUM_INT) { + long long val = obs_data_item_get_int(item); + obs_data_set_int(returnData, name, val); + } else { + double val = obs_data_item_get_double(item); + obs_data_set_double(returnData, name, val); + } + } else if (type == OBS_DATA_BOOLEAN) { + bool val = obs_data_item_get_bool(item); + obs_data_set_bool(returnData, name, val); + } else if (type == OBS_DATA_OBJECT) { + OBSDataAutoRelease obj = obs_data_item_get_obj(item); + obs_data_set_obj(returnData, name, obj); + } else if (type == OBS_DATA_ARRAY) { + OBSDataArrayAutoRelease array = obs_data_item_get_array(item); + obs_data_set_array(returnData, name, array); + } + } + return returnData; +} diff --git a/src/Utils.h b/src/Utils.h index 47a19f2d..d15427b5 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -94,4 +94,6 @@ namespace Utils { bool setVisible; }; void AddSourceHelper(void *_data, obs_scene_t *scene); + + obs_data_t *OBSDataGetDefaults(obs_data_t *data); }; diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index d3427885..0b366761 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -118,6 +118,7 @@ const QHash WSRequestHandler::messageMap{ { "SetSourceSettings", &WSRequestHandler::SetSourceSettings }, { "GetAudioMonitorType", &WSRequestHandler::GetAudioMonitorType }, { "SetAudioMonitorType", &WSRequestHandler::SetAudioMonitorType }, + { "GetSourceDefaultSettings", &WSRequestHandler::GetSourceDefaultSettings }, { "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot }, { "GetSourceFilters", &WSRequestHandler::GetSourceFilters }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index d2da4439..16a64742 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -135,6 +135,7 @@ class WSRequestHandler { RpcResponse SetSourceSettings(const RpcRequest&); RpcResponse GetAudioMonitorType(const RpcRequest&); RpcResponse SetAudioMonitorType(const RpcRequest&); + RpcResponse GetSourceDefaultSettings(const RpcRequest&); RpcResponse TakeSourceScreenshot(const RpcRequest&); RpcResponse GetSourceFilters(const RpcRequest&); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 94e9a06c..8df1bf5c 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1679,6 +1679,40 @@ RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request) return request.success(); } +/** +* Get the default settings for a given source type. +* +* @param {String} `sourceKind` Source name. +* +* @api requests +* @name GetSourceDefaultSettings +* @category sources +* @since 4.9.0 +*/ +RpcResponse WSRequestHandler::GetSourceDefaultSettings(const RpcRequest& request) +{ + if (!request.hasField("sourceKind")) { + return request.failed("missing request parameters"); + } + + QString sourceKind = obs_data_get_string(request.parameters(), "sourceKind"); + + if (sourceKind.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSDataAutoRelease defaultData = obs_get_source_defaults(sourceKind.toUtf8()); + if (!defaultData) { + return request.failed("invalid sourceKind"); + } + + OBSDataAutoRelease defaultSettings = Utils::OBSDataGetDefaults(defaultData); + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_obj(response, "defaultSettings", defaultSettings); + return request.success(response); +} + /** * Takes a picture snapshot of a source and then can either or both: * - Send it over as a Data URI (base64-encoded data) in the response (by specifying `embedPictureFormat` in the request) From ba143f2636b0d17e6e83090acbcd85664dfd8dfe Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 19 Dec 2020 23:54:07 -0800 Subject: [PATCH 195/278] Translations: Update translations with latest from crowdin --- data/locale/ar-SA.ini | 1 + data/locale/de-DE.ini | 4 +++- data/locale/es-ES.ini | 6 +++++- data/locale/fr-FR.ini | 2 +- data/locale/hi-IN.ini | 1 + data/locale/it-IT.ini | 8 +++++++- data/locale/ja-JP.ini | 5 +++++ data/locale/ko-KR.ini | 18 ++++++++++++++++++ data/locale/nl-NL.ini | 9 +++++++++ data/locale/pl-PL.ini | 6 +++++- data/locale/pt-PT.ini | 8 +++++++- data/locale/ru-RU.ini | 5 ++++- data/locale/zh-CN.ini | 6 +++++- data/locale/zh-TW.ini | 2 +- 14 files changed, 72 insertions(+), 9 deletions(-) diff --git a/data/locale/ar-SA.ini b/data/locale/ar-SA.ini index e69de29b..8b137891 100644 --- a/data/locale/ar-SA.ini +++ b/data/locale/ar-SA.ini @@ -0,0 +1 @@ + diff --git a/data/locale/de-DE.ini b/data/locale/de-DE.ini index 3cd6e23b..d2f5efc9 100644 --- a/data/locale/de-DE.ini +++ b/data/locale/de-DE.ini @@ -3,6 +3,7 @@ OBSWebsocket.Settings.ServerEnable="WebSockets-Server aktivieren" OBSWebsocket.Settings.ServerPort="Server-Port" OBSWebsocket.Settings.AuthRequired="Authentifizierung aktivieren" OBSWebsocket.Settings.Password="Passwort" +OBSWebsocket.Settings.LockToIPv4="Nur IPv4 verwenden (deaktiviert IPv6)" OBSWebsocket.Settings.DebugEnable="Debug-Protokollierung aktivieren" OBSWebsocket.Settings.AlertsEnable="Infobereichbenachrichtigungen aktivieren" OBSWebsocket.NotifyConnect.Title="Neue Websocket-Verbindung" @@ -10,7 +11,8 @@ OBSWebsocket.NotifyConnect.Message="Client %1 verbunden" OBSWebsocket.NotifyDisconnect.Title="Websocket-Client getrennt" OBSWebsocket.NotifyDisconnect.Message="Client %1 getrennt" OBSWebsocket.Server.StartFailed.Title="Websocket-Serverfehler" -OBSWebsocket.Server.StartFailed.Message="Der WebSockets-Server konnte nicht gestartet werden, mögliche Gründe:\n - Der TCP-Port %1 wird möglicherweise gerade von einem anderen Programm verwendet. Versuchen Sie einen anderen Port in den Websocket-Servereinstellungen zu setzen oder alle Programme zu beenden, die den Port möglicherweise verwenden.\n - Ein unbekannter Netzwerkfehler ist aufgetreten. Versuchen Sie es mit anderen Einstellungen, einem OBS-Neustart oder einem Systemneustart erneut." +OBSWebsocket.Server.StartFailed.Message="Der WebSocket-Server konnte nicht gestartet werden, mögliche Gründe:\n - Der TCP-Port %1 wird möglicherweise gerade von einem anderen Programm verwendet. Versuchen Sie einen anderen Port in den Websocket-Servereinstellungen zu setzen oder alle Programme zu beenden, die den Port möglicherweise verwenden.\n - Fehler: %2" OBSWebsocket.ProfileChanged.Started="WebSockets-Server in diesem Profil aktiviert. Server gestartet." OBSWebsocket.ProfileChanged.Stopped="WebSockets-Server in diesem Profil deaktiviert. Server gestoppt." OBSWebsocket.ProfileChanged.Restarted="WebSockets-Server in diesem Profil geändert. Server startet neu." + diff --git a/data/locale/es-ES.ini b/data/locale/es-ES.ini index 511d2f91..7c82ef2b 100644 --- a/data/locale/es-ES.ini +++ b/data/locale/es-ES.ini @@ -1,3 +1,4 @@ +OBSWebsocket.Settings.DialogTitle="Configuración del servidor WebSocket" OBSWebsocket.Settings.ServerEnable="Habilitar el servidor WebSockets" OBSWebsocket.Settings.ServerPort="Puerto del Servidor" OBSWebsocket.Settings.AuthRequired="Habilitar autenticación" @@ -9,4 +10,7 @@ OBSWebsocket.NotifyConnect.Message="Cliente %1 conectado" OBSWebsocket.NotifyDisconnect.Title="Cliente WebSocket desconectado" OBSWebsocket.NotifyDisconnect.Message="Cliente %1 desconectado" OBSWebsocket.Server.StartFailed.Title="Falla en el servidor WebSockets" -OBSWebsocket.Server.StartFailed.Message="El servidor obs-websocket no se pudo iniciar, tal vez porque: \n - el puerto TCP %1 podría estar actualmente siendo usado este sistema, posiblemente por otra aplicación. Intente configurar un puerto TCP diferente en la configuración del servidor WebSocket, o detenga cualquier aplicación que pudiese estar utilizando este puerto \n - Un error de red desconocido ha afectado su sistema. Inténtelo de nuevo cambiando la configuración, reiniciando OBS o reiniciando su sistema." +OBSWebsocket.ProfileChanged.Started="El servidor WebSocket esta habilitado en este perfil. El servidor ha iniciado." +OBSWebsocket.ProfileChanged.Stopped="Servidor WebSockets deshabilitado en este perfil. Servidor detenido." +OBSWebsocket.ProfileChanged.Restarted="Puerto del servidor WebSockets cambiado en este perfil. Servidor reiniciado." + diff --git a/data/locale/fr-FR.ini b/data/locale/fr-FR.ini index b2ba9238..ed8fb085 100644 --- a/data/locale/fr-FR.ini +++ b/data/locale/fr-FR.ini @@ -10,7 +10,7 @@ OBSWebsocket.NotifyConnect.Message="Le client %1 s'est connecté" OBSWebsocket.NotifyDisconnect.Title="Déconnexion WebSocket" OBSWebsocket.NotifyDisconnect.Message="Le client %1 s'est déconnecté" OBSWebsocket.Server.StartFailed.Title="Impossible de démarrer le serveur WebSockets" -OBSWebsocket.Server.StartFailed.Message="Le serveur WebSockets n'a pas pu démarrer, peut-être parce que :\n - Le port TCP %1 est en cours d'utilisation sur ce système, certainement par un autre programme. Essayez un port différent dans les réglages du serveur WebSocket, ou arrêtez tout programme susceptible d'utiliser ce port.\n - Une erreur réseau inconnue est survenue. Essayez à nouveau en modifiant vos réglages, en redémarrant OBS ou en redémarrant votre ordinateur." OBSWebsocket.ProfileChanged.Started="Serveur WebSockets actif dans ce profil." OBSWebsocket.ProfileChanged.Stopped="Serveur WebSockets désactivé dans ce profil." OBSWebsocket.ProfileChanged.Restarted="Le port actuel diffère du port configuré dans ce profil. Serveur WebSockets redémarré." + diff --git a/data/locale/hi-IN.ini b/data/locale/hi-IN.ini index e69de29b..8b137891 100644 --- a/data/locale/hi-IN.ini +++ b/data/locale/hi-IN.ini @@ -0,0 +1 @@ + diff --git a/data/locale/it-IT.ini b/data/locale/it-IT.ini index df133462..4142ed47 100644 --- a/data/locale/it-IT.ini +++ b/data/locale/it-IT.ini @@ -1,7 +1,9 @@ +OBSWebsocket.Settings.DialogTitle="Impostazioni del server di WebSocket" OBSWebsocket.Settings.ServerEnable="Abilitare il server WebSockets" OBSWebsocket.Settings.ServerPort="Porta del server" OBSWebsocket.Settings.AuthRequired="Abilitare l'autenticazione" OBSWebsocket.Settings.Password="Password" +OBSWebsocket.Settings.LockToIPv4="Blocca il server per usare solo IPv4" OBSWebsocket.Settings.DebugEnable="Attivare la registrazione di debug" OBSWebsocket.Settings.AlertsEnable="Attivare gli avvisi di vassoio di sistema" OBSWebsocket.NotifyConnect.Title="Nuova connessione WebSocket" @@ -9,4 +11,8 @@ OBSWebsocket.NotifyConnect.Message="%1 cliente collegato" OBSWebsocket.NotifyDisconnect.Title="WebSocket cliente disconnesso" OBSWebsocket.NotifyDisconnect.Message="%1 cliente disconnesso" OBSWebsocket.Server.StartFailed.Title="Errore del WebSocket Server" -OBSWebsocket.Server.StartFailed.Message="Impossibile avviare, forse perché il server di WebSockets: \n - %1 porta TCP potrebbe essere attualmente in uso altrove su questo sistema, possibilmente da un'altra applicazione. Provare a impostare una porta TCP diversa nelle impostazioni del server di WebSockets, o arrestare tutte le applicazioni che potrebbero utilizzare questa porta. \n - è verificato un errore di rete sconosciuto sul sistema. Riprova modificando le impostazioni, riavviare OBS o riavvio del sistema." +OBSWebsocket.Server.StartFailed.Message="L'avvio del server WebSockets è fallito, forse perché:\n - La porta TCP %1 può attualmente essere in uso altrove su questo sistema, forse da un'altra applicazione. Provare a impostare una porta TCP diversa nelle impostazioni del server WebSocket o interrompere qualsiasi applicazione che potrebbe utilizzare questa porta.\n - Messaggio di errore: %2" +OBSWebsocket.ProfileChanged.Started="Server WebSockets abilitato in questo profilo. Il server è avviato." +OBSWebsocket.ProfileChanged.Stopped="Server WebSocket disabilitato in questo profilo. Server interrotto." +OBSWebsocket.ProfileChanged.Restarted="La porta del server WebSocket è stata modificata in questo profilo. Il server è stato riavviato." + diff --git a/data/locale/ja-JP.ini b/data/locale/ja-JP.ini index e80892a1..4218cdd7 100644 --- a/data/locale/ja-JP.ini +++ b/data/locale/ja-JP.ini @@ -1,3 +1,4 @@ +OBSWebsocket.Settings.DialogTitle="Websocket サーバー設定" OBSWebsocket.Settings.ServerEnable="WebSockets サーバーを有効にする" OBSWebsocket.Settings.ServerPort="サーバーポート" OBSWebsocket.Settings.AuthRequired="認証を有効にする" @@ -9,3 +10,7 @@ OBSWebsocket.NotifyConnect.Message="接続されているクライアント %1" OBSWebsocket.NotifyDisconnect.Title="WebSocket クライアントが切断されました" OBSWebsocket.NotifyDisconnect.Message="切断されたクライアント %1" OBSWebsocket.Server.StartFailed.Title="WebSockets サーバー障害" +OBSWebsocket.ProfileChanged.Started="このプロファイルでWebSocketサーバが有効になりました。サーバを起動しました。" +OBSWebsocket.ProfileChanged.Stopped="このプロファイルでWebSocketサーバが無効になりました。サーバを停止しました。" +OBSWebsocket.ProfileChanged.Restarted="このプロファイルでWebSocketサーバの通信ポートが変更されました。サーバを再起動しました。" + diff --git a/data/locale/ko-KR.ini b/data/locale/ko-KR.ini index e69de29b..0154da74 100644 --- a/data/locale/ko-KR.ini +++ b/data/locale/ko-KR.ini @@ -0,0 +1,18 @@ +OBSWebsocket.Settings.DialogTitle="웹소켓 서버 설정" +OBSWebsocket.Settings.ServerEnable="웹소켓 서버 활성화" +OBSWebsocket.Settings.ServerPort="서버 포트" +OBSWebsocket.Settings.AuthRequired="인증 활성화" +OBSWebsocket.Settings.Password="비밀번호" +OBSWebsocket.Settings.LockToIPv4="IPv4만 이용하여 서버를 연결" +OBSWebsocket.Settings.DebugEnable="디버그 로깅 활성화" +OBSWebsocket.Settings.AlertsEnable="시스템 트레이 알림 활성화" +OBSWebsocket.NotifyConnect.Title="새로운 웹소켓 연결" +OBSWebsocket.NotifyConnect.Message="클라이언트 %1 가 연결되었습니다" +OBSWebsocket.NotifyDisconnect.Title="웹소켓 클라이언트가 연결 해제되었습니다" +OBSWebsocket.NotifyDisconnect.Message="클라이언트 %1 가 연결이 해제되었습니다" +OBSWebsocket.Server.StartFailed.Title="웹소켓 서버 시작이 실패하였습니다" +OBSWebsocket.Server.StartFailed.Message="웹소켓 서버가 시작되지 못했습니다. \n 시스템 상의 다른 프로그램이 TCP 포트 %1을 사용 중인 것으로 보입니다. 다른 TCP 포트를 사용하거나, 해당 포트를 사용 중인 프로그램을 종료하고 다시 시도해주세요. \n 에러 메시지: %2" +OBSWebsocket.ProfileChanged.Started="프로파일 설정에 따라 웹소켓 서버가 활성화되었습니다. 서버가 시작되었습니다." +OBSWebsocket.ProfileChanged.Stopped="프로파일 설정에 따라 웹소켓 서버가 비활성화되었습니다. 서버가 중지되었습니다." +OBSWebsocket.ProfileChanged.Restarted="프로파일 설정에 따라 웹소켓 포트가 변경되었습니다. 서버가 재시작되었습니다." + diff --git a/data/locale/nl-NL.ini b/data/locale/nl-NL.ini index f3151408..29e72d61 100644 --- a/data/locale/nl-NL.ini +++ b/data/locale/nl-NL.ini @@ -1,4 +1,9 @@ +OBSWebsocket.Settings.DialogTitle="WebSockets Server Instellingen" +OBSWebsocket.Settings.ServerEnable="WebSockets server inschakelen" OBSWebsocket.Settings.ServerPort="Serverpoort" +OBSWebsocket.Settings.AuthRequired="Verificatie inschakelen" +OBSWebsocket.Settings.Password="Wachtwoord" +OBSWebsocket.Settings.LockToIPv4="Server vergrendelen om alleen IPv4 te gebruiken" OBSWebsocket.Settings.DebugEnable="Activeer debug logs" OBSWebsocket.Settings.AlertsEnable="Systemvak waarschuwingen inschakelen" OBSWebsocket.NotifyConnect.Title="Nieuwe WebSocket verbinding" @@ -7,3 +12,7 @@ OBSWebsocket.NotifyDisconnect.Title="WebSocket client connectie verbroken" OBSWebsocket.NotifyDisconnect.Message="Client %1 losgekoppeld" OBSWebsocket.Server.StartFailed.Title="Fout in WebSocket server" OBSWebsocket.Server.StartFailed.Message="De obs-websocket server kan niet worden gestart, misschien omdat: \n - TCP-poort %1 momenteel elders wordt gebruikt op dit systeem, eventueel door een andere toepassing. Probeer een andere TCP-poort in te stellen in de WebSocket Server-instellingen of stop elke toepassing die deze poort zou kunnen gebruiken.\n Een onbekende Netwerkfout op uw systeem. Probeer het opnieuw door de instellingen te wijzigen, OBS te herstarten of uw systeem te herstarten." +OBSWebsocket.ProfileChanged.Started="WebSockets server ingeschakeld in dit profiel. Server gestart." +OBSWebsocket.ProfileChanged.Stopped="WebSockets server uitgeschakeld in dit profiel. Server is gestopt." +OBSWebsocket.ProfileChanged.Restarted="WebSockets server poort is veranderd in dit profiel. Server is herstart." + diff --git a/data/locale/pl-PL.ini b/data/locale/pl-PL.ini index 984b630d..d83c3cea 100644 --- a/data/locale/pl-PL.ini +++ b/data/locale/pl-PL.ini @@ -1,5 +1,8 @@ +OBSWebsocket.Settings.DialogTitle="Ustawienia serwera WebSockets" OBSWebsocket.Settings.ServerEnable="Włącz serwer WebSockets" +OBSWebsocket.Settings.ServerPort="Port serwera" OBSWebsocket.Settings.AuthRequired="Wymagaj uwierzytelniania" +OBSWebsocket.Settings.Password="Hasło" OBSWebsocket.Settings.DebugEnable="Włącz rejestrowanie debugowania" OBSWebsocket.Settings.AlertsEnable="Włącz powiadomienia w zasobniku systemowym" OBSWebsocket.NotifyConnect.Title="Nowe połączenie WebSocket" @@ -7,4 +10,5 @@ OBSWebsocket.NotifyConnect.Message="Klient %1 połączony" OBSWebsocket.NotifyDisconnect.Title="Klient WebSocket odłączony" OBSWebsocket.NotifyDisconnect.Message="Klient %1 rozłączony" OBSWebsocket.Server.StartFailed.Title="Awaria serwera WebSockets" -OBSWebsocket.Server.StartFailed.Message="Nie udało się uruchomić serwera WebSockets, może a powodu: \n - TCP port %1 może być obecnie używany gdzie indziej w tym systemie, możliwie przez inną aplikację. Spróbuj ustawić inny port TCP w ustawieniach serwera WebSockets, lub zatrzymać dowolną aplikację, która może używać tego portu \n -nieznany błąd sieci wydarzył się w systemie. Spróbuj ponownie, zmieniając ustawienia, ponownie uruchamiając OBS lub ponownie uruchamiając system." +OBSWebsocket.ProfileChanged.Started="Serwer WebSockets włączony w tym profilu. Serwer uruchomiony." + diff --git a/data/locale/pt-PT.ini b/data/locale/pt-PT.ini index 211d8547..99de1931 100644 --- a/data/locale/pt-PT.ini +++ b/data/locale/pt-PT.ini @@ -1,7 +1,9 @@ +OBSWebsocket.Settings.DialogTitle="Configurações do servidor de WebSockets" OBSWebsocket.Settings.ServerEnable="Habilitar servidor de WebSockets" OBSWebsocket.Settings.ServerPort="Porta do Servidor" OBSWebsocket.Settings.AuthRequired="Activar autenticação" OBSWebsocket.Settings.Password="Palavra passe" +OBSWebsocket.Settings.LockToIPv4="Forçar apenas o uso de IPv4 (desabilitar IPv6)" OBSWebsocket.Settings.DebugEnable="Habilitar registro de debug" OBSWebsocket.Settings.AlertsEnable="Ativar Alertas da bandeja do sistema" OBSWebsocket.NotifyConnect.Title="Nova conexão WebSocket" @@ -9,4 +11,8 @@ OBSWebsocket.NotifyConnect.Message="Cliente %1 conectado" OBSWebsocket.NotifyDisconnect.Title="WebSocket cliente desconectado" OBSWebsocket.NotifyDisconnect.Message="Cliente %1 desconectado" OBSWebsocket.Server.StartFailed.Title="Falha do servidor de WebSocket" -OBSWebsocket.Server.StartFailed.Message="O servidor de WebSockets falhou ao iniciar, talvez porque: \n - TCP port %1 pode estar atualmente em uso em outro lugar sobre este sistema, possivelmente por outro aplicativo. Tente definir uma porta TCP diferente nas configurações do servidor de WebSockets, ou parar qualquer aplicativo que poderia estar usando este porto \n - um erro de rede desconhecido aconteceu no seu sistema. Tente novamente alterar configurações, reiniciando OBS ou reiniciando o sistema." +OBSWebsocket.Server.StartFailed.Message="O servidor de WebSockets falhou ao iniciar, possivelmente porque:\n - A porta TCP %1 pode já estar em uso em algum outro lugar neste sistema, possivelmente por outro aplicativo. Tente definir uma porta TCP diferente nas configurações do servidor de WebSockets, ou pare qualquer aplicativo que possa estar usando essa porta.\n - Mensagem de erro: %2" +OBSWebsocket.ProfileChanged.Started="Servidor de WebSockets habilitado nesse perfil. Servidor iniciado." +OBSWebsocket.ProfileChanged.Stopped="Servidor de WebSockets desabilitado nesse perfil. Servidor parado." +OBSWebsocket.ProfileChanged.Restarted="Porta do servidor de WebSockets foi alterada neste perfil. Servidor reiniciado." + diff --git a/data/locale/ru-RU.ini b/data/locale/ru-RU.ini index 782f33c0..d4893ff2 100644 --- a/data/locale/ru-RU.ini +++ b/data/locale/ru-RU.ini @@ -10,4 +10,7 @@ OBSWebsocket.NotifyConnect.Message="Клиент %1 подключен" OBSWebsocket.NotifyDisconnect.Title="Клиент WebSocket отключён" OBSWebsocket.NotifyDisconnect.Message="Клиент %1 отключен" OBSWebsocket.Server.StartFailed.Title="Сбой сервера WebSockets" -OBSWebsocket.Server.StartFailed.Message="Ошибка запуска сервера WebSockets. Вероятные причины:\n - Возможно, TCP-порт %1 занят другим приложением в системе. Попробуйте задать другой TCP-порт в настройках сервера WebSockets или закройте приложение, которое может использовать данный порт.\n - Произошла неизвестная сетевая ошибка в системе. Попробуйте снова после изменения настроек, перезапуска OBS или системы." +OBSWebsocket.ProfileChanged.Started="Сервер WebSockets включен в этом профиле. Сервер запущен." +OBSWebsocket.ProfileChanged.Stopped="Сервер WebSockets отключен в этом профиле. Сервер остановлен." +OBSWebsocket.ProfileChanged.Restarted="Порт сервера WebSockets изменен в этом профиле. Сервер перезапущен." + diff --git a/data/locale/zh-CN.ini b/data/locale/zh-CN.ini index a40d3432..cab511cc 100644 --- a/data/locale/zh-CN.ini +++ b/data/locale/zh-CN.ini @@ -1,3 +1,4 @@ +OBSWebsocket.Settings.DialogTitle="WebSockets 服务器设置" OBSWebsocket.Settings.ServerEnable="启用 WebSockets 服务器" OBSWebsocket.Settings.ServerPort="服务器端口" OBSWebsocket.Settings.AuthRequired="启用身份验证" @@ -9,4 +10,7 @@ OBSWebsocket.NotifyConnect.Message="客户端 %1 已连接" OBSWebsocket.NotifyDisconnect.Title="WebSocket 客户端已断开" OBSWebsocket.NotifyDisconnect.Message="客户端 %1 已断开连接" OBSWebsocket.Server.StartFailed.Title="WebSockets 服务器错误" -OBSWebsocket.Server.StartFailed.Message="WebSockets 服务器启动失败,可能是因为:\n - TCP 端口 %1 可能被本机的另一个应用程序占用。尝试在 WebSockets 服务器设置中设置不同的 TCP 端口,或关闭任何可能使用此端口的应用程序。\n - 在您的系统上发生了未知的网络错误。请尝试更改设置、重新启动 OBS 或重新启动系统。" +OBSWebsocket.ProfileChanged.Started="此配置文件中启用了 WebSockets 服务器。服务器已启动。" +OBSWebsocket.ProfileChanged.Stopped="此配置文件中禁用了 WebSockets 服务器。服务器已停止。" +OBSWebsocket.ProfileChanged.Restarted="此配置文件中的 WebSockets 服务器端口已更改。服务器已重新启动。" + diff --git a/data/locale/zh-TW.ini b/data/locale/zh-TW.ini index e72077df..41cc0591 100644 --- a/data/locale/zh-TW.ini +++ b/data/locale/zh-TW.ini @@ -6,4 +6,4 @@ OBSWebsocket.NotifyConnect.Message="客戶端 %1 已連線" OBSWebsocket.NotifyDisconnect.Title="WebSocket 客戶端已離線" OBSWebsocket.NotifyDisconnect.Message="客戶端 %1 已離線" OBSWebsocket.Server.StartFailed.Title="WebSocket 伺服器錯誤" -OBSWebsocket.Server.StartFailed.Message="WebSockets 伺服器啟動失敗,可能的原因有:\n - TCP 連接埠 %1 被系統的其他程式所使用,試著為 WebSockets 伺服器指定不同的 TCP 連接埠,或是關閉任何可能使用此連接埠的程式。\n - 發生的未知的網路錯誤,試著更改設定、重新啟動 OBS 或是重新啟動您的系統。" + From 229641aba06a148d00dd6f33cdf6e11591e7e2e7 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 24 Dec 2020 13:38:58 -0800 Subject: [PATCH 196/278] Requests: Fix wrong docs version for CreateScene --- src/WSRequestHandler_Scenes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp index 9c317cdf..70734c1a 100644 --- a/src/WSRequestHandler_Scenes.cpp +++ b/src/WSRequestHandler_Scenes.cpp @@ -87,7 +87,7 @@ RpcResponse WSRequestHandler::GetSceneList(const RpcRequest& request) { * @api requests * @name CreateScene * @category scenes - * @since 4.8.0 + * @since unreleased */ RpcResponse WSRequestHandler::CreateScene(const RpcRequest& request) { if (!request.hasField("sceneName")) { From 9275f7c2a98a9b93b586c80098503060e8c32048 Mon Sep 17 00:00:00 2001 From: Eric Lindvall Date: Tue, 29 Dec 2020 11:27:51 -0800 Subject: [PATCH 197/278] Server: Stop WSServer on OBS exit This should fix issues with race conditions and crashes on OBS exit. --- src/WSEvents.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 80b565f7..38660b24 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -233,6 +233,7 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private case OBS_FRONTEND_EVENT_EXIT: owner->unhookTransitionPlaybackEvents(); owner->OnExit(); + owner->_srv->stop(); break; } } From 2735f8063711862d457a4c73e90aa894212ca5a6 Mon Sep 17 00:00:00 2001 From: Dominik Nakamura Date: Thu, 7 Jan 2021 21:49:41 +0900 Subject: [PATCH 198/278] readme: Add Homebrew installation instructions Give a short hint that the project can be installed with Homebrew on MacOS as alternative to installing manually. --- README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index df8d28e8..2fdb7321 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,14 @@ WebSockets API for OBS Studio. Binaries for Windows, MacOS, and Linux are available in the [Releases](https://github.com/Palakis/obs-websocket/releases) section. +### Homebrew + +If you're using MacOS you can use Homebrew for installation as well: + +```sh +brew install obs-websocket +``` + ## Using obs-websocket Here is a list of available web clients: (compatible with tablets and other touch interfaces) @@ -40,7 +48,7 @@ Here's a list of available language APIs for obs-websocket : - C#/VB.NET: [obs-websocket-dotnet](https://github.com/Palakis/obs-websocket-dotnet) - Python 2 and 3: [obs-websocket-py](https://github.com/Elektordi/obs-websocket-py) by Guillaume Genty a.k.a Elektordi - Python 3.5+ with asyncio: [obs-ws-rc](https://github.com/KirillMysnik/obs-ws-rc) by Kirill Mysnik -- Python 3.6+ with asyncio: [simpleobsws](https://github.com/IRLToolkit/simpleobsws) by tt2468 +- Python 3.6+ with asyncio: [simpleobsws](https://github.com/IRLToolkit/simpleobsws) by tt2468 - Java 8+: [obs-websocket-java](https://github.com/Twasi/websocket-obs-java) by TwasiNET - Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf - HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468 @@ -80,12 +88,12 @@ These supporters have contributed financially to the project and made possible t --- -[Support Class](http://supportclass.net) designs and develops professional livestreams, with services ranging from broadcast graphics design and integration to event organization, along many other skills. +[Support Class](http://supportclass.net) designs and develops professional livestreams, with services ranging from broadcast graphics design and integration to event organization, along many other skills. [![Support Class](.github/images/supportclass_logo_blacktext.png)](http://supportclass.net) --- -[MediaUnit](http://www.mediaunit.no) is a Norwegian media company developing products and services for the media industry, primarly focused on web and events. +[MediaUnit](http://www.mediaunit.no) is a Norwegian media company developing products and services for the media industry, primarly focused on web and events. [![MediaUnit](.github/images/mediaunit_logo_black.png)](http://www.mediaunit.no/) From 3d704702baa7ab0e817d599e70034c93fb6f2c10 Mon Sep 17 00:00:00 2001 From: Dominik Nakamura Date: Thu, 7 Jan 2021 21:58:29 +0900 Subject: [PATCH 199/278] readme: Add Rust client library I created a client library in Rust to remote control OBS with obs-websocket. Adding my library to the list of available language APIs. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index df8d28e8..591f2013 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Here's a list of available language APIs for obs-websocket : - Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf - HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468 - CLI: [obs-cli](https://github.com/leafac/obs-cli) by leafac +- Rust: [obws](https://github.com/dnaka91/obws) by dnaka91 I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop a message in `#project-showoff` in the [discord server!](https://discord.gg/WBaSQ3A) From 7e0874abb4d81253e870ad91f975c23b202ce8c3 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 24 Dec 2020 13:32:32 -0800 Subject: [PATCH 200/278] Translations: Remove dutch StartFailed message and update other translations For some reason crowdin included it even though it is not approved. The current unapproved translation uses the old string. --- data/locale/pl-PL.ini | 1 + data/locale/ru-RU.ini | 2 ++ 2 files changed, 3 insertions(+) diff --git a/data/locale/pl-PL.ini b/data/locale/pl-PL.ini index d83c3cea..8b434cda 100644 --- a/data/locale/pl-PL.ini +++ b/data/locale/pl-PL.ini @@ -3,6 +3,7 @@ OBSWebsocket.Settings.ServerEnable="Włącz serwer WebSockets" OBSWebsocket.Settings.ServerPort="Port serwera" OBSWebsocket.Settings.AuthRequired="Wymagaj uwierzytelniania" OBSWebsocket.Settings.Password="Hasło" +OBSWebsocket.Settings.LockToIPv4="Zablokuj serwer tylko za pomocą IPv4" OBSWebsocket.Settings.DebugEnable="Włącz rejestrowanie debugowania" OBSWebsocket.Settings.AlertsEnable="Włącz powiadomienia w zasobniku systemowym" OBSWebsocket.NotifyConnect.Title="Nowe połączenie WebSocket" diff --git a/data/locale/ru-RU.ini b/data/locale/ru-RU.ini index d4893ff2..efbf1f46 100644 --- a/data/locale/ru-RU.ini +++ b/data/locale/ru-RU.ini @@ -3,6 +3,7 @@ OBSWebsocket.Settings.ServerEnable="Включить сервер WebSockets" OBSWebsocket.Settings.ServerPort="Порт сервера" OBSWebsocket.Settings.AuthRequired="Включить авторизацию" OBSWebsocket.Settings.Password="Пароль" +OBSWebsocket.Settings.LockToIPv4="Блокировка сервера только с использованием IPv4" OBSWebsocket.Settings.DebugEnable="Включить ведение журнала отладки" OBSWebsocket.Settings.AlertsEnable="Включить оповещения в системном трее" OBSWebsocket.NotifyConnect.Title="Новое соединение WebSocket" @@ -10,6 +11,7 @@ OBSWebsocket.NotifyConnect.Message="Клиент %1 подключен" OBSWebsocket.NotifyDisconnect.Title="Клиент WebSocket отключён" OBSWebsocket.NotifyDisconnect.Message="Клиент %1 отключен" OBSWebsocket.Server.StartFailed.Title="Сбой сервера WebSockets" +OBSWebsocket.Server.StartFailed.Message="Сервер WebSockets не запустился, возможно, потому, что:\n-TCP-порт %1 в настоящее время может использоваться в другом месте этой системы, возможно, другим приложением. Попробуйте установить другой TCP-порт в настройках сервера WebSocket или остановить любое приложение, которое может использовать этот порт.\n-сообщение об ошибке: %2" OBSWebsocket.ProfileChanged.Started="Сервер WebSockets включен в этом профиле. Сервер запущен." OBSWebsocket.ProfileChanged.Stopped="Сервер WebSockets отключен в этом профиле. Сервер остановлен." OBSWebsocket.ProfileChanged.Restarted="Порт сервера WebSockets изменен в этом профиле. Сервер перезапущен." From bc436e9ec4a1fb25ccaca0152162566218f1ccc3 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 8 Jan 2021 04:58:07 -0800 Subject: [PATCH 201/278] Reorder library list --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 591f2013..308e1f15 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,9 @@ Here's a list of available language APIs for obs-websocket : - Python 3.6+ with asyncio: [simpleobsws](https://github.com/IRLToolkit/simpleobsws) by tt2468 - Java 8+: [obs-websocket-java](https://github.com/Twasi/websocket-obs-java) by TwasiNET - Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf +- Rust: [obws](https://github.com/dnaka91/obws) by dnaka91 - HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468 - CLI: [obs-cli](https://github.com/leafac/obs-cli) by leafac -- Rust: [obws](https://github.com/dnaka91/obws) by dnaka91 I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop a message in `#project-showoff` in the [discord server!](https://discord.gg/WBaSQ3A) From 77f38bbf63c0550823514349630ce0db12335d87 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Mon, 18 Jan 2021 10:52:53 +0000 Subject: [PATCH 202/278] docs(ci): Update protocol.md - 853eeb2 [skip ci] --- docs/generated/comments.json | 20 +++++++++++++------- docs/generated/protocol.md | 3 ++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index cc02464d..34d49864 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -9680,9 +9680,10 @@ "return": [ "{boolean} `streaming` Current streaming status.", "{boolean} `recording` Current recording status.", + "{boolean} `recording-paused` If recording is paused.", + "{boolean} `preview-only` Always false. Retrocompatibility with OBSRemote.", "{String (optional)} `stream-timecode` Time elapsed since streaming started (only present if currently streaming).", - "{String (optional)} `rec-timecode` Time elapsed since recording started (only present if currently recording).", - "{boolean} `preview-only` Always false. Retrocompatibility with OBSRemote." + "{String (optional)} `rec-timecode` Time elapsed since recording started (only present if currently recording)." ], "api": "requests", "name": "GetStreamingStatus", @@ -9699,6 +9700,16 @@ "name": "recording", "description": "Current recording status." }, + { + "type": "boolean", + "name": "recording-paused", + "description": "If recording is paused." + }, + { + "type": "boolean", + "name": "preview-only", + "description": "Always false. Retrocompatibility with OBSRemote." + }, { "type": "String (optional)", "name": "stream-timecode", @@ -9708,11 +9719,6 @@ "type": "String (optional)", "name": "rec-timecode", "description": "Time elapsed since recording started (only present if currently recording)." - }, - { - "type": "boolean", - "name": "preview-only", - "description": "Always false. Retrocompatibility with OBSRemote." } ], "names": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 9d9bd8a7..13e6b39a 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -3825,9 +3825,10 @@ _No specified parameters._ | ---- | :---: | ------------| | `streaming` | _boolean_ | Current streaming status. | | `recording` | _boolean_ | Current recording status. | +| `recording-paused` | _boolean_ | If recording is paused. | +| `preview-only` | _boolean_ | Always false. Retrocompatibility with OBSRemote. | | `stream-timecode` | _String (optional)_ | Time elapsed since streaming started (only present if currently streaming). | | `rec-timecode` | _String (optional)_ | Time elapsed since recording started (only present if currently recording). | -| `preview-only` | _boolean_ | Always false. Retrocompatibility with OBSRemote. | --- From 77380a098eb5dec174d1e477e3526fca9e93d3ff Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Tue, 19 Jan 2021 18:07:43 +0000 Subject: [PATCH 203/278] docs(ci): Update protocol.md - afc9c54 [skip ci] --- docs/generated/comments.json | 4 ++-- docs/generated/protocol.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 34d49864..ead2dae5 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -9424,7 +9424,7 @@ "api": "requests", "name": "CreateScene", "category": "scenes", - "since": "4.8.0", + "since": "unreleased", "params": [ { "type": "String", @@ -9447,7 +9447,7 @@ "sinces": [ { "name": "", - "description": "4.8.0" + "description": "unreleased" } ], "heading": { diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 13e6b39a..87d69c10 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -3700,7 +3700,7 @@ _No specified parameters._ ### CreateScene -- Added in v4.8.0 +- Unreleased Create a new scene scene. From bc3b09dce4bf25eb99007dabff078deb2f5b027d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 20 Jan 2021 00:08:24 +0100 Subject: [PATCH 204/278] WSServer(stop): wait for all connections to close --- src/WSServer.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/WSServer.cpp b/src/WSServer.cpp index add0e1e1..33639dd1 100644 --- a/src/WSServer.cpp +++ b/src/WSServer.cpp @@ -121,13 +121,11 @@ void WSServer::stop() for (connection_hdl hdl : _connections) { _server.close(hdl, websocketpp::close::status::going_away, "Server stopping"); } - _connections.clear(); - _connectionProperties.clear(); _threadPool.waitForDone(); - while (!_server.stopped()) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + while (_connections.size() > 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } blog(LOG_INFO, "server stopped successfully"); From 492e9d8df72b1e5d83d298555cbfe7434d0d60fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 20 Jan 2021 00:28:50 +0100 Subject: [PATCH 205/278] editorconfig: set indent_size for C/C++ files --- .editorconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/.editorconfig b/.editorconfig index 9d1d51a1..14b0f7bd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,6 +3,7 @@ insert_final_newline = true [*.{c,cpp,h,hpp}] indent_style = tab +indent_size = 4 [*.{yml,yaml}] indent_style = space From dd9ad67e6b611dc98a18d7c729a7f11843bace35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 20 Jan 2021 00:44:34 +0100 Subject: [PATCH 206/278] Config: authentication enabled by default --- src/Config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config.cpp b/src/Config.cpp index 1a17065f..3c955d8c 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -45,7 +45,7 @@ Config::Config() : LockToIPv4(false), DebugEnabled(false), AlertsEnabled(true), - AuthRequired(false), + AuthRequired(true), Secret(""), Salt(""), SettingsLoaded(false) From 488a57e2debe30a93a1fce86ce0f1258bac424a1 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 19 Jan 2021 18:23:54 -0800 Subject: [PATCH 207/278] Requests: Fix GetSourceDefaultSettings indentation and docs --- src/Utils.cpp | 44 ++++++++++++++++---------------- src/Utils.h | 14 +++++----- src/WSRequestHandler.cpp | 2 +- src/WSRequestHandler_Sources.cpp | 27 ++++++++++---------- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index 2752b8ab..b2d7a288 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -917,41 +917,41 @@ struct obs_data_item { // Used for OBSDataGetDefaults size_t data_size; size_t default_len; size_t default_size; - size_t autoselect_size; + size_t autoselect_size; size_t capacity; }; obs_data_t *Utils::OBSDataGetDefaults(obs_data_t *data) { - obs_data_t *returnData = obs_data_create(); - obs_data_item_t *item = NULL; + obs_data_t *returnData = obs_data_create(); + obs_data_item_t *item = NULL; - for (item = obs_data_first(data); item; obs_data_item_next(&item)) { - enum obs_data_type type = obs_data_item_gettype(item); - const char *name = (char *)item + sizeof(struct obs_data_item); + for (item = obs_data_first(data); item; obs_data_item_next(&item)) { + enum obs_data_type type = obs_data_item_gettype(item); + const char *name = (char *)item + sizeof(struct obs_data_item); - if (type == OBS_DATA_STRING) { + if (type == OBS_DATA_STRING) { const char *val = obs_data_item_get_string(item); - obs_data_set_string(returnData, name, val); + obs_data_set_string(returnData, name, val); } else if (type == OBS_DATA_NUMBER) { enum obs_data_number_type type = obs_data_item_numtype(item); - if (type == OBS_DATA_NUM_INT) { - long long val = obs_data_item_get_int(item); - obs_data_set_int(returnData, name, val); - } else { - double val = obs_data_item_get_double(item); - obs_data_set_double(returnData, name, val); - } + if (type == OBS_DATA_NUM_INT) { + long long val = obs_data_item_get_int(item); + obs_data_set_int(returnData, name, val); + } else { + double val = obs_data_item_get_double(item); + obs_data_set_double(returnData, name, val); + } } else if (type == OBS_DATA_BOOLEAN) { bool val = obs_data_item_get_bool(item); - obs_data_set_bool(returnData, name, val); + obs_data_set_bool(returnData, name, val); } else if (type == OBS_DATA_OBJECT) { OBSDataAutoRelease obj = obs_data_item_get_obj(item); - obs_data_set_obj(returnData, name, obj); + obs_data_set_obj(returnData, name, obj); } else if (type == OBS_DATA_ARRAY) { - OBSDataArrayAutoRelease array = obs_data_item_get_array(item); - obs_data_set_array(returnData, name, array); - } - } - return returnData; + OBSDataArrayAutoRelease array = obs_data_item_get_array(item); + obs_data_set_array(returnData, name, array); + } + } + return returnData; } diff --git a/src/Utils.h b/src/Utils.h index d15427b5..ba227f15 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -88,12 +88,12 @@ namespace Utils { const char* GetCurrentRecordingFilename(); QString nsToTimestamp(uint64_t ns); - struct AddSourceData { - obs_source_t *source; - obs_sceneitem_t *sceneItem; - bool setVisible; - }; - void AddSourceHelper(void *_data, obs_scene_t *scene); + struct AddSourceData { + obs_source_t *source; + obs_sceneitem_t *sceneItem; + bool setVisible; + }; + void AddSourceHelper(void *_data, obs_scene_t *scene); - obs_data_t *OBSDataGetDefaults(obs_data_t *data); + obs_data_t *OBSDataGetDefaults(obs_data_t *data); }; diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 0b366761..5c502727 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -118,7 +118,7 @@ const QHash WSRequestHandler::messageMap{ { "SetSourceSettings", &WSRequestHandler::SetSourceSettings }, { "GetAudioMonitorType", &WSRequestHandler::GetAudioMonitorType }, { "SetAudioMonitorType", &WSRequestHandler::SetAudioMonitorType }, - { "GetSourceDefaultSettings", &WSRequestHandler::GetSourceDefaultSettings }, + { "GetSourceDefaultSettings", &WSRequestHandler::GetSourceDefaultSettings }, { "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot }, { "GetSourceFilters", &WSRequestHandler::GetSourceFilters }, diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 8df1bf5c..a14bcb81 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1684,6 +1684,8 @@ RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request) * * @param {String} `sourceKind` Source name. * +* @return {Object} `defaultSettings` Settings object for source. +* * @api requests * @name GetSourceDefaultSettings * @category sources @@ -1695,22 +1697,21 @@ RpcResponse WSRequestHandler::GetSourceDefaultSettings(const RpcRequest& request return request.failed("missing request parameters"); } - QString sourceKind = obs_data_get_string(request.parameters(), "sourceKind"); + QString sourceKind = obs_data_get_string(request.parameters(), "sourceKind"); + if (sourceKind.isEmpty()) { + return request.failed("invalid request parameters"); + } - if (sourceKind.isEmpty()) { - return request.failed("invalid request parameters"); - } + OBSDataAutoRelease defaultData = obs_get_source_defaults(sourceKind.toUtf8()); + if (!defaultData) { + return request.failed("invalid sourceKind"); + } - OBSDataAutoRelease defaultData = obs_get_source_defaults(sourceKind.toUtf8()); - if (!defaultData) { - return request.failed("invalid sourceKind"); - } + OBSDataAutoRelease defaultSettings = Utils::OBSDataGetDefaults(defaultData); - OBSDataAutoRelease defaultSettings = Utils::OBSDataGetDefaults(defaultData); - - OBSDataAutoRelease response = obs_data_create(); - obs_data_set_obj(response, "defaultSettings", defaultSettings); - return request.success(response); + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_obj(response, "defaultSettings", defaultSettings); + return request.success(response); } /** From 0d59983d1a971fcc1c4d070295dbed32270b58be Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 19 Jan 2021 18:30:18 -0800 Subject: [PATCH 208/278] Misc: Fix indentation of src files Replace 4-space code with tabs to conform with the way its supposed to be. --- src/ConnectionProperties.cpp | 6 +++--- src/ConnectionProperties.h | 8 ++++---- src/Utils.h | 12 ++++++------ src/WSRequestHandler_Recording.cpp | 18 +++++++++--------- src/WSRequestHandler_ReplayBuffer.cpp | 6 +++--- src/WSRequestHandler_Sources.cpp | 10 +++++----- src/WSServer.cpp | 2 +- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/ConnectionProperties.cpp b/src/ConnectionProperties.cpp index 485123de..aa7c4525 100644 --- a/src/ConnectionProperties.cpp +++ b/src/ConnectionProperties.cpp @@ -19,16 +19,16 @@ with this program. If not, see #include "ConnectionProperties.h" ConnectionProperties::ConnectionProperties() - : _authenticated(false) + : _authenticated(false) { } bool ConnectionProperties::isAuthenticated() { - return _authenticated.load(); + return _authenticated.load(); } void ConnectionProperties::setAuthenticated(bool authenticated) { - _authenticated.store(authenticated); + _authenticated.store(authenticated); } \ No newline at end of file diff --git a/src/ConnectionProperties.h b/src/ConnectionProperties.h index a766ef6a..8bb87a27 100644 --- a/src/ConnectionProperties.h +++ b/src/ConnectionProperties.h @@ -23,9 +23,9 @@ with this program. If not, see class ConnectionProperties { public: - explicit ConnectionProperties(); - bool isAuthenticated(); - void setAuthenticated(bool authenticated); + explicit ConnectionProperties(); + bool isAuthenticated(); + void setAuthenticated(bool authenticated); private: - std::atomic _authenticated; + std::atomic _authenticated; }; \ No newline at end of file diff --git a/src/Utils.h b/src/Utils.h index 47a19f2d..0983ba4b 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -88,10 +88,10 @@ namespace Utils { const char* GetCurrentRecordingFilename(); QString nsToTimestamp(uint64_t ns); - struct AddSourceData { - obs_source_t *source; - obs_sceneitem_t *sceneItem; - bool setVisible; - }; - void AddSourceHelper(void *_data, obs_scene_t *scene); + struct AddSourceData { + obs_source_t *source; + obs_sceneitem_t *sceneItem; + bool setVisible; + }; + void AddSourceHelper(void *_data, obs_scene_t *scene); }; diff --git a/src/WSRequestHandler_Recording.cpp b/src/WSRequestHandler_Recording.cpp index db0c529d..d0a53f82 100644 --- a/src/WSRequestHandler_Recording.cpp +++ b/src/WSRequestHandler_Recording.cpp @@ -29,19 +29,19 @@ RpcResponse ifCanPause(const RpcRequest& request, std::function c * @since unreleased */ RpcResponse WSRequestHandler::GetRecordingStatus(const RpcRequest& request) { - auto events = GetEventsSystem(); + auto events = GetEventsSystem(); - OBSDataAutoRelease data = obs_data_create(); - obs_data_set_bool(data, "isRecording", obs_frontend_recording_active()); - obs_data_set_bool(data, "isRecordingPaused", obs_frontend_recording_paused()); + OBSDataAutoRelease data = obs_data_create(); + obs_data_set_bool(data, "isRecording", obs_frontend_recording_active()); + obs_data_set_bool(data, "isRecordingPaused", obs_frontend_recording_paused()); - if (obs_frontend_recording_active()) { - QString recordingTimecode = events->getRecordingTimecode(); - obs_data_set_string(data, "recordTimecode", recordingTimecode.toUtf8().constData()); + if (obs_frontend_recording_active()) { + QString recordingTimecode = events->getRecordingTimecode(); + obs_data_set_string(data, "recordTimecode", recordingTimecode.toUtf8().constData()); obs_data_set_string(data, "recordingFilename", Utils::GetCurrentRecordingFilename()); - } + } - return request.success(data); + return request.success(data); } /** diff --git a/src/WSRequestHandler_ReplayBuffer.cpp b/src/WSRequestHandler_ReplayBuffer.cpp index d4c83a27..2210fcf7 100644 --- a/src/WSRequestHandler_ReplayBuffer.cpp +++ b/src/WSRequestHandler_ReplayBuffer.cpp @@ -16,10 +16,10 @@ * @since unreleased */ RpcResponse WSRequestHandler::GetReplayBufferStatus(const RpcRequest& request) { - OBSDataAutoRelease data = obs_data_create(); - obs_data_set_bool(data, "isReplayBufferActive", obs_frontend_replay_buffer_active()); + OBSDataAutoRelease data = obs_data_create(); + obs_data_set_bool(data, "isReplayBufferActive", obs_frontend_replay_buffer_active()); - return request.success(data); + return request.success(data); } /** diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 94e9a06c..8b044cf6 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -975,10 +975,10 @@ RpcResponse WSRequestHandler::GetTextFreetype2Properties(const RpcRequest& reque */ RpcResponse WSRequestHandler::SetTextFreetype2Properties(const RpcRequest& request) { - const char* sourceName = obs_data_get_string(request.parameters(), "source"); - if (!sourceName) { + const char* sourceName = obs_data_get_string(request.parameters(), "source"); + if (!sourceName) { return request.failed("invalid request parameters"); - } + } OBSSourceAutoRelease source = obs_get_source_by_name(sourceName); if (!source) { @@ -1681,8 +1681,8 @@ RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request) /** * Takes a picture snapshot of a source and then can either or both: -* - Send it over as a Data URI (base64-encoded data) in the response (by specifying `embedPictureFormat` in the request) -* - Save it to disk (by specifying `saveToFilePath` in the request) +* - Send it over as a Data URI (base64-encoded data) in the response (by specifying `embedPictureFormat` in the request) +* - Save it to disk (by specifying `saveToFilePath` in the request) * * At least `embedPictureFormat` or `saveToFilePath` must be specified. * diff --git a/src/WSServer.cpp b/src/WSServer.cpp index 33639dd1..6d155658 100644 --- a/src/WSServer.cpp +++ b/src/WSServer.cpp @@ -44,7 +44,7 @@ WSServer::WSServer() _connections(), _clMutex(QMutex::Recursive) { - _server.get_alog().clear_channels(websocketpp::log::alevel::frame_header | websocketpp::log::alevel::frame_payload | websocketpp::log::alevel::control); + _server.get_alog().clear_channels(websocketpp::log::alevel::frame_header | websocketpp::log::alevel::frame_payload | websocketpp::log::alevel::control); _server.init_asio(); #ifndef _WIN32 _server.set_reuse_addr(true); From 59ab9548c29ba31fd3675668001a9799354fc4a0 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Wed, 20 Jan 2021 09:22:17 +0000 Subject: [PATCH 209/278] docs(ci): Update protocol.md - a1cf7f8 [skip ci] --- docs/generated/comments.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index ead2dae5..26798952 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -7327,7 +7327,7 @@ "level": 2, "text": "TakeSourceScreenshot" }, - "lead": "Takes a picture snapshot of a source and then can either or both: - Send it over as a Data URI (base64-encoded data) in the response (by specifying `embedPictureFormat` in the request) - Save it to disk (by specifying `saveToFilePath` in the request)", + "lead": "Takes a picture snapshot of a source and then can either or both: \t- Send it over as a Data URI (base64-encoded data) in the response (by specifying `embedPictureFormat` in the request) \t- Save it to disk (by specifying `saveToFilePath` in the request)", "type": "class", "examples": [] } From 740a8a8d06e8e8312cdc522a1dbd4be83e2bbd5e Mon Sep 17 00:00:00 2001 From: Valter Minute Date: Wed, 20 Jan 2021 09:00:14 +0100 Subject: [PATCH 210/278] General: Add ScenesCollection object Adds a ScenesCollection object in the protocol definition, replacing the current Array return with Array, keeping it more coherent with other requests that return objects in the same format. This will help automated code generation from comment.json that otherwise would require ad-hoc handling for that specific request. Signed-off-by: Valter Minute --- docs/generated/comments.json | 37 +++++++++++++++++------ docs/generated/protocol.md | 8 +++-- src/WSRequestHandler_SceneCollections.cpp | 8 +++-- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index cc02464d..c61bea27 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -474,6 +474,31 @@ }, "examples": [] }, + { + "subheads": [], + "typedef": "{Object} `ScenesCollection`", + "property": "{String} `sc-name` Name of the scene collection", + "properties": [ + { + "type": "String", + "name": "sc-name", + "description": "Name of the scene collection" + } + ], + "typedefs": [ + { + "type": "Object", + "name": "ScenesCollection", + "description": "" + } + ], + "name": "", + "heading": { + "level": 2, + "text": "" + }, + "examples": [] + }, { "subheads": [], "typedef": "{Object} `Scene`", @@ -8218,24 +8243,16 @@ { "subheads": [], "description": "List available scene collections", - "return": [ - "{Array} `scene-collections` Scene collections list", - "{String} `scene-collections.*.sc-name` Scene collection name" - ], + "return": "{Array} `scene-collections` Scene collections list", "api": "requests", "name": "ListSceneCollections", "category": "scene collections", "since": "4.0.0", "returns": [ { - "type": "Array", + "type": "Array", "name": "scene-collections", "description": "Scene collections list" - }, - { - "type": "String", - "name": "scene-collections.*.sc-name", - "description": "Scene collection name" } ], "names": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 9d9bd8a7..d3007b24 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -49,6 +49,7 @@ You can also refer to any of the client libraries listed on the [README](README. * [SceneItemTransform](#sceneitemtransform) * [OBSStats](#obsstats) * [Output](#output) + * [ScenesCollection](#scenescollection) * [Scene](#scene) - [Events](#events) * [Scenes](#scenes) @@ -344,6 +345,10 @@ These are complex types, such as `Source` and `Scene`, which are used as argumen | `totalFrames` | _int_ | Number of frames sent | | `droppedFrames` | _int_ | Number of frames dropped | | `totalBytes` | _int_ | Total bytes sent | +## ScenesCollection +| Name | Type | Description | +| ---- | :---: | ------------| +| `sc-name` | _String_ | Name of the scene collection | ## Scene | Name | Type | Description | | ---- | :---: | ------------| @@ -3308,8 +3313,7 @@ _No specified parameters._ | Name | Type | Description | | ---- | :---: | ------------| -| `scene-collections` | _Array<String>_ | Scene collections list | -| `scene-collections.*.sc-name` | _String_ | Scene collection name | +| `scene-collections` | _Array<ScenesCollection>_ | Scene collections list | --- diff --git a/src/WSRequestHandler_SceneCollections.cpp b/src/WSRequestHandler_SceneCollections.cpp index 77ce5d0a..8fc0ef42 100644 --- a/src/WSRequestHandler_SceneCollections.cpp +++ b/src/WSRequestHandler_SceneCollections.cpp @@ -2,6 +2,11 @@ #include "WSRequestHandler.h" +/** +* @typedef {Object} `ScenesCollection` +* @property {String} `sc-name` Name of the scene collection +*/ + /** * Change the active scene collection. * @@ -59,8 +64,7 @@ RpcResponse WSRequestHandler::GetCurrentSceneCollection(const RpcRequest& reques /** * List available scene collections * - * @return {Array} `scene-collections` Scene collections list - * @return {String} `scene-collections.*.sc-name` Scene collection name + * @return {Array} `scene-collections` Scene collections list * * @api requests * @name ListSceneCollections From 9859c7b25f13949674c1236ec6bce9135306dcac Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 20 Jan 2021 04:21:43 -0800 Subject: [PATCH 211/278] Utils: Use obs_data_item_get_name in OBSDataGetDefaults --- src/Utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index b2d7a288..3d85af7a 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -928,7 +928,7 @@ obs_data_t *Utils::OBSDataGetDefaults(obs_data_t *data) for (item = obs_data_first(data); item; obs_data_item_next(&item)) { enum obs_data_type type = obs_data_item_gettype(item); - const char *name = (char *)item + sizeof(struct obs_data_item); + const char *name = obs_data_item_get_name(item); if (type == OBS_DATA_STRING) { const char *val = obs_data_item_get_string(item); From ab453a0322168844672b499c09031ce035ce43bd Mon Sep 17 00:00:00 2001 From: Harm Date: Thu, 21 Jan 2021 10:56:46 +0100 Subject: [PATCH 212/278] readme: Added obs-java-client to the available API's --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1a1419f4..2ca82777 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Here's a list of available language APIs for obs-websocket : - Python 3.5+ with asyncio: [obs-ws-rc](https://github.com/KirillMysnik/obs-ws-rc) by Kirill Mysnik - Python 3.6+ with asyncio: [simpleobsws](https://github.com/IRLToolkit/simpleobsws) by tt2468 - Java 8+: [obs-websocket-java](https://github.com/Twasi/websocket-obs-java) by TwasiNET +- Java 11+: [obs-java-client](https://github.com/harm27/obs-java-client) by harm27 - Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf - Rust: [obws](https://github.com/dnaka91/obws) by dnaka91 - HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468 From 599eaf85ce3591e9ba3505e6a703584de1e77344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Thu, 28 Jan 2021 22:55:40 +0100 Subject: [PATCH 213/278] requests(SceneItems): remove unused variables --- src/WSRequestHandler_SceneItems.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 65af35d4..1f72e064 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -427,13 +427,11 @@ RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) { OBSSceneItemAutoRelease sceneItem; if (strlen(itemName)) { - OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "source"); sceneItem = Utils::GetSceneItemFromName(scene, itemName); if (!sceneItem) { return request.failed("specified scene item name doesn't exist"); } } else { - OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item"); sceneItem = Utils::GetSceneItemFromId(scene, itemId); if (!sceneItem) { return request.failed("specified scene item ID doesn't exist"); From 6cbe50e3e7563e0fbf15f5cd6cd1012055df2db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Thu, 28 Jan 2021 22:56:01 +0100 Subject: [PATCH 214/278] request(SetSceneItemRender): refactor conditional --- src/WSRequestHandler_SceneItems.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 1f72e064..732fe619 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -403,9 +403,8 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) { * @since 0.3 */ RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) { - if (!request.hasField("render") || - (!request.hasField("source") && !request.hasField("item")) - ) + bool doesntHaveSourceOrItemParameter = !(request.hasField("source") || request.hasField("item")); + if (!request.hasField("render") || doesntHaveSourceOrItemParameter) { return request.failed("missing request parameters"); } From 332876495df4c22d02e40499d201acd9dfd5cadb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Thu, 28 Jan 2021 23:04:02 +0100 Subject: [PATCH 215/278] Utils: remove unused struct --- src/Utils.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index 3d85af7a..c67281e6 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -907,20 +907,6 @@ void Utils::AddSourceHelper(void *_data, obs_scene_t *scene) obs_sceneitem_set_visible(data->sceneItem, data->setVisible); } -struct obs_data_item { // Used for OBSDataGetDefaults - volatile long ref; - struct obs_data *parent; - struct obs_data_item *next; - enum obs_data_type type; - size_t name_len; - size_t data_len; - size_t data_size; - size_t default_len; - size_t default_size; - size_t autoselect_size; - size_t capacity; -}; - obs_data_t *Utils::OBSDataGetDefaults(obs_data_t *data) { obs_data_t *returnData = obs_data_create(); From c2aa7263bd0b0aa13b6dfd928cfc56181e5d4363 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Thu, 28 Jan 2021 22:57:37 +0000 Subject: [PATCH 216/278] docs(ci): Update protocol.md - 25be7ee [skip ci] --- docs/generated/comments.json | 10 ++++++++-- docs/generated/protocol.md | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index fca8ab37..4ed4fd97 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -8802,7 +8802,8 @@ "description": "Show or hide a specified source item in a specified scene.", "param": [ "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the currently active scene.", - "{String} `source` Scene Item name.", + "{String (optional)} `source` Scene Item name.", + "{int (optional)} `item` Scene Item id", "{boolean} `render` true = shown ; false = hidden" ], "api": "requests", @@ -8816,10 +8817,15 @@ "description": "Name of the scene the scene item belongs to. Defaults to the currently active scene." }, { - "type": "String", + "type": "String (optional)", "name": "source", "description": "Scene Item name." }, + { + "type": "int (optional)", + "name": "item", + "description": "Scene Item id" + }, { "type": "boolean", "name": "render", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 006d327c..051b7c13 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -3474,7 +3474,8 @@ Show or hide a specified source item in a specified scene. | Name | Type | Description | | ---- | :---: | ------------| | `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the currently active scene. | -| `source` | _String_ | Scene Item name. | +| `source` | _String (optional)_ | Scene Item name. | +| `item` | _int (optional)_ | Scene Item id | | `render` | _boolean_ | true = shown ; false = hidden | From c6db90ae07b46694a39319ec3b3bbfca0e2e2ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 29 Jan 2021 00:08:20 +0100 Subject: [PATCH 217/278] request(GetSourceDefaultSettings): add sourceKind return value + fix param spec --- src/WSRequestHandler_Sources.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index b72c9879..23a31bed 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1682,8 +1682,9 @@ RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request) /** * Get the default settings for a given source type. * -* @param {String} `sourceKind` Source name. +* @param {String} `sourceKind` Source kind. Also called "source id" in libobs terminology. * +* @return {String} `sourceKind` Source kind. Same value as the `sourceKind` parameter. * @return {Object} `defaultSettings` Settings object for source. * * @api requests @@ -1710,6 +1711,7 @@ RpcResponse WSRequestHandler::GetSourceDefaultSettings(const RpcRequest& request OBSDataAutoRelease defaultSettings = Utils::OBSDataGetDefaults(defaultData); OBSDataAutoRelease response = obs_data_create(); + obs_data_set_string(response, "sourceKind", sourceKind.toUtf8().constData()); obs_data_set_obj(response, "defaultSettings", defaultSettings); return request.success(response); } From cb7c77d29e3fa25047a18b42c594eb7f42cf56b2 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Thu, 28 Jan 2021 23:26:40 +0000 Subject: [PATCH 218/278] docs(ci): Update protocol.md - 83e23c9 [skip ci] --- docs/generated/comments.json | 57 ++++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 25 ++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 4ed4fd97..d9251a2a 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -7255,6 +7255,63 @@ "type": "class", "examples": [] }, + { + "subheads": [], + "description": "Get the default settings for a given source type.", + "param": "{String} `sourceKind` Source kind. Also called \"source id\" in libobs terminology.", + "return": [ + "{String} `sourceKind` Source kind. Same value as the `sourceKind` parameter.", + "{Object} `defaultSettings` Settings object for source." + ], + "api": "requests", + "name": "GetSourceDefaultSettings", + "category": "sources", + "since": "4.9.0", + "returns": [ + { + "type": "String", + "name": "sourceKind", + "description": "Source kind. Same value as the `sourceKind` parameter." + }, + { + "type": "Object", + "name": "defaultSettings", + "description": "Settings object for source." + } + ], + "params": [ + { + "type": "String", + "name": "sourceKind", + "description": "Source kind. Also called \"source id\" in libobs terminology." + } + ], + "names": [ + { + "name": "", + "description": "GetSourceDefaultSettings" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "GetSourceDefaultSettings" + }, + "lead": "", + "type": "class", + "examples": [] + }, { "subheads": [], "description": "\n\nAt least `embedPictureFormat` or `saveToFilePath` must be specified.\n\nClients can specify `width` and `height` parameters to receive scaled pictures. Aspect ratio is\npreserved if only one of these two parameters is specified.", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 051b7c13..beb289b2 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -183,6 +183,7 @@ You can also refer to any of the client libraries listed on the [README](README. + [SetSourceFilterVisibility](#setsourcefiltervisibility) + [GetAudioMonitorType](#getaudiomonitortype) + [SetAudioMonitorType](#setaudiomonitortype) + + [GetSourceDefaultSettings](#getsourcedefaultsettings) + [TakeSourceScreenshot](#takesourcescreenshot) * [Outputs](#outputs) + [ListOutputs](#listoutputs) @@ -2810,6 +2811,30 @@ Set the audio monitoring type of the specified source. _No additional response items._ +--- + +### GetSourceDefaultSettings + + +- Added in v4.9.0 + +Get the default settings for a given source type. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceKind` | _String_ | Source kind. Also called "source id" in libobs terminology. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceKind` | _String_ | Source kind. Same value as the `sourceKind` parameter. | +| `defaultSettings` | _Object_ | Settings object for source. | + + --- ### TakeSourceScreenshot From c02382b6e5475890822a6f62d8bb5770b0cb842d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Fri, 29 Jan 2021 19:16:53 +0100 Subject: [PATCH 219/278] config: initial password setup prompt wip --- src/Config.cpp | 166 ++++++++++++++++++++++++++++++++----------------- src/Config.h | 1 + 2 files changed, 109 insertions(+), 58 deletions(-) diff --git a/src/Config.cpp b/src/Config.cpp index 3c955d8c..1148b892 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -18,9 +18,13 @@ with this program. If not, see #include +#include #include #include #include +#include +#include +#include #define SECTION_NAME "WebsocketAPI" #define PARAM_ENABLE "ServerEnabled" @@ -32,6 +36,8 @@ with this program. If not, see #define PARAM_SECRET "AuthSecret" #define PARAM_SALT "AuthSalt" +#define GLOBAL_AUTH_SETUP_PROMPTED "AuthSetupPrompted" + #include "Utils.h" #include "WSServer.h" @@ -130,6 +136,70 @@ config_t* Config::GetConfigStore() return obs_frontend_get_profile_config(); } +void Config::MigrateFromGlobalSettings() +{ + config_t* source = obs_frontend_get_global_config(); + config_t* destination = obs_frontend_get_profile_config(); + + if(config_has_user_value(source, SECTION_NAME, PARAM_ENABLE)) { + bool value = config_get_bool(source, SECTION_NAME, PARAM_ENABLE); + config_set_bool(destination, SECTION_NAME, PARAM_ENABLE, value); + + config_remove_value(source, SECTION_NAME, PARAM_ENABLE); + } + + if(config_has_user_value(source, SECTION_NAME, PARAM_PORT)) { + uint64_t value = config_get_uint(source, SECTION_NAME, PARAM_PORT); + config_set_uint(destination, SECTION_NAME, PARAM_PORT, value); + + config_remove_value(source, SECTION_NAME, PARAM_PORT); + } + + if(config_has_user_value(source, SECTION_NAME, PARAM_LOCKTOIPV4)) { + bool value = config_get_bool(source, SECTION_NAME, PARAM_LOCKTOIPV4); + config_set_bool(destination, SECTION_NAME, PARAM_LOCKTOIPV4, value); + + config_remove_value(source, SECTION_NAME, PARAM_LOCKTOIPV4); + } + + if(config_has_user_value(source, SECTION_NAME, PARAM_DEBUG)) { + bool value = config_get_bool(source, SECTION_NAME, PARAM_DEBUG); + config_set_bool(destination, SECTION_NAME, PARAM_DEBUG, value); + + config_remove_value(source, SECTION_NAME, PARAM_DEBUG); + } + + if(config_has_user_value(source, SECTION_NAME, PARAM_ALERT)) { + bool value = config_get_bool(source, SECTION_NAME, PARAM_ALERT); + config_set_bool(destination, SECTION_NAME, PARAM_ALERT, value); + + config_remove_value(source, SECTION_NAME, PARAM_ALERT); + } + + if(config_has_user_value(source, SECTION_NAME, PARAM_AUTHREQUIRED)) { + bool value = config_get_bool(source, SECTION_NAME, PARAM_AUTHREQUIRED); + config_set_bool(destination, SECTION_NAME, PARAM_AUTHREQUIRED, value); + + config_remove_value(source, SECTION_NAME, PARAM_AUTHREQUIRED); + } + + if(config_has_user_value(source, SECTION_NAME, PARAM_SECRET)) { + const char* value = config_get_string(source, SECTION_NAME, PARAM_SECRET); + config_set_string(destination, SECTION_NAME, PARAM_SECRET, value); + + config_remove_value(source, SECTION_NAME, PARAM_SECRET); + } + + if(config_has_user_value(source, SECTION_NAME, PARAM_SALT)) { + const char* value = config_get_string(source, SECTION_NAME, PARAM_SALT); + config_set_string(destination, SECTION_NAME, PARAM_SALT, value); + + config_remove_value(source, SECTION_NAME, PARAM_SALT); + } + + config_save(destination); +} + QString Config::GenerateSalt() { // Generate 32 random chars @@ -233,68 +303,48 @@ void Config::OnFrontendEvent(enum obs_frontend_event event, void* param) } } } + else if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) { + FirstRunPasswordSetup(); + } } -void Config::MigrateFromGlobalSettings() +void Config::FirstRunPasswordSetup() { - config_t* source = obs_frontend_get_global_config(); - config_t* destination = obs_frontend_get_profile_config(); - - if(config_has_user_value(source, SECTION_NAME, PARAM_ENABLE)) { - bool value = config_get_bool(source, SECTION_NAME, PARAM_ENABLE); - config_set_bool(destination, SECTION_NAME, PARAM_ENABLE, value); - - config_remove_value(source, SECTION_NAME, PARAM_ENABLE); + // check if we already showed the auth setup prompt to the user, independently of the current settings (tied to the current profile) + config_t* globalConfig = obs_frontend_get_global_config(); + bool alreadyPrompted = config_get_bool(globalConfig, SECTION_NAME, GLOBAL_AUTH_SETUP_PROMPTED); + if (alreadyPrompted) { + return; } - if(config_has_user_value(source, SECTION_NAME, PARAM_PORT)) { - uint64_t value = config_get_uint(source, SECTION_NAME, PARAM_PORT); - config_set_uint(destination, SECTION_NAME, PARAM_PORT, value); + // lift the flag up and save it + config_set_bool(globalConfig, SECTION_NAME, GLOBAL_AUTH_SETUP_PROMPTED, true); + config_save(globalConfig); + + obs_frontend_push_ui_translation(obs_module_get_string); + QString initialPasswordSetupTitle = QObject::tr("OBSWebsocket.InitialPasswordSetup.Title"); + QString initialPasswordSetupText = QObject::tr("OBSWebsocket.InitialPasswordSetup.Text"); + QString setupDismissedTitle = QObject::tr("OBSWebsocket.InitialPasswordSetupDismissed.Title"); + QString setupDismissedText = QObject::tr("OBSWebsocket.InitialPasswordSetupDismissed.Text"); + obs_frontend_pop_ui_translation(); + + // prompt for a password + auto mainWindow = reinterpret_cast( + obs_frontend_get_main_window() + ); + bool promptAccepted = false; + QString newPassword = QInputDialog::getText( + mainWindow, + initialPasswordSetupTitle, initialPasswordSetupText, + QLineEdit::PasswordEchoOnEdit, QString::Null(), &promptAccepted + ); + + if (promptAccepted) { + // set new password + GetConfig()->SetPassword(newPassword); + } else { + // tell the user they still can set the password later in our settings dialog + QMessageBox::information(mainWindow, setupDismissedTitle, setupDismissedText, QMessageBox::Ok); - config_remove_value(source, SECTION_NAME, PARAM_PORT); } - - if(config_has_user_value(source, SECTION_NAME, PARAM_LOCKTOIPV4)) { - bool value = config_get_bool(source, SECTION_NAME, PARAM_LOCKTOIPV4); - config_set_bool(destination, SECTION_NAME, PARAM_LOCKTOIPV4, value); - - config_remove_value(source, SECTION_NAME, PARAM_LOCKTOIPV4); - } - - if(config_has_user_value(source, SECTION_NAME, PARAM_DEBUG)) { - bool value = config_get_bool(source, SECTION_NAME, PARAM_DEBUG); - config_set_bool(destination, SECTION_NAME, PARAM_DEBUG, value); - - config_remove_value(source, SECTION_NAME, PARAM_DEBUG); - } - - if(config_has_user_value(source, SECTION_NAME, PARAM_ALERT)) { - bool value = config_get_bool(source, SECTION_NAME, PARAM_ALERT); - config_set_bool(destination, SECTION_NAME, PARAM_ALERT, value); - - config_remove_value(source, SECTION_NAME, PARAM_ALERT); - } - - if(config_has_user_value(source, SECTION_NAME, PARAM_AUTHREQUIRED)) { - bool value = config_get_bool(source, SECTION_NAME, PARAM_AUTHREQUIRED); - config_set_bool(destination, SECTION_NAME, PARAM_AUTHREQUIRED, value); - - config_remove_value(source, SECTION_NAME, PARAM_AUTHREQUIRED); - } - - if(config_has_user_value(source, SECTION_NAME, PARAM_SECRET)) { - const char* value = config_get_string(source, SECTION_NAME, PARAM_SECRET); - config_set_string(destination, SECTION_NAME, PARAM_SECRET, value); - - config_remove_value(source, SECTION_NAME, PARAM_SECRET); - } - - if(config_has_user_value(source, SECTION_NAME, PARAM_SALT)) { - const char* value = config_get_string(source, SECTION_NAME, PARAM_SALT); - config_set_string(destination, SECTION_NAME, PARAM_SALT, value); - - config_remove_value(source, SECTION_NAME, PARAM_SALT); - } - - config_save(destination); -} +} \ No newline at end of file diff --git a/src/Config.h b/src/Config.h index f21e396a..1aa29ebf 100644 --- a/src/Config.h +++ b/src/Config.h @@ -55,4 +55,5 @@ class Config { private: static void OnFrontendEvent(enum obs_frontend_event event, void* param); + static void FirstRunPasswordSetup(); }; From c8ca79f00b4f9aca8972e5b2b1d4f7ae6acfc72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 31 Jan 2021 00:58:56 +0100 Subject: [PATCH 220/278] config: add locale texts for initial password setup --- data/locale/en-US.ini | 4 ++++ src/Config.cpp | 49 ++++++++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 69fbed16..b32da6b0 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -15,3 +15,7 @@ OBSWebsocket.Server.StartFailed.Message="The WebSockets server failed to start, OBSWebsocket.ProfileChanged.Started="WebSockets server enabled in this profile. Server started." OBSWebsocket.ProfileChanged.Stopped="WebSockets server disabled in this profile. Server stopped." OBSWebsocket.ProfileChanged.Restarted="WebSockets server port changed in this profile. Server restarted." +OBSWebsocket.InitialPasswordSetup.Title="obs-websocket - Server Password Configuration" +OBSWebsocket.InitialPasswordSetup.Text="It looks like you are running obs-websocket for the first time. Do you want set a password for the WebSockets server now?" +OBSWebsocket.InitialPasswordSetup.SuccessText="Server Password set successfully." +OBSWebsocket.InitialPasswordSetup.DismissedText="You can configure a server password anytime in obs-websocket settings (under the Tools menu of OBS Studio)" diff --git a/src/Config.cpp b/src/Config.cpp index 1148b892..6f148739 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -310,6 +310,12 @@ void Config::OnFrontendEvent(enum obs_frontend_event event, void* param) void Config::FirstRunPasswordSetup() { + // check if the password is already set + auto config = GetConfig(); + if (!(config->Secret.isEmpty()) && !(config->Salt.isEmpty())) { + return; + } + // check if we already showed the auth setup prompt to the user, independently of the current settings (tied to the current profile) config_t* globalConfig = obs_frontend_get_global_config(); bool alreadyPrompted = config_get_bool(globalConfig, SECTION_NAME, GLOBAL_AUTH_SETUP_PROMPTED); @@ -322,29 +328,34 @@ void Config::FirstRunPasswordSetup() config_save(globalConfig); obs_frontend_push_ui_translation(obs_module_get_string); - QString initialPasswordSetupTitle = QObject::tr("OBSWebsocket.InitialPasswordSetup.Title"); - QString initialPasswordSetupText = QObject::tr("OBSWebsocket.InitialPasswordSetup.Text"); - QString setupDismissedTitle = QObject::tr("OBSWebsocket.InitialPasswordSetupDismissed.Title"); - QString setupDismissedText = QObject::tr("OBSWebsocket.InitialPasswordSetupDismissed.Text"); + QString dialogTitle = QObject::tr("OBSWebsocket.InitialPasswordSetup.Title"); + QString dialogText = QObject::tr("OBSWebsocket.InitialPasswordSetup.Text"); + QString successText = QObject::tr("OBSWebsocket.InitialPasswordSetup.SuccessText"); + QString dismissedText = QObject::tr("OBSWebsocket.InitialPasswordSetup.DismissedText"); + QString passwordLabel = QObject::tr("OBSWebsocket.Settings.Password"); obs_frontend_pop_ui_translation(); - // prompt for a password auto mainWindow = reinterpret_cast( obs_frontend_get_main_window() ); - bool promptAccepted = false; - QString newPassword = QInputDialog::getText( - mainWindow, - initialPasswordSetupTitle, initialPasswordSetupText, - QLineEdit::PasswordEchoOnEdit, QString::Null(), &promptAccepted - ); - - if (promptAccepted) { - // set new password - GetConfig()->SetPassword(newPassword); - } else { - // tell the user they still can set the password later in our settings dialog - QMessageBox::information(mainWindow, setupDismissedTitle, setupDismissedText, QMessageBox::Ok); + + int proceed = QMessageBox::question(mainWindow, dialogTitle, dialogText, QMessageBox::No, QMessageBox::Yes); + if (proceed) { + bool promptAccepted = false; + QString newPassword = QInputDialog::getText( + mainWindow, + dialogTitle, passwordLabel, + QLineEdit::PasswordEchoOnEdit, QString::Null(), &promptAccepted + ); + if (promptAccepted) { + // set new password + GetConfig()->SetPassword(newPassword); + QMessageBox::information(mainWindow, dialogTitle, successText); + return; + } } -} \ No newline at end of file + + // tell the user they still can set the password later in our settings dialog + QMessageBox::information(mainWindow, dialogTitle, dismissedText); +} From 38cf0270b155056db99296f853641d55ca44a99c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 31 Jan 2021 01:26:38 +0100 Subject: [PATCH 221/278] settings: add auth disabled warning --- data/locale/en-US.ini | 1 + src/Config.cpp | 4 ++-- src/forms/settings-dialog.cpp | 30 +++++++++++++++++++++++------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index b32da6b0..b5b9fdc4 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -6,6 +6,7 @@ OBSWebsocket.Settings.Password="Password" OBSWebsocket.Settings.LockToIPv4="Lock server to only using IPv4" OBSWebsocket.Settings.DebugEnable="Enable debug logging" OBSWebsocket.Settings.AlertsEnable="Enable System Tray Alerts" +OBSWebsocket.Settings.AuthDisabledWarning="Running obs-websocket with authentication disabled is not recommended. Are you sure you want to proceed with that change?" OBSWebsocket.NotifyConnect.Title="New WebSocket connection" OBSWebsocket.NotifyConnect.Message="Client %1 connected" OBSWebsocket.NotifyDisconnect.Title="WebSocket client disconnected" diff --git a/src/Config.cpp b/src/Config.cpp index 6f148739..af62aeeb 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -339,8 +339,8 @@ void Config::FirstRunPasswordSetup() obs_frontend_get_main_window() ); - int proceed = QMessageBox::question(mainWindow, dialogTitle, dialogText, QMessageBox::No, QMessageBox::Yes); - if (proceed) { + QMessageBox::StandardButton response = QMessageBox::question(mainWindow, dialogTitle, dialogText); + if (response == QMessageBox::Yes) { bool promptAccepted = false; QString newPassword = QInputDialog::getText( mainWindow, diff --git a/src/forms/settings-dialog.cpp b/src/forms/settings-dialog.cpp index ca686dfe..338ffb1e 100644 --- a/src/forms/settings-dialog.cpp +++ b/src/forms/settings-dialog.cpp @@ -16,12 +16,16 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ +#include "settings-dialog.h" + #include +#include +#include #include "../obs-websocket.h" #include "../Config.h" #include "../WSServer.h" -#include "settings-dialog.h" + #define CHANGE_ME "changeme" @@ -35,9 +39,6 @@ SettingsDialog::SettingsDialog(QWidget* parent) : this, &SettingsDialog::AuthCheckboxChanged); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SettingsDialog::FormAccepted); - - - AuthCheckboxChanged(); } void SettingsDialog::showEvent(QShowEvent* event) { @@ -50,8 +51,12 @@ void SettingsDialog::showEvent(QShowEvent* event) { ui->debugEnabled->setChecked(conf->DebugEnabled); ui->alertsEnabled->setChecked(conf->AlertsEnabled); + ui->authRequired->blockSignals(true); ui->authRequired->setChecked(conf->AuthRequired); + ui->authRequired->blockSignals(false); + ui->password->setText(CHANGE_ME); + ui->password->setEnabled(ui->authRequired->isChecked()); } void SettingsDialog::ToggleShowHide() { @@ -62,10 +67,21 @@ void SettingsDialog::ToggleShowHide() { } void SettingsDialog::AuthCheckboxChanged() { - if (ui->authRequired->isChecked()) + if (ui->authRequired->isChecked()) { ui->password->setEnabled(true); - else - ui->password->setEnabled(false); + } + else { + obs_frontend_push_ui_translation(obs_module_get_string); + QString authDisabledWarning = QObject::tr("OBSWebsocket.Settings.AuthDisabledWarning"); + obs_frontend_pop_ui_translation(); + + QMessageBox::StandardButton response = QMessageBox::question(this, "obs-websocket", authDisabledWarning); + if (response == QMessageBox::Yes) { + ui->password->setEnabled(false); + } else { + ui->authRequired->setChecked(true); + } + } } void SettingsDialog::FormAccepted() { From a8aa34529e03ec7eb9913aa9c8271abf20e29998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 31 Jan 2021 01:51:19 +0100 Subject: [PATCH 222/278] config: simplify initial password setup --- data/locale/en-US.ini | 5 ++--- src/Config.cpp | 35 +++++++++++------------------------ src/obs-websocket.cpp | 11 +++++++++-- src/obs-websocket.h | 1 + 4 files changed, 23 insertions(+), 29 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index b5b9fdc4..9647ac72 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -17,6 +17,5 @@ OBSWebsocket.ProfileChanged.Started="WebSockets server enabled in this profile. OBSWebsocket.ProfileChanged.Stopped="WebSockets server disabled in this profile. Server stopped." OBSWebsocket.ProfileChanged.Restarted="WebSockets server port changed in this profile. Server restarted." OBSWebsocket.InitialPasswordSetup.Title="obs-websocket - Server Password Configuration" -OBSWebsocket.InitialPasswordSetup.Text="It looks like you are running obs-websocket for the first time. Do you want set a password for the WebSockets server now?" -OBSWebsocket.InitialPasswordSetup.SuccessText="Server Password set successfully." -OBSWebsocket.InitialPasswordSetup.DismissedText="You can configure a server password anytime in obs-websocket settings (under the Tools menu of OBS Studio)" +OBSWebsocket.InitialPasswordSetup.Text="It looks like you are running obs-websocket for the first time. Do you want to configure a password now for the WebSockets server?" +OBSWebsocket.InitialPasswordSetup.DismissedText="You can configure a server password later in obs-websocket settings (under the Tools menu of OBS Studio)" diff --git a/src/Config.cpp b/src/Config.cpp index af62aeeb..60bdddca 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -310,12 +310,6 @@ void Config::OnFrontendEvent(enum obs_frontend_event event, void* param) void Config::FirstRunPasswordSetup() { - // check if the password is already set - auto config = GetConfig(); - if (!(config->Secret.isEmpty()) && !(config->Salt.isEmpty())) { - return; - } - // check if we already showed the auth setup prompt to the user, independently of the current settings (tied to the current profile) config_t* globalConfig = obs_frontend_get_global_config(); bool alreadyPrompted = config_get_bool(globalConfig, SECTION_NAME, GLOBAL_AUTH_SETUP_PROMPTED); @@ -327,12 +321,16 @@ void Config::FirstRunPasswordSetup() config_set_bool(globalConfig, SECTION_NAME, GLOBAL_AUTH_SETUP_PROMPTED, true); config_save(globalConfig); + // check if the password is already set + auto config = GetConfig(); + if (!(config->Secret.isEmpty()) && !(config->Salt.isEmpty())) { + return; + } + obs_frontend_push_ui_translation(obs_module_get_string); QString dialogTitle = QObject::tr("OBSWebsocket.InitialPasswordSetup.Title"); QString dialogText = QObject::tr("OBSWebsocket.InitialPasswordSetup.Text"); - QString successText = QObject::tr("OBSWebsocket.InitialPasswordSetup.SuccessText"); QString dismissedText = QObject::tr("OBSWebsocket.InitialPasswordSetup.DismissedText"); - QString passwordLabel = QObject::tr("OBSWebsocket.Settings.Password"); obs_frontend_pop_ui_translation(); auto mainWindow = reinterpret_cast( @@ -341,21 +339,10 @@ void Config::FirstRunPasswordSetup() QMessageBox::StandardButton response = QMessageBox::question(mainWindow, dialogTitle, dialogText); if (response == QMessageBox::Yes) { - bool promptAccepted = false; - QString newPassword = QInputDialog::getText( - mainWindow, - dialogTitle, passwordLabel, - QLineEdit::PasswordEchoOnEdit, QString::Null(), &promptAccepted - ); - - if (promptAccepted) { - // set new password - GetConfig()->SetPassword(newPassword); - QMessageBox::information(mainWindow, dialogTitle, successText); - return; - } + ShowSettingsDialog(); + } + else { + // tell the user they still can set the password later in our settings dialog + QMessageBox::information(mainWindow, dialogTitle, dismissedText); } - - // tell the user they still can set the password later in our settings dialog - QMessageBox::information(mainWindow, dialogTitle, dismissedText); } diff --git a/src/obs-websocket.cpp b/src/obs-websocket.cpp index b198ebb7..12324906 100644 --- a/src/obs-websocket.cpp +++ b/src/obs-websocket.cpp @@ -47,6 +47,7 @@ OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US") ConfigPtr _config; WSServerPtr _server; WSEventsPtr _eventsSystem; +SettingsDialog* settingsDialog = nullptr; bool obs_module_load(void) { blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION); @@ -64,14 +65,14 @@ bool obs_module_load(void) { // UI setup obs_frontend_push_ui_translation(obs_module_get_string); QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window(); - SettingsDialog* settingsDialog = new SettingsDialog(mainWindow); + settingsDialog = new SettingsDialog(mainWindow); obs_frontend_pop_ui_translation(); const char* menuActionText = obs_module_text("OBSWebsocket.Settings.DialogTitle"); QAction* menuAction = (QAction*)obs_frontend_add_tools_menu_qaction(menuActionText); - QObject::connect(menuAction, &QAction::triggered, [settingsDialog] { + QObject::connect(menuAction, &QAction::triggered, [] { // The settings dialog belongs to the main window. Should be ok // to pass the pointer to this QAction belonging to the main window settingsDialog->ToggleShowHide(); @@ -115,3 +116,9 @@ WSServerPtr GetServer() { WSEventsPtr GetEventsSystem() { return _eventsSystem; } + +void ShowSettingsDialog() { + if (settingsDialog) { + settingsDialog->setVisible(true); + } +} diff --git a/src/obs-websocket.h b/src/obs-websocket.h index 5f118d28..671b22ba 100644 --- a/src/obs-websocket.h +++ b/src/obs-websocket.h @@ -55,6 +55,7 @@ typedef std::shared_ptr WSEventsPtr; ConfigPtr GetConfig(); WSServerPtr GetServer(); WSEventsPtr GetEventsSystem(); +void ShowSettingsDialog(); #define OBS_WEBSOCKET_VERSION "4.8.0" From 2015d401863f70ff8f3fbcab9dd6c4e61dd283ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 31 Jan 2021 01:58:22 +0100 Subject: [PATCH 223/278] docs(introduction): update authentication section --- docs/partials/introduction.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/partials/introduction.md b/docs/partials/introduction.md index 496363e7..c63111bf 100644 --- a/docs/partials/introduction.md +++ b/docs/partials/introduction.md @@ -5,6 +5,8 @@ Messages are exchanged between the client and the server as JSON objects. This protocol is based on the original OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio. As of v5.0.0, backwards compatability with the protocol will not be kept. # Authentication +**Starting with obs-websocket 4.9, authentication is enabled by default and users are encouraged to configure a password on first run.** + `obs-websocket` uses SHA256 to transmit credentials. A request for [`GetAuthRequired`](#getauthrequired) returns two elements: From 98712c7b7169a944c7304ed4ce63ad86418e1f65 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Mon, 1 Feb 2021 05:16:47 -0800 Subject: [PATCH 224/278] Password Prompt: Enable authRequired toggle and highlight password --- src/Config.cpp | 2 +- src/forms/settings-dialog.cpp | 10 ++++++++++ src/forms/settings-dialog.h | 1 + src/obs-websocket.cpp | 7 +++++++ src/obs-websocket.h | 1 + 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Config.cpp b/src/Config.cpp index 60bdddca..3cb9730e 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -339,7 +339,7 @@ void Config::FirstRunPasswordSetup() QMessageBox::StandardButton response = QMessageBox::question(mainWindow, dialogTitle, dialogText); if (response == QMessageBox::Yes) { - ShowSettingsDialog(); + ShowPasswordSetting(); } else { // tell the user they still can set the password later in our settings dialog diff --git a/src/forms/settings-dialog.cpp b/src/forms/settings-dialog.cpp index 338ffb1e..ebe21073 100644 --- a/src/forms/settings-dialog.cpp +++ b/src/forms/settings-dialog.cpp @@ -66,6 +66,16 @@ void SettingsDialog::ToggleShowHide() { setVisible(false); } +void SettingsDialog::PreparePasswordEntry() { + auto conf = GetConfig(); + conf->AuthRequired = true; + ui->authRequired->blockSignals(true); + ui->authRequired->setChecked(true); + ui->authRequired->blockSignals(false); + ui->password->setEnabled(true); + ui->password->setFocus(); +} + void SettingsDialog::AuthCheckboxChanged() { if (ui->authRequired->isChecked()) { ui->password->setEnabled(true); diff --git a/src/forms/settings-dialog.h b/src/forms/settings-dialog.h index 138ae395..f67f1f98 100644 --- a/src/forms/settings-dialog.h +++ b/src/forms/settings-dialog.h @@ -31,6 +31,7 @@ public: ~SettingsDialog(); void showEvent(QShowEvent* event); void ToggleShowHide(); + void PreparePasswordEntry(); private Q_SLOTS: void AuthCheckboxChanged(); diff --git a/src/obs-websocket.cpp b/src/obs-websocket.cpp index 12324906..ef5ea7e2 100644 --- a/src/obs-websocket.cpp +++ b/src/obs-websocket.cpp @@ -122,3 +122,10 @@ void ShowSettingsDialog() { settingsDialog->setVisible(true); } } + +void ShowPasswordSetting() { + if (settingsDialog) { + settingsDialog->PreparePasswordEntry(); + settingsDialog->setVisible(true); + } +} \ No newline at end of file diff --git a/src/obs-websocket.h b/src/obs-websocket.h index 671b22ba..b900d476 100644 --- a/src/obs-websocket.h +++ b/src/obs-websocket.h @@ -56,6 +56,7 @@ ConfigPtr GetConfig(); WSServerPtr GetServer(); WSEventsPtr GetEventsSystem(); void ShowSettingsDialog(); +void ShowPasswordSetting(); #define OBS_WEBSOCKET_VERSION "4.8.0" From e6c2c906779d697f925a670eb682e973da20218a Mon Sep 17 00:00:00 2001 From: tt2468 Date: Mon, 1 Feb 2021 05:33:01 -0800 Subject: [PATCH 225/278] Password Prompt: Improve english strings --- data/locale/en-US.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 9647ac72..0382082f 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -6,7 +6,7 @@ OBSWebsocket.Settings.Password="Password" OBSWebsocket.Settings.LockToIPv4="Lock server to only using IPv4" OBSWebsocket.Settings.DebugEnable="Enable debug logging" OBSWebsocket.Settings.AlertsEnable="Enable System Tray Alerts" -OBSWebsocket.Settings.AuthDisabledWarning="Running obs-websocket with authentication disabled is not recommended. Are you sure you want to proceed with that change?" +OBSWebsocket.Settings.AuthDisabledWarning="Running obs-websocket with authentication disabled is not recommended, as it allows attackers to easily collect sensetive data. Are you sure you want to proceed?" OBSWebsocket.NotifyConnect.Title="New WebSocket connection" OBSWebsocket.NotifyConnect.Message="Client %1 connected" OBSWebsocket.NotifyDisconnect.Title="WebSocket client disconnected" @@ -17,5 +17,5 @@ OBSWebsocket.ProfileChanged.Started="WebSockets server enabled in this profile. OBSWebsocket.ProfileChanged.Stopped="WebSockets server disabled in this profile. Server stopped." OBSWebsocket.ProfileChanged.Restarted="WebSockets server port changed in this profile. Server restarted." OBSWebsocket.InitialPasswordSetup.Title="obs-websocket - Server Password Configuration" -OBSWebsocket.InitialPasswordSetup.Text="It looks like you are running obs-websocket for the first time. Do you want to configure a password now for the WebSockets server?" -OBSWebsocket.InitialPasswordSetup.DismissedText="You can configure a server password later in obs-websocket settings (under the Tools menu of OBS Studio)" +OBSWebsocket.InitialPasswordSetup.Text="It looks like you are running obs-websocket for the first time. Do you want to configure a password now for the WebSockets server? Setting a password is highly recommended." +OBSWebsocket.InitialPasswordSetup.DismissedText="You can configure a server password later in the WebSockets Server Settings. (Under the Tools menu of OBS Studio)" From 10910aa06ddb2d872ce8274eda72aabab5cf4cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Tue, 2 Feb 2021 11:26:22 +0100 Subject: [PATCH 226/278] requests(general): ExecuteBatch WIP --- src/WSRequestHandler.cpp | 4 +- src/WSRequestHandler.h | 2 + src/WSRequestHandler_General.cpp | 45 +++++++++++++++++++ src/WSServer.cpp | 6 +-- src/protocol/OBSRemoteProtocol.cpp | 69 +++++++++++++++++++----------- src/protocol/OBSRemoteProtocol.h | 15 ++++--- src/rpc/RpcResponse.h | 2 +- 7 files changed, 105 insertions(+), 38 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 5c502727..67036179 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -46,6 +46,8 @@ const QHash WSRequestHandler::messageMap{ { "TriggerHotkeyByName", &WSRequestHandler::TriggerHotkeyByName }, { "TriggerHotkeyBySequence", &WSRequestHandler::TriggerHotkeyBySequence }, + { "ExecuteBatch", &WSRequestHandler::ExecuteBatch }, + { "SetCurrentScene", &WSRequestHandler::SetCurrentScene }, { "GetCurrentScene", &WSRequestHandler::GetCurrentScene }, { "GetSceneList", &WSRequestHandler::GetSceneList }, @@ -189,7 +191,7 @@ WSRequestHandler::WSRequestHandler(ConnectionProperties& connProperties) : { } -RpcResponse WSRequestHandler::processRequest(const RpcRequest& request){ +RpcResponse WSRequestHandler::processRequest(const RpcRequest& request) { if (GetConfig()->AuthRequired && (!authNotRequired.contains(request.methodName())) && (!_connProperties.isAuthenticated())) diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 16a64742..6ac744a4 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -64,6 +64,8 @@ class WSRequestHandler { RpcResponse TriggerHotkeyByName(const RpcRequest&); RpcResponse TriggerHotkeyBySequence(const RpcRequest&); + RpcResponse ExecuteBatch(const RpcRequest&); + RpcResponse SetCurrentScene(const RpcRequest&); RpcResponse GetCurrentScene(const RpcRequest&); RpcResponse GetSceneList(const RpcRequest&); diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index d42513bc..fdef74e4 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -7,6 +7,7 @@ #include "Config.h" #include "Utils.h" #include "WSEvents.h" +#include "protocol/OBSRemoteProtocol.h" #define CASE(x) case x: return #x; const char *describe_output_format(int format) { @@ -415,3 +416,47 @@ RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request) return request.success(); } + +/** +* Executes a list of requests sequentially. +* +* @param {Array} `requests` +* +* @return {Array} `results` +* +* @api requests +* @name ExecuteBatch +* @category general +* @since unreleased +*/ +RpcResponse WSRequestHandler::ExecuteBatch(const RpcRequest& request) { + if (!request.hasField("requests")) { + return request.failed("missing request parameters"); + } + + OBSDataArrayAutoRelease results = obs_data_array_create(); + + OBSDataArrayAutoRelease requests = obs_data_get_array(request.parameters(), "requests"); + size_t requestsCount = obs_data_array_count(requests); + for (size_t i = 0; i < requestsCount; i++) { + OBSDataAutoRelease requestData = obs_data_array_item(requests, i); + QString methodName = obs_data_get_string(requestData, "request-type"); + obs_data_unset_user_value(requestData, "request-type"); + obs_data_unset_user_value(requestData, "message-id"); + + // build RpcRequest from json data object + RpcRequest subRequest(QString::Null(), methodName, requestData); + + // execute the request + RpcResponse subResponse = processRequest(subRequest); + + // transform response into json data + OBSDataAutoRelease subResponseData = OBSRemoteProtocol::rpcResponseToJsonData(subResponse); + + obs_data_array_push_back(results, subResponseData); + } + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_array(response, "results", results); + return request.success(response); +} diff --git a/src/WSServer.cpp b/src/WSServer.cpp index 6d155658..8f1ec8bf 100644 --- a/src/WSServer.cpp +++ b/src/WSServer.cpp @@ -133,8 +133,7 @@ void WSServer::stop() void WSServer::broadcast(const RpcEvent& event) { - OBSRemoteProtocol protocol; - std::string message = protocol.encodeEvent(event); + std::string message = OBSRemoteProtocol::encodeEvent(event); if (GetConfig()->DebugEnabled) { blog(LOG_INFO, "Update << '%s'", message.c_str()); @@ -190,8 +189,7 @@ void WSServer::onMessage(connection_hdl hdl, server::message_ptr message) } WSRequestHandler requestHandler(connProperties); - OBSRemoteProtocol protocol; - std::string response = protocol.processMessage(requestHandler, payload); + std::string response = OBSRemoteProtocol::processMessage(requestHandler, payload); if (GetConfig()->DebugEnabled) { blog(LOG_INFO, "Response << '%s'", response.c_str()); diff --git a/src/protocol/OBSRemoteProtocol.cpp b/src/protocol/OBSRemoteProtocol.cpp index a7555b6e..e02be58d 100644 --- a/src/protocol/OBSRemoteProtocol.cpp +++ b/src/protocol/OBSRemoteProtocol.cpp @@ -31,11 +31,15 @@ std::string OBSRemoteProtocol::processMessage(WSRequestHandler& requestHandler, OBSDataAutoRelease data = obs_data_create_from_json(msg); if (!data) { blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg); - return errorResponse(QString::Null(), "invalid JSON payload"); + return jsonDataToString( + errorResponse(nullptr, "invalid JSON payload") + ); } if (!obs_data_has_user_value(data, "request-type") || !obs_data_has_user_value(data, "message-id")) { - return errorResponse(QString::Null(), "missing request parameters"); + return jsonDataToString( + errorResponse(nullptr, "missing request parameters") + ); } QString methodName = obs_data_get_string(data, "request-type"); @@ -49,15 +53,8 @@ std::string OBSRemoteProtocol::processMessage(WSRequestHandler& requestHandler, RpcRequest request(messageId, methodName, params); RpcResponse response = requestHandler.processRequest(request); - OBSData additionalFields = response.additionalFields(); - switch (response.status()) { - case RpcResponse::Status::Ok: - return successResponse(messageId, additionalFields); - case RpcResponse::Status::Error: - return errorResponse(messageId, response.errorMessage(), additionalFields); - } - - return std::string(); + OBSData responseData = rpcResponseToJsonData(response); + return jsonDataToString(responseData); } std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event) @@ -87,33 +84,53 @@ std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event) return std::string(obs_data_get_json(eventData)); } -std::string OBSRemoteProtocol::buildResponse(QString messageId, QString status, obs_data_t* fields) +obs_data_t* OBSRemoteProtocol::rpcResponseToJsonData(const RpcResponse& response) { - OBSDataAutoRelease response = obs_data_create(); - if (!messageId.isNull()) { - obs_data_set_string(response, "message-id", messageId.toUtf8().constData()); + const char* messageId = response.messageId().toUtf8().constData(); + OBSData additionalFields = response.additionalFields(); + switch (response.status()) { + case RpcResponse::Status::Ok: + return successResponse(messageId, additionalFields); + case RpcResponse::Status::Error: + return errorResponse(messageId, response.errorMessage().toUtf8().constData(), additionalFields); + default: + assert(false); } - 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) +obs_data_t* OBSRemoteProtocol::successResponse(const char* messageId, obs_data_t* fields) { return buildResponse(messageId, "ok", fields); } -std::string OBSRemoteProtocol::errorResponse(QString messageId, QString errorMessage, obs_data_t* additionalFields) +obs_data_t* OBSRemoteProtocol::errorResponse(const char* messageId, const char* errorMessage, obs_data_t* additionalFields) { OBSDataAutoRelease fields = obs_data_create(); if (additionalFields) { obs_data_apply(fields, additionalFields); } - obs_data_set_string(fields, "error", errorMessage.toUtf8().constData()); + obs_data_set_string(fields, "error", errorMessage); return buildResponse(messageId, "error", fields); } + +obs_data_t* OBSRemoteProtocol::buildResponse(const char* messageId, const char* status, obs_data_t* fields) +{ + OBSDataAutoRelease response = obs_data_create(); + if (messageId) { + obs_data_set_string(response, "message-id", messageId); + } + obs_data_set_string(response, "status", status); + + if (fields) { + obs_data_apply(response, fields); + } + + obs_data_addref(response); + return response; +} + +std::string OBSRemoteProtocol::jsonDataToString(obs_data_t* data) +{ + std::string responseString = obs_data_get_json(data); + return responseString; +} diff --git a/src/protocol/OBSRemoteProtocol.h b/src/protocol/OBSRemoteProtocol.h index 03d8aa7d..f518a002 100644 --- a/src/protocol/OBSRemoteProtocol.h +++ b/src/protocol/OBSRemoteProtocol.h @@ -20,7 +20,8 @@ with this program. If not, see #include #include -#include + +#include "../rpc/RpcResponse.h" class WSRequestHandler; class RpcEvent; @@ -28,11 +29,13 @@ class RpcEvent; class OBSRemoteProtocol { public: - std::string processMessage(WSRequestHandler& requestHandler, std::string message); - std::string encodeEvent(const RpcEvent& event); + static std::string processMessage(WSRequestHandler& requestHandler, std::string message); + static std::string encodeEvent(const RpcEvent& event); + static obs_data_t* rpcResponseToJsonData(const RpcResponse& response); private: - std::string buildResponse(QString messageId, QString status, obs_data_t* fields = nullptr); - std::string successResponse(QString messageId, obs_data_t* fields = nullptr); - std::string errorResponse(QString messageId, QString errorMessage, obs_data_t* additionalFields = nullptr); + static obs_data_t* successResponse(const char* messageId, obs_data_t* fields = nullptr); + static obs_data_t* errorResponse(const char* messageId, const char* errorMessage, obs_data_t* additionalFields = nullptr); + static obs_data_t* buildResponse(const char* messageId, const char*, obs_data_t* fields = nullptr); + static std::string jsonDataToString(obs_data_t* data); }; diff --git a/src/rpc/RpcResponse.h b/src/rpc/RpcResponse.h index a6381bfd..d0c96d6e 100644 --- a/src/rpc/RpcResponse.h +++ b/src/rpc/RpcResponse.h @@ -36,7 +36,7 @@ public: obs_data_t* additionalFields = nullptr ); - Status status() { + const Status status() const { return _status; } From 20fa14563cc58a62a06a77bdbf9740192e65fcc2 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Mon, 1 Feb 2021 05:35:58 -0800 Subject: [PATCH 227/278] Settings Dialog: Don't need ShowSettingsDialog() --- src/obs-websocket.cpp | 8 +------- src/obs-websocket.h | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/obs-websocket.cpp b/src/obs-websocket.cpp index ef5ea7e2..4fb3e729 100644 --- a/src/obs-websocket.cpp +++ b/src/obs-websocket.cpp @@ -117,15 +117,9 @@ WSEventsPtr GetEventsSystem() { return _eventsSystem; } -void ShowSettingsDialog() { - if (settingsDialog) { - settingsDialog->setVisible(true); - } -} - void ShowPasswordSetting() { if (settingsDialog) { settingsDialog->PreparePasswordEntry(); settingsDialog->setVisible(true); } -} \ No newline at end of file +} diff --git a/src/obs-websocket.h b/src/obs-websocket.h index b900d476..38da66a4 100644 --- a/src/obs-websocket.h +++ b/src/obs-websocket.h @@ -55,7 +55,6 @@ typedef std::shared_ptr WSEventsPtr; ConfigPtr GetConfig(); WSServerPtr GetServer(); WSEventsPtr GetEventsSystem(); -void ShowSettingsDialog(); void ShowPasswordSetting(); #define OBS_WEBSOCKET_VERSION "4.8.0" From e39585befc703615cc0548605378f7b00f5dda3e Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 2 Feb 2021 03:04:03 -0800 Subject: [PATCH 228/278] Password Dialog: Only change form options and not underlying config --- src/forms/settings-dialog.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/forms/settings-dialog.cpp b/src/forms/settings-dialog.cpp index ebe21073..f852186c 100644 --- a/src/forms/settings-dialog.cpp +++ b/src/forms/settings-dialog.cpp @@ -67,8 +67,6 @@ void SettingsDialog::ToggleShowHide() { } void SettingsDialog::PreparePasswordEntry() { - auto conf = GetConfig(); - conf->AuthRequired = true; ui->authRequired->blockSignals(true); ui->authRequired->setChecked(true); ui->authRequired->blockSignals(false); From 3989ea0780c5384288e3fe071db9073687133b26 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 2 Feb 2021 06:16:49 -0800 Subject: [PATCH 229/278] Requests: Add RefreshBrowserSource --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_Sources.cpp | 35 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 5c502727..0d800c56 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -120,6 +120,7 @@ const QHash WSRequestHandler::messageMap{ { "SetAudioMonitorType", &WSRequestHandler::SetAudioMonitorType }, { "GetSourceDefaultSettings", &WSRequestHandler::GetSourceDefaultSettings }, { "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot }, + { "RefreshBrowserSource", &WSRequestHandler::RefreshBrowserSource }, { "GetSourceFilters", &WSRequestHandler::GetSourceFilters }, { "GetSourceFilterInfo", &WSRequestHandler::GetSourceFilterInfo }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 16a64742..d0a8ba25 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -137,6 +137,7 @@ class WSRequestHandler { RpcResponse SetAudioMonitorType(const RpcRequest&); RpcResponse GetSourceDefaultSettings(const RpcRequest&); RpcResponse TakeSourceScreenshot(const RpcRequest&); + RpcResponse RefreshBrowserSource(const RpcRequest&); RpcResponse GetSourceFilters(const RpcRequest&); RpcResponse GetSourceFilterInfo(const RpcRequest&); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 23a31bed..f4035b6a 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1894,3 +1894,38 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) { obs_data_set_string(response, "sourceName", obs_source_get_name(source)); return request.success(response); } + +/** +* Refreshes the specified browser source. +* +* @param {String} `sourceName` Source name. +* +* @api requests +* @name RefreshBrowserSource +* @category sources +* @since 4.9.0 +*/ +RpcResponse WSRequestHandler::RefreshBrowserSource(const RpcRequest& request) +{ + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + if (strcmp(obs_source_get_id(source), "browser_source") != 0) { + return request.failed("specified source is not a browser"); + } + + obs_properties_t *sourceProperties = obs_source_properties(source); + obs_property_t *property = obs_properties_get(sourceProperties, "refreshnocache"); + obs_property_button_clicked(property, source); // This returns a boolean but we ignore it because the browser plugin always returns `false`. + obs_properties_destroy(sourceProperties); + + return request.success(); +} From 04304ecf94a29b3302a24b31838a7a8e2ca2f1ee Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 2 Feb 2021 07:23:45 -0800 Subject: [PATCH 230/278] Requests/Events: Change all unreleased tags to 4.9.0 --- src/WSEvents.cpp | 20 ++++++++++---------- src/WSRequestHandler_General.cpp | 4 ++-- src/WSRequestHandler_MediaControl.cpp | 22 +++++++++++----------- src/WSRequestHandler_Recording.cpp | 2 +- src/WSRequestHandler_ReplayBuffer.cpp | 2 +- src/WSRequestHandler_SceneItems.cpp | 4 ++-- src/WSRequestHandler_Scenes.cpp | 2 +- src/WSRequestHandler_Sources.cpp | 4 ++-- src/WSRequestHandler_Transitions.cpp | 10 +++++----- 9 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 38660b24..b63fe2c3 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -1208,7 +1208,7 @@ void WSEvents::OnSourceMuteStateChange(void* param, calldata_t* data) { * @api events * @name SourceAudioDeactivated * @category sources - * @since unreleased + * @since 4.9.0 */ void WSEvents::OnSourceAudioDeactivated(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1231,7 +1231,7 @@ void WSEvents::OnSourceAudioDeactivated(void* param, calldata_t* data) { * @api events * @name SourceAudioActivated * @category sources - * @since unreleased + * @since 4.9.0 */ void WSEvents::OnSourceAudioActivated(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1498,7 +1498,7 @@ void WSEvents::OnSourceFilterOrderChanged(void* param, calldata_t* data) { * @api events * @name MediaPlaying * @category media - * @since unreleased + * @since 4.9.0 */ void WSEvents::OnMediaPlaying(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1519,7 +1519,7 @@ void WSEvents::OnMediaPlaying(void* param, calldata_t* data) { * @api events * @name MediaPaused * @category media - * @since unreleased + * @since 4.9.0 */ void WSEvents::OnMediaPaused(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1540,7 +1540,7 @@ void WSEvents::OnMediaPaused(void* param, calldata_t* data) { * @api events * @name MediaRestarted * @category media - * @since unreleased + * @since 4.9.0 */ void WSEvents::OnMediaRestarted(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1561,7 +1561,7 @@ void WSEvents::OnMediaRestarted(void* param, calldata_t* data) { * @api events * @name MediaStopped * @category media - * @since unreleased + * @since 4.9.0 */ void WSEvents::OnMediaStopped(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1582,7 +1582,7 @@ void WSEvents::OnMediaStopped(void* param, calldata_t* data) { * @api events * @name MediaNext * @category media - * @since unreleased + * @since 4.9.0 */ void WSEvents::OnMediaNext(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1603,7 +1603,7 @@ void WSEvents::OnMediaNext(void* param, calldata_t* data) { * @api events * @name MediaPrevious * @category media - * @since unreleased + * @since 4.9.0 */ void WSEvents::OnMediaPrevious(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1624,7 +1624,7 @@ void WSEvents::OnMediaPrevious(void* param, calldata_t* data) { * @api events * @name MediaStarted * @category media - * @since unreleased + * @since 4.9.0 */ void WSEvents::OnMediaStarted(void* param, calldata_t* data) { auto self = reinterpret_cast(param); @@ -1645,7 +1645,7 @@ void WSEvents::OnMediaStarted(void* param, calldata_t* data) { * @api events * @name MediaEnded * @category media - * @since unreleased + * @since 4.9.0 */ void WSEvents::OnMediaEnded(void* param, calldata_t* data) { auto self = reinterpret_cast(param); diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index d42513bc..64471766 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -354,7 +354,7 @@ RpcResponse WSRequestHandler::OpenProjector(const RpcRequest& request) { * @api requests * @name TriggerHotkeyByName * @category general -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::TriggerHotkeyByName(const RpcRequest& request) { const char* name = obs_data_get_string(request.parameters(), "hotkeyName"); @@ -380,7 +380,7 @@ RpcResponse WSRequestHandler::TriggerHotkeyByName(const RpcRequest& request) { * @api requests * @name TriggerHotkeyBySequence * @category general -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request) { if (!request.hasField("keyId")) { diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp index 225c40c5..1b7a73bf 100644 --- a/src/WSRequestHandler_MediaControl.cpp +++ b/src/WSRequestHandler_MediaControl.cpp @@ -51,7 +51,7 @@ QString getSourceMediaState(obs_source_t *source) * @api requests * @name PlayPauseMedia * @category media control -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) { if ((!request.hasField("sourceName")) || (!request.hasField("playPause"))) { @@ -81,7 +81,7 @@ RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) { * @api requests * @name RestartMedia * @category media control -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::RestartMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -110,7 +110,7 @@ RpcResponse WSRequestHandler::RestartMedia(const RpcRequest& request) { * @api requests * @name StopMedia * @category media control -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::StopMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -139,7 +139,7 @@ RpcResponse WSRequestHandler::StopMedia(const RpcRequest& request) { * @api requests * @name NextMedia * @category media control -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::NextMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -168,7 +168,7 @@ RpcResponse WSRequestHandler::NextMedia(const RpcRequest& request) { * @api requests * @name PreviousMedia * @category media control -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::PreviousMedia(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -200,7 +200,7 @@ RpcResponse WSRequestHandler::PreviousMedia(const RpcRequest& request) { * @api requests * @name GetMediaDuration * @category media control -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -232,7 +232,7 @@ RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) { * @api requests * @name GetMediaTime * @category media control -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -263,7 +263,7 @@ RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) { * @api requests * @name SetMediaTime * @category media control -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) { if (!request.hasField("sourceName") || !request.hasField("timestamp")) { @@ -295,7 +295,7 @@ RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) { * @api requests * @name ScrubMedia * @category media control -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) { if (!request.hasField("sourceName") || !request.hasField("timeOffset")) { @@ -332,7 +332,7 @@ RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) { * @api requests * @name GetMediaState * @category media control -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) { if (!request.hasField("sourceName")) { @@ -366,7 +366,7 @@ RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) { * @api requests * @name GetMediaSourcesList * @category sources -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::GetMediaSourcesList(const RpcRequest& request) { diff --git a/src/WSRequestHandler_Recording.cpp b/src/WSRequestHandler_Recording.cpp index d0a53f82..0d9ce2c7 100644 --- a/src/WSRequestHandler_Recording.cpp +++ b/src/WSRequestHandler_Recording.cpp @@ -26,7 +26,7 @@ RpcResponse ifCanPause(const RpcRequest& request, std::function c * @api requests * @name GetRecordingStatus * @category recording - * @since unreleased + * @since 4.9.0 */ RpcResponse WSRequestHandler::GetRecordingStatus(const RpcRequest& request) { auto events = GetEventsSystem(); diff --git a/src/WSRequestHandler_ReplayBuffer.cpp b/src/WSRequestHandler_ReplayBuffer.cpp index 2210fcf7..035a41f1 100644 --- a/src/WSRequestHandler_ReplayBuffer.cpp +++ b/src/WSRequestHandler_ReplayBuffer.cpp @@ -13,7 +13,7 @@ * @api requests * @name GetReplayBufferStatus * @category replay buffer - * @since unreleased + * @since 4.9.0 */ RpcResponse WSRequestHandler::GetReplayBufferStatus(const RpcRequest& request) { OBSDataAutoRelease data = obs_data_create(); diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 732fe619..22caad5c 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -17,7 +17,7 @@ * @api requests * @name GetSceneItemList * @category scene items -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) { const char* sceneName = obs_data_get_string(request.parameters(), "sceneName"); @@ -635,7 +635,7 @@ RpcResponse WSRequestHandler::DeleteSceneItem(const RpcRequest& request) { * @api requests * @name AddSceneItem * @category scene items - * @since unreleased + * @since 4.9.0 */ RpcResponse WSRequestHandler::AddSceneItem(const RpcRequest& request) { if (!request.hasField("sceneName") || !request.hasField("sourceName")) { diff --git a/src/WSRequestHandler_Scenes.cpp b/src/WSRequestHandler_Scenes.cpp index 70734c1a..9fd837a4 100644 --- a/src/WSRequestHandler_Scenes.cpp +++ b/src/WSRequestHandler_Scenes.cpp @@ -87,7 +87,7 @@ RpcResponse WSRequestHandler::GetSceneList(const RpcRequest& request) { * @api requests * @name CreateScene * @category scenes - * @since unreleased + * @since 4.9.0 */ RpcResponse WSRequestHandler::CreateScene(const RpcRequest& request) { if (!request.hasField("sceneName")) { diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 23a31bed..0107eb9a 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -32,7 +32,7 @@ bool isTextFreeType2Source(const QString& sourceKind) * @api requests * @name CreateSource * @category sources - * @since unreleased + * @since 4.9.0 */ RpcResponse WSRequestHandler::CreateSource(const RpcRequest& request) { @@ -424,7 +424,7 @@ RpcResponse WSRequestHandler::ToggleMute(const RpcRequest& request) * @api requests * @name GetAudioActive * @category sources -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::GetAudioActive(const RpcRequest& request) { diff --git a/src/WSRequestHandler_Transitions.cpp b/src/WSRequestHandler_Transitions.cpp index 7fd15349..c9daa0eb 100644 --- a/src/WSRequestHandler_Transitions.cpp +++ b/src/WSRequestHandler_Transitions.cpp @@ -129,7 +129,7 @@ RpcResponse WSRequestHandler::GetTransitionDuration(const RpcRequest& request) { * @api requests * @name GetTransitionPosition * @category transitions - * @since unreleased + * @since 4.9.0 */ RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) { OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); @@ -150,7 +150,7 @@ RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) { * @api requests * @name GetTransitionSettings * @category transitions - * @since unreleased + * @since 4.9.0 */ RpcResponse WSRequestHandler::GetTransitionSettings(const RpcRequest& request) { if (!request.hasField("transitionName")) { @@ -181,7 +181,7 @@ RpcResponse WSRequestHandler::GetTransitionSettings(const RpcRequest& request) { * @api requests * @name SetTransitionSettings * @category transitions - * @since unreleased + * @since 4.9.0 */ RpcResponse WSRequestHandler::SetTransitionSettings(const RpcRequest& request) { if (!request.hasField("transitionName") || !request.hasField("transitionSettings")) { @@ -212,7 +212,7 @@ RpcResponse WSRequestHandler::SetTransitionSettings(const RpcRequest& request) { * @api requests * @name ReleaseTBar * @category transitions - * @since unreleased + * @since 4.9.0 */ RpcResponse WSRequestHandler::ReleaseTBar(const RpcRequest& request) { if (!obs_frontend_preview_program_mode_active()) { @@ -240,7 +240,7 @@ RpcResponse WSRequestHandler::ReleaseTBar(const RpcRequest& request) { * @api requests * @name SetTBarPosition * @category transitions - * @since unreleased + * @since 4.9.0 */ RpcResponse WSRequestHandler::SetTBarPosition(const RpcRequest& request) { if (!obs_frontend_preview_program_mode_active()) { From e95d8d36aa590efa50f161a0624fbb7d347fc474 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 2 Feb 2021 07:29:11 -0800 Subject: [PATCH 231/278] Remove deprecated requests --- src/WSRequestHandler.cpp | 3 - src/WSRequestHandler.h | 3 - src/WSRequestHandler_SceneItems.cpp | 149 +--------------------------- 3 files changed, 1 insertion(+), 154 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 5c502727..cbc2bf08 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -57,9 +57,6 @@ const QHash WSRequestHandler::messageMap{ { "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat { "GetSceneItemList", &WSRequestHandler::GetSceneItemList }, { "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender }, - { "SetSceneItemPosition", &WSRequestHandler::SetSceneItemPosition }, - { "SetSceneItemTransform", &WSRequestHandler::SetSceneItemTransform }, - { "SetSceneItemCrop", &WSRequestHandler::SetSceneItemCrop }, { "GetSceneItemProperties", &WSRequestHandler::GetSceneItemProperties }, { "SetSceneItemProperties", &WSRequestHandler::SetSceneItemProperties }, { "ResetSceneItem", &WSRequestHandler::ResetSceneItem }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 16a64742..6d17e139 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -74,9 +74,6 @@ class WSRequestHandler { RpcResponse GetSceneItemList(const RpcRequest&); RpcResponse SetSceneItemRender(const RpcRequest&); - RpcResponse SetSceneItemPosition(const RpcRequest&); - RpcResponse SetSceneItemTransform(const RpcRequest&); - RpcResponse SetSceneItemCrop(const RpcRequest&); RpcResponse GetSceneItemProperties(const RpcRequest&); RpcResponse SetSceneItemProperties(const RpcRequest&); RpcResponse ResetSceneItem(const RpcRequest&); diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 22caad5c..65064f55 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -391,6 +391,7 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) { /** * Show or hide a specified source item in a specified scene. +* Note: This request was previously deprecated, but has been kept because it is better when changing scene item render at a fast rate. * * @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the currently active scene. * @param {String (optional)} `source` Scene Item name. @@ -440,154 +441,6 @@ RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) { return request.success(); } -/** -* Sets the coordinates of a specified source item. -* -* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. -* @param {String} `item` Scene Item name. -* @param {double} `x` X coordinate. -* @param {double} `y` Y coordinate. - -* -* @api requests -* @name SetSceneItemPosition -* @category scene items -* @since 4.0.0 -* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties. -*/ -RpcResponse WSRequestHandler::SetSceneItemPosition(const RpcRequest& request) { - if (!request.hasField("item") || - !request.hasField("x") || !request.hasField("y")) { - return request.failed("missing request parameters"); - } - - QString itemName = obs_data_get_string(request.parameters(), "item"); - if (itemName.isEmpty()) { - return request.failed("invalid request parameters"); - } - - QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); - OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); - if (!scene) { - return request.failed("requested scene could not be found"); - } - - OBSSceneItem sceneItem = Utils::GetSceneItemFromName(scene, itemName); - if (!sceneItem) { - return request.failed("specified scene item doesn't exist"); - } - - vec2 item_position = { 0 }; - item_position.x = obs_data_get_double(request.parameters(), "x"); - item_position.y = obs_data_get_double(request.parameters(), "y"); - obs_sceneitem_set_pos(sceneItem, &item_position); - - return request.success(); -} - -/** -* Set the transform of the specified source item. -* -* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. -* @param {String} `item` Scene Item name. -* @param {double} `x-scale` Width scale factor. -* @param {double} `y-scale` Height scale factor. -* @param {double} `rotation` Source item rotation (in degrees). -* -* @api requests -* @name SetSceneItemTransform -* @category scene items -* @since 4.0.0 -* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties. -*/ -RpcResponse WSRequestHandler::SetSceneItemTransform(const RpcRequest& request) { - if (!request.hasField("item") || - !request.hasField("x-scale") || - !request.hasField("y-scale") || - !request.hasField("rotation")) - { - return request.failed("missing request parameters"); - } - - QString itemName = obs_data_get_string(request.parameters(), "item"); - if (itemName.isEmpty()) { - return request.failed("invalid request parameters"); - } - - QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); - OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); - if (!scene) { - return request.failed("requested scene doesn't exist"); - } - - vec2 scale; - scale.x = obs_data_get_double(request.parameters(), "x-scale"); - scale.y = obs_data_get_double(request.parameters(), "y-scale"); - float rotation = obs_data_get_double(request.parameters(), "rotation"); - - OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); - if (!sceneItem) { - return request.failed("specified scene item doesn't exist"); - } - - obs_sceneitem_defer_update_begin(sceneItem); - - obs_sceneitem_set_scale(sceneItem, &scale); - obs_sceneitem_set_rot(sceneItem, rotation); - - obs_sceneitem_defer_update_end(sceneItem); - - return request.success(); -} - -/** -* Sets the crop coordinates of the specified source item. -* -* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. -* @param {String} `item` Scene Item name. -* @param {int} `top` Pixel position of the top of the source item. -* @param {int} `bottom` Pixel position of the bottom of the source item. -* @param {int} `left` Pixel position of the left of the source item. -* @param {int} `right` Pixel position of the right of the source item. -* -* @api requests -* @name SetSceneItemCrop -* @category scene items -* @since 4.1.0 -* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties. -*/ -RpcResponse WSRequestHandler::SetSceneItemCrop(const RpcRequest& request) { - if (!request.hasField("item")) { - return request.failed("missing request parameters"); - } - - QString itemName = obs_data_get_string(request.parameters(), "item"); - if (itemName.isEmpty()) { - return request.failed("invalid request parameters"); - } - - QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); - OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); - if (!scene) { - return request.failed("requested scene doesn't exist"); - } - - OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); - if (!sceneItem) { - return request.failed("specified scene item doesn't exist"); - } - - struct obs_sceneitem_crop crop = { 0 }; - crop.top = obs_data_get_int(request.parameters(), "top"); - crop.bottom = obs_data_get_int(request.parameters(), "bottom"); - crop.left = obs_data_get_int(request.parameters(), "left"); - crop.right = obs_data_get_int(request.parameters(), "right"); - - obs_sceneitem_set_crop(sceneItem, &crop); - - return request.success(); -} - /** * Deletes a scene item. * From 861cde7e6128eca1281c55ceaab6974f0edd4a94 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 2 Feb 2021 07:39:52 -0800 Subject: [PATCH 232/278] Requests/Events: Use `Note:` for all notes Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah my ocdddddddddddddddddddddddddddddddddddddd --- src/WSEvents.cpp | 2 +- src/WSRequestHandler_Recording.cpp | 2 +- src/WSRequestHandler_Sources.cpp | 2 +- src/WSRequestHandler_Streaming.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index b63fe2c3..2f11ede1 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -1024,7 +1024,7 @@ void WSEvents::OnTransitionBegin(void* param, calldata_t* data) { /** * A transition (other than "cut") has ended. -* Please note that the `from-scene` field is not available in TransitionEnd. +* Note: The `from-scene` field is not available in TransitionEnd. * * @return {String} `name` Transition name. * @return {String} `type` Transition type. diff --git a/src/WSRequestHandler_Recording.cpp b/src/WSRequestHandler_Recording.cpp index 0d9ce2c7..e00e4555 100644 --- a/src/WSRequestHandler_Recording.cpp +++ b/src/WSRequestHandler_Recording.cpp @@ -137,7 +137,7 @@ RpcResponse WSRequestHandler::ResumeRecording(const RpcRequest& request) { * In the current profile, sets the recording folder of the Simple and Advanced * output modes to the specified value. * - * Please note: if `SetRecordingFolder` is called while a recording is + * Note: If `SetRecordingFolder` is called while a recording is * in progress, the change won't be applied immediately and will be * effective on the next recording. * diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 0107eb9a..2442f0cd 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -1726,7 +1726,7 @@ RpcResponse WSRequestHandler::GetSourceDefaultSettings(const RpcRequest& request * Clients can specify `width` and `height` parameters to receive scaled pictures. Aspect ratio is * preserved if only one of these two parameters is specified. * -* @param {String (optional)} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used. +* @param {String (optional)} `sourceName` Source name. Note: Since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used. * @param {String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module) * @param {String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path. * @param {String (optional)} `fileFormat` Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension. diff --git a/src/WSRequestHandler_Streaming.cpp b/src/WSRequestHandler_Streaming.cpp index 141dd8ee..56e9a001 100644 --- a/src/WSRequestHandler_Streaming.cpp +++ b/src/WSRequestHandler_Streaming.cpp @@ -62,7 +62,7 @@ RpcResponse WSRequestHandler::StartStopStreaming(const RpcRequest& request) { * Start streaming. * Will return an `error` if streaming is already active. * - * @param {Object (optional)} `stream` Special stream configuration. Please note: these won't be saved to OBS' configuration. + * @param {Object (optional)} `stream` Special stream configuration. Note: these won't be saved to OBS' configuration. * @param {String (optional)} `stream.type` If specified ensures the type of stream matches the given type (usually 'rtmp_custom' or 'rtmp_common'). If the currently configured stream type does not match the given stream type, all settings must be specified in the `settings` object or an error will occur when starting the stream. * @param {Object (optional)} `stream.metadata` Adds the given object parameters as encoded query string parameters to the 'key' of the RTMP stream. Used to pass data to the RTMP service about the streaming. May be any String, Numeric, or Boolean field. * @param {Object (optional)} `stream.settings` Settings for the stream. From 6bb33fa18b2ae2b0454842cd9aa2ec963e844b73 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Tue, 2 Feb 2021 16:10:24 +0000 Subject: [PATCH 233/278] docs(ci): Update protocol.md - 59e6695 [skip ci] --- docs/generated/comments.json | 41 ++++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 21 ++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index d9251a2a..e44cf177 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -7412,6 +7412,47 @@ "lead": "Takes a picture snapshot of a source and then can either or both: \t- Send it over as a Data URI (base64-encoded data) in the response (by specifying `embedPictureFormat` in the request) \t- Save it to disk (by specifying `saveToFilePath` in the request)", "type": "class", "examples": [] + }, + { + "subheads": [], + "description": "Refreshes the specified browser source.", + "param": "{String} `sourceName` Source name.", + "api": "requests", + "name": "RefreshBrowserSource", + "category": "sources", + "since": "4.9.0", + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + } + ], + "names": [ + { + "name": "", + "description": "RefreshBrowserSource" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "RefreshBrowserSource" + }, + "lead": "", + "type": "class", + "examples": [] } ], "outputs": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index beb289b2..419cb3c4 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -185,6 +185,7 @@ You can also refer to any of the client libraries listed on the [README](README. + [SetAudioMonitorType](#setaudiomonitortype) + [GetSourceDefaultSettings](#getsourcedefaultsettings) + [TakeSourceScreenshot](#takesourcescreenshot) + + [RefreshBrowserSource](#refreshbrowsersource) * [Outputs](#outputs) + [ListOutputs](#listoutputs) + [GetOutputInfo](#getoutputinfo) @@ -2871,6 +2872,26 @@ preserved if only one of these two parameters is specified. | `imageFile` | _String_ | Absolute path to the saved image file (if `saveToFilePath` was specified in the request) | +--- + +### RefreshBrowserSource + + +- Added in v4.9.0 + +Refreshes the specified browser source. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | + + +**Response Items:** + +_No additional response items._ + --- ## Outputs From a18da3e6cdf292c62529a6793e453821c7951f01 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 2 Feb 2021 08:24:59 -0800 Subject: [PATCH 234/278] WSRequestHandler: Organize requests Organize requests into their categories aphabetically, and how they appear in their appriate source files. --- src/WSRequestHandler.cpp | 250 ++++++++++++++++++++------------------- src/WSRequestHandler.h | 246 +++++++++++++++++++------------------- 2 files changed, 250 insertions(+), 246 deletions(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index cbc2bf08..2ab2d4fc 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -29,139 +29,21 @@ using namespace std::placeholders; const QHash WSRequestHandler::messageMap{ + // Category: General { "GetVersion", &WSRequestHandler::GetVersion }, { "GetAuthRequired", &WSRequestHandler::GetAuthRequired }, { "Authenticate", &WSRequestHandler::Authenticate }, - - { "GetStats", &WSRequestHandler::GetStats }, { "SetHeartbeat", &WSRequestHandler::SetHeartbeat }, - { "GetVideoInfo", &WSRequestHandler::GetVideoInfo }, - { "OpenProjector", &WSRequestHandler::OpenProjector }, - { "SetFilenameFormatting", &WSRequestHandler::SetFilenameFormatting }, { "GetFilenameFormatting", &WSRequestHandler::GetFilenameFormatting }, - + { "GetStats", &WSRequestHandler::GetStats }, { "BroadcastCustomMessage", &WSRequestHandler::BroadcastCustomMessage }, - + { "GetVideoInfo", &WSRequestHandler::GetVideoInfo }, + { "OpenProjector", &WSRequestHandler::OpenProjector }, { "TriggerHotkeyByName", &WSRequestHandler::TriggerHotkeyByName }, { "TriggerHotkeyBySequence", &WSRequestHandler::TriggerHotkeyBySequence }, - { "SetCurrentScene", &WSRequestHandler::SetCurrentScene }, - { "GetCurrentScene", &WSRequestHandler::GetCurrentScene }, - { "GetSceneList", &WSRequestHandler::GetSceneList }, - { "CreateScene", &WSRequestHandler::CreateScene }, - { "SetSceneTransitionOverride", &WSRequestHandler::SetSceneTransitionOverride }, - { "RemoveSceneTransitionOverride", &WSRequestHandler::RemoveSceneTransitionOverride }, - { "GetSceneTransitionOverride", &WSRequestHandler::GetSceneTransitionOverride }, - - { "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat - { "GetSceneItemList", &WSRequestHandler::GetSceneItemList }, - { "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender }, - { "GetSceneItemProperties", &WSRequestHandler::GetSceneItemProperties }, - { "SetSceneItemProperties", &WSRequestHandler::SetSceneItemProperties }, - { "ResetSceneItem", &WSRequestHandler::ResetSceneItem }, - { "DeleteSceneItem", &WSRequestHandler::DeleteSceneItem }, - { "AddSceneItem", &WSRequestHandler::AddSceneItem }, - { "DuplicateSceneItem", &WSRequestHandler::DuplicateSceneItem }, - { "ReorderSceneItems", &WSRequestHandler::ReorderSceneItems }, - - { "GetStreamingStatus", &WSRequestHandler::GetStreamingStatus }, - { "StartStopStreaming", &WSRequestHandler::StartStopStreaming }, - { "StartStopRecording", &WSRequestHandler::StartStopRecording }, - - { "StartStreaming", &WSRequestHandler::StartStreaming }, - { "StopStreaming", &WSRequestHandler::StopStreaming }, - - { "GetRecordingStatus", &WSRequestHandler::GetRecordingStatus }, - { "StartRecording", &WSRequestHandler::StartRecording }, - { "StopRecording", &WSRequestHandler::StopRecording }, - { "PauseRecording", &WSRequestHandler::PauseRecording }, - { "ResumeRecording", &WSRequestHandler::ResumeRecording }, - - { "GetReplayBufferStatus", &WSRequestHandler::GetReplayBufferStatus }, - { "StartStopReplayBuffer", &WSRequestHandler::StartStopReplayBuffer }, - { "StartReplayBuffer", &WSRequestHandler::StartReplayBuffer }, - { "StopReplayBuffer", &WSRequestHandler::StopReplayBuffer }, - { "SaveReplayBuffer", &WSRequestHandler::SaveReplayBuffer }, - - { "SetRecordingFolder", &WSRequestHandler::SetRecordingFolder }, - { "GetRecordingFolder", &WSRequestHandler::GetRecordingFolder }, - - { "GetTransitionList", &WSRequestHandler::GetTransitionList }, - { "GetCurrentTransition", &WSRequestHandler::GetCurrentTransition }, - { "SetCurrentTransition", &WSRequestHandler::SetCurrentTransition }, - { "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration }, - { "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration }, - { "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition }, - { "GetTransitionSettings", &WSRequestHandler::GetTransitionSettings }, - { "SetTransitionSettings", &WSRequestHandler::SetTransitionSettings }, - { "ReleaseTBar", &WSRequestHandler::ReleaseTBar }, - { "SetTBarPosition", &WSRequestHandler::SetTBarPosition }, - - { "CreateSource", &WSRequestHandler::CreateSource }, - { "SetVolume", &WSRequestHandler::SetVolume }, - { "GetVolume", &WSRequestHandler::GetVolume }, - { "ToggleMute", &WSRequestHandler::ToggleMute }, - { "SetMute", &WSRequestHandler::SetMute }, - { "GetMute", &WSRequestHandler::GetMute }, - { "GetAudioActive", &WSRequestHandler::GetAudioActive }, - { "SetSourceName", &WSRequestHandler::SetSourceName }, - { "SetSyncOffset", &WSRequestHandler::SetSyncOffset }, - { "GetSyncOffset", &WSRequestHandler::GetSyncOffset }, - { "GetSpecialSources", &WSRequestHandler::GetSpecialSources }, - { "GetSourcesList", &WSRequestHandler::GetSourcesList }, - { "GetSourceTypesList", &WSRequestHandler::GetSourceTypesList }, - { "GetSourceSettings", &WSRequestHandler::GetSourceSettings }, - { "SetSourceSettings", &WSRequestHandler::SetSourceSettings }, - { "GetAudioMonitorType", &WSRequestHandler::GetAudioMonitorType }, - { "SetAudioMonitorType", &WSRequestHandler::SetAudioMonitorType }, - { "GetSourceDefaultSettings", &WSRequestHandler::GetSourceDefaultSettings }, - { "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot }, - - { "GetSourceFilters", &WSRequestHandler::GetSourceFilters }, - { "GetSourceFilterInfo", &WSRequestHandler::GetSourceFilterInfo }, - { "AddFilterToSource", &WSRequestHandler::AddFilterToSource }, - { "RemoveFilterFromSource", &WSRequestHandler::RemoveFilterFromSource }, - { "ReorderSourceFilter", &WSRequestHandler::ReorderSourceFilter }, - { "MoveSourceFilter", &WSRequestHandler::MoveSourceFilter }, - { "SetSourceFilterSettings", &WSRequestHandler::SetSourceFilterSettings }, - { "SetSourceFilterVisibility", &WSRequestHandler::SetSourceFilterVisibility }, - - { "SetCurrentSceneCollection", &WSRequestHandler::SetCurrentSceneCollection }, - { "GetCurrentSceneCollection", &WSRequestHandler::GetCurrentSceneCollection }, - { "ListSceneCollections", &WSRequestHandler::ListSceneCollections }, - - { "SetCurrentProfile", &WSRequestHandler::SetCurrentProfile }, - { "GetCurrentProfile", &WSRequestHandler::GetCurrentProfile }, - { "ListProfiles", &WSRequestHandler::ListProfiles }, - - { "SetStreamSettings", &WSRequestHandler::SetStreamSettings }, - { "GetStreamSettings", &WSRequestHandler::GetStreamSettings }, - { "SaveStreamSettings", &WSRequestHandler::SaveStreamSettings }, - { "SendCaptions", &WSRequestHandler::SendCaptions }, - - { "GetStudioModeStatus", &WSRequestHandler::GetStudioModeStatus }, - { "GetPreviewScene", &WSRequestHandler::GetPreviewScene }, - { "SetPreviewScene", &WSRequestHandler::SetPreviewScene }, - { "TransitionToProgram", &WSRequestHandler::TransitionToProgram }, - { "EnableStudioMode", &WSRequestHandler::EnableStudioMode }, - { "DisableStudioMode", &WSRequestHandler::DisableStudioMode }, - { "ToggleStudioMode", &WSRequestHandler::ToggleStudioMode }, - - { "SetTextGDIPlusProperties", &WSRequestHandler::SetTextGDIPlusProperties }, - { "GetTextGDIPlusProperties", &WSRequestHandler::GetTextGDIPlusProperties }, - - { "SetTextFreetype2Properties", &WSRequestHandler::SetTextFreetype2Properties }, - { "GetTextFreetype2Properties", &WSRequestHandler::GetTextFreetype2Properties }, - - { "GetBrowserSourceProperties", &WSRequestHandler::GetBrowserSourceProperties }, - { "SetBrowserSourceProperties", &WSRequestHandler::SetBrowserSourceProperties }, - - { "ListOutputs", &WSRequestHandler::ListOutputs }, - { "GetOutputInfo", &WSRequestHandler::GetOutputInfo }, - { "StartOutput", &WSRequestHandler::StartOutput }, - { "StopOutput", &WSRequestHandler::StopOutput }, - + // Category: Media Control { "PlayPauseMedia", &WSRequestHandler::PlayPauseMedia }, { "RestartMedia", &WSRequestHandler::RestartMedia }, { "StopMedia", &WSRequestHandler::StopMedia }, @@ -172,7 +54,127 @@ const QHash WSRequestHandler::messageMap{ { "SetMediaTime", &WSRequestHandler::SetMediaTime }, { "ScrubMedia", &WSRequestHandler::ScrubMedia }, { "GetMediaState", &WSRequestHandler::GetMediaState }, - { "GetMediaSourcesList", &WSRequestHandler::GetMediaSourcesList } + { "GetMediaSourcesList", &WSRequestHandler::GetMediaSourcesList }, + + // Category: Outputs + { "ListOutputs", &WSRequestHandler::ListOutputs }, + { "GetOutputInfo", &WSRequestHandler::GetOutputInfo }, + { "StartOutput", &WSRequestHandler::StartOutput }, + { "StopOutput", &WSRequestHandler::StopOutput }, + + // Category: Profiles + { "SetCurrentProfile", &WSRequestHandler::SetCurrentProfile }, + { "GetCurrentProfile", &WSRequestHandler::GetCurrentProfile }, + { "ListProfiles", &WSRequestHandler::ListProfiles }, + + // Category: Recording + { "GetRecordingStatus", &WSRequestHandler::GetRecordingStatus }, + { "StartStopRecording", &WSRequestHandler::StartStopRecording }, + { "StartRecording", &WSRequestHandler::StartRecording }, + { "StopRecording", &WSRequestHandler::StopRecording }, + { "PauseRecording", &WSRequestHandler::PauseRecording }, + { "ResumeRecording", &WSRequestHandler::ResumeRecording }, + { "SetRecordingFolder", &WSRequestHandler::SetRecordingFolder }, + { "GetRecordingFolder", &WSRequestHandler::GetRecordingFolder }, + + // Category: Replay Buffer + { "GetReplayBufferStatus", &WSRequestHandler::GetReplayBufferStatus }, + { "StartStopReplayBuffer", &WSRequestHandler::StartStopReplayBuffer }, + { "StartReplayBuffer", &WSRequestHandler::StartReplayBuffer }, + { "StopReplayBuffer", &WSRequestHandler::StopReplayBuffer }, + { "SaveReplayBuffer", &WSRequestHandler::SaveReplayBuffer }, + + // Category: Scene Collections + { "SetCurrentSceneCollection", &WSRequestHandler::SetCurrentSceneCollection }, + { "GetCurrentSceneCollection", &WSRequestHandler::GetCurrentSceneCollection }, + { "ListSceneCollections", &WSRequestHandler::ListSceneCollections }, + + // Category: Scene Items + { "GetSceneItemList", &WSRequestHandler::GetSceneItemList }, + { "GetSceneItemProperties", &WSRequestHandler::GetSceneItemProperties }, + { "SetSceneItemProperties", &WSRequestHandler::SetSceneItemProperties }, + { "ResetSceneItem", &WSRequestHandler::ResetSceneItem }, + { "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender }, + { "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat TODO: Remove in 5.0.0 + { "DeleteSceneItem", &WSRequestHandler::DeleteSceneItem }, + { "AddSceneItem", &WSRequestHandler::AddSceneItem }, + { "DuplicateSceneItem", &WSRequestHandler::DuplicateSceneItem }, + + // Category: Scenes + { "SetCurrentScene", &WSRequestHandler::SetCurrentScene }, + { "GetCurrentScene", &WSRequestHandler::GetCurrentScene }, + { "GetSceneList", &WSRequestHandler::GetSceneList }, + { "CreateScene", &WSRequestHandler::CreateScene }, + { "ReorderSceneItems", &WSRequestHandler::ReorderSceneItems }, + { "SetSceneTransitionOverride", &WSRequestHandler::SetSceneTransitionOverride }, + { "RemoveSceneTransitionOverride", &WSRequestHandler::RemoveSceneTransitionOverride }, + { "GetSceneTransitionOverride", &WSRequestHandler::GetSceneTransitionOverride }, + + // Category: Sources + { "CreateSource", &WSRequestHandler::CreateSource }, + { "GetSourcesList", &WSRequestHandler::GetSourcesList }, + { "GetSourceTypesList", &WSRequestHandler::GetSourceTypesList }, + { "GetVolume", &WSRequestHandler::GetVolume }, + { "SetVolume", &WSRequestHandler::SetVolume }, + { "GetMute", &WSRequestHandler::GetMute }, + { "SetMute", &WSRequestHandler::SetMute }, + { "ToggleMute", &WSRequestHandler::ToggleMute }, + { "GetAudioActive", &WSRequestHandler::GetAudioActive }, + { "SetSourceName", &WSRequestHandler::SetSourceName }, + { "SetSyncOffset", &WSRequestHandler::SetSyncOffset }, + { "GetSyncOffset", &WSRequestHandler::GetSyncOffset }, + { "GetSourceSettings", &WSRequestHandler::GetSourceSettings }, + { "SetSourceSettings", &WSRequestHandler::SetSourceSettings }, + { "GetTextGDIPlusProperties", &WSRequestHandler::GetTextGDIPlusProperties }, + { "SetTextGDIPlusProperties", &WSRequestHandler::SetTextGDIPlusProperties }, + { "GetTextFreetype2Properties", &WSRequestHandler::GetTextFreetype2Properties }, + { "SetTextFreetype2Properties", &WSRequestHandler::SetTextFreetype2Properties }, + { "GetBrowserSourceProperties", &WSRequestHandler::GetBrowserSourceProperties }, + { "SetBrowserSourceProperties", &WSRequestHandler::SetBrowserSourceProperties }, + { "GetSpecialSources", &WSRequestHandler::GetSpecialSources }, + { "GetSourceFilters", &WSRequestHandler::GetSourceFilters }, + { "GetSourceFilterInfo", &WSRequestHandler::GetSourceFilterInfo }, + { "AddFilterToSource", &WSRequestHandler::AddFilterToSource }, + { "RemoveFilterFromSource", &WSRequestHandler::RemoveFilterFromSource }, + { "ReorderSourceFilter", &WSRequestHandler::ReorderSourceFilter }, + { "MoveSourceFilter", &WSRequestHandler::MoveSourceFilter }, + { "SetSourceFilterSettings", &WSRequestHandler::SetSourceFilterSettings }, + { "SetSourceFilterVisibility", &WSRequestHandler::SetSourceFilterVisibility }, + { "GetAudioMonitorType", &WSRequestHandler::GetAudioMonitorType }, + { "SetAudioMonitorType", &WSRequestHandler::SetAudioMonitorType }, + { "GetSourceDefaultSettings", &WSRequestHandler::GetSourceDefaultSettings }, + { "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot }, + + // Category: Streaming + { "GetStreamingStatus", &WSRequestHandler::GetStreamingStatus }, + { "StartStopStreaming", &WSRequestHandler::StartStopStreaming }, + { "StartStreaming", &WSRequestHandler::StartStreaming }, + { "StopStreaming", &WSRequestHandler::StopStreaming }, + { "SetStreamSettings", &WSRequestHandler::SetStreamSettings }, + { "GetStreamSettings", &WSRequestHandler::GetStreamSettings }, + { "SaveStreamSettings", &WSRequestHandler::SaveStreamSettings }, + { "SendCaptions", &WSRequestHandler::SendCaptions }, + + // Category: Studio Mode + { "GetStudioModeStatus", &WSRequestHandler::GetStudioModeStatus }, + { "GetPreviewScene", &WSRequestHandler::GetPreviewScene }, + { "SetPreviewScene", &WSRequestHandler::SetPreviewScene }, + { "TransitionToProgram", &WSRequestHandler::TransitionToProgram }, + { "EnableStudioMode", &WSRequestHandler::EnableStudioMode }, + { "DisableStudioMode", &WSRequestHandler::DisableStudioMode }, + { "ToggleStudioMode", &WSRequestHandler::ToggleStudioMode }, + + // Category: Transitions + { "GetTransitionList", &WSRequestHandler::GetTransitionList }, + { "GetCurrentTransition", &WSRequestHandler::GetCurrentTransition }, + { "SetCurrentTransition", &WSRequestHandler::SetCurrentTransition }, + { "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration }, + { "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration }, + { "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition }, + { "GetTransitionSettings", &WSRequestHandler::GetTransitionSettings }, + { "SetTransitionSettings", &WSRequestHandler::SetTransitionSettings }, + { "ReleaseTBar", &WSRequestHandler::ReleaseTBar }, + { "SetTBarPosition", &WSRequestHandler::SetTBarPosition } }; const QSet WSRequestHandler::authNotRequired { diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 6d17e139..40207a4f 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -47,138 +47,21 @@ class WSRequestHandler { static const QHash messageMap; static const QSet authNotRequired; + // Category: General RpcResponse GetVersion(const RpcRequest&); RpcResponse GetAuthRequired(const RpcRequest&); RpcResponse Authenticate(const RpcRequest&); - - RpcResponse GetStats(const RpcRequest&); RpcResponse SetHeartbeat(const RpcRequest&); - RpcResponse GetVideoInfo(const RpcRequest&); - RpcResponse OpenProjector(const RpcRequest&); - RpcResponse SetFilenameFormatting(const RpcRequest&); RpcResponse GetFilenameFormatting(const RpcRequest&); - + RpcResponse GetStats(const RpcRequest&); RpcResponse BroadcastCustomMessage(const RpcRequest&); - + RpcResponse GetVideoInfo(const RpcRequest&); + RpcResponse OpenProjector(const RpcRequest&); RpcResponse TriggerHotkeyByName(const RpcRequest&); RpcResponse TriggerHotkeyBySequence(const RpcRequest&); - RpcResponse SetCurrentScene(const RpcRequest&); - RpcResponse GetCurrentScene(const RpcRequest&); - RpcResponse GetSceneList(const RpcRequest&); - RpcResponse CreateScene(const RpcRequest&); - RpcResponse SetSceneTransitionOverride(const RpcRequest&); - RpcResponse RemoveSceneTransitionOverride(const RpcRequest&); - RpcResponse GetSceneTransitionOverride(const RpcRequest&); - - RpcResponse GetSceneItemList(const RpcRequest&); - RpcResponse SetSceneItemRender(const RpcRequest&); - RpcResponse GetSceneItemProperties(const RpcRequest&); - RpcResponse SetSceneItemProperties(const RpcRequest&); - RpcResponse ResetSceneItem(const RpcRequest&); - RpcResponse DuplicateSceneItem(const RpcRequest&); - RpcResponse DeleteSceneItem(const RpcRequest&); - RpcResponse AddSceneItem(const RpcRequest&); - RpcResponse ReorderSceneItems(const RpcRequest&); - - RpcResponse GetStreamingStatus(const RpcRequest&); - RpcResponse StartStopStreaming(const RpcRequest&); - RpcResponse StartStopRecording(const RpcRequest&); - - RpcResponse StartStreaming(const RpcRequest&); - RpcResponse StopStreaming(const RpcRequest&); - - RpcResponse GetRecordingStatus(const RpcRequest&); - RpcResponse StartRecording(const RpcRequest&); - RpcResponse StopRecording(const RpcRequest&); - RpcResponse PauseRecording(const RpcRequest&); - RpcResponse ResumeRecording(const RpcRequest&); - - RpcResponse GetReplayBufferStatus(const RpcRequest&); - RpcResponse StartStopReplayBuffer(const RpcRequest&); - RpcResponse StartReplayBuffer(const RpcRequest&); - RpcResponse StopReplayBuffer(const RpcRequest&); - RpcResponse SaveReplayBuffer(const RpcRequest&); - - RpcResponse SetRecordingFolder(const RpcRequest&); - RpcResponse GetRecordingFolder(const RpcRequest&); - - RpcResponse GetTransitionList(const RpcRequest&); - RpcResponse GetCurrentTransition(const RpcRequest&); - RpcResponse SetCurrentTransition(const RpcRequest&); - RpcResponse SetTransitionDuration(const RpcRequest&); - RpcResponse GetTransitionDuration(const RpcRequest&); - RpcResponse GetTransitionPosition(const RpcRequest&); - RpcResponse GetTransitionSettings(const RpcRequest&); - RpcResponse SetTransitionSettings(const RpcRequest&); - RpcResponse ReleaseTBar(const RpcRequest&); - RpcResponse SetTBarPosition(const RpcRequest&); - - RpcResponse CreateSource(const RpcRequest&); - RpcResponse SetVolume(const RpcRequest&); - RpcResponse GetVolume(const RpcRequest&); - RpcResponse ToggleMute(const RpcRequest&); - RpcResponse SetMute(const RpcRequest&); - RpcResponse GetMute(const RpcRequest&); - RpcResponse GetAudioActive(const RpcRequest&); - RpcResponse SetSourceName(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 GetAudioMonitorType(const RpcRequest&); - RpcResponse SetAudioMonitorType(const RpcRequest&); - RpcResponse GetSourceDefaultSettings(const RpcRequest&); - RpcResponse TakeSourceScreenshot(const RpcRequest&); - - RpcResponse GetSourceFilters(const RpcRequest&); - RpcResponse GetSourceFilterInfo(const RpcRequest&); - RpcResponse AddFilterToSource(const RpcRequest&); - RpcResponse RemoveFilterFromSource(const RpcRequest&); - RpcResponse ReorderSourceFilter(const RpcRequest&); - RpcResponse MoveSourceFilter(const RpcRequest&); - RpcResponse SetSourceFilterSettings(const RpcRequest&); - RpcResponse SetSourceFilterVisibility(const RpcRequest&); - - RpcResponse SetCurrentSceneCollection(const RpcRequest&); - RpcResponse GetCurrentSceneCollection(const RpcRequest&); - RpcResponse ListSceneCollections(const RpcRequest&); - - RpcResponse SetCurrentProfile(const RpcRequest&); - RpcResponse GetCurrentProfile(const RpcRequest&); - RpcResponse ListProfiles(const RpcRequest&); - - RpcResponse SetStreamSettings(const RpcRequest&); - RpcResponse GetStreamSettings(const RpcRequest&); - RpcResponse SaveStreamSettings(const RpcRequest&); - RpcResponse SendCaptions(const RpcRequest&); - - RpcResponse GetStudioModeStatus(const RpcRequest&); - RpcResponse GetPreviewScene(const RpcRequest&); - RpcResponse SetPreviewScene(const RpcRequest&); - RpcResponse TransitionToProgram(const RpcRequest&); - RpcResponse EnableStudioMode(const RpcRequest&); - RpcResponse DisableStudioMode(const RpcRequest&); - RpcResponse ToggleStudioMode(const RpcRequest&); - - RpcResponse SetTextGDIPlusProperties(const RpcRequest&); - RpcResponse GetTextGDIPlusProperties(const RpcRequest&); - - RpcResponse SetTextFreetype2Properties(const RpcRequest&); - RpcResponse GetTextFreetype2Properties(const RpcRequest&); - - RpcResponse SetBrowserSourceProperties(const RpcRequest&); - RpcResponse GetBrowserSourceProperties(const RpcRequest&); - - RpcResponse ListOutputs(const RpcRequest&); - RpcResponse GetOutputInfo(const RpcRequest&); - RpcResponse StartOutput(const RpcRequest&); - RpcResponse StopOutput(const RpcRequest&); - + // Category: Media Control RpcResponse PlayPauseMedia(const RpcRequest&); RpcResponse RestartMedia(const RpcRequest&); RpcResponse StopMedia(const RpcRequest&); @@ -190,4 +73,123 @@ class WSRequestHandler { RpcResponse ScrubMedia(const RpcRequest&); RpcResponse GetMediaState(const RpcRequest&); RpcResponse GetMediaSourcesList(const RpcRequest&); + + // Category: Outputs + RpcResponse ListOutputs(const RpcRequest&); + RpcResponse GetOutputInfo(const RpcRequest&); + RpcResponse StartOutput(const RpcRequest&); + RpcResponse StopOutput(const RpcRequest&); + + // Category: Profiles + RpcResponse SetCurrentProfile(const RpcRequest&); + RpcResponse GetCurrentProfile(const RpcRequest&); + RpcResponse ListProfiles(const RpcRequest&); + + // Category: Recording + RpcResponse GetRecordingStatus(const RpcRequest&); + RpcResponse StartStopRecording(const RpcRequest&); + RpcResponse StartRecording(const RpcRequest&); + RpcResponse StopRecording(const RpcRequest&); + RpcResponse PauseRecording(const RpcRequest&); + RpcResponse ResumeRecording(const RpcRequest&); + RpcResponse SetRecordingFolder(const RpcRequest&); + RpcResponse GetRecordingFolder(const RpcRequest&); + + // Category: Replay Buffer + RpcResponse GetReplayBufferStatus(const RpcRequest&); + RpcResponse StartStopReplayBuffer(const RpcRequest&); + RpcResponse StartReplayBuffer(const RpcRequest&); + RpcResponse StopReplayBuffer(const RpcRequest&); + RpcResponse SaveReplayBuffer(const RpcRequest&); + + // Category: Scene Collections + RpcResponse SetCurrentSceneCollection(const RpcRequest&); + RpcResponse GetCurrentSceneCollection(const RpcRequest&); + RpcResponse ListSceneCollections(const RpcRequest&); + + // Category: Scene Items + RpcResponse GetSceneItemList(const RpcRequest&); + RpcResponse GetSceneItemProperties(const RpcRequest&); + RpcResponse SetSceneItemProperties(const RpcRequest&); + RpcResponse ResetSceneItem(const RpcRequest&); + RpcResponse SetSceneItemRender(const RpcRequest&); + RpcResponse DeleteSceneItem(const RpcRequest&); + RpcResponse AddSceneItem(const RpcRequest&); + RpcResponse DuplicateSceneItem(const RpcRequest&); + + // Category: Scenes + RpcResponse SetCurrentScene(const RpcRequest&); + RpcResponse GetCurrentScene(const RpcRequest&); + RpcResponse GetSceneList(const RpcRequest&); + RpcResponse CreateScene(const RpcRequest&); + RpcResponse ReorderSceneItems(const RpcRequest&); + RpcResponse SetSceneTransitionOverride(const RpcRequest&); + RpcResponse RemoveSceneTransitionOverride(const RpcRequest&); + RpcResponse GetSceneTransitionOverride(const RpcRequest&); + + // Category: Sources + RpcResponse CreateSource(const RpcRequest&); + RpcResponse GetSourcesList(const RpcRequest&); + RpcResponse GetSourceTypesList(const RpcRequest&); + RpcResponse GetVolume(const RpcRequest&); + RpcResponse SetVolume(const RpcRequest&); + RpcResponse GetMute(const RpcRequest&); + RpcResponse SetMute(const RpcRequest&); + RpcResponse ToggleMute(const RpcRequest&); + RpcResponse GetAudioActive(const RpcRequest&); + RpcResponse SetSourceName(const RpcRequest&); + RpcResponse SetSyncOffset(const RpcRequest&); + RpcResponse GetSyncOffset(const RpcRequest&); + RpcResponse GetSourceSettings(const RpcRequest&); + RpcResponse SetSourceSettings(const RpcRequest&); + RpcResponse GetTextGDIPlusProperties(const RpcRequest&); + RpcResponse SetTextGDIPlusProperties(const RpcRequest&); + RpcResponse GetTextFreetype2Properties(const RpcRequest&); + RpcResponse SetTextFreetype2Properties(const RpcRequest&); + RpcResponse GetBrowserSourceProperties(const RpcRequest&); + RpcResponse SetBrowserSourceProperties(const RpcRequest&); + RpcResponse GetSpecialSources(const RpcRequest&); + RpcResponse GetSourceFilters(const RpcRequest&); + RpcResponse GetSourceFilterInfo(const RpcRequest&); + RpcResponse AddFilterToSource(const RpcRequest&); + RpcResponse RemoveFilterFromSource(const RpcRequest&); + RpcResponse ReorderSourceFilter(const RpcRequest&); + RpcResponse MoveSourceFilter(const RpcRequest&); + RpcResponse SetSourceFilterSettings(const RpcRequest&); + RpcResponse SetSourceFilterVisibility(const RpcRequest&); + RpcResponse GetAudioMonitorType(const RpcRequest&); + RpcResponse SetAudioMonitorType(const RpcRequest&); + RpcResponse GetSourceDefaultSettings(const RpcRequest&); + RpcResponse TakeSourceScreenshot(const RpcRequest&); + + // Category: Streaming + RpcResponse GetStreamingStatus(const RpcRequest&); + RpcResponse StartStopStreaming(const RpcRequest&); + RpcResponse StartStreaming(const RpcRequest&); + RpcResponse StopStreaming(const RpcRequest&); + RpcResponse SetStreamSettings(const RpcRequest&); + RpcResponse GetStreamSettings(const RpcRequest&); + RpcResponse SaveStreamSettings(const RpcRequest&); + RpcResponse SendCaptions(const RpcRequest&); + + // Category: Studio Mode + RpcResponse GetStudioModeStatus(const RpcRequest&); + RpcResponse GetPreviewScene(const RpcRequest&); + RpcResponse SetPreviewScene(const RpcRequest&); + RpcResponse TransitionToProgram(const RpcRequest&); + RpcResponse EnableStudioMode(const RpcRequest&); + RpcResponse DisableStudioMode(const RpcRequest&); + RpcResponse ToggleStudioMode(const RpcRequest&); + + // Category: Transitions + RpcResponse GetTransitionList(const RpcRequest&); + RpcResponse GetCurrentTransition(const RpcRequest&); + RpcResponse SetCurrentTransition(const RpcRequest&); + RpcResponse SetTransitionDuration(const RpcRequest&); + RpcResponse GetTransitionDuration(const RpcRequest&); + RpcResponse GetTransitionPosition(const RpcRequest&); + RpcResponse GetTransitionSettings(const RpcRequest&); + RpcResponse SetTransitionSettings(const RpcRequest&); + RpcResponse ReleaseTBar(const RpcRequest&); + RpcResponse SetTBarPosition(const RpcRequest&); }; From 87cd36673ed8fa59f2a6f47decc4279996d45f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 3 Feb 2021 04:16:04 +0100 Subject: [PATCH 235/278] OBSRemoteProtocol: fix memory leak --- src/protocol/OBSRemoteProtocol.cpp | 7 +++---- src/protocol/OBSRemoteProtocol.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/protocol/OBSRemoteProtocol.cpp b/src/protocol/OBSRemoteProtocol.cpp index e02be58d..87693add 100644 --- a/src/protocol/OBSRemoteProtocol.cpp +++ b/src/protocol/OBSRemoteProtocol.cpp @@ -53,7 +53,7 @@ std::string OBSRemoteProtocol::processMessage(WSRequestHandler& requestHandler, RpcRequest request(messageId, methodName, params); RpcResponse response = requestHandler.processRequest(request); - OBSData responseData = rpcResponseToJsonData(response); + OBSDataAutoRelease responseData = rpcResponseToJsonData(response); return jsonDataToString(responseData); } @@ -115,7 +115,7 @@ obs_data_t* OBSRemoteProtocol::errorResponse(const char* messageId, const char* obs_data_t* OBSRemoteProtocol::buildResponse(const char* messageId, const char* status, obs_data_t* fields) { - OBSDataAutoRelease response = obs_data_create(); + obs_data_t* response = obs_data_create(); if (messageId) { obs_data_set_string(response, "message-id", messageId); } @@ -125,11 +125,10 @@ obs_data_t* OBSRemoteProtocol::buildResponse(const char* messageId, const char* obs_data_apply(response, fields); } - obs_data_addref(response); return response; } -std::string OBSRemoteProtocol::jsonDataToString(obs_data_t* data) +std::string OBSRemoteProtocol::jsonDataToString(OBSDataAutoRelease data) { std::string responseString = obs_data_get_json(data); return responseString; diff --git a/src/protocol/OBSRemoteProtocol.h b/src/protocol/OBSRemoteProtocol.h index f518a002..354ca514 100644 --- a/src/protocol/OBSRemoteProtocol.h +++ b/src/protocol/OBSRemoteProtocol.h @@ -37,5 +37,5 @@ private: static obs_data_t* successResponse(const char* messageId, obs_data_t* fields = nullptr); static obs_data_t* errorResponse(const char* messageId, const char* errorMessage, obs_data_t* additionalFields = nullptr); static obs_data_t* buildResponse(const char* messageId, const char*, obs_data_t* fields = nullptr); - static std::string jsonDataToString(obs_data_t* data); + static std::string jsonDataToString(OBSDataAutoRelease data); }; From 5b100d15d72f9439e74c42cd6fb4b14f7c2805ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 3 Feb 2021 05:31:53 +0100 Subject: [PATCH 236/278] OBSRemoteProtocol: fix missing message id --- src/protocol/OBSRemoteProtocol.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/protocol/OBSRemoteProtocol.cpp b/src/protocol/OBSRemoteProtocol.cpp index 87693add..13ca1591 100644 --- a/src/protocol/OBSRemoteProtocol.cpp +++ b/src/protocol/OBSRemoteProtocol.cpp @@ -86,7 +86,9 @@ std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event) obs_data_t* OBSRemoteProtocol::rpcResponseToJsonData(const RpcResponse& response) { - const char* messageId = response.messageId().toUtf8().constData(); + QByteArray messageIdBytes = response.messageId().toUtf8(); + const char* messageId = messageIdBytes.constData(); + OBSData additionalFields = response.additionalFields(); switch (response.status()) { case RpcResponse::Status::Ok: @@ -96,6 +98,8 @@ obs_data_t* OBSRemoteProtocol::rpcResponseToJsonData(const RpcResponse& response default: assert(false); } + + return nullptr; } obs_data_t* OBSRemoteProtocol::successResponse(const char* messageId, obs_data_t* fields) From fe2e87074a045d5fdec6891e1d687c40e9f28313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 3 Feb 2021 05:33:08 +0100 Subject: [PATCH 237/278] requests(ExecuteBatch): handle message-id in sub-requests --- src/WSRequestHandler_General.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index fdef74e4..31d66d30 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -440,12 +440,13 @@ RpcResponse WSRequestHandler::ExecuteBatch(const RpcRequest& request) { size_t requestsCount = obs_data_array_count(requests); for (size_t i = 0; i < requestsCount; i++) { OBSDataAutoRelease requestData = obs_data_array_item(requests, i); + QString messageId = obs_data_get_string(requestData, "message-id"); QString methodName = obs_data_get_string(requestData, "request-type"); obs_data_unset_user_value(requestData, "request-type"); obs_data_unset_user_value(requestData, "message-id"); // build RpcRequest from json data object - RpcRequest subRequest(QString::Null(), methodName, requestData); + RpcRequest subRequest(messageId, methodName, requestData); // execute the request RpcResponse subResponse = processRequest(subRequest); From f4465e2e9b3b1c49a32172a740e59461ca0fec02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 3 Feb 2021 05:47:53 +0100 Subject: [PATCH 238/278] requests(ExecuteBatch): documentation --- src/WSRequestHandler_General.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index 31d66d30..dc6610d6 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -418,16 +418,16 @@ RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request) } /** -* Executes a list of requests sequentially. +* Executes a list of requests sequentially (one-by-one on the same thread). * -* @param {Array} `requests` +* @param {Array} `requests` Array of batch requests. They have the same fields as a traditional request object: `message-id`, `request-type` and the method-specific parameters. * -* @return {Array} `results` +* @return {Array} `results` Batch requests results, ordered sequentially. * * @api requests * @name ExecuteBatch * @category general -* @since unreleased +* @since 4.9.0 */ RpcResponse WSRequestHandler::ExecuteBatch(const RpcRequest& request) { if (!request.hasField("requests")) { From 3847e5d6af433819278373ba1a321046402db167 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 4 Feb 2021 05:40:01 -0800 Subject: [PATCH 239/278] Revert "Remove deprecated requests" This reverts commit e95d8d36aa590efa50f161a0624fbb7d347fc474. --- src/WSRequestHandler.cpp | 3 + src/WSRequestHandler.h | 3 + src/WSRequestHandler_SceneItems.cpp | 149 +++++++++++++++++++++++++++- 3 files changed, 154 insertions(+), 1 deletion(-) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index c1c615c5..31d0cf90 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -95,6 +95,9 @@ const QHash WSRequestHandler::messageMap{ { "SetSceneItemProperties", &WSRequestHandler::SetSceneItemProperties }, { "ResetSceneItem", &WSRequestHandler::ResetSceneItem }, { "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender }, + { "SetSceneItemPosition", &WSRequestHandler::SetSceneItemPosition }, + { "SetSceneItemTransform", &WSRequestHandler::SetSceneItemTransform }, + { "SetSceneItemCrop", &WSRequestHandler::SetSceneItemCrop }, { "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat TODO: Remove in 5.0.0 { "DeleteSceneItem", &WSRequestHandler::DeleteSceneItem }, { "AddSceneItem", &WSRequestHandler::AddSceneItem }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 1dd90382..54d982ba 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -113,6 +113,9 @@ class WSRequestHandler { RpcResponse SetSceneItemProperties(const RpcRequest&); RpcResponse ResetSceneItem(const RpcRequest&); RpcResponse SetSceneItemRender(const RpcRequest&); + RpcResponse SetSceneItemPosition(const RpcRequest&); + RpcResponse SetSceneItemTransform(const RpcRequest&); + RpcResponse SetSceneItemCrop(const RpcRequest&); RpcResponse DeleteSceneItem(const RpcRequest&); RpcResponse AddSceneItem(const RpcRequest&); RpcResponse DuplicateSceneItem(const RpcRequest&); diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 65064f55..22caad5c 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -391,7 +391,6 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) { /** * Show or hide a specified source item in a specified scene. -* Note: This request was previously deprecated, but has been kept because it is better when changing scene item render at a fast rate. * * @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the currently active scene. * @param {String (optional)} `source` Scene Item name. @@ -441,6 +440,154 @@ RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) { return request.success(); } +/** +* Sets the coordinates of a specified source item. +* +* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. +* @param {String} `item` Scene Item name. +* @param {double} `x` X coordinate. +* @param {double} `y` Y coordinate. + +* +* @api requests +* @name SetSceneItemPosition +* @category scene items +* @since 4.0.0 +* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties. +*/ +RpcResponse WSRequestHandler::SetSceneItemPosition(const RpcRequest& request) { + if (!request.hasField("item") || + !request.hasField("x") || !request.hasField("y")) { + return request.failed("missing request parameters"); + } + + QString itemName = obs_data_get_string(request.parameters(), "item"); + if (itemName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); + OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); + if (!scene) { + return request.failed("requested scene could not be found"); + } + + OBSSceneItem sceneItem = Utils::GetSceneItemFromName(scene, itemName); + if (!sceneItem) { + return request.failed("specified scene item doesn't exist"); + } + + vec2 item_position = { 0 }; + item_position.x = obs_data_get_double(request.parameters(), "x"); + item_position.y = obs_data_get_double(request.parameters(), "y"); + obs_sceneitem_set_pos(sceneItem, &item_position); + + return request.success(); +} + +/** +* Set the transform of the specified source item. +* +* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. +* @param {String} `item` Scene Item name. +* @param {double} `x-scale` Width scale factor. +* @param {double} `y-scale` Height scale factor. +* @param {double} `rotation` Source item rotation (in degrees). +* +* @api requests +* @name SetSceneItemTransform +* @category scene items +* @since 4.0.0 +* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties. +*/ +RpcResponse WSRequestHandler::SetSceneItemTransform(const RpcRequest& request) { + if (!request.hasField("item") || + !request.hasField("x-scale") || + !request.hasField("y-scale") || + !request.hasField("rotation")) + { + return request.failed("missing request parameters"); + } + + QString itemName = obs_data_get_string(request.parameters(), "item"); + if (itemName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); + OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); + if (!scene) { + return request.failed("requested scene doesn't exist"); + } + + vec2 scale; + scale.x = obs_data_get_double(request.parameters(), "x-scale"); + scale.y = obs_data_get_double(request.parameters(), "y-scale"); + float rotation = obs_data_get_double(request.parameters(), "rotation"); + + OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); + if (!sceneItem) { + return request.failed("specified scene item doesn't exist"); + } + + obs_sceneitem_defer_update_begin(sceneItem); + + obs_sceneitem_set_scale(sceneItem, &scale); + obs_sceneitem_set_rot(sceneItem, rotation); + + obs_sceneitem_defer_update_end(sceneItem); + + return request.success(); +} + +/** +* Sets the crop coordinates of the specified source item. +* +* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene. +* @param {String} `item` Scene Item name. +* @param {int} `top` Pixel position of the top of the source item. +* @param {int} `bottom` Pixel position of the bottom of the source item. +* @param {int} `left` Pixel position of the left of the source item. +* @param {int} `right` Pixel position of the right of the source item. +* +* @api requests +* @name SetSceneItemCrop +* @category scene items +* @since 4.1.0 +* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties. +*/ +RpcResponse WSRequestHandler::SetSceneItemCrop(const RpcRequest& request) { + if (!request.hasField("item")) { + return request.failed("missing request parameters"); + } + + QString itemName = obs_data_get_string(request.parameters(), "item"); + if (itemName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); + OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); + if (!scene) { + return request.failed("requested scene doesn't exist"); + } + + OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); + if (!sceneItem) { + return request.failed("specified scene item doesn't exist"); + } + + struct obs_sceneitem_crop crop = { 0 }; + crop.top = obs_data_get_int(request.parameters(), "top"); + crop.bottom = obs_data_get_int(request.parameters(), "bottom"); + crop.left = obs_data_get_int(request.parameters(), "left"); + crop.right = obs_data_get_int(request.parameters(), "right"); + + obs_sceneitem_set_crop(sceneItem, &crop); + + return request.success(); +} + /** * Deletes a scene item. * From ed23aba0ace3ca23a4376d8cbe9026aff6a36587 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 4 Feb 2021 07:15:37 -0800 Subject: [PATCH 240/278] Requests: Improve documentation of `ExecuteBatch` --- src/WSRequestHandler_General.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index dc6610d6..ec9bda58 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -420,9 +420,14 @@ RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request) /** * Executes a list of requests sequentially (one-by-one on the same thread). * -* @param {Array} `requests` Array of batch requests. They have the same fields as a traditional request object: `message-id`, `request-type` and the method-specific parameters. +* @param {Array} `requests` Array of requests to perform. Executed in order. +* @param {String} `requests.*.request-type` Request type. Eg. `GetVersion`. +* @param {String} `requests.*.message-id` ID of the individual request. Can be any string and not required to be unique. * * @return {Array} `results` Batch requests results, ordered sequentially. +* @return {String} `results.*.message-id` ID of the individual request which was originally provided by the client. +* @return {String} `results.*.status` Status response as string. Either `ok` or `error`. +* @return {String (Optional)} `results.*.error` Error message accompanying an `error` status. * * @api requests * @name ExecuteBatch From 6d3aa3a828ea3655f927fccadb5bcda967e55223 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 4 Feb 2021 07:37:27 -0800 Subject: [PATCH 241/278] Requests: Add `abortOnFail` to ExecuteBatch request We do not currently have atomicy in this request, as it would be incredibly difficult to add, but this is at least useful for avoiding further data corruption in the case that there is a malformed request and multiple requests depend on the success of the previous one. --- src/WSRequestHandler_General.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index ec9bda58..6be43fc8 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -422,7 +422,8 @@ RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request) * * @param {Array} `requests` Array of requests to perform. Executed in order. * @param {String} `requests.*.request-type` Request type. Eg. `GetVersion`. -* @param {String} `requests.*.message-id` ID of the individual request. Can be any string and not required to be unique. +* @param {String (Optional)} `requests.*.message-id` ID of the individual request. Can be any string and not required to be unique. Defaults to empty string if not specified. +* @param {boolean (Optional)} `abortOnFail` Stop processing batch requests if one returns a failure. * * @return {Array} `results` Batch requests results, ordered sequentially. * @return {String} `results.*.message-id` ID of the individual request which was originally provided by the client. @@ -439,6 +440,8 @@ RpcResponse WSRequestHandler::ExecuteBatch(const RpcRequest& request) { return request.failed("missing request parameters"); } + bool abortOnFail = obs_data_get_bool(request.parameters(), "abortOnFail"); + OBSDataArrayAutoRelease results = obs_data_array_create(); OBSDataArrayAutoRelease requests = obs_data_get_array(request.parameters(), "requests"); @@ -460,6 +463,10 @@ RpcResponse WSRequestHandler::ExecuteBatch(const RpcRequest& request) { OBSDataAutoRelease subResponseData = OBSRemoteProtocol::rpcResponseToJsonData(subResponse); obs_data_array_push_back(results, subResponseData); + + // if told to abort on fail and a failure occurs, stop request processing and return the progress + if (abortOnFail && (subResponse.status() == RpcResponse::Status::Error)) + break; } OBSDataAutoRelease response = obs_data_create(); From 8d034f53a46f019d07f4ee3548b4b94ffdc2e5d5 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 4 Feb 2021 07:47:36 -0800 Subject: [PATCH 242/278] Chore: Bump version to 4.9.0 --- CI/macos/obs-websocket.pkgproj | 2 +- CMakeLists.txt | 2 +- docs/partials/introduction.md | 2 +- installer/installer.iss | 2 +- src/obs-websocket.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CI/macos/obs-websocket.pkgproj b/CI/macos/obs-websocket.pkgproj index e3b175e4..7c574505 100644 --- a/CI/macos/obs-websocket.pkgproj +++ b/CI/macos/obs-websocket.pkgproj @@ -518,7 +518,7 @@ OVERWRITE_PERMISSIONS VERSION - 4.8.0 + 4.9.0 PROJECT_COMMENTS diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b6f2550..0de5f1f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.5) -project(obs-websocket VERSION 4.8.0) +project(obs-websocket VERSION 4.9.0) set(CMAKE_PREFIX_PATH "${QTDIR}") set(CMAKE_INCLUDE_CURRENT_DIR ON) diff --git a/docs/partials/introduction.md b/docs/partials/introduction.md index 496363e7..db7781bf 100644 --- a/docs/partials/introduction.md +++ b/docs/partials/introduction.md @@ -1,4 +1,4 @@ -# obs-websocket 4.8.0 protocol reference +# obs-websocket 4.9.0 protocol reference # General Introduction Messages are exchanged between the client and the server as JSON objects. diff --git a/installer/installer.iss b/installer/installer.iss index e94ff579..0109c12d 100644 --- a/installer/installer.iss +++ b/installer/installer.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "obs-websocket" -#define MyAppVersion "4.8.0" +#define MyAppVersion "4.9.0" #define MyAppPublisher "Stephane Lepin" #define MyAppURL "http://github.com/Palakis/obs-websocket" diff --git a/src/obs-websocket.h b/src/obs-websocket.h index 5f118d28..7f139e50 100644 --- a/src/obs-websocket.h +++ b/src/obs-websocket.h @@ -56,6 +56,6 @@ ConfigPtr GetConfig(); WSServerPtr GetServer(); WSEventsPtr GetEventsSystem(); -#define OBS_WEBSOCKET_VERSION "4.8.0" +#define OBS_WEBSOCKET_VERSION "4.9.0" #define blog(level, msg, ...) blog(level, "[obs-websocket] " msg, ##__VA_ARGS__) From 73d93e476f4e2803c2be1b4fcf2a9f700937a227 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Thu, 4 Feb 2021 15:51:04 +0000 Subject: [PATCH 243/278] docs(ci): Update protocol.md - 80d8286 [skip ci] --- docs/generated/comments.json | 89 ++++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 30 ++++++++++++ 2 files changed, 119 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index e44cf177..0349b437 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -4300,6 +4300,95 @@ "lead": "", "type": "class", "examples": [] + }, + { + "subheads": [], + "description": "Executes a list of requests sequentially (one-by-one on the same thread).", + "param": [ + "{Array} `requests` Array of requests to perform. Executed in order.", + "{String} `requests.*.request-type` Request type. Eg. `GetVersion`.", + "{String (Optional)} `requests.*.message-id` ID of the individual request. Can be any string and not required to be unique. Defaults to empty string if not specified.", + "{boolean (Optional)} `abortOnFail` Stop processing batch requests if one returns a failure." + ], + "return": [ + "{Array} `results` Batch requests results, ordered sequentially.", + "{String} `results.*.message-id` ID of the individual request which was originally provided by the client.", + "{String} `results.*.status` Status response as string. Either `ok` or `error`.", + "{String (Optional)} `results.*.error` Error message accompanying an `error` status." + ], + "api": "requests", + "name": "ExecuteBatch", + "category": "general", + "since": "4.9.0", + "returns": [ + { + "type": "Array", + "name": "results", + "description": "Batch requests results, ordered sequentially." + }, + { + "type": "String", + "name": "results.*.message-id", + "description": "ID of the individual request which was originally provided by the client." + }, + { + "type": "String", + "name": "results.*.status", + "description": "Status response as string. Either `ok` or `error`." + }, + { + "type": "String (Optional)", + "name": "results.*.error", + "description": "Error message accompanying an `error` status." + } + ], + "params": [ + { + "type": "Array", + "name": "requests", + "description": "Array of requests to perform. Executed in order." + }, + { + "type": "String", + "name": "requests.*.request-type", + "description": "Request type. Eg. `GetVersion`." + }, + { + "type": "String (Optional)", + "name": "requests.*.message-id", + "description": "ID of the individual request. Can be any string and not required to be unique. Defaults to empty string if not specified." + }, + { + "type": "boolean (Optional)", + "name": "abortOnFail", + "description": "Stop processing batch requests if one returns a failure." + } + ], + "names": [ + { + "name": "", + "description": "ExecuteBatch" + } + ], + "categories": [ + { + "name": "", + "description": "general" + } + ], + "sinces": [ + { + "name": "", + "description": "4.9.0" + } + ], + "heading": { + "level": 2, + "text": "ExecuteBatch" + }, + "lead": "", + "type": "class", + "examples": [] } ], "media control": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 419cb3c4..c0f6ccff 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -139,6 +139,7 @@ You can also refer to any of the client libraries listed on the [README](README. + [OpenProjector](#openprojector) + [TriggerHotkeyByName](#triggerhotkeybyname) + [TriggerHotkeyBySequence](#triggerhotkeybysequence) + + [ExecuteBatch](#executebatch) * [Media Control](#media-control) + [PlayPauseMedia](#playpausemedia) + [RestartMedia](#restartmedia) @@ -1745,6 +1746,35 @@ Executes hotkey routine, identified by bound combination of keys. A single key c _No additional response items._ +--- + +### ExecuteBatch + + +- Added in v4.9.0 + +Executes a list of requests sequentially (one-by-one on the same thread). + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `requests` | _Array<Object>_ | Array of requests to perform. Executed in order. | +| `requests.*.request-type` | _String_ | Request type. Eg. `GetVersion`. | +| `requests.*.message-id` | _String (Optional)_ | ID of the individual request. Can be any string and not required to be unique. Defaults to empty string if not specified. | +| `abortOnFail` | _boolean (Optional)_ | Stop processing batch requests if one returns a failure. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `results` | _Array<Object>_ | Batch requests results, ordered sequentially. | +| `results.*.message-id` | _String_ | ID of the individual request which was originally provided by the client. | +| `results.*.status` | _String_ | Status response as string. Either `ok` or `error`. | +| `results.*.error` | _String (Optional)_ | Error message accompanying an `error` status. | + + --- ## Media Control From b6ee6e9e22ec2ec12fc164e0616fab27145358b8 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Thu, 4 Feb 2021 15:53:51 +0000 Subject: [PATCH 244/278] docs(ci): Update protocol.md - 1891f62 [skip ci] --- docs/generated/protocol.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index c0f6ccff..c4ab350b 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -7,6 +7,8 @@ Messages are exchanged between the client and the server as JSON objects. This protocol is based on the original OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio. As of v5.0.0, backwards compatability with the protocol will not be kept. # Authentication +**Starting with obs-websocket 4.9, authentication is enabled by default and users are encouraged to configure a password on first run.** + `obs-websocket` uses SHA256 to transmit credentials. A request for [`GetAuthRequired`](#getauthrequired) returns two elements: From 6217f009fdee1ec8ec7bd99eee3975a6501e9af3 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Thu, 4 Feb 2021 21:53:47 +0000 Subject: [PATCH 245/278] docs(ci): Update protocol.md - 4647312 [skip ci] --- docs/generated/comments.json | 152 +++++++++++++++++------------------ docs/generated/protocol.md | 80 +++++++++--------- 2 files changed, 116 insertions(+), 116 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 0349b437..42c8baec 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -917,7 +917,7 @@ }, { "subheads": [], - "description": "A transition (other than \"cut\") has ended.\nPlease note that the `from-scene` field is not available in TransitionEnd.", + "description": "A transition (other than \"cut\") has ended.\nNote: The `from-scene` field is not available in TransitionEnd.", "return": [ "{String} `name` Transition name.", "{String} `type` Transition type.", @@ -2212,7 +2212,7 @@ "api": "events", "name": "SourceAudioDeactivated", "category": "sources", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -2235,7 +2235,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -2253,7 +2253,7 @@ "api": "events", "name": "SourceAudioActivated", "category": "sources", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -2276,7 +2276,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -2708,7 +2708,7 @@ "api": "events", "name": "MediaPlaying", "category": "media", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -2736,7 +2736,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -2757,7 +2757,7 @@ "api": "events", "name": "MediaPaused", "category": "media", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -2785,7 +2785,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -2806,7 +2806,7 @@ "api": "events", "name": "MediaRestarted", "category": "media", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -2834,7 +2834,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -2855,7 +2855,7 @@ "api": "events", "name": "MediaStopped", "category": "media", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -2883,7 +2883,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -2904,7 +2904,7 @@ "api": "events", "name": "MediaNext", "category": "media", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -2932,7 +2932,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -2953,7 +2953,7 @@ "api": "events", "name": "MediaPrevious", "category": "media", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -2981,7 +2981,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -3002,7 +3002,7 @@ "api": "events", "name": "MediaStarted", "category": "media", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -3030,7 +3030,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -3051,7 +3051,7 @@ "api": "events", "name": "MediaEnded", "category": "media", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -3079,7 +3079,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4194,7 +4194,7 @@ "api": "requests", "name": "TriggerHotkeyByName", "category": "general", - "since": "unreleased", + "since": "4.9.0", "params": [ { "type": "String", @@ -4217,7 +4217,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4242,7 +4242,7 @@ "api": "requests", "name": "TriggerHotkeyBySequence", "category": "general", - "since": "unreleased", + "since": "4.9.0", "params": [ { "type": "String", @@ -4290,7 +4290,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4402,7 +4402,7 @@ "api": "requests", "name": "PlayPauseMedia", "category": "media control", - "since": "unreleased", + "since": "4.9.0", "params": [ { "type": "String", @@ -4430,7 +4430,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4448,7 +4448,7 @@ "api": "requests", "name": "RestartMedia", "category": "media control", - "since": "unreleased", + "since": "4.9.0", "params": [ { "type": "String", @@ -4471,7 +4471,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4489,7 +4489,7 @@ "api": "requests", "name": "StopMedia", "category": "media control", - "since": "unreleased", + "since": "4.9.0", "params": [ { "type": "String", @@ -4512,7 +4512,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4530,7 +4530,7 @@ "api": "requests", "name": "NextMedia", "category": "media control", - "since": "unreleased", + "since": "4.9.0", "params": [ { "type": "String", @@ -4553,7 +4553,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4571,7 +4571,7 @@ "api": "requests", "name": "PreviousMedia", "category": "media control", - "since": "unreleased", + "since": "4.9.0", "params": [ { "type": "String", @@ -4594,7 +4594,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4613,7 +4613,7 @@ "api": "requests", "name": "GetMediaDuration", "category": "media control", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "int", @@ -4643,7 +4643,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4662,7 +4662,7 @@ "api": "requests", "name": "GetMediaTime", "category": "media control", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "int", @@ -4692,7 +4692,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4713,7 +4713,7 @@ "api": "requests", "name": "SetMediaTime", "category": "media control", - "since": "unreleased", + "since": "4.9.0", "params": [ { "type": "String", @@ -4741,7 +4741,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4762,7 +4762,7 @@ "api": "requests", "name": "ScrubMedia", "category": "media control", - "since": "unreleased", + "since": "4.9.0", "params": [ { "type": "String", @@ -4790,7 +4790,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4809,7 +4809,7 @@ "api": "requests", "name": "GetMediaState", "category": "media control", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -4839,7 +4839,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4864,7 +4864,7 @@ "api": "requests", "name": "GetMediaSourcesList", "category": "sources", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "Array", @@ -4902,7 +4902,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -4927,7 +4927,7 @@ "api": "requests", "name": "CreateSource", "category": "sources", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "int", @@ -4977,7 +4977,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -5445,7 +5445,7 @@ "api": "requests", "name": "GetAudioActive", "category": "sources", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "boolean", @@ -5475,7 +5475,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -7405,7 +7405,7 @@ "subheads": [], "description": "\n\nAt least `embedPictureFormat` or `saveToFilePath` must be specified.\n\nClients can specify `width` and `height` parameters to receive scaled pictures. Aspect ratio is\npreserved if only one of these two parameters is specified.", "param": [ - "{String (optional)} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used.", + "{String (optional)} `sourceName` Source name. Note: Since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used.", "{String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be \"png\", \"jpg\", \"jpeg\" or \"bmp\" (or any other value supported by Qt's Image module)", "{String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path.", "{String (optional)} `fileFormat` Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension.", @@ -7443,7 +7443,7 @@ { "type": "String (optional)", "name": "sourceName", - "description": "Source name. Note that, since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used." + "description": "Source name. Note: Since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used." }, { "type": "String (optional)", @@ -7872,7 +7872,7 @@ "api": "requests", "name": "GetRecordingStatus", "category": "recording", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "boolean", @@ -7910,7 +7910,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -8088,7 +8088,7 @@ }, { "subheads": [], - "description": "\n\nPlease note: if `SetRecordingFolder` is called while a recording is\nin progress, the change won't be applied immediately and will be\neffective on the next recording.", + "description": "\n\nNote: If `SetRecordingFolder` is called while a recording is\nin progress, the change won't be applied immediately and will be\neffective on the next recording.", "param": "{String} `rec-folder` Path of the recording folder.", "api": "requests", "name": "SetRecordingFolder", @@ -8177,7 +8177,7 @@ "api": "requests", "name": "GetReplayBufferStatus", "category": "replay buffer", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "boolean", @@ -8200,7 +8200,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -8485,7 +8485,7 @@ "api": "requests", "name": "GetSceneItemList", "category": "scene items", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "String", @@ -8540,7 +8540,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -9340,7 +9340,7 @@ "api": "requests", "name": "AddSceneItem", "category": "scene items", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "int", @@ -9380,7 +9380,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -9634,7 +9634,7 @@ "api": "requests", "name": "CreateScene", "category": "scenes", - "since": "unreleased", + "since": "4.9.0", "params": [ { "type": "String", @@ -9657,7 +9657,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -9994,7 +9994,7 @@ "subheads": [], "description": "Start streaming.\nWill return an `error` if streaming is already active.", "param": [ - "{Object (optional)} `stream` Special stream configuration. Please note: these won't be saved to OBS' configuration.", + "{Object (optional)} `stream` Special stream configuration. Note: these won't be saved to OBS' configuration.", "{String (optional)} `stream.type` If specified ensures the type of stream matches the given type (usually 'rtmp_custom' or 'rtmp_common'). If the currently configured stream type does not match the given stream type, all settings must be specified in the `settings` object or an error will occur when starting the stream.", "{Object (optional)} `stream.metadata` Adds the given object parameters as encoded query string parameters to the 'key' of the RTMP stream. Used to pass data to the RTMP service about the streaming. May be any String, Numeric, or Boolean field.", "{Object (optional)} `stream.settings` Settings for the stream.", @@ -10012,7 +10012,7 @@ { "type": "Object (optional)", "name": "stream", - "description": "Special stream configuration. Please note: these won't be saved to OBS' configuration." + "description": "Special stream configuration. Note: these won't be saved to OBS' configuration." }, { "type": "String (optional)", @@ -10875,7 +10875,7 @@ "api": "requests", "name": "GetTransitionPosition", "category": "transitions", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "double", @@ -10898,7 +10898,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -10917,7 +10917,7 @@ "api": "requests", "name": "GetTransitionSettings", "category": "transitions", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "Object", @@ -10947,7 +10947,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -10969,7 +10969,7 @@ "api": "requests", "name": "SetTransitionSettings", "category": "transitions", - "since": "unreleased", + "since": "4.9.0", "returns": [ { "type": "Object", @@ -11004,7 +11004,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -11021,7 +11021,7 @@ "api": "requests", "name": "ReleaseTBar", "category": "transitions", - "since": "unreleased", + "since": "4.9.0", "names": [ { "name": "", @@ -11037,7 +11037,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { @@ -11058,7 +11058,7 @@ "api": "requests", "name": "SetTBarPosition", "category": "transitions", - "since": "unreleased", + "since": "4.9.0", "params": [ { "type": "double", @@ -11086,7 +11086,7 @@ "sinces": [ { "name": "", - "description": "unreleased" + "description": "4.9.0" } ], "heading": { diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index c4ab350b..803b3fee 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -1,6 +1,6 @@ -# obs-websocket 4.8.0 protocol reference +# obs-websocket 4.9.0 protocol reference # General Introduction Messages are exchanged between the client and the server as JSON objects. @@ -523,7 +523,7 @@ A transition (other than "cut") has begun. - Added in v4.8.0 A transition (other than "cut") has ended. -Please note that the `from-scene` field is not available in TransitionEnd. +Note: The `from-scene` field is not available in TransitionEnd. **Response Items:** @@ -969,7 +969,7 @@ A source has been muted or unmuted. ### SourceAudioDeactivated -- Unreleased +- Added in v4.9.0 A source has removed audio. @@ -985,7 +985,7 @@ A source has removed audio. ### SourceAudioActivated -- Unreleased +- Added in v4.9.0 A source has added audio. @@ -1133,7 +1133,7 @@ Filters in a source have been reordered. ### MediaPlaying -- Unreleased +- Added in v4.9.0 @@ -1152,7 +1152,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaPaused -- Unreleased +- Added in v4.9.0 @@ -1171,7 +1171,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaRestarted -- Unreleased +- Added in v4.9.0 @@ -1190,7 +1190,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaStopped -- Unreleased +- Added in v4.9.0 @@ -1209,7 +1209,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaNext -- Unreleased +- Added in v4.9.0 @@ -1228,7 +1228,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaPrevious -- Unreleased +- Added in v4.9.0 @@ -1247,7 +1247,7 @@ Note: This event is only emitted when something actively controls the media/VLC ### MediaStarted -- Unreleased +- Added in v4.9.0 @@ -1266,7 +1266,7 @@ Note: These events are emitted by the OBS sources themselves. For example when t ### MediaEnded -- Unreleased +- Added in v4.9.0 @@ -1708,7 +1708,7 @@ _No additional response items._ ### TriggerHotkeyByName -- Unreleased +- Added in v4.9.0 Executes hotkey routine, identified by hotkey unique name @@ -1728,7 +1728,7 @@ _No additional response items._ ### TriggerHotkeyBySequence -- Unreleased +- Added in v4.9.0 Executes hotkey routine, identified by bound combination of keys. A single key combination might trigger multiple hotkey routines depending on user settings @@ -1784,7 +1784,7 @@ Executes a list of requests sequentially (one-by-one on the same thread). ### PlayPauseMedia -- Unreleased +- Added in v4.9.0 Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -1805,7 +1805,7 @@ _No additional response items._ ### RestartMedia -- Unreleased +- Added in v4.9.0 Restart a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -1825,7 +1825,7 @@ _No additional response items._ ### StopMedia -- Unreleased +- Added in v4.9.0 Stop a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -1845,7 +1845,7 @@ _No additional response items._ ### NextMedia -- Unreleased +- Added in v4.9.0 Skip to the next media item in the playlist. Supports only vlc media source (as of OBS v25.0.8) @@ -1865,7 +1865,7 @@ _No additional response items._ ### PreviousMedia -- Unreleased +- Added in v4.9.0 Go to the previous media item in the playlist. Supports only vlc media source (as of OBS v25.0.8) @@ -1885,7 +1885,7 @@ _No additional response items._ ### GetMediaDuration -- Unreleased +- Added in v4.9.0 Get the length of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) Note: For some reason, for the first 5 or so seconds that the media is playing, the total duration can be off by upwards of 50ms. @@ -1909,7 +1909,7 @@ Note: For some reason, for the first 5 or so seconds that the media is playing, ### GetMediaTime -- Unreleased +- Added in v4.9.0 Get the current timestamp of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -1932,7 +1932,7 @@ Get the current timestamp of media in milliseconds. Supports ffmpeg and vlc medi ### SetMediaTime -- Unreleased +- Added in v4.9.0 Set the timestamp of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -1953,7 +1953,7 @@ _No additional response items._ ### ScrubMedia -- Unreleased +- Added in v4.9.0 Scrub media using a supplied offset. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) Note: Due to processing/network delays, this request is not perfect. The processing rate of this request has also not been tested. @@ -1975,7 +1975,7 @@ _No additional response items._ ### GetMediaState -- Unreleased +- Added in v4.9.0 Get the current playing state of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) @@ -2000,7 +2000,7 @@ Get the current playing state of a media source. Supports ffmpeg and vlc media s ### GetMediaSourcesList -- Unreleased +- Added in v4.9.0 List the media state of all media sources (vlc and media source) @@ -2023,7 +2023,7 @@ _No specified parameters._ ### CreateSource -- Unreleased +- Added in v4.9.0 Create a source and add it as a sceneitem to a scene. @@ -2218,7 +2218,7 @@ _No additional response items._ ### GetAudioActive -- Unreleased +- Added in v4.9.0 Get the audio's active status of a specified source. @@ -2886,7 +2886,7 @@ preserved if only one of these two parameters is specified. | Name | Type | Description | | ---- | :---: | ------------| -| `sourceName` | _String (optional)_ | Source name. Note that, since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used. | +| `sourceName` | _String (optional)_ | Source name. Note: Since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used. | | `embedPictureFormat` | _String (optional)_ | Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module) | | `saveToFilePath` | _String (optional)_ | Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path. | | `fileFormat` | _String (optional)_ | Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension. | @@ -3084,7 +3084,7 @@ _No specified parameters._ ### GetRecordingStatus -- Unreleased +- Added in v4.9.0 Get current recording status. @@ -3200,7 +3200,7 @@ _No additional response items._ -Please note: if `SetRecordingFolder` is called while a recording is +Note: If `SetRecordingFolder` is called while a recording is in progress, the change won't be applied immediately and will be effective on the next recording. @@ -3242,7 +3242,7 @@ _No specified parameters._ ### GetReplayBufferStatus -- Unreleased +- Added in v4.9.0 Get the status of the OBS replay buffer. @@ -3401,7 +3401,7 @@ _No specified parameters._ ### GetSceneItemList -- Unreleased +- Added in v4.9.0 Get a list of all scene items in a scene. @@ -3664,7 +3664,7 @@ _No additional response items._ ### AddSceneItem -- Unreleased +- Added in v4.9.0 Creates a scene item in a scene. In other words, this is how you add a source into a scene. @@ -3783,7 +3783,7 @@ _No specified parameters._ ### CreateScene -- Unreleased +- Added in v4.9.0 Create a new scene scene. @@ -3945,7 +3945,7 @@ Will return an `error` if streaming is already active. | Name | Type | Description | | ---- | :---: | ------------| -| `stream` | _Object (optional)_ | Special stream configuration. Please note: these won't be saved to OBS' configuration. | +| `stream` | _Object (optional)_ | Special stream configuration. Note: these won't be saved to OBS' configuration. | | `stream.type` | _String (optional)_ | If specified ensures the type of stream matches the given type (usually 'rtmp_custom' or 'rtmp_common'). If the currently configured stream type does not match the given stream type, all settings must be specified in the `settings` object or an error will occur when starting the stream. | | `stream.metadata` | _Object (optional)_ | Adds the given object parameters as encoded query string parameters to the 'key' of the RTMP stream. Used to pass data to the RTMP service about the streaming. May be any String, Numeric, or Boolean field. | | `stream.settings` | _Object (optional)_ | Settings for the stream. | @@ -4317,7 +4317,7 @@ _No specified parameters._ ### GetTransitionPosition -- Unreleased +- Added in v4.9.0 Get the position of the current transition. @@ -4337,7 +4337,7 @@ _No specified parameters._ ### GetTransitionSettings -- Unreleased +- Added in v4.9.0 Get the current settings of a transition @@ -4360,7 +4360,7 @@ Get the current settings of a transition ### SetTransitionSettings -- Unreleased +- Added in v4.9.0 Change the current settings of a transition @@ -4384,7 +4384,7 @@ Change the current settings of a transition ### ReleaseTBar -- Unreleased +- Added in v4.9.0 Release the T-Bar (like a user releasing their mouse button after moving it). *YOU MUST CALL THIS if you called `SetTBarPosition` with the `release` parameter set to `false`.* @@ -4402,7 +4402,7 @@ _No additional response items._ ### SetTBarPosition -- Unreleased +- Added in v4.9.0 From 9e554ce527ecb4027e771fe98e0884e2c596d632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 10 Feb 2021 13:32:13 +0100 Subject: [PATCH 246/278] ci(macos): update InstallAppleCertificate action to version 2 --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bccfedb9..a42fc561 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -160,7 +160,7 @@ jobs: - script: ./CI/build-macos.sh displayName: 'Build obs-websocket' - - task: InstallAppleCertificate@1 + - task: InstallAppleCertificate@2 displayName: 'Install release signing certificates' condition: eq(variables['isReleaseMode'], true) inputs: From d45d98d536319819ee27cf2f83b6f7198dae6232 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 10 Feb 2021 16:09:07 -0800 Subject: [PATCH 247/278] CI: Fix tag detection --- CI/package-ubuntu.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CI/package-ubuntu.sh b/CI/package-ubuntu.sh index 5f6998d6..517e0054 100755 --- a/CI/package-ubuntu.sh +++ b/CI/package-ubuntu.sh @@ -5,8 +5,9 @@ set -e export GIT_HASH=$(git rev-parse --short HEAD) export PKG_VERSION="1-$GIT_HASH-$BRANCH_SHORT_NAME-git" -if [[ "$BRANCH_FULL_NAME" =~ "^refs/tags/" ]]; then +if [[ $BRANCH_FULL_NAME =~ ^refs/tags/ ]]; then export PKG_VERSION="$BRANCH_SHORT_NAME" + echo "[obs-websocket] Branch is a tag. Setting version to $PKG_VERSION." fi cd ./build From 9bbfb73622addd843828e339261d29b5cb83f67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Wed, 24 Feb 2021 19:06:08 +0100 Subject: [PATCH 248/278] chore: safety-checks before accessing the result of GetConfig() --- src/Config.cpp | 4 ++++ src/Utils.cpp | 6 +++--- src/WSRequestHandler.cpp | 3 ++- src/WSRequestHandler_General.cpp | 7 ++++--- src/WSServer.cpp | 10 ++++++---- src/forms/settings-dialog.cpp | 24 ++++++++++++++---------- 6 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/Config.cpp b/src/Config.cpp index 3cb9730e..f3c615e6 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -323,6 +323,10 @@ void Config::FirstRunPasswordSetup() // check if the password is already set auto config = GetConfig(); + if (!config) { + return; + } + if (!(config->Secret.isEmpty()) && !(config->Salt.isEmpty())) { return; } diff --git a/src/Utils.cpp b/src/Utils.cpp index c67281e6..01ccdcac 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -509,9 +509,9 @@ QSystemTrayIcon* Utils::GetTrayIcon() { return reinterpret_cast(systemTray); } -void Utils::SysTrayNotify(QString text, - QSystemTrayIcon::MessageIcon icon, QString title) { - if (!GetConfig()->AlertsEnabled || +void Utils::SysTrayNotify(QString text, QSystemTrayIcon::MessageIcon icon, QString title) { + auto config = GetConfig(); + if ((config && !config->AlertsEnabled) || !QSystemTrayIcon::isSystemTrayAvailable() || !QSystemTrayIcon::supportsMessages()) { diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 20f9cb73..0ad58204 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -194,7 +194,8 @@ WSRequestHandler::WSRequestHandler(ConnectionProperties& connProperties) : } RpcResponse WSRequestHandler::processRequest(const RpcRequest& request) { - if (GetConfig()->AuthRequired + auto config = GetConfig(); + if ((config && config->AuthRequired) && (!authNotRequired.contains(request.methodName())) && (!_connProperties.isAuthenticated())) { diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index 15d4ba76..39824735 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -115,13 +115,13 @@ RpcResponse WSRequestHandler::GetVersion(const RpcRequest& request) { * @since 0.3 */ RpcResponse WSRequestHandler::GetAuthRequired(const RpcRequest& request) { - bool authRequired = GetConfig()->AuthRequired; + auto config = GetConfig(); + bool authRequired = (config && config->AuthRequired); OBSDataAutoRelease data = obs_data_create(); obs_data_set_bool(data, "authRequired", authRequired); if (authRequired) { - auto config = GetConfig(); obs_data_set_string(data, "challenge", config->SessionChallenge.toUtf8()); obs_data_set_string(data, "salt", @@ -155,7 +155,8 @@ RpcResponse WSRequestHandler::Authenticate(const RpcRequest& request) { return request.failed("auth not specified!"); } - if (GetConfig()->CheckAuth(auth) == false) { + auto config = GetConfig(); + if (!config || (config->CheckAuth(auth) == false)) { return request.failed("Authentication Failed."); } diff --git a/src/WSServer.cpp b/src/WSServer.cpp index 8f1ec8bf..3e6f9934 100644 --- a/src/WSServer.cpp +++ b/src/WSServer.cpp @@ -135,13 +135,14 @@ void WSServer::broadcast(const RpcEvent& event) { std::string message = OBSRemoteProtocol::encodeEvent(event); - if (GetConfig()->DebugEnabled) { + auto config = GetConfig(); + if (config && config->DebugEnabled) { blog(LOG_INFO, "Update << '%s'", message.c_str()); } QMutexLocker locker(&_clMutex); for (connection_hdl hdl : _connections) { - if (GetConfig()->AuthRequired) { + if (config && config->AuthRequired) { bool authenticated = _connectionProperties[hdl].isAuthenticated(); if (!authenticated) { continue; @@ -184,14 +185,15 @@ void WSServer::onMessage(connection_hdl hdl, server::message_ptr message) ConnectionProperties& connProperties = _connectionProperties[hdl]; locker.unlock(); - if (GetConfig()->DebugEnabled) { + auto config = GetConfig(); + if (config && config->DebugEnabled) { blog(LOG_INFO, "Request >> '%s'", payload.c_str()); } WSRequestHandler requestHandler(connProperties); std::string response = OBSRemoteProtocol::processMessage(requestHandler, payload); - if (GetConfig()->DebugEnabled) { + if (config && config->DebugEnabled) { blog(LOG_INFO, "Response << '%s'", response.c_str()); } diff --git a/src/forms/settings-dialog.cpp b/src/forms/settings-dialog.cpp index f852186c..4835a8c4 100644 --- a/src/forms/settings-dialog.cpp +++ b/src/forms/settings-dialog.cpp @@ -43,17 +43,18 @@ SettingsDialog::SettingsDialog(QWidget* parent) : void SettingsDialog::showEvent(QShowEvent* event) { auto conf = GetConfig(); + if (conf) { + ui->serverEnabled->setChecked(conf->ServerEnabled); + ui->serverPort->setValue(conf->ServerPort); + ui->lockToIPv4->setChecked(conf->LockToIPv4); - ui->serverEnabled->setChecked(conf->ServerEnabled); - ui->serverPort->setValue(conf->ServerPort); - ui->lockToIPv4->setChecked(conf->LockToIPv4); + ui->debugEnabled->setChecked(conf->DebugEnabled); + ui->alertsEnabled->setChecked(conf->AlertsEnabled); - ui->debugEnabled->setChecked(conf->DebugEnabled); - ui->alertsEnabled->setChecked(conf->AlertsEnabled); - - ui->authRequired->blockSignals(true); - ui->authRequired->setChecked(conf->AuthRequired); - ui->authRequired->blockSignals(false); + ui->authRequired->blockSignals(true); + ui->authRequired->setChecked(conf->AuthRequired); + ui->authRequired->blockSignals(false); + } ui->password->setText(CHANGE_ME); ui->password->setEnabled(ui->authRequired->isChecked()); @@ -94,6 +95,9 @@ void SettingsDialog::AuthCheckboxChanged() { void SettingsDialog::FormAccepted() { auto conf = GetConfig(); + if (!conf) { + return; + } conf->ServerEnabled = ui->serverEnabled->isChecked(); conf->ServerPort = ui->serverPort->value(); @@ -107,7 +111,7 @@ void SettingsDialog::FormAccepted() { conf->SetPassword(ui->password->text()); } - if (!GetConfig()->Secret.isEmpty()) + if (!conf->Secret.isEmpty()) conf->AuthRequired = true; else conf->AuthRequired = false; From bb0a0acbda226ea26cf223d907a7e6189027b8b9 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 27 Feb 2021 08:52:19 -0800 Subject: [PATCH 249/278] Requests: Add Sleep New request to induce delay in the processing of `ExecuteBatch` --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_General.cpp | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 0ad58204..0eec2529 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -43,6 +43,7 @@ const QHash WSRequestHandler::messageMap{ { "TriggerHotkeyByName", &WSRequestHandler::TriggerHotkeyByName }, { "TriggerHotkeyBySequence", &WSRequestHandler::TriggerHotkeyBySequence }, { "ExecuteBatch", &WSRequestHandler::ExecuteBatch }, + { "Sleep", &WSRequestHandler::Sleep }, // Category: Media Control { "PlayPauseMedia", &WSRequestHandler::PlayPauseMedia }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 15f189e4..82aa5ee3 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -61,6 +61,7 @@ class WSRequestHandler { RpcResponse TriggerHotkeyByName(const RpcRequest&); RpcResponse TriggerHotkeyBySequence(const RpcRequest&); RpcResponse ExecuteBatch(const RpcRequest&); + RpcResponse Sleep(const RpcRequest&); // Category: Media Control RpcResponse PlayPauseMedia(const RpcRequest&); diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index 39824735..300bc5be 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -474,3 +474,24 @@ RpcResponse WSRequestHandler::ExecuteBatch(const RpcRequest& request) { obs_data_set_array(response, "results", results); return request.success(response); } + +/** + * Waits for the specified delay. Designed to be used in `ExecuteBatch` operations. + * + * @param {int} `delayMillis` Delay in milliseconds to wait before continuing. + * + * @api requests + * @name Sleep + * @category general + * @since unreleased + */ +RpcResponse WSRequestHandler::Sleep(const RpcRequest& request) { + if (!request.hasField("delayMillis")) { + return request.failed("missing request parameters"); + } + + long long delayMillis = obs_data_get_int(request.parameters(), "delayMillis"); + std::this_thread::sleep_for(std::chrono::milliseconds(delayMillis)); + + return request.success(); +} From 7e14032d2b68331b965d1bd35f4d3916d7d2aa42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 28 Feb 2021 03:05:42 +0100 Subject: [PATCH 250/278] requests(Sleep): update parameter name --- src/WSRequestHandler_General.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/WSRequestHandler_General.cpp b/src/WSRequestHandler_General.cpp index 300bc5be..b08023d0 100644 --- a/src/WSRequestHandler_General.cpp +++ b/src/WSRequestHandler_General.cpp @@ -476,9 +476,9 @@ RpcResponse WSRequestHandler::ExecuteBatch(const RpcRequest& request) { } /** - * Waits for the specified delay. Designed to be used in `ExecuteBatch` operations. + * Waits for the specified duration. Designed to be used in `ExecuteBatch` operations. * - * @param {int} `delayMillis` Delay in milliseconds to wait before continuing. + * @param {int} `sleepMillis` Delay in milliseconds to wait before continuing. * * @api requests * @name Sleep @@ -486,12 +486,12 @@ RpcResponse WSRequestHandler::ExecuteBatch(const RpcRequest& request) { * @since unreleased */ RpcResponse WSRequestHandler::Sleep(const RpcRequest& request) { - if (!request.hasField("delayMillis")) { + if (!request.hasField("sleepMillis")) { return request.failed("missing request parameters"); } - long long delayMillis = obs_data_get_int(request.parameters(), "delayMillis"); - std::this_thread::sleep_for(std::chrono::milliseconds(delayMillis)); + long long sleepMillis = obs_data_get_int(request.parameters(), "sleepMillis"); + std::this_thread::sleep_for(std::chrono::milliseconds(sleepMillis)); return request.success(); } From 6f0d05605935f4627c3007b2900b550439810c7c Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Sun, 28 Feb 2021 02:17:20 +0000 Subject: [PATCH 251/278] docs(ci): Update protocol.md - 6b05d03 [skip ci] --- docs/generated/comments.json | 41 ++++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 21 ++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 42c8baec..6a89b98e 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -4389,6 +4389,47 @@ "lead": "", "type": "class", "examples": [] + }, + { + "subheads": [], + "description": "Waits for the specified duration. Designed to be used in `ExecuteBatch` operations.", + "param": "{int} `sleepMillis` Delay in milliseconds to wait before continuing.", + "api": "requests", + "name": "Sleep", + "category": "general", + "since": "unreleased", + "params": [ + { + "type": "int", + "name": "sleepMillis", + "description": "Delay in milliseconds to wait before continuing." + } + ], + "names": [ + { + "name": "", + "description": "Sleep" + } + ], + "categories": [ + { + "name": "", + "description": "general" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "Sleep" + }, + "lead": "", + "type": "class", + "examples": [] } ], "media control": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 803b3fee..cb2a0671 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -142,6 +142,7 @@ You can also refer to any of the client libraries listed on the [README](README. + [TriggerHotkeyByName](#triggerhotkeybyname) + [TriggerHotkeyBySequence](#triggerhotkeybysequence) + [ExecuteBatch](#executebatch) + + [Sleep](#sleep) * [Media Control](#media-control) + [PlayPauseMedia](#playpausemedia) + [RestartMedia](#restartmedia) @@ -1777,6 +1778,26 @@ Executes a list of requests sequentially (one-by-one on the same thread). | `results.*.error` | _String (Optional)_ | Error message accompanying an `error` status. | +--- + +### Sleep + + +- Unreleased + +Waits for the specified duration. Designed to be used in `ExecuteBatch` operations. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sleepMillis` | _int_ | Delay in milliseconds to wait before continuing. | + + +**Response Items:** + +_No additional response items._ + --- ## Media Control From 18468c17f2eeada2267cc62c7e52f315f59a1d40 Mon Sep 17 00:00:00 2001 From: VodBox Date: Wed, 3 Mar 2021 19:43:50 +1300 Subject: [PATCH 252/278] Requests: Add scale.filter to Scene Item Properties --- src/Utils.cpp | 23 +++++++++++++++++++++++ src/WSRequestHandler_SceneItems.cpp | 28 ++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index 01ccdcac..3a6266b7 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -52,6 +52,24 @@ obs_bounds_type getBoundsTypeFromName(QString name) { return boundTypeNames.key(name); } +const QHash scaleTypeNames = { + { OBS_SCALE_DISABLE, "OBS_SCALE_DISABLE" }, + { OBS_SCALE_POINT, "OBS_SCALE_POINT" }, + { OBS_SCALE_BICUBIC, "OBS_SCALE_BICUBIC" }, + { OBS_SCALE_BILINEAR, "OBS_SCALE_BILINEAR" }, + { OBS_SCALE_LANCZOS, "OBS_SCALE_LANCZOS" }, + { OBS_SCALE_AREA, "OBS_SCALE_AREA" }, +}; + +QString getScaleNameFromType(obs_scale_type type) { + QString fallback = scaleTypeNames.value(OBS_SCALE_DISABLE); + return scaleTypeNames.value(type, fallback); +} + +obs_scale_type getFilterTypeFromName(QString name) { + return scaleTypeNames.key(name); +} + bool Utils::StringInStringList(char** strings, const char* string) { if (!strings) { return false; @@ -730,6 +748,7 @@ const char* Utils::GetCurrentRecordingFilename() * @property {double} `rotation` The clockwise rotation of the scene item in degrees around the point of alignment. * @property {double} `scale.x` The x-scale factor of the scene item. * @property {double} `scale.y` The y-scale factor of the scene item. + * @property {String} `scale.filter` The scale filter of the source. Can be "OBS_SCALE_DISABLE", "OBS_SCALE_POINT", "OBS_SCALE_BICUBIC", "OBS_SCALE_BILINEAR", "OBS_SCALE_LANCZOS" or "OBS_SCALE_AREA". * @property {int} `crop.top` The number of pixels cropped off the top of the scene item before scaling. * @property {int} `crop.right` The number of pixels cropped off the right of the scene item before scaling. * @property {int} `crop.bottom` The number of pixels cropped off the bottom of the scene item before scaling. @@ -773,12 +792,16 @@ obs_data_t* Utils::GetSceneItemPropertiesData(obs_sceneitem_t* sceneItem) { uint32_t boundsAlignment = obs_sceneitem_get_bounds_alignment(sceneItem); QString boundsTypeName = getBoundsNameFromType(boundsType); + obs_scale_type scaleFilter = obs_sceneitem_get_scale_filter(sceneItem); + QString scaleFilterName = getScaleNameFromType(scaleFilter); + OBSDataAutoRelease posData = obs_data_create(); obs_data_set_double(posData, "x", pos.x); obs_data_set_double(posData, "y", pos.y); obs_data_set_int(posData, "alignment", alignment); OBSDataAutoRelease scaleData = obs_data_create(); + obs_data_set_string(scaleData, "filter", scaleFilterName.toUtf8()); obs_data_set_double(scaleData, "x", scale.x); obs_data_set_double(scaleData, "y", scale.y); diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 22caad5c..23ef6236 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -91,6 +91,7 @@ RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) { * @return {double} `rotation` The clockwise rotation of the item in degrees around the point of alignment. * @return {double} `scale.x` The x-scale factor of the source. * @return {double} `scale.y` The y-scale factor of the source. +* @return {String} `scale.filter` The scale filter of the source. Can be "OBS_SCALE_DISABLE", "OBS_SCALE_POINT", "OBS_SCALE_BICUBIC", "OBS_SCALE_BILINEAR", "OBS_SCALE_LANCZOS" or "OBS_SCALE_AREA". * @return {int} `crop.top` The number of pixels cropped off the top of the source before scaling. * @return {int} `crop.right` The number of pixels cropped off the right of the source before scaling. * @return {int} `crop.bottom` The number of pixels cropped off the bottom of the source before scaling. @@ -156,6 +157,7 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request) * @param {double (optional)} `rotation` The new clockwise rotation of the item in degrees. * @param {double (optional)} `scale.x` The new x scale of the item. * @param {double (optional)} `scale.y` The new y scale of the item. +* @param {String (optional)} `scale.filter` The new scale filter of the source. Can be "OBS_SCALE_DISABLE", "OBS_SCALE_POINT", "OBS_SCALE_BICUBIC", "OBS_SCALE_BILINEAR", "OBS_SCALE_LANCZOS" or "OBS_SCALE_AREA". * @param {int (optional)} `crop.top` The new amount of pixels cropped off the top of the source before scaling. * @param {int (optional)} `crop.bottom` The new amount of pixels cropped off the bottom of the source before scaling. * @param {int (optional)} `crop.left` The new amount of pixels cropped off the left of the source before scaling. @@ -230,12 +232,34 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request) } if (request.hasField("scale")) { + OBSDataAutoRelease reqScale = obs_data_get_obj(params, "scale"); + + if (obs_data_has_user_value(reqScale, "filter")) { + QString newScaleFilter = obs_data_get_string(reqScale, "filter"); + if (newScaleFilter == "OBS_SCALE_DISABLE") { + obs_sceneitem_set_scale_filter(sceneItem, OBS_SCALE_DISABLE); + } + else if (newScaleFilter == "OBS_SCALE_POINT") { + obs_sceneitem_set_scale_filter(sceneItem, OBS_SCALE_POINT); + } + else if (newScaleFilter == "OBS_SCALE_BICUBIC") { + obs_sceneitem_set_scale_filter(sceneItem, OBS_SCALE_BICUBIC); + } + else if (newScaleFilter == "OBS_SCALE_BILINEAR") { + obs_sceneitem_set_scale_filter(sceneItem, OBS_SCALE_BICUBIC); + } + else if (newScaleFilter == "OBS_SCALE_LANCZOS") { + obs_sceneitem_set_scale_filter(sceneItem, OBS_SCALE_LANCZOS); + } + else if (newScaleFilter == "OBS_SCALE_AREA") { + obs_sceneitem_set_scale_filter(sceneItem, OBS_SCALE_AREA); + } + } + vec2 oldScale; obs_sceneitem_get_scale(sceneItem, &oldScale); vec2 newScale = oldScale; - OBSDataAutoRelease reqScale = obs_data_get_obj(params, "scale"); - if (obs_data_has_user_value(reqScale, "x")) { newScale.x = obs_data_get_double(reqScale, "x"); } From b8e693c97d150d1f373edc438e85d91ff2552169 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Wed, 3 Mar 2021 14:50:01 +0000 Subject: [PATCH 253/278] docs(ci): Update protocol.md - 99e66cc [skip ci] --- docs/generated/comments.json | 18 ++++++++++++++++++ docs/generated/protocol.md | 3 +++ 2 files changed, 21 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 6a89b98e..e3922a8e 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -127,6 +127,7 @@ "{double} `rotation` The clockwise rotation of the scene item in degrees around the point of alignment.", "{double} `scale.x` The x-scale factor of the scene item.", "{double} `scale.y` The y-scale factor of the scene item.", + "{String} `scale.filter` The scale filter of the source. Can be \"OBS_SCALE_DISABLE\", \"OBS_SCALE_POINT\", \"OBS_SCALE_BICUBIC\", \"OBS_SCALE_BILINEAR\", \"OBS_SCALE_LANCZOS\" or \"OBS_SCALE_AREA\".", "{int} `crop.top` The number of pixels cropped off the top of the scene item before scaling.", "{int} `crop.right` The number of pixels cropped off the right of the scene item before scaling.", "{int} `crop.bottom` The number of pixels cropped off the bottom of the scene item before scaling.", @@ -175,6 +176,11 @@ "name": "scale.y", "description": "The y-scale factor of the scene item." }, + { + "type": "String", + "name": "scale.filter", + "description": "The scale filter of the source. Can be \"OBS_SCALE_DISABLE\", \"OBS_SCALE_POINT\", \"OBS_SCALE_BICUBIC\", \"OBS_SCALE_BILINEAR\", \"OBS_SCALE_LANCZOS\" or \"OBS_SCALE_AREA\"." + }, { "type": "int", "name": "crop.top", @@ -8610,6 +8616,7 @@ "{double} `rotation` The clockwise rotation of the item in degrees around the point of alignment.", "{double} `scale.x` The x-scale factor of the source.", "{double} `scale.y` The y-scale factor of the source.", + "{String} `scale.filter` The scale filter of the source. Can be \"OBS_SCALE_DISABLE\", \"OBS_SCALE_POINT\", \"OBS_SCALE_BICUBIC\", \"OBS_SCALE_BILINEAR\", \"OBS_SCALE_LANCZOS\" or \"OBS_SCALE_AREA\".", "{int} `crop.top` The number of pixels cropped off the top of the source before scaling.", "{int} `crop.right` The number of pixels cropped off the right of the source before scaling.", "{int} `crop.bottom` The number of pixels cropped off the bottom of the source before scaling.", @@ -8673,6 +8680,11 @@ "name": "scale.y", "description": "The y-scale factor of the source." }, + { + "type": "String", + "name": "scale.filter", + "description": "The scale filter of the source. Can be \"OBS_SCALE_DISABLE\", \"OBS_SCALE_POINT\", \"OBS_SCALE_BICUBIC\", \"OBS_SCALE_BILINEAR\", \"OBS_SCALE_LANCZOS\" or \"OBS_SCALE_AREA\"." + }, { "type": "int", "name": "crop.top", @@ -8821,6 +8833,7 @@ "{double (optional)} `rotation` The new clockwise rotation of the item in degrees.", "{double (optional)} `scale.x` The new x scale of the item.", "{double (optional)} `scale.y` The new y scale of the item.", + "{String (optional)} `scale.filter` The new scale filter of the source. Can be \"OBS_SCALE_DISABLE\", \"OBS_SCALE_POINT\", \"OBS_SCALE_BICUBIC\", \"OBS_SCALE_BILINEAR\", \"OBS_SCALE_LANCZOS\" or \"OBS_SCALE_AREA\".", "{int (optional)} `crop.top` The new amount of pixels cropped off the top of the source before scaling.", "{int (optional)} `crop.bottom` The new amount of pixels cropped off the bottom of the source before scaling.", "{int (optional)} `crop.left` The new amount of pixels cropped off the left of the source before scaling.", @@ -8887,6 +8900,11 @@ "name": "scale.y", "description": "The new y scale of the item." }, + { + "type": "String (optional)", + "name": "scale.filter", + "description": "The new scale filter of the source. Can be \"OBS_SCALE_DISABLE\", \"OBS_SCALE_POINT\", \"OBS_SCALE_BICUBIC\", \"OBS_SCALE_BILINEAR\", \"OBS_SCALE_LANCZOS\" or \"OBS_SCALE_AREA\"." + }, { "type": "int (optional)", "name": "crop.top", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index cb2a0671..4ea8b89e 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -302,6 +302,7 @@ These are complex types, such as `Source` and `Scene`, which are used as argumen | `rotation` | _double_ | The clockwise rotation of the scene item in degrees around the point of alignment. | | `scale.x` | _double_ | The x-scale factor of the scene item. | | `scale.y` | _double_ | The y-scale factor of the scene item. | +| `scale.filter` | _String_ | The scale filter of the source. Can be "OBS_SCALE_DISABLE", "OBS_SCALE_POINT", "OBS_SCALE_BICUBIC", "OBS_SCALE_BILINEAR", "OBS_SCALE_LANCZOS" or "OBS_SCALE_AREA". | | `crop.top` | _int_ | The number of pixels cropped off the top of the scene item before scaling. | | `crop.right` | _int_ | The number of pixels cropped off the right of the scene item before scaling. | | `crop.bottom` | _int_ | The number of pixels cropped off the bottom of the scene item before scaling. | @@ -3477,6 +3478,7 @@ Coordinates are relative to the item's parent (the scene or group it belongs to) | `rotation` | _double_ | The clockwise rotation of the item in degrees around the point of alignment. | | `scale.x` | _double_ | The x-scale factor of the source. | | `scale.y` | _double_ | The y-scale factor of the source. | +| `scale.filter` | _String_ | The scale filter of the source. Can be "OBS_SCALE_DISABLE", "OBS_SCALE_POINT", "OBS_SCALE_BICUBIC", "OBS_SCALE_BILINEAR", "OBS_SCALE_LANCZOS" or "OBS_SCALE_AREA". | | `crop.top` | _int_ | The number of pixels cropped off the top of the source before scaling. | | `crop.right` | _int_ | The number of pixels cropped off the right of the source before scaling. | | `crop.bottom` | _int_ | The number of pixels cropped off the bottom of the source before scaling. | @@ -3520,6 +3522,7 @@ Coordinates are relative to the item's parent (the scene or group it belongs to) | `rotation` | _double (optional)_ | The new clockwise rotation of the item in degrees. | | `scale.x` | _double (optional)_ | The new x scale of the item. | | `scale.y` | _double (optional)_ | The new y scale of the item. | +| `scale.filter` | _String (optional)_ | The new scale filter of the source. Can be "OBS_SCALE_DISABLE", "OBS_SCALE_POINT", "OBS_SCALE_BICUBIC", "OBS_SCALE_BILINEAR", "OBS_SCALE_LANCZOS" or "OBS_SCALE_AREA". | | `crop.top` | _int (optional)_ | The new amount of pixels cropped off the top of the source before scaling. | | `crop.bottom` | _int (optional)_ | The new amount of pixels cropped off the bottom of the source before scaling. | | `crop.left` | _int (optional)_ | The new amount of pixels cropped off the left of the source before scaling. | From 234484216398cf14f9003085a32a76b6ecd231b8 Mon Sep 17 00:00:00 2001 From: "Christopher P. Yarger" Date: Wed, 3 Mar 2021 19:18:57 -0500 Subject: [PATCH 254/278] Requests: Edit PlayPauseMedia (#685) * Request: Edit PlayPauseMedia Removed requirement for boolean paramater from PlayPauseMedia. Changed PlayPauseMedia to toggle play state if there is no boolean paramater. * Docs: Edit PlayPauseMedia This commit also fixes some syntax * will reset * Add missing star to L48 Co-authored-by: tt2468 --- src/WSRequestHandler_MediaControl.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/WSRequestHandler_MediaControl.cpp b/src/WSRequestHandler_MediaControl.cpp index 1b7a73bf..20df9681 100644 --- a/src/WSRequestHandler_MediaControl.cpp +++ b/src/WSRequestHandler_MediaControl.cpp @@ -44,9 +44,10 @@ QString getSourceMediaState(obs_source_t *source) /** * Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +* Note :Leaving out `playPause` toggles the current pause state * * @param {String} `sourceName` Source name. -* @param {boolean} `playPause` Whether to pause or play the source. `false` for play, `true` for pause. +* @param {boolean} `playPause` (optional) Whether to pause or play the source. `false` for play, `true` for pause. * * @api requests * @name PlayPauseMedia @@ -54,7 +55,7 @@ QString getSourceMediaState(obs_source_t *source) * @since 4.9.0 */ RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) { - if ((!request.hasField("sourceName")) || (!request.hasField("playPause"))) { + if (!request.hasField("sourceName")) { return request.failed("missing request parameters"); } @@ -68,8 +69,16 @@ RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) { if (!source) { return request.failed("specified source doesn't exist"); } - - obs_source_media_play_pause(source, playPause); + if (!request.hasField("playPause")) { + if (obs_source_media_get_state(source) == obs_media_state::OBS_MEDIA_STATE_PLAYING) { + obs_source_media_play_pause(source, true); + } else { + obs_source_media_play_pause(source, false); + } + } else { + bool playPause = obs_data_get_bool(request.parameters(), "playPause"); + obs_source_media_play_pause(source, playPause); + } return request.success(); } From 01c898b104b0c05b7caf5514c6ef918c57f66eab Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Thu, 4 Mar 2021 00:19:45 +0000 Subject: [PATCH 255/278] docs(ci): Update protocol.md - 2344842 [skip ci] --- docs/generated/comments.json | 6 +++--- docs/generated/protocol.md | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index e3922a8e..97d76817 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -4441,10 +4441,10 @@ "media control": [ { "subheads": [], - "description": "Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)", + "description": "Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)\nNote :Leaving out `playPause` toggles the current pause state", "param": [ "{String} `sourceName` Source name.", - "{boolean} `playPause` Whether to pause or play the source. `false` for play, `true` for pause." + "{boolean} `playPause` (optional) Whether to pause or play the source. `false` for play, `true` for pause." ], "api": "requests", "name": "PlayPauseMedia", @@ -4459,7 +4459,7 @@ { "type": "boolean", "name": "playPause", - "description": "Whether to pause or play the source. `false` for play, `true` for pause." + "description": "(optional) Whether to pause or play the source. `false` for play, `true` for pause." } ], "names": [ diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 4ea8b89e..1579d919 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -1809,13 +1809,14 @@ _No additional response items._ - Added in v4.9.0 Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8) +Note :Leaving out `playPause` toggles the current pause state **Request Fields:** | Name | Type | Description | | ---- | :---: | ------------| | `sourceName` | _String_ | Source name. | -| `playPause` | _boolean_ | Whether to pause or play the source. `false` for play, `true` for pause. | +| `playPause` | _boolean_ | (optional) Whether to pause or play the source. `false` for play, `true` for pause. | **Response Items:** From 0db1abcd2ecc88cb81004a0fef7530011c611000 Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Fri, 5 Mar 2021 17:00:54 +0100 Subject: [PATCH 256/278] Requests: Add GetSourceActive --- src/WSRequestHandler.cpp | 1 + src/WSRequestHandler.h | 1 + src/WSRequestHandler_Sources.cpp | 34 ++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 0eec2529..3f36b534 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -124,6 +124,7 @@ const QHash WSRequestHandler::messageMap{ { "GetMute", &WSRequestHandler::GetMute }, { "SetMute", &WSRequestHandler::SetMute }, { "ToggleMute", &WSRequestHandler::ToggleMute }, + { "GetSourceActive", &WSRequestHandler::GetSourceActive }, { "GetAudioActive", &WSRequestHandler::GetAudioActive }, { "SetSourceName", &WSRequestHandler::SetSourceName }, { "SetSyncOffset", &WSRequestHandler::SetSyncOffset }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 82aa5ee3..1c697fc7 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -141,6 +141,7 @@ class WSRequestHandler { RpcResponse GetMute(const RpcRequest&); RpcResponse SetMute(const RpcRequest&); RpcResponse ToggleMute(const RpcRequest&); + RpcResponse GetSourceActive(const RpcRequest&); RpcResponse GetAudioActive(const RpcRequest&); RpcResponse SetSourceName(const RpcRequest&); RpcResponse SetSyncOffset(const RpcRequest&); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 35f6e56b..6c65944f 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -414,6 +414,40 @@ RpcResponse WSRequestHandler::ToggleMute(const RpcRequest& request) return request.success(); } +/** +* Get the source's active status of a specified source (if it is showing in the final mix). +* +* @param {String} `sourceName` Source name. +* +* @return {boolean} `sourceActive` Source active status of the source. +* +* @api requests +* @name GetSourceActive +* @category sources +* @since unreleased +*/ +RpcResponse WSRequestHandler::GetSourceActive(const RpcRequest& request) +{ + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_bool(response, "sourceActive", obs_source_active(source)); + + return request.success(response); +} + /** * Get the audio's active status of a specified source. * From 452a307b33860b18f6a7387ecf11a980bb09760b Mon Sep 17 00:00:00 2001 From: Ruggero Tomaselli Date: Wed, 10 Mar 2021 11:15:17 +0100 Subject: [PATCH 257/278] Requests: Add volumedb field to SourceVolumeChanged Event --- src/WSEvents.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 2f11ede1..7f69124a 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -1145,6 +1145,7 @@ void WSEvents::OnSourceDestroy(void* param, calldata_t* data) { * * @return {String} `sourceName` Source name * @return {float} `volume` Source volume + * @return {float} `volumeDb` Source volume in Decibel * * @api events * @name SourceVolumeChanged @@ -1164,9 +1165,15 @@ void WSEvents::OnSourceVolumeChange(void* param, calldata_t* data) { return; } + double volumeDb = obs_mul_to_db(volume); + if (volumeDb == -INFINITY) { + volumeDb = -100.0; + } + OBSDataAutoRelease fields = obs_data_create(); obs_data_set_string(fields, "sourceName", obs_source_get_name(source)); obs_data_set_double(fields, "volume", volume); + obs_data_set_double(fields, "volumeDb", volumeDb); self->broadcastUpdate("SourceVolumeChanged", fields); } From b13822db8d6c7e47720be27ebd4ef1e76b40c1f0 Mon Sep 17 00:00:00 2001 From: Blake B Date: Fri, 19 Mar 2021 01:02:30 -0400 Subject: [PATCH 258/278] Requests: Add SetAudioTracks and GetAudioTracks (#690) * Requests: Add SetAudioTracks and GetAudioTracks * Renamed mixer to track. * Fix: Prevent pushing track out of range * Fix: Changed result values back to track. * Fix: Changed source parameter to sourceName. * Fix: Updated @param. * Revert "Fix: Updated @param." This reverts commit 24a5eb6fabf9ddccd1bd0176cb695408e46306f9. --- src/WSRequestHandler.cpp | 2 + src/WSRequestHandler.h | 2 + src/WSRequestHandler_Sources.cpp | 90 ++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/src/WSRequestHandler.cpp b/src/WSRequestHandler.cpp index 0eec2529..4c474f30 100644 --- a/src/WSRequestHandler.cpp +++ b/src/WSRequestHandler.cpp @@ -121,6 +121,8 @@ const QHash WSRequestHandler::messageMap{ { "GetSourceTypesList", &WSRequestHandler::GetSourceTypesList }, { "GetVolume", &WSRequestHandler::GetVolume }, { "SetVolume", &WSRequestHandler::SetVolume }, + { "SetAudioTracks", &WSRequestHandler::SetAudioTracks }, + { "GetAudioTracks", &WSRequestHandler::GetAudioTracks }, { "GetMute", &WSRequestHandler::GetMute }, { "SetMute", &WSRequestHandler::SetMute }, { "ToggleMute", &WSRequestHandler::ToggleMute }, diff --git a/src/WSRequestHandler.h b/src/WSRequestHandler.h index 82aa5ee3..d56d57e8 100644 --- a/src/WSRequestHandler.h +++ b/src/WSRequestHandler.h @@ -138,6 +138,8 @@ class WSRequestHandler { RpcResponse GetSourceTypesList(const RpcRequest&); RpcResponse GetVolume(const RpcRequest&); RpcResponse SetVolume(const RpcRequest&); + RpcResponse SetAudioTracks(const RpcRequest&); + RpcResponse GetAudioTracks(const RpcRequest&); RpcResponse GetMute(const RpcRequest&); RpcResponse SetMute(const RpcRequest&); RpcResponse ToggleMute(const RpcRequest&); diff --git a/src/WSRequestHandler_Sources.cpp b/src/WSRequestHandler_Sources.cpp index 35f6e56b..c6e95e6a 100644 --- a/src/WSRequestHandler_Sources.cpp +++ b/src/WSRequestHandler_Sources.cpp @@ -315,6 +315,96 @@ RpcResponse WSRequestHandler::SetVolume(const RpcRequest& request) return request.success(); } +/** +* Changes whether an audio track is active for a source. +* +* @param {String} `sourceName` Source name. +* @param {int} `track` Audio tracks 1-6. +* @param {boolean} `active` Whether audio track is active or not. +* +* @api requests +* @name SetTracks +* @category sources +* @since unreleased +*/ +RpcResponse WSRequestHandler::SetAudioTracks(const RpcRequest& request) + { + if (!request.hasField("sourceName") || !request.hasField("track") || !request.hasField("active")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + bool active = obs_data_get_bool(request.parameters(), "active"); + int track = obs_data_get_int(request.parameters(), "track")-1; + + if (sourceName.isEmpty() || track > 5 || track < 0) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + uint32_t mixers = obs_source_get_audio_mixers(source); + + if (active && !(mixers & (1 << track))) + mixers |= (1 << track); + else if (mixers & (1 << track)) + mixers &= ~(1 << track); + + obs_source_set_audio_mixers(source, mixers); + + return request.success(); +} + + +/** +* Gets whether an audio track is active for a source. +* +* @param {String} `sourceName` Source name. +* +* @return {boolean} `track1` +* @return {boolean} `track2` +* @return {boolean} `track3` +* @return {boolean} `track4` +* @return {boolean} `track5` +* @return {boolean} `track6` +* +* @api requests +* @name GetTracks +* @category sources +* @since unreleased +*/ +RpcResponse WSRequestHandler::GetAudioTracks(const RpcRequest& request) +{ + if (!request.hasField("sourceName")) { + return request.failed("missing request parameters"); + } + + QString sourceName = obs_data_get_string(request.parameters(), "sourceName"); + if (sourceName.isEmpty()) { + return request.failed("invalid request parameters"); + } + + OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + return request.failed("specified source doesn't exist"); + } + + uint32_t mixers = obs_source_get_audio_mixers(source); + + OBSDataAutoRelease response = obs_data_create(); + obs_data_set_string(response, "name", obs_source_get_name(source)); + obs_data_set_bool(response, "track1", mixers & (1 << 0)); + obs_data_set_bool(response, "track2", mixers & (1 << 1)); + obs_data_set_bool(response, "track3", mixers & (1 << 2)); + obs_data_set_bool(response, "track4", mixers & (1 << 3)); + obs_data_set_bool(response, "track5", mixers & (1 << 4)); + obs_data_set_bool(response, "track6", mixers & (1 << 5)); + return request.success(response); +} + /** * Get the mute status of a specified source. * From 2be20a91793ec7ba65351207bb50499d8e160288 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Fri, 19 Mar 2021 05:03:26 +0000 Subject: [PATCH 259/278] docs(ci): Update protocol.md - 77f6335 [skip ci] --- docs/generated/comments.json | 144 ++++++++++++++++++++++++++++++++++- docs/generated/protocol.md | 53 +++++++++++++ 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 97d76817..d70b460e 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -2118,7 +2118,8 @@ "description": "The volume of a source has changed.", "return": [ "{String} `sourceName` Source name", - "{float} `volume` Source volume" + "{float} `volume` Source volume", + "{float} `volumeDb` Source volume in Decibel" ], "api": "events", "name": "SourceVolumeChanged", @@ -2134,6 +2135,11 @@ "type": "float", "name": "volume", "description": "Source volume" + }, + { + "type": "float", + "name": "volumeDb", + "description": "Source volume in Decibel" } ], "names": [ @@ -5337,6 +5343,142 @@ "type": "class", "examples": [] }, + { + "subheads": [], + "description": "Changes whether an audio track is active for a source.", + "param": [ + "{String} `sourceName` Source name.", + "{int} `track` Audio tracks 1-6.", + "{boolean} `active` Whether audio track is active or not." + ], + "api": "requests", + "name": "SetTracks", + "category": "sources", + "since": "unreleased", + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + }, + { + "type": "int", + "name": "track", + "description": "Audio tracks 1-6." + }, + { + "type": "boolean", + "name": "active", + "description": "Whether audio track is active or not." + } + ], + "names": [ + { + "name": "", + "description": "SetTracks" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "SetTracks" + }, + "lead": "", + "type": "class", + "examples": [] + }, + { + "subheads": [], + "description": "Gets whether an audio track is active for a source.", + "param": "{String} `sourceName` Source name.", + "return": [ + "{boolean} `track1`", + "{boolean} `track2`", + "{boolean} `track3`", + "{boolean} `track4`", + "{boolean} `track5`", + "{boolean} `track6`" + ], + "api": "requests", + "name": "GetTracks", + "category": "sources", + "since": "unreleased", + "returns": [ + { + "type": "boolean", + "name": "track1", + "description": "" + }, + { + "type": "boolean", + "name": "track2", + "description": "" + }, + { + "type": "boolean", + "name": "track3", + "description": "" + }, + { + "type": "boolean", + "name": "track4", + "description": "" + }, + { + "type": "boolean", + "name": "track5", + "description": "" + }, + { + "type": "boolean", + "name": "track6", + "description": "" + } + ], + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + } + ], + "names": [ + { + "name": "", + "description": "GetTracks" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "GetTracks" + }, + "lead": "", + "type": "class", + "examples": [] + }, { "subheads": [], "description": "Get the mute status of a specified source.", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 1579d919..6a6a49c0 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -161,6 +161,8 @@ You can also refer to any of the client libraries listed on the [README](README. + [GetSourceTypesList](#getsourcetypeslist) + [GetVolume](#getvolume) + [SetVolume](#setvolume) + + [SetTracks](#settracks) + + [GetTracks](#gettracks) + [GetMute](#getmute) + [SetMute](#setmute) + [ToggleMute](#togglemute) @@ -947,6 +949,7 @@ The volume of a source has changed. | ---- | :---: | ------------| | `sourceName` | _String_ | Source name | | `volume` | _float_ | Source volume | +| `volumeDb` | _float_ | Source volume in Decibel | --- @@ -2171,6 +2174,56 @@ Set the volume of the specified source. Default request format uses mul, NOT SLI _No additional response items._ +--- + +### SetTracks + + +- Unreleased + +Changes whether an audio track is active for a source. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | +| `track` | _int_ | Audio tracks 1-6. | +| `active` | _boolean_ | Whether audio track is active or not. | + + +**Response Items:** + +_No additional response items._ + +--- + +### GetTracks + + +- Unreleased + +Gets whether an audio track is active for a source. + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `track1` | _boolean_ | | +| `track2` | _boolean_ | | +| `track3` | _boolean_ | | +| `track4` | _boolean_ | | +| `track5` | _boolean_ | | +| `track6` | _boolean_ | | + + --- ### GetMute From 035767bbbbdb06684c87e000caeaf966ff22cff4 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Fri, 19 Mar 2021 05:05:52 +0000 Subject: [PATCH 260/278] docs(ci): Update protocol.md - 8e43958 [skip ci] --- docs/generated/comments.json | 49 ++++++++++++++++++++++++++++++++++++ docs/generated/protocol.md | 24 ++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index d70b460e..f45b32cb 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -5626,6 +5626,55 @@ "type": "class", "examples": [] }, + { + "subheads": [], + "description": "Get the source's active status of a specified source (if it is showing in the final mix).", + "param": "{String} `sourceName` Source name.", + "return": "{boolean} `sourceActive` Source active status of the source.", + "api": "requests", + "name": "GetSourceActive", + "category": "sources", + "since": "unreleased", + "returns": [ + { + "type": "boolean", + "name": "sourceActive", + "description": "Source active status of the source." + } + ], + "params": [ + { + "type": "String", + "name": "sourceName", + "description": "Source name." + } + ], + "names": [ + { + "name": "", + "description": "GetSourceActive" + } + ], + "categories": [ + { + "name": "", + "description": "sources" + } + ], + "sinces": [ + { + "name": "", + "description": "unreleased" + } + ], + "heading": { + "level": 2, + "text": "GetSourceActive" + }, + "lead": "", + "type": "class", + "examples": [] + }, { "subheads": [], "description": "Get the audio's active status of a specified source.", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 6a6a49c0..bfcd2670 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -166,6 +166,7 @@ You can also refer to any of the client libraries listed on the [README](README. + [GetMute](#getmute) + [SetMute](#setmute) + [ToggleMute](#togglemute) + + [GetSourceActive](#getsourceactive) + [GetAudioActive](#getaudioactive) + [SetSourceName](#setsourcename) + [SetSyncOffset](#setsyncoffset) @@ -2289,6 +2290,29 @@ Inverts the mute status of a specified source. _No additional response items._ +--- + +### GetSourceActive + + +- Unreleased + +Get the source's active status of a specified source (if it is showing in the final mix). + +**Request Fields:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceName` | _String_ | Source name. | + + +**Response Items:** + +| Name | Type | Description | +| ---- | :---: | ------------| +| `sourceActive` | _boolean_ | Source active status of the source. | + + --- ### GetAudioActive From 4505612f7528a81163949c357955ed161641e244 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 18 Mar 2021 22:51:06 -0700 Subject: [PATCH 261/278] CI: Update Qt and OBS deps on Windows - Update Qt to 5.15 - Update OBS dependencies to 2019 --- .github/workflows/pr_push.yml | 4 ++-- .github/workflows/tag_release.yml | 4 ++-- CI/download-obs-deps.cmd | 9 +++++---- CI/install-qt-win.cmd | 10 +++++----- azure-pipelines.yml | 18 ++++++++++++++---- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/.github/workflows/pr_push.yml b/.github/workflows/pr_push.yml index cad2023f..d828521d 100644 --- a/.github/workflows/pr_push.yml +++ b/.github/workflows/pr_push.yml @@ -18,8 +18,8 @@ jobs: runs-on: [windows-latest] if: contains(github.event.head_commit.message, '[skip ci]') != true env: - QT_VERSION: '5.10.1' - WINDOWS_DEPS_VERSION: '2017' + QT_VERSION: '5.15.2' + WINDOWS_DEPS_VERSION: '2019' CMAKE_GENERATOR: "Visual Studio 16 2019" CMAKE_SYSTEM_VERSION: "10.0" steps: diff --git a/.github/workflows/tag_release.yml b/.github/workflows/tag_release.yml index f499e0bb..51d6813a 100644 --- a/.github/workflows/tag_release.yml +++ b/.github/workflows/tag_release.yml @@ -12,8 +12,8 @@ jobs: name: 'Windows 32+64bit' runs-on: [windows-latest] env: - QT_VERSION: '5.10.1' - WINDOWS_DEPS_VERSION: '2017' + QT_VERSION: '5.15.2' + WINDOWS_DEPS_VERSION: '2019' CMAKE_GENERATOR: "Visual Studio 16 2019" CMAKE_SYSTEM_VERSION: "10.0" steps: diff --git a/CI/download-obs-deps.cmd b/CI/download-obs-deps.cmd index ff4ffd57..2063e297 100644 --- a/CI/download-obs-deps.cmd +++ b/CI/download-obs-deps.cmd @@ -1,6 +1,7 @@ -if not exist %DepsBasePath% ( - curl -o %DepsBasePath%.zip -kLO https://obsproject.com/downloads/dependencies2017.zip -f --retry 5 -C - - 7z x %DepsBasePath%.zip -o%DepsBasePath% +if exist %DepsBasePath% ( + echo "OBS dependencies found. Download skipped." ) else ( - echo "OBS dependencies are already there. Download skipped." + echo "OBS dependencies not found. Downloading..." + curl -o %DepsBasePath%.zip -kLO https://cdn-fastly.obsproject.com/downloads/dependencies2019.zip -f --retry 5 -C - + 7z x %DepsBasePath%.zip -o%DepsBasePath% ) diff --git a/CI/install-qt-win.cmd b/CI/install-qt-win.cmd index e0537fe8..4a131280 100644 --- a/CI/install-qt-win.cmd +++ b/CI/install-qt-win.cmd @@ -1,8 +1,8 @@ -if not exist %QtBaseDir% ( - curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_5.10.1.7z -f --retry 5 -z Qt_5.10.1.7z - 7z x Qt_5.10.1.7z -o%QtBaseDir% +if exist %QtBaseDir% ( + echo "Qt directory found. Download skipped." ) else ( - echo "Qt is already installed. Download skipped." + echo "Qt directory not found. Downloading..." + curl -kLO http://home.tt2468.net:86/dl/Qt_5.15.2.7z -f --retry 5 -C - + 7z x Qt_5.15.2.7z -o%QtBaseDir% ) - dir %QtBaseDir% diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a42fc561..449bd137 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -33,17 +33,27 @@ jobs: vmImage: 'windows-2019' variables: build_config: RelWithDebInfo + DepsCacheVersion: '1' # Change whenever updating OBS dependencies URL, in order to force a cache reset DepsBasePath: 'D:\obsdependencies' DepsPath32: '$(DepsBasePath)\win32' DepsPath64: '$(DepsBasePath)\win64' + QtCacheVersion: '1' # Change whenever updating Qt dependency URL, in order to force a cache reset QtBaseDir: 'D:\QtDep' - QTDIR32: '$(QtBaseDir)\5.10.1\msvc2017' - QTDIR64: '$(QtBaseDir)\5.10.1\msvc2017_64' + QTDIR32: '$(QtBaseDir)\5.15.2\msvc2019' + QTDIR64: '$(QtBaseDir)\5.15.2\msvc2019_64' OBSPath: 'D:\obs-studio' steps: - checkout: self submodules: true + - task: Cache@2 + displayName: Restore cached Qt archive file + inputs: + key: 'qtdep-"$(QtCacheVersion)" | "$(Agent.OS)"' + restoreKeys: | + qtdep-"$(QtCacheVersion)" | "$(Agent.OS)" + path: $(QtBaseDir) + - script: ./CI/install-qt-win.cmd displayName: 'Install Qt' env: @@ -52,9 +62,9 @@ jobs: - task: Cache@2 displayName: Restore cached OBS Studio dependencies inputs: - key: 'obsdeps | "$(Agent.OS)"' + key: 'obsdeps-"$(DepsCacheVersion)" | "$(Agent.OS)"' restoreKeys: | - obsdeps | "$(Agent.OS)" + obsdeps-"$(DepsCacheVersion)" | "$(Agent.OS)" path: $(DepsBasePath) - script: ./CI/download-obs-deps.cmd From 7556c5f855714a80cc3e51d5f650fbd290afa660 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 19 Mar 2021 01:45:52 -0700 Subject: [PATCH 262/278] CMake: Copy other imageformats plugins too. --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0de5f1f1..ed7c93d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,11 +133,21 @@ if(WIN32) # In Release mode, copy Qt image format plugins COMMAND if $==1 ( "${CMAKE_COMMAND}" -E copy + "${QTDIR}/plugins/imageformats/qicns.dll" + "${QTDIR}/plugins/imageformats/qico.dll" "${QTDIR}/plugins/imageformats/qjpeg.dll" + "${QTDIR}/plugins/imageformats/qtiff.dll" + "${QTDIR}/plugins/imageformats/qwbmp.dll" + "${QTDIR}/plugins/imageformats/qwebp.dll" "${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats/qjpeg.dll") COMMAND if $==1 ( "${CMAKE_COMMAND}" -E copy + "${QTDIR}/plugins/imageformats/qicns.dll" + "${QTDIR}/plugins/imageformats/qico.dll" "${QTDIR}/plugins/imageformats/qjpeg.dll" + "${QTDIR}/plugins/imageformats/qtiff.dll" + "${QTDIR}/plugins/imageformats/qwbmp.dll" + "${QTDIR}/plugins/imageformats/qwebp.dll" "${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats/qjpeg.dll") # If config is RelWithDebInfo, package release files From fde1ada69995f240731bf557279f3e9318e7ab2a Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 20 Mar 2021 07:44:26 -0700 Subject: [PATCH 263/278] Docs: Properly mark `setVisible` as optional --- src/WSRequestHandler_SceneItems.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WSRequestHandler_SceneItems.cpp b/src/WSRequestHandler_SceneItems.cpp index 23ef6236..7c6f967c 100644 --- a/src/WSRequestHandler_SceneItems.cpp +++ b/src/WSRequestHandler_SceneItems.cpp @@ -652,7 +652,7 @@ RpcResponse WSRequestHandler::DeleteSceneItem(const RpcRequest& request) { * * @param {String} `sceneName` Name of the scene to create the scene item in * @param {String} `sourceName` Name of the source to be added - * @param {boolean} `setVisible` Whether to make the sceneitem visible on creation or not. Default `true` + * @param {boolean (optional)} `setVisible` Whether to make the sceneitem visible on creation or not. Default `true` * * @return {int} `itemId` Numerical ID of the created scene item * From b7bd95dcc4a89f7017d64f2c1dbb82530e276346 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 20 Mar 2021 11:34:49 -0700 Subject: [PATCH 264/278] CI: Use new source for Packages.pkg --- CI/install-build-obs-macos.sh | 4 ++-- CI/install-dependencies-macos.sh | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CI/install-build-obs-macos.sh b/CI/install-build-obs-macos.sh index 4af63b49..8a5eb145 100755 --- a/CI/install-build-obs-macos.sh +++ b/CI/install-build-obs-macos.sh @@ -21,8 +21,8 @@ if [ "${HAS_GIT}" = "" ]; then fi echo "[obs-websocket] Downloading and unpacking OBS dependencies" -wget --quiet --retry-connrefused --waitretry=1 https://obs-nightly.s3.amazonaws.com/osx-deps-2018-08-09.tar.gz -tar -xf ./osx-deps-2018-08-09.tar.gz -C /tmp +wget --quiet --retry-connrefused --waitretry=1 https://github.com/obsproject/obs-deps/releases/download/2021-02-28/macos-deps-2021-02-28.tar.gz +tar -xf ./macos-deps-2021-02-28.tar.gz -C /tmp # Build obs-studio cd .. diff --git a/CI/install-dependencies-macos.sh b/CI/install-dependencies-macos.sh index 267fa49a..6991e098 100755 --- a/CI/install-dependencies-macos.sh +++ b/CI/install-dependencies-macos.sh @@ -51,6 +51,7 @@ HAS_PACKAGES=$(type packagesbuild 2>/dev/null) if [ "${HAS_PACKAGES}" = "" ]; then echo "[obs-websocket] Installing Packaging app (might require password due to 'sudo').." - curl -o './Packages.pkg' --retry-connrefused -s --retry-delay 1 'https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg' - sudo installer -pkg ./Packages.pkg -target / + curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg + sudo hdiutil attach ./Packages.dmg + sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target / fi From 7e0bc1fd109f75da3421111a07f7a6e6c4476294 Mon Sep 17 00:00:00 2001 From: Dominik Nakamura Date: Sun, 21 Mar 2021 14:53:45 +0900 Subject: [PATCH 265/278] docs: Mark from-scene as optional --- docs/generated/comments.json | 8 +- docs/generated/protocol.md | 4 +- docs/npm-shrinkwrap.json | 5168 ++++++++++++++++++++++++++++++++++ src/WSEvents.cpp | 38 +- 4 files changed, 5193 insertions(+), 25 deletions(-) create mode 100644 docs/npm-shrinkwrap.json diff --git a/docs/generated/comments.json b/docs/generated/comments.json index f45b32cb..8f050ec7 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -861,7 +861,7 @@ "{String} `name` Transition name.", "{String} `type` Transition type.", "{int} `duration` Transition duration (in milliseconds). Will be -1 for any transition with a fixed duration, such as a Stinger, due to limitations of the OBS API.", - "{String} `from-scene` Source scene of the transition", + "{String (optional)} `from-scene` Source scene of the transition", "{String} `to-scene` Destination scene of the transition" ], "api": "events", @@ -885,7 +885,7 @@ "description": "Transition duration (in milliseconds). Will be -1 for any transition with a fixed duration, such as a Stinger, due to limitations of the OBS API." }, { - "type": "String", + "type": "String (optional)", "name": "from-scene", "description": "Source scene of the transition" }, @@ -989,7 +989,7 @@ "{String} `name` Transition name.", "{String} `type` Transition type.", "{int} `duration` Transition duration (in milliseconds).", - "{String} `from-scene` Source scene of the transition", + "{String (optional)} `from-scene` Source scene of the transition", "{String} `to-scene` Destination scene of the transition" ], "api": "events", @@ -1013,7 +1013,7 @@ "description": "Transition duration (in milliseconds)." }, { - "type": "String", + "type": "String (optional)", "name": "from-scene", "description": "Source scene of the transition" }, diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index bfcd2670..a1e3b500 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -516,7 +516,7 @@ A transition (other than "cut") has begun. | `name` | _String_ | Transition name. | | `type` | _String_ | Transition type. | | `duration` | _int_ | Transition duration (in milliseconds). Will be -1 for any transition with a fixed duration, such as a Stinger, due to limitations of the OBS API. | -| `from-scene` | _String_ | Source scene of the transition | +| `from-scene` | _String (optional)_ | Source scene of the transition | | `to-scene` | _String_ | Destination scene of the transition | @@ -556,7 +556,7 @@ A stinger transition has finished playing its video. | `name` | _String_ | Transition name. | | `type` | _String_ | Transition type. | | `duration` | _int_ | Transition duration (in milliseconds). | -| `from-scene` | _String_ | Source scene of the transition | +| `from-scene` | _String (optional)_ | Source scene of the transition | | `to-scene` | _String_ | Destination scene of the transition | diff --git a/docs/npm-shrinkwrap.json b/docs/npm-shrinkwrap.json new file mode 100644 index 00000000..2e8c2392 --- /dev/null +++ b/docs/npm-shrinkwrap.json @@ -0,0 +1,5168 @@ +{ + "name": "obs-websocket-docs", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "obs-websocket-docs", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "glob": "^7.1.2", + "handlebars": "^4.0.10", + "handlebars-helpers": "^0.9.6", + "markdown-toc": "^1.1.0", + "parse-comments": "^0.4.3" + } + }, + "node_modules/ansi-bgblack": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgblack/-/ansi-bgblack-0.1.1.tgz", + "integrity": "sha1-poulAHiHcBtqr74/oNrf36juPKI=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-bgblue": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgblue/-/ansi-bgblue-0.1.1.tgz", + "integrity": "sha1-Z73ATtybm1J4lp2hlt6j11yMNhM=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-bgcyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgcyan/-/ansi-bgcyan-0.1.1.tgz", + "integrity": "sha1-WEiUJWAL3p9VBwaN2Wnr/bUP52g=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-bggreen": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bggreen/-/ansi-bggreen-0.1.1.tgz", + "integrity": "sha1-TjGRJIUplD9DIelr8THRwTgWr0k=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-bgmagenta": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgmagenta/-/ansi-bgmagenta-0.1.1.tgz", + "integrity": "sha1-myhDLAduqpmUGGcqPvvhk5HCx6E=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-bgred": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgred/-/ansi-bgred-0.1.1.tgz", + "integrity": "sha1-p2+Sg4OCukMpCmwXeEJPmE1vEEE=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-bgwhite": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgwhite/-/ansi-bgwhite-0.1.1.tgz", + "integrity": "sha1-ZQRlE3elim7OzQMxmU5IAljhG6g=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-bgyellow": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgyellow/-/ansi-bgyellow-0.1.1.tgz", + "integrity": "sha1-w/4usIzUdmSAKeaHTRWgs49h1E8=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-black": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-black/-/ansi-black-0.1.1.tgz", + "integrity": "sha1-9hheiJNgslRaHsUMC/Bj/EMDJFM=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-blue": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-blue/-/ansi-blue-0.1.1.tgz", + "integrity": "sha1-FbgEmQ6S/JyoxUds6PaZd3wh7b8=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-bold": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bold/-/ansi-bold-0.1.1.tgz", + "integrity": "sha1-PmOVCvWswq4uZw5vZ96xFdGl9QU=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-colors": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-0.2.0.tgz", + "integrity": "sha1-csMd4qDZoszQysMMyYI+6y9kNLU=", + "dependencies": { + "ansi-bgblack": "^0.1.1", + "ansi-bgblue": "^0.1.1", + "ansi-bgcyan": "^0.1.1", + "ansi-bggreen": "^0.1.1", + "ansi-bgmagenta": "^0.1.1", + "ansi-bgred": "^0.1.1", + "ansi-bgwhite": "^0.1.1", + "ansi-bgyellow": "^0.1.1", + "ansi-black": "^0.1.1", + "ansi-blue": "^0.1.1", + "ansi-bold": "^0.1.1", + "ansi-cyan": "^0.1.1", + "ansi-dim": "^0.1.1", + "ansi-gray": "^0.1.1", + "ansi-green": "^0.1.1", + "ansi-grey": "^0.1.1", + "ansi-hidden": "^0.1.1", + "ansi-inverse": "^0.1.1", + "ansi-italic": "^0.1.1", + "ansi-magenta": "^0.1.1", + "ansi-red": "^0.1.1", + "ansi-reset": "^0.1.1", + "ansi-strikethrough": "^0.1.1", + "ansi-underline": "^0.1.1", + "ansi-white": "^0.1.1", + "ansi-yellow": "^0.1.1", + "lazy-cache": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-dim": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-dim/-/ansi-dim-0.1.1.tgz", + "integrity": "sha1-QN5MYDqoCG2Oeoa4/5mNXDbu/Ww=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-green": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-green/-/ansi-green-0.1.1.tgz", + "integrity": "sha1-il2al55FjVfEDjNYCzc5C44Q0Pc=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-grey": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-grey/-/ansi-grey-0.1.1.tgz", + "integrity": "sha1-WdmLasK6GfilF5jphT+6eDOaM8E=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-hidden": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-hidden/-/ansi-hidden-0.1.1.tgz", + "integrity": "sha1-7WpMSY0rt8uyidvyqNHcyFZ/rg8=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-inverse": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-inverse/-/ansi-inverse-0.1.1.tgz", + "integrity": "sha1-tq9Fgm/oJr+1KKbHmIV5Q1XM0mk=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-italic": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-italic/-/ansi-italic-0.1.1.tgz", + "integrity": "sha1-EEdDRj9iXBQqA2c5z4XtpoiYbyM=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-magenta": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-magenta/-/ansi-magenta-0.1.1.tgz", + "integrity": "sha1-BjtboW+z8j4c/aKwfAqJ3hHkMK4=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-reset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-reset/-/ansi-reset-0.1.1.tgz", + "integrity": "sha1-5+cSksPH3c1NYu9KbHwFmAkRw7c=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-strikethrough": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-strikethrough/-/ansi-strikethrough-0.1.1.tgz", + "integrity": "sha1-2Eh3FAss/wfRyT685pkE9oiF5Wg=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-underline": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-underline/-/ansi-underline-0.1.1.tgz", + "integrity": "sha1-38kg9Ml7WXfqFi34/7mIMIqqcaQ=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-white": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-white/-/ansi-white-0.1.1.tgz", + "integrity": "sha1-nHe3wZPF7pkuYBHTbsTJIbRXiUQ=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-yellow": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-yellow/-/ansi-yellow-0.1.1.tgz", + "integrity": "sha1-y5NW8vRscy8OMZnmEClVp32oPB0=", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "node_modules/array-sort": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-0.1.4.tgz", + "integrity": "sha512-BNcM+RXxndPxiZ2rd76k6nyQLRZr2/B/sdi8pQ+Joafr5AH279L40dfokSUTp8O+AaqYjXWhblBWa2st2nc4fQ==", + "dependencies": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrayify-compact": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/arrayify-compact/-/arrayify-compact-0.1.1.tgz", + "integrity": "sha512-3R2V/8ixAhDXxyeWZ9aVzncUscrASg1TPrVsUgkxaMb0/WJLo5u769wYYf916OKqRCF+GKXaosouk1ixIfX89g==", + "dependencies": { + "array-flatten": "^2.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autolinker": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz", + "integrity": "sha1-BlK0kYgYefB3XazgzcoyM5QqTkc=", + "dependencies": { + "gulp-header": "^1.7.1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/braces/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==", + "deprecated": "CoffeeScript on NPM has moved to \"coffeescript\" (no hyphen)", + "bin": { + "cake": "bin/cake", + "coffee": "bin/coffee" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "node_modules/create-frame": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/create-frame/-/create-frame-1.0.0.tgz", + "integrity": "sha1-i5XyaR4ySbYIBEPjPQutn49pdao=", + "dependencies": { + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "isobject": "^3.0.0", + "lazy-cache": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/create-frame/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/create-frame/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/create-frame/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/create-frame/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/create-frame/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/create-frame/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/date.js": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/date.js/-/date.js-0.3.3.tgz", + "integrity": "sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw==", + "dependencies": { + "debug": "~3.1.0" + } + }, + "node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "dependencies": { + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/diacritics-map": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz", + "integrity": "sha1-bfwP+dAQAKLt8oZTccrDFulJd68=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=" + }, + "node_modules/error-symbol": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/error-symbol/-/error-symbol-0.1.0.tgz", + "integrity": "sha1-Ck2uN9YA0VopukU9jvkg8YRDM/Y=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dependencies": { + "fill-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-comments": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/extract-comments/-/extract-comments-0.7.3.tgz", + "integrity": "sha1-HF3sFzAHLFsM36VVfl9X9pJ3/DQ=", + "dependencies": { + "is-whitespace": "^0.3.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/falsey": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/falsey/-/falsey-0.3.2.tgz", + "integrity": "sha512-lxEuefF5MBIVDmE6XeqCdM4BWk1+vYmGZtkbKZ/VFcg6uBBw6fXNEbWmxCjDdQlFc9hy450nkiWwM3VAW6G1qg==", + "dependencies": { + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dependencies": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fs-exists-sync": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", + "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/get-object": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/get-object/-/get-object-0.2.0.tgz", + "integrity": "sha1-2S/31RkMZFMM2gVD2sY6PUf+jAw=", + "dependencies": { + "is-number": "^2.0.2", + "isobject": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-object/node_modules/is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-object/node_modules/isobject": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-0.2.0.tgz", + "integrity": "sha1-o0MhkvObkQtfAsyYlIeDbscKqF4=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-object/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gfm-code-block-regex": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/gfm-code-block-regex/-/gfm-code-block-regex-0.2.3.tgz", + "integrity": "sha1-C5/mis8sWlAi/W0tQYpjT26XwRA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gfm-code-blocks": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/gfm-code-blocks/-/gfm-code-blocks-0.2.2.tgz", + "integrity": "sha1-KZnYFIxFG4ZjLHH6bevhFe4HH/4=", + "dependencies": { + "gfm-code-block-regex": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gray-matter": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz", + "integrity": "sha1-MELZrewqHe1qdwep7SOA+KF6Qw4=", + "dependencies": { + "ansi-red": "^0.1.1", + "coffee-script": "^1.12.4", + "extend-shallow": "^2.0.1", + "js-yaml": "^3.8.1", + "toml": "^2.3.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-header": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz", + "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==", + "deprecated": "Removed event-stream from gulp-header", + "dependencies": { + "concat-with-sourcemaps": "*", + "lodash.template": "^4.4.0", + "through2": "^2.0.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars-helper-create-frame": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/handlebars-helper-create-frame/-/handlebars-helper-create-frame-0.1.0.tgz", + "integrity": "sha1-iqUdEK62QI/MZgXUDXc1YohIegM=", + "dependencies": { + "create-frame": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/handlebars-helpers": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/handlebars-helpers/-/handlebars-helpers-0.9.8.tgz", + "integrity": "sha512-N9MoNopXTOzNv9L2oDFUo1ZhWTzUd8YURVrksZaXVRybgs1JFnUXohCnFTOJL8m4t+jKn1xU6Vi7qxtCu4mRsg==", + "dependencies": { + "arr-flatten": "^1.0.3", + "array-sort": "^0.1.2", + "create-frame": "^1.0.0", + "define-property": "^1.0.0", + "falsey": "^0.3.0", + "for-in": "^1.0.2", + "for-own": "^1.0.0", + "get-object": "^0.2.0", + "get-value": "^2.0.6", + "handlebars": "^4.0.10", + "handlebars-helper-create-frame": "^0.1.0", + "handlebars-utils": "^1.0.1", + "has-value": "^1.0.0", + "helper-date": "^1.0.0", + "helper-markdown": "^0.2.2", + "helper-md": "^0.2.2", + "html-tag": "^1.0.0", + "is-even": "^1.0.0", + "is-glob": "^3.1.0", + "is-number": "^3.0.0", + "kind-of": "^5.0.0", + "lazy-cache": "^2.0.2", + "logging-helpers": "^1.0.0", + "micromatch": "^3.0.3", + "relative": "^3.0.2", + "striptags": "^3.0.1", + "to-gfm-code-block": "^0.1.1", + "year": "^0.2.1" + }, + "engines": { + "node": ">=5" + } + }, + "node_modules/handlebars-utils": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/handlebars-utils/-/handlebars-utils-1.0.6.tgz", + "integrity": "sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw==", + "dependencies": { + "kind-of": "^6.0.0", + "typeof-article": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/handlebars-utils/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/helper-date": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/helper-date/-/helper-date-1.0.1.tgz", + "integrity": "sha512-wU3VOwwTJvGr/w5rZr3cprPHO+hIhlblTJHD6aFBrKLuNbf4lAmkawd2iK3c6NbJEvY7HAmDpqjOFSI5/+Ey2w==", + "dependencies": { + "date.js": "^0.3.1", + "handlebars-utils": "^1.0.4", + "moment": "^2.18.1" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/helper-markdown": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/helper-markdown/-/helper-markdown-0.2.2.tgz", + "integrity": "sha1-ONt/dxhJ4wrpXJL8AhuutT8uMEA=", + "dependencies": { + "isobject": "^2.0.0", + "mixin-deep": "^1.1.3", + "remarkable": "^1.6.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/helper-markdown/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/helper-md": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/helper-md/-/helper-md-0.2.2.tgz", + "integrity": "sha1-wfWdflW7riM2L9ig6XFgeuxp1B8=", + "dependencies": { + "ent": "^2.2.0", + "extend-shallow": "^2.0.1", + "fs-exists-sync": "^0.1.0", + "remarkable": "^1.6.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/html-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/html-tag/-/html-tag-1.0.0.tgz", + "integrity": "sha1-leVhKuyCvqko7URZX4VBRen34LU=", + "dependencies": { + "isobject": "^3.0.0", + "void-elements": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflection": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", + "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=", + "engines": [ + "node >= 0.4.0" + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/info-symbol": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/info-symbol/-/info-symbol-0.1.0.tgz", + "integrity": "sha1-J4QdcoZ920JCzWEtecEGM4gcang=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-even": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-even/-/is-even-1.0.0.tgz", + "integrity": "sha1-drUFX7rY0pSoa2qUkBXhyXtxfAY=", + "dependencies": { + "is-odd": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-odd": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-0.1.2.tgz", + "integrity": "sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc=", + "dependencies": { + "is-number": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-whitespace": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", + "integrity": "sha1-Fjnssb4DauxppUy7QBz77XEUq38=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lazy-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", + "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", + "dependencies": { + "set-getter": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/list-item": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz", + "integrity": "sha1-DGXQDih8tmPMs8s4Sad+iewmilY=", + "dependencies": { + "expand-range": "^1.8.1", + "extend-shallow": "^2.0.1", + "is-number": "^2.1.0", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/list-item/node_modules/is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/list-item/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + }, + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" + }, + "node_modules/lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/log-ok": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/log-ok/-/log-ok-0.1.1.tgz", + "integrity": "sha1-vqPdNqzQuKckDXhza1uXxlREozQ=", + "dependencies": { + "ansi-green": "^0.1.1", + "success-symbol": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/log-utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/log-utils/-/log-utils-0.2.1.tgz", + "integrity": "sha1-pMIXoN2aUFFdm5ICBgkas9TgMc8=", + "dependencies": { + "ansi-colors": "^0.2.0", + "error-symbol": "^0.1.0", + "info-symbol": "^0.1.0", + "log-ok": "^0.1.1", + "success-symbol": "^0.1.0", + "time-stamp": "^1.0.1", + "warning-symbol": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/logging-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/logging-helpers/-/logging-helpers-1.0.0.tgz", + "integrity": "sha512-qyIh2goLt1sOgQQrrIWuwkRjUx4NUcEqEGAcYqD8VOnOC6ItwkrVE8/tA4smGpjzyp4Svhc6RodDp9IO5ghpyA==", + "dependencies": { + "isobject": "^3.0.0", + "log-utils": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/markdown-link": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz", + "integrity": "sha1-MsXGUZmmRXMWMi0eQinRNAfIx88=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/markdown-toc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz", + "integrity": "sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg==", + "dependencies": { + "concat-stream": "^1.5.2", + "diacritics-map": "^0.1.0", + "gray-matter": "^2.1.0", + "lazy-cache": "^2.0.2", + "list-item": "^1.1.1", + "markdown-link": "^0.1.1", + "minimist": "^1.2.0", + "mixin-deep": "^1.1.3", + "object.pick": "^1.2.0", + "remarkable": "^1.7.1", + "repeat-string": "^1.6.1", + "strip-color": "^0.1.0" + }, + "bin": { + "markdown-toc": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==" + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parse-code-context": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/parse-code-context/-/parse-code-context-0.1.3.tgz", + "integrity": "sha1-sMr+ZcNLkWQ0EAAz6zNOnSgstGE=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-comments": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/parse-comments/-/parse-comments-0.4.3.tgz", + "integrity": "sha1-aMlV8eybZVpPJBsRJMedsTENZ70=", + "dependencies": { + "arrayify-compact": "^0.1.0", + "extract-comments": "^0.7.3", + "gfm-code-blocks": "^0.2.0", + "inflection": "^1.7.0", + "lodash": "^3.6.0", + "parse-code-context": "^0.1.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dependencies": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/randomatic/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/randomatic/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/relative": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/relative/-/relative-3.0.2.tgz", + "integrity": "sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8=", + "dependencies": { + "isobject": "^2.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/relative/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remarkable": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz", + "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==", + "dependencies": { + "argparse": "^1.0.10", + "autolinker": "~0.28.0" + }, + "bin": { + "remarkable": "bin/remarkable.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated" + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/set-getter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", + "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", + "dependencies": { + "to-object-path": "^0.3.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/strip-color": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz", + "integrity": "sha1-EG9l09PmotlAHKwOsM6LinArT3s=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/striptags": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/striptags/-/striptags-3.1.1.tgz", + "integrity": "sha1-yMPn/db7S7OjKjt1LltePjgJPr0=" + }, + "node_modules/success-symbol": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/success-symbol/-/success-symbol-0.1.0.tgz", + "integrity": "sha1-JAIuSG878c3KCUKDt2nEctO3KJc=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-gfm-code-block": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/to-gfm-code-block/-/to-gfm-code-block-0.1.1.tgz", + "integrity": "sha1-JdBFpfrlUxielje1kJANpzLYqoI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/toml": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz", + "integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==" + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "node_modules/typeof-article": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/typeof-article/-/typeof-article-0.1.1.tgz", + "integrity": "sha1-nwfnM8P7tkb/qeYcCN66zUYOBq8=", + "dependencies": { + "kind-of": "^3.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/typeof-article/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-js": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.1.tgz", + "integrity": "sha512-EWhx3fHy3M9JbaeTnO+rEqzCe1wtyQClv6q3YWq0voOj4E+bMZBErVS1GAHPDiRGONYq34M1/d8KuQMgvi6Gjw==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated" + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/warning-symbol": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/warning-symbol/-/warning-symbol-0.1.0.tgz", + "integrity": "sha1-uzHdEbeg+dZ6su2V9Fe2WCW7rSE=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/year": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/year/-/year-0.2.1.tgz", + "integrity": "sha1-QIOuUgoxiyPshgN/MADLiSvfm7A=", + "engines": { + "node": ">=0.8" + } + } + }, + "dependencies": { + "ansi-bgblack": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgblack/-/ansi-bgblack-0.1.1.tgz", + "integrity": "sha1-poulAHiHcBtqr74/oNrf36juPKI=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-bgblue": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgblue/-/ansi-bgblue-0.1.1.tgz", + "integrity": "sha1-Z73ATtybm1J4lp2hlt6j11yMNhM=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-bgcyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgcyan/-/ansi-bgcyan-0.1.1.tgz", + "integrity": "sha1-WEiUJWAL3p9VBwaN2Wnr/bUP52g=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-bggreen": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bggreen/-/ansi-bggreen-0.1.1.tgz", + "integrity": "sha1-TjGRJIUplD9DIelr8THRwTgWr0k=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-bgmagenta": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgmagenta/-/ansi-bgmagenta-0.1.1.tgz", + "integrity": "sha1-myhDLAduqpmUGGcqPvvhk5HCx6E=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-bgred": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgred/-/ansi-bgred-0.1.1.tgz", + "integrity": "sha1-p2+Sg4OCukMpCmwXeEJPmE1vEEE=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-bgwhite": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgwhite/-/ansi-bgwhite-0.1.1.tgz", + "integrity": "sha1-ZQRlE3elim7OzQMxmU5IAljhG6g=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-bgyellow": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bgyellow/-/ansi-bgyellow-0.1.1.tgz", + "integrity": "sha1-w/4usIzUdmSAKeaHTRWgs49h1E8=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-black": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-black/-/ansi-black-0.1.1.tgz", + "integrity": "sha1-9hheiJNgslRaHsUMC/Bj/EMDJFM=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-blue": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-blue/-/ansi-blue-0.1.1.tgz", + "integrity": "sha1-FbgEmQ6S/JyoxUds6PaZd3wh7b8=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-bold": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-bold/-/ansi-bold-0.1.1.tgz", + "integrity": "sha1-PmOVCvWswq4uZw5vZ96xFdGl9QU=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-colors": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-0.2.0.tgz", + "integrity": "sha1-csMd4qDZoszQysMMyYI+6y9kNLU=", + "requires": { + "ansi-bgblack": "^0.1.1", + "ansi-bgblue": "^0.1.1", + "ansi-bgcyan": "^0.1.1", + "ansi-bggreen": "^0.1.1", + "ansi-bgmagenta": "^0.1.1", + "ansi-bgred": "^0.1.1", + "ansi-bgwhite": "^0.1.1", + "ansi-bgyellow": "^0.1.1", + "ansi-black": "^0.1.1", + "ansi-blue": "^0.1.1", + "ansi-bold": "^0.1.1", + "ansi-cyan": "^0.1.1", + "ansi-dim": "^0.1.1", + "ansi-gray": "^0.1.1", + "ansi-green": "^0.1.1", + "ansi-grey": "^0.1.1", + "ansi-hidden": "^0.1.1", + "ansi-inverse": "^0.1.1", + "ansi-italic": "^0.1.1", + "ansi-magenta": "^0.1.1", + "ansi-red": "^0.1.1", + "ansi-reset": "^0.1.1", + "ansi-strikethrough": "^0.1.1", + "ansi-underline": "^0.1.1", + "ansi-white": "^0.1.1", + "ansi-yellow": "^0.1.1", + "lazy-cache": "^2.0.1" + } + }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-dim": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-dim/-/ansi-dim-0.1.1.tgz", + "integrity": "sha1-QN5MYDqoCG2Oeoa4/5mNXDbu/Ww=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-green": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-green/-/ansi-green-0.1.1.tgz", + "integrity": "sha1-il2al55FjVfEDjNYCzc5C44Q0Pc=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-grey": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-grey/-/ansi-grey-0.1.1.tgz", + "integrity": "sha1-WdmLasK6GfilF5jphT+6eDOaM8E=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-hidden": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-hidden/-/ansi-hidden-0.1.1.tgz", + "integrity": "sha1-7WpMSY0rt8uyidvyqNHcyFZ/rg8=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-inverse": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-inverse/-/ansi-inverse-0.1.1.tgz", + "integrity": "sha1-tq9Fgm/oJr+1KKbHmIV5Q1XM0mk=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-italic": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-italic/-/ansi-italic-0.1.1.tgz", + "integrity": "sha1-EEdDRj9iXBQqA2c5z4XtpoiYbyM=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-magenta": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-magenta/-/ansi-magenta-0.1.1.tgz", + "integrity": "sha1-BjtboW+z8j4c/aKwfAqJ3hHkMK4=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-reset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-reset/-/ansi-reset-0.1.1.tgz", + "integrity": "sha1-5+cSksPH3c1NYu9KbHwFmAkRw7c=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-strikethrough": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-strikethrough/-/ansi-strikethrough-0.1.1.tgz", + "integrity": "sha1-2Eh3FAss/wfRyT685pkE9oiF5Wg=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-underline": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-underline/-/ansi-underline-0.1.1.tgz", + "integrity": "sha1-38kg9Ml7WXfqFi34/7mIMIqqcaQ=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-white": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-white/-/ansi-white-0.1.1.tgz", + "integrity": "sha1-nHe3wZPF7pkuYBHTbsTJIbRXiUQ=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" + }, + "ansi-yellow": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-yellow/-/ansi-yellow-0.1.1.tgz", + "integrity": "sha1-y5NW8vRscy8OMZnmEClVp32oPB0=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-sort": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-0.1.4.tgz", + "integrity": "sha512-BNcM+RXxndPxiZ2rd76k6nyQLRZr2/B/sdi8pQ+Joafr5AH279L40dfokSUTp8O+AaqYjXWhblBWa2st2nc4fQ==", + "requires": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + } + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "arrayify-compact": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/arrayify-compact/-/arrayify-compact-0.1.1.tgz", + "integrity": "sha512-3R2V/8ixAhDXxyeWZ9aVzncUscrASg1TPrVsUgkxaMb0/WJLo5u769wYYf916OKqRCF+GKXaosouk1ixIfX89g==", + "requires": { + "array-flatten": "^2.1.2" + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "autolinker": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz", + "integrity": "sha1-BlK0kYgYefB3XazgzcoyM5QqTkc=", + "requires": { + "gulp-header": "^1.7.1" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + } + } + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + } + } + }, + "coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "requires": { + "source-map": "^0.6.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "create-frame": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/create-frame/-/create-frame-1.0.0.tgz", + "integrity": "sha1-i5XyaR4ySbYIBEPjPQutn49pdao=", + "requires": { + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "isobject": "^3.0.0", + "lazy-cache": "^2.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + } + } + }, + "date.js": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/date.js/-/date.js-0.3.3.tgz", + "integrity": "sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw==", + "requires": { + "debug": "~3.1.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "requires": { + "kind-of": "^5.0.2" + } + }, + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "diacritics-map": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz", + "integrity": "sha1-bfwP+dAQAKLt8oZTccrDFulJd68=" + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=" + }, + "error-symbol": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/error-symbol/-/error-symbol-0.1.0.tgz", + "integrity": "sha1-Ck2uN9YA0VopukU9jvkg8YRDM/Y=" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + } + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "requires": { + "fill-range": "^2.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "extract-comments": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/extract-comments/-/extract-comments-0.7.3.tgz", + "integrity": "sha1-HF3sFzAHLFsM36VVfl9X9pJ3/DQ=", + "requires": { + "is-whitespace": "^0.3.0" + } + }, + "falsey": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/falsey/-/falsey-0.3.2.tgz", + "integrity": "sha512-lxEuefF5MBIVDmE6XeqCdM4BWk1+vYmGZtkbKZ/VFcg6uBBw6fXNEbWmxCjDdQlFc9hy450nkiWwM3VAW6G1qg==", + "requires": { + "kind-of": "^5.0.2" + } + }, + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "^1.0.1" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs-exists-sync": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", + "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "get-object": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/get-object/-/get-object-0.2.0.tgz", + "integrity": "sha1-2S/31RkMZFMM2gVD2sY6PUf+jAw=", + "requires": { + "is-number": "^2.0.2", + "isobject": "^0.2.0" + }, + "dependencies": { + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-0.2.0.tgz", + "integrity": "sha1-o0MhkvObkQtfAsyYlIeDbscKqF4=" + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "gfm-code-block-regex": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/gfm-code-block-regex/-/gfm-code-block-regex-0.2.3.tgz", + "integrity": "sha1-C5/mis8sWlAi/W0tQYpjT26XwRA=" + }, + "gfm-code-blocks": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/gfm-code-blocks/-/gfm-code-blocks-0.2.2.tgz", + "integrity": "sha1-KZnYFIxFG4ZjLHH6bevhFe4HH/4=", + "requires": { + "gfm-code-block-regex": "^0.2.1" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "gray-matter": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz", + "integrity": "sha1-MELZrewqHe1qdwep7SOA+KF6Qw4=", + "requires": { + "ansi-red": "^0.1.1", + "coffee-script": "^1.12.4", + "extend-shallow": "^2.0.1", + "js-yaml": "^3.8.1", + "toml": "^2.3.2" + } + }, + "gulp-header": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz", + "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==", + "requires": { + "concat-with-sourcemaps": "*", + "lodash.template": "^4.4.0", + "through2": "^2.0.0" + } + }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, + "handlebars-helper-create-frame": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/handlebars-helper-create-frame/-/handlebars-helper-create-frame-0.1.0.tgz", + "integrity": "sha1-iqUdEK62QI/MZgXUDXc1YohIegM=", + "requires": { + "create-frame": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "handlebars-helpers": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/handlebars-helpers/-/handlebars-helpers-0.9.8.tgz", + "integrity": "sha512-N9MoNopXTOzNv9L2oDFUo1ZhWTzUd8YURVrksZaXVRybgs1JFnUXohCnFTOJL8m4t+jKn1xU6Vi7qxtCu4mRsg==", + "requires": { + "arr-flatten": "^1.0.3", + "array-sort": "^0.1.2", + "create-frame": "^1.0.0", + "define-property": "^1.0.0", + "falsey": "^0.3.0", + "for-in": "^1.0.2", + "for-own": "^1.0.0", + "get-object": "^0.2.0", + "get-value": "^2.0.6", + "handlebars": "^4.0.10", + "handlebars-helper-create-frame": "^0.1.0", + "handlebars-utils": "^1.0.1", + "has-value": "^1.0.0", + "helper-date": "^1.0.0", + "helper-markdown": "^0.2.2", + "helper-md": "^0.2.2", + "html-tag": "^1.0.0", + "is-even": "^1.0.0", + "is-glob": "^3.1.0", + "is-number": "^3.0.0", + "kind-of": "^5.0.0", + "lazy-cache": "^2.0.2", + "logging-helpers": "^1.0.0", + "micromatch": "^3.0.3", + "relative": "^3.0.2", + "striptags": "^3.0.1", + "to-gfm-code-block": "^0.1.1", + "year": "^0.2.1" + } + }, + "handlebars-utils": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/handlebars-utils/-/handlebars-utils-1.0.6.tgz", + "integrity": "sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw==", + "requires": { + "kind-of": "^6.0.0", + "typeof-article": "^0.1.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "helper-date": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/helper-date/-/helper-date-1.0.1.tgz", + "integrity": "sha512-wU3VOwwTJvGr/w5rZr3cprPHO+hIhlblTJHD6aFBrKLuNbf4lAmkawd2iK3c6NbJEvY7HAmDpqjOFSI5/+Ey2w==", + "requires": { + "date.js": "^0.3.1", + "handlebars-utils": "^1.0.4", + "moment": "^2.18.1" + } + }, + "helper-markdown": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/helper-markdown/-/helper-markdown-0.2.2.tgz", + "integrity": "sha1-ONt/dxhJ4wrpXJL8AhuutT8uMEA=", + "requires": { + "isobject": "^2.0.0", + "mixin-deep": "^1.1.3", + "remarkable": "^1.6.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "helper-md": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/helper-md/-/helper-md-0.2.2.tgz", + "integrity": "sha1-wfWdflW7riM2L9ig6XFgeuxp1B8=", + "requires": { + "ent": "^2.2.0", + "extend-shallow": "^2.0.1", + "fs-exists-sync": "^0.1.0", + "remarkable": "^1.6.2" + } + }, + "html-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/html-tag/-/html-tag-1.0.0.tgz", + "integrity": "sha1-leVhKuyCvqko7URZX4VBRen34LU=", + "requires": { + "isobject": "^3.0.0", + "void-elements": "^2.0.1" + } + }, + "inflection": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", + "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "info-symbol": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/info-symbol/-/info-symbol-0.1.0.tgz", + "integrity": "sha1-J4QdcoZ920JCzWEtecEGM4gcang=" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "is-even": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-even/-/is-even-1.0.0.tgz", + "integrity": "sha1-drUFX7rY0pSoa2qUkBXhyXtxfAY=", + "requires": { + "is-odd": "^0.1.2" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-odd": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-0.1.2.tgz", + "integrity": "sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc=", + "requires": { + "is-number": "^3.0.0" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-whitespace": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", + "integrity": "sha1-Fjnssb4DauxppUy7QBz77XEUq38=" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + }, + "lazy-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", + "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", + "requires": { + "set-getter": "^0.1.0" + } + }, + "list-item": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz", + "integrity": "sha1-DGXQDih8tmPMs8s4Sad+iewmilY=", + "requires": { + "expand-range": "^1.8.1", + "extend-shallow": "^2.0.1", + "is-number": "^2.1.0", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "log-ok": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/log-ok/-/log-ok-0.1.1.tgz", + "integrity": "sha1-vqPdNqzQuKckDXhza1uXxlREozQ=", + "requires": { + "ansi-green": "^0.1.1", + "success-symbol": "^0.1.0" + } + }, + "log-utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/log-utils/-/log-utils-0.2.1.tgz", + "integrity": "sha1-pMIXoN2aUFFdm5ICBgkas9TgMc8=", + "requires": { + "ansi-colors": "^0.2.0", + "error-symbol": "^0.1.0", + "info-symbol": "^0.1.0", + "log-ok": "^0.1.1", + "success-symbol": "^0.1.0", + "time-stamp": "^1.0.1", + "warning-symbol": "^0.1.0" + } + }, + "logging-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/logging-helpers/-/logging-helpers-1.0.0.tgz", + "integrity": "sha512-qyIh2goLt1sOgQQrrIWuwkRjUx4NUcEqEGAcYqD8VOnOC6ItwkrVE8/tA4smGpjzyp4Svhc6RodDp9IO5ghpyA==", + "requires": { + "isobject": "^3.0.0", + "log-utils": "^0.2.1" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "markdown-link": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz", + "integrity": "sha1-MsXGUZmmRXMWMi0eQinRNAfIx88=" + }, + "markdown-toc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz", + "integrity": "sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg==", + "requires": { + "concat-stream": "^1.5.2", + "diacritics-map": "^0.1.0", + "gray-matter": "^2.1.0", + "lazy-cache": "^2.0.2", + "list-item": "^1.1.1", + "markdown-link": "^0.1.1", + "minimist": "^1.2.0", + "mixin-deep": "^1.1.3", + "object.pick": "^1.2.0", + "remarkable": "^1.7.1", + "repeat-string": "^1.6.1", + "strip-color": "^0.1.0" + } + }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "parse-code-context": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/parse-code-context/-/parse-code-context-0.1.3.tgz", + "integrity": "sha1-sMr+ZcNLkWQ0EAAz6zNOnSgstGE=" + }, + "parse-comments": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/parse-comments/-/parse-comments-0.4.3.tgz", + "integrity": "sha1-aMlV8eybZVpPJBsRJMedsTENZ70=", + "requires": { + "arrayify-compact": "^0.1.0", + "extract-comments": "^0.7.3", + "gfm-code-blocks": "^0.2.0", + "inflection": "^1.7.0", + "lodash": "^3.6.0", + "parse-code-context": "^0.1.3" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "relative": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/relative/-/relative-3.0.2.tgz", + "integrity": "sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8=", + "requires": { + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "remarkable": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz", + "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==", + "requires": { + "argparse": "^1.0.10", + "autolinker": "~0.28.0" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "set-getter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", + "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", + "requires": { + "to-object-path": "^0.3.0" + } + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-color": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz", + "integrity": "sha1-EG9l09PmotlAHKwOsM6LinArT3s=" + }, + "striptags": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/striptags/-/striptags-3.1.1.tgz", + "integrity": "sha1-yMPn/db7S7OjKjt1LltePjgJPr0=" + }, + "success-symbol": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/success-symbol/-/success-symbol-0.1.0.tgz", + "integrity": "sha1-JAIuSG878c3KCUKDt2nEctO3KJc=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" + }, + "to-gfm-code-block": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/to-gfm-code-block/-/to-gfm-code-block-0.1.1.tgz", + "integrity": "sha1-JdBFpfrlUxielje1kJANpzLYqoI=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "dependencies": { + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toml": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz", + "integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typeof-article": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/typeof-article/-/typeof-article-0.1.1.tgz", + "integrity": "sha1-nwfnM8P7tkb/qeYcCN66zUYOBq8=", + "requires": { + "kind-of": "^3.1.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "uglify-js": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.1.tgz", + "integrity": "sha512-EWhx3fHy3M9JbaeTnO+rEqzCe1wtyQClv6q3YWq0voOj4E+bMZBErVS1GAHPDiRGONYq34M1/d8KuQMgvi6Gjw==", + "optional": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + }, + "warning-symbol": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/warning-symbol/-/warning-symbol-0.1.0.tgz", + "integrity": "sha1-uzHdEbeg+dZ6su2V9Fe2WCW7rSE=" + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "year": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/year/-/year-0.2.1.tgz", + "integrity": "sha1-QIOuUgoxiyPshgN/MADLiSvfm7A=" + } + } +} diff --git a/src/WSEvents.cpp b/src/WSEvents.cpp index 7f69124a..da1bf052 100644 --- a/src/WSEvents.cpp +++ b/src/WSEvents.cpp @@ -124,7 +124,7 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private case OBS_FRONTEND_EVENT_FINISHED_LOADING: owner->hookTransitionPlaybackEvents(); break; - + case OBS_FRONTEND_EVENT_SCENE_CHANGED: owner->OnSceneChange(); break; @@ -278,7 +278,7 @@ void WSEvents::connectSourceSignals(obs_source_t* source) { signal_handler_connect(sh, "filter_add", OnSourceFilterAdded, this); signal_handler_connect(sh, "filter_remove", OnSourceFilterRemoved, this); signal_handler_connect(sh, "reorder_filters", OnSourceFilterOrderChanged, this); - + signal_handler_connect(sh, "media_play", OnMediaPlaying, this); signal_handler_connect(sh, "media_pause", OnMediaPaused, this); signal_handler_connect(sh, "media_restart", OnMediaRestarted, this); @@ -336,7 +336,7 @@ void WSEvents::disconnectSourceSignals(obs_source_t* source) { signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this); signal_handler_disconnect(sh, "transition_stop", OnTransitionEnd, this); signal_handler_disconnect(sh, "transition_video_stop", OnTransitionVideoEnd, this); - + signal_handler_disconnect(sh, "media_play", OnMediaPlaying, this); signal_handler_disconnect(sh, "media_pause", OnMediaPaused, this); signal_handler_disconnect(sh, "media_restart", OnMediaRestarted, this); @@ -469,7 +469,7 @@ void WSEvents::OnSceneChange() { * Note: This event is not fired when the scenes are reordered. * * @return {Array} `scenes` Scenes list. - * + * * @api events * @name ScenesChanged * @category scenes @@ -487,7 +487,7 @@ void WSEvents::OnSceneListChange() { * Triggered when switching to another scene collection or when renaming the current scene collection. * * @return {String} `sceneCollection` Name of the new current scene collection. - * + * * @api events * @name SceneCollectionChanged * @category scenes @@ -510,7 +510,7 @@ void WSEvents::OnSceneCollectionChange() { * * @return {Array} `sceneCollections` Scene collections list. * @return {String} `sceneCollections.*.name` Scene collection name. - * + * * @api events * @name SceneCollectionListChanged * @category scenes @@ -553,7 +553,7 @@ void WSEvents::OnTransitionChange() { * * @return {Array} `transitions` Transitions list. * @return {String} `transitions.*.name` Transition name. - * + * * @api events * @name TransitionListChanged * @category transitions @@ -582,7 +582,7 @@ void WSEvents::OnTransitionListChange() { * Triggered when switching to another profile or when renaming the current profile. * * @return {String} `profile` Name of the new current profile. - * + * * @api events * @name ProfileChanged * @category profiles @@ -599,7 +599,7 @@ void WSEvents::OnProfileChange() { * * @return {Array} `profiles` Profiles list. * @return {String} `profiles.*.name` Profile name. - * + * * @api events * @name ProfileListChanged * @category profiles @@ -679,10 +679,10 @@ void WSEvents::OnStreamStopped() { /** * A request to start recording has been issued. - * + * * Note: `recordingFilename` is not provided in this event because this information * is not available at the time this event is emitted. - * + * * @api events * @name RecordingStarting * @category recording @@ -696,7 +696,7 @@ void WSEvents::OnRecordingStarting() { * Recording started successfully. * * @return {String} `recordingFilename` Absolute path to the file of the current recording. - * + * * @api events * @name RecordingStarted * @category recording @@ -712,7 +712,7 @@ void WSEvents::OnRecordingStarted() { * A request to stop recording has been issued. * * @return {String} `recordingFilename` Absolute path to the file of the current recording. - * + * * @api events * @name RecordingStopping * @category recording @@ -728,7 +728,7 @@ void WSEvents::OnRecordingStopping() { * Recording stopped successfully. * * @return {String} `recordingFilename` Absolute path to the file of the current recording. - * + * * @api events * @name RecordingStopped * @category recording @@ -999,10 +999,10 @@ void WSEvents::TransitionDurationChanged(int ms) { * * @return {String} `name` Transition name. * @return {String} `type` Transition type. - * @return {int} `duration` Transition duration (in milliseconds). - * Will be -1 for any transition with a fixed duration, + * @return {int} `duration` Transition duration (in milliseconds). + * Will be -1 for any transition with a fixed duration, * such as a Stinger, due to limitations of the OBS API. - * @return {String} `from-scene` Source scene of the transition + * @return {String (optional)} `from-scene` Source scene of the transition * @return {String} `to-scene` Destination scene of the transition * * @api events @@ -1054,7 +1054,7 @@ void WSEvents::OnTransitionEnd(void* param, calldata_t* data) { * @return {String} `name` Transition name. * @return {String} `type` Transition type. * @return {int} `duration` Transition duration (in milliseconds). -* @return {String} `from-scene` Source scene of the transition +* @return {String (optional)} `from-scene` Source scene of the transition * @return {String} `to-scene` Destination scene of the transition * * @api events @@ -1387,7 +1387,7 @@ void WSEvents::OnSourceFilterAdded(void* param, calldata_t* data) { if (!filter) { return; } - + self->connectFilterSignals(filter); OBSDataAutoRelease filterSettings = obs_source_get_settings(filter); From 30af0cbd9afb5ccb513d8b5c5e3310c42436e1ec Mon Sep 17 00:00:00 2001 From: ZyanKLee Date: Mon, 29 Mar 2021 10:06:08 +0200 Subject: [PATCH 266/278] allow inline build with obs-studio when replacing CMAKE_SOURCE_DIR with CMAKE_CURRENT_SOURCE_DIR in the CMakeLists.txt this should allow for building the plugin directly inside of obs-studio as well as separately --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0de5f1f1..b027a051 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,8 +72,8 @@ include_directories( "${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api" ${Qt5Core_INCLUDES} ${Qt5Widgets_INCLUDES} - "${CMAKE_SOURCE_DIR}/deps/asio/asio/include" - "${CMAKE_SOURCE_DIR}/deps/websocketpp") + "${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include" + "${CMAKE_CURRENT_SOURCE_DIR}/deps/websocketpp") target_link_libraries(obs-websocket libobs From b61211b4cb714cb8faf7ba40dc9d1dec6b32d79e Mon Sep 17 00:00:00 2001 From: Floris Bernard Date: Thu, 8 Apr 2021 17:42:01 +0200 Subject: [PATCH 267/278] Docs: fix typo in output properties --- docs/generated/comments.json | 4 ++-- docs/generated/protocol.md | 3 +-- src/WSRequestHandler_Outputs.cpp | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index f45b32cb..c6aaf88b 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -366,7 +366,7 @@ "{boolean} `flags.encoded` Output is encoded", "{boolean} `flags.multiTrack` Output uses several audio tracks", "{boolean} `flags.service` Output uses a service", - "{Object} `settings` Output name", + "{Object} `settings` Output settings", "{boolean} `active` Output status (active or not)", "{boolean} `reconnecting` Output reconnection status (reconnecting or not)", "{double} `congestion` Output congestion", @@ -433,7 +433,7 @@ { "type": "Object", "name": "settings", - "description": "Output name" + "description": "Output settings" }, { "type": "boolean", diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index bfcd2670..f4c1af02 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -348,7 +348,7 @@ These are complex types, such as `Source` and `Scene`, which are used as argumen | `flags.encoded` | _boolean_ | Output is encoded | | `flags.multiTrack` | _boolean_ | Output uses several audio tracks | | `flags.service` | _boolean_ | Output uses a service | -| `settings` | _Object_ | Output name | +| `settings` | _Object_ | Output settings | | `active` | _boolean_ | Output status (active or not) | | `reconnecting` | _boolean_ | Output reconnection status (reconnecting or not) | | `congestion` | _double_ | Output congestion | @@ -4523,4 +4523,3 @@ If your code needs to perform multiple successive T-Bar moves (e.g. : in an anim _No additional response items._ --- - diff --git a/src/WSRequestHandler_Outputs.cpp b/src/WSRequestHandler_Outputs.cpp index c30f9ef5..d87a8ca8 100644 --- a/src/WSRequestHandler_Outputs.cpp +++ b/src/WSRequestHandler_Outputs.cpp @@ -15,7 +15,7 @@ * @property {boolean} `flags.encoded` Output is encoded * @property {boolean} `flags.multiTrack` Output uses several audio tracks * @property {boolean} `flags.service` Output uses a service -* @property {Object} `settings` Output name +* @property {Object} `settings` Output settings * @property {boolean} `active` Output status (active or not) * @property {boolean} `reconnecting` Output reconnection status (reconnecting or not) * @property {double} `congestion` Output congestion From 1154ba5b141a4dccd1e4cbc64ca08f203300ed6f Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Fri, 9 Apr 2021 17:59:19 +0000 Subject: [PATCH 268/278] docs(ci): Update protocol.md - ffd89db [skip ci] --- docs/generated/protocol.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index f4c1af02..86e79b90 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -4523,3 +4523,4 @@ If your code needs to perform multiple successive T-Bar moves (e.g. : in an anim _No additional response items._ --- + From 30e2a5428fe41897c8e304621e0a3a0b99a5ccb7 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sun, 11 Apr 2021 20:21:18 -0700 Subject: [PATCH 269/278] CI: Update Qt windows installer download --- CI/install-qt-win.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/install-qt-win.cmd b/CI/install-qt-win.cmd index 4a131280..2fa20c8f 100644 --- a/CI/install-qt-win.cmd +++ b/CI/install-qt-win.cmd @@ -2,7 +2,7 @@ if exist %QtBaseDir% ( echo "Qt directory found. Download skipped." ) else ( echo "Qt directory not found. Downloading..." - curl -kLO http://home.tt2468.net:86/dl/Qt_5.15.2.7z -f --retry 5 -C - + curl -kLO https://tt2468.net/dl/Qt_5.15.2.7z -f --retry 5 -C - 7z x Qt_5.15.2.7z -o%QtBaseDir% ) dir %QtBaseDir% From ffb24da1357321a5af42d713fb2dc188d23b9653 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 13 Apr 2021 03:31:50 -0700 Subject: [PATCH 270/278] CI: Update MacOS Qt version to 5.15.2 --- CI/build-macos.sh | 2 +- CI/install-build-obs-macos.sh | 7 ++----- CI/install-dependencies-macos.sh | 24 ++++++++++++------------ CI/package-macos.sh | 8 ++++---- azure-pipelines.yml | 6 ++++++ 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/CI/build-macos.sh b/CI/build-macos.sh index f528ce2e..3a371a78 100755 --- a/CI/build-macos.sh +++ b/CI/build-macos.sh @@ -19,7 +19,7 @@ fi echo "[obs-websocket] Building 'obs-websocket' for macOS." mkdir -p build && cd build cmake .. \ - -DQTDIR=/usr/local/opt/qt \ + -DQTDIR=/tmp/obsdeps \ -DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \ -DLIBOBS_LIB=../../obs-studio/libobs \ -DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \ diff --git a/CI/install-build-obs-macos.sh b/CI/install-build-obs-macos.sh index 8a5eb145..7fea6fb8 100755 --- a/CI/install-build-obs-macos.sh +++ b/CI/install-build-obs-macos.sh @@ -20,10 +20,6 @@ if [ "${HAS_GIT}" = "" ]; then exit 1 fi -echo "[obs-websocket] Downloading and unpacking OBS dependencies" -wget --quiet --retry-connrefused --waitretry=1 https://github.com/obsproject/obs-deps/releases/download/2021-02-28/macos-deps-2021-02-28.tar.gz -tar -xf ./macos-deps-2021-02-28.tar.gz -C /tmp - # Build obs-studio cd .. echo "[obs-websocket] Cloning obs-studio from GitHub.." @@ -34,9 +30,10 @@ git checkout $OBSLatestTag mkdir build && cd build echo "[obs-websocket] Building obs-studio.." cmake .. \ + -DQTDIR=/tmp/obsdeps \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \ -DDISABLE_PLUGINS=true \ - -DENABLE_SCRIPTING=0 \ + -DENABLE_SCRIPTING=0 \ -DDepsPath=/tmp/obsdeps \ -DCMAKE_PREFIX_PATH=/usr/local/opt/qt/lib/cmake \ && make -j4 diff --git a/CI/install-dependencies-macos.sh b/CI/install-dependencies-macos.sh index 6991e098..9974fed4 100755 --- a/CI/install-dependencies-macos.sh +++ b/CI/install-dependencies-macos.sh @@ -1,7 +1,5 @@ #!/bin/sh - - OSTYPE=$(uname) if [ "${OSTYPE}" != "Darwin" ]; then @@ -20,7 +18,7 @@ fi echo "[obs-websocket] Updating Homebrew.." brew update >/dev/null echo "[obs-websocket] Checking installed Homebrew formulas.." -BREW_PACKAGES=$(brew list) +BREW_PACKAGES=$(brew list --formula) BREW_DEPENDENCIES="jack speexdsp ccache swig mbedtls" for DEPENDENCY in ${BREW_DEPENDENCIES}; do @@ -33,15 +31,6 @@ for DEPENDENCY in ${BREW_DEPENDENCIES}; do fi done -# qtwebsockets deps -echo "[obs-websocket] Installing obs-websocket dependency 'QT 5.10.1'.." - -brew install ./CI/macos/qt.rb - -# Pin this version of QT5 to avoid `brew upgrade` -# upgrading it to incompatible version -brew pin qt - # Fetch and install Packages app # =!= NOTICE =!= # Installs a LaunchDaemon under /Library/LaunchDaemons/fr.whitebox.packages.build.dispatcher.plist @@ -55,3 +44,14 @@ if [ "${HAS_PACKAGES}" = "" ]; then sudo hdiutil attach ./Packages.dmg sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target / fi + +# Qt deps +echo "[obs-websocket] Installing obs-websocket dependency 'Qt ${QT_VERSION}'.." +curl -L -O https://github.com/obsproject/obs-deps/releases/download/${OBS_DEPS_VERSION}/macos-qt-${QT_VERSION}-${OBS_DEPS_VERSION}.tar.gz +tar -xf ./macos-qt-${QT_VERSION}-${OBS_DEPS_VERSION}.tar.gz -C "/tmp" +xattr -r -d com.apple.quarantine /tmp/obsdeps + +# OBS Deps +echo "[obs-websocket] Downloading and unpacking OBS dependencies" +wget --quiet --retry-connrefused --waitretry=1 https://github.com/obsproject/obs-deps/releases/download/${OBS_DEPS_VERSION}/macos-deps-${OBS_DEPS_VERSION}.tar.gz +tar -xf ./macos-deps-${OBS_DEPS_VERSION}.tar.gz -C /tmp \ No newline at end of file diff --git a/CI/package-macos.sh b/CI/package-macos.sh index f309cf34..4363a8ab 100755 --- a/CI/package-macos.sh +++ b/CI/package-macos.sh @@ -20,13 +20,13 @@ VERSION="$GIT_HASH-$GIT_BRANCH_OR_TAG" FILENAME_UNSIGNED="obs-websocket-$VERSION-Unsigned.pkg" FILENAME="obs-websocket-$VERSION.pkg" -echo "[obs-websocket] Modifying obs-websocket.so" +echo "[obs-websocket] Modifying obs-websocket.so linking" install_name_tool \ - -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets \ + -change /tmp/obsdeps/lib/QtWidgets.framework/Versions/5/QtWidgets \ @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets \ - -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui \ + -change /tmp/obsdeps/lib/QtGui.framework/Versions/5/QtGui \ @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui \ - -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore \ + -change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore \ @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore \ ./build/obs-websocket.so diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 449bd137..d5da8c6f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -157,12 +157,18 @@ jobs: - job: 'Build_macOS' pool: vmImage: 'macos-10.14' + variables: + obsDepsVersion: '2020-12-22' + qtVersion: '5.15.2' steps: - checkout: self submodules: true - script: ./CI/install-dependencies-macos.sh displayName: 'Install dependencies' + env: + OBS_DEPS_VERSION: $(obsDepsVersion) + QT_VERSION: $(qtVersion) - script: ./CI/install-build-obs-macos.sh displayName: 'Build OBS' From 458d59b4013326d152edcc9eddf50ffb212bdd53 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 13 Apr 2021 05:04:08 -0700 Subject: [PATCH 271/278] CI: Refactor much of the MacOS CI --- CI/macos/Brewfile | 5 - .../build-plugin-macos.sh} | 2 - CI/{ => macos}/install-build-obs-macos.sh | 6 +- CI/{ => macos}/install-dependencies-macos.sh | 36 ++--- CI/{ => macos}/package-macos.sh | 1 - CI/macos/qt.rb | 145 ------------------ azure-pipelines.yml | 10 +- 7 files changed, 26 insertions(+), 179 deletions(-) rename CI/{build-macos.sh => macos/build-plugin-macos.sh} (91%) rename CI/{ => macos}/install-build-obs-macos.sh (89%) rename CI/{ => macos}/install-dependencies-macos.sh (69%) rename CI/{ => macos}/package-macos.sh (96%) delete mode 100644 CI/macos/qt.rb diff --git a/CI/macos/Brewfile b/CI/macos/Brewfile index 20fabb71..923af906 100644 --- a/CI/macos/Brewfile +++ b/CI/macos/Brewfile @@ -1,10 +1,5 @@ -tap "akeru-inc/tap" brew "jack" brew "speexdsp" brew "cmake" brew "freetype" brew "fdk-aac" -brew "https://gist.githubusercontent.com/DDRBoxman/9c7a2b08933166f4b61ed9a44b242609/raw/ef4de6c587c6bd7f50210eccd5bd51ff08e6de13/qt.rb" -brew "swig", link: false -brew "https://gist.githubusercontent.com/DDRBoxman/4cada55c51803a2f963fa40ce55c9d3e/raw/572c67e908bfbc1bcb8c476ea77ea3935133f5b5/swig.rb" -brew "akeru-inc/tap/xcnotary" \ No newline at end of file diff --git a/CI/build-macos.sh b/CI/macos/build-plugin-macos.sh similarity index 91% rename from CI/build-macos.sh rename to CI/macos/build-plugin-macos.sh index 3a371a78..260fe50f 100755 --- a/CI/build-macos.sh +++ b/CI/macos/build-plugin-macos.sh @@ -14,8 +14,6 @@ if [ "${HAS_CMAKE}" = "" ]; then exit 1 fi -#export QT_PREFIX="$(find /usr/local/Cellar/qt5 -d 1 | tail -n 1)" - echo "[obs-websocket] Building 'obs-websocket' for macOS." mkdir -p build && cd build cmake .. \ diff --git a/CI/install-build-obs-macos.sh b/CI/macos/install-build-obs-macos.sh similarity index 89% rename from CI/install-build-obs-macos.sh rename to CI/macos/install-build-obs-macos.sh index 7fea6fb8..a6e8ca82 100755 --- a/CI/install-build-obs-macos.sh +++ b/CI/macos/install-build-obs-macos.sh @@ -31,9 +31,9 @@ mkdir build && cd build echo "[obs-websocket] Building obs-studio.." cmake .. \ -DQTDIR=/tmp/obsdeps \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \ + -DDepsPath=/tmp/obsdeps \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \ -DDISABLE_PLUGINS=true \ -DENABLE_SCRIPTING=0 \ - -DDepsPath=/tmp/obsdeps \ - -DCMAKE_PREFIX_PATH=/usr/local/opt/qt/lib/cmake \ + -DCMAKE_PREFIX_PATH=/tmp/obsdeps/lib/cmake \ && make -j4 diff --git a/CI/install-dependencies-macos.sh b/CI/macos/install-dependencies-macos.sh similarity index 69% rename from CI/install-dependencies-macos.sh rename to CI/macos/install-dependencies-macos.sh index 9974fed4..872dcca5 100755 --- a/CI/install-dependencies-macos.sh +++ b/CI/macos/install-dependencies-macos.sh @@ -14,22 +14,22 @@ if [ "${HAS_BREW}" = "" ]; then exit 1 fi -# OBS Studio deps +# OBS Studio Brew Deps echo "[obs-websocket] Updating Homebrew.." brew update >/dev/null echo "[obs-websocket] Checking installed Homebrew formulas.." -BREW_PACKAGES=$(brew list --formula) -BREW_DEPENDENCIES="jack speexdsp ccache swig mbedtls" -for DEPENDENCY in ${BREW_DEPENDENCIES}; do - if echo "${BREW_PACKAGES}" | grep -q "^${DEPENDENCY}\$"; then - echo "[obs-websocket] Upgrading OBS-Studio dependency '${DEPENDENCY}'.." - brew upgrade ${DEPENDENCY} 2>/dev/null - else - echo "[obs-websocket] Installing OBS-Studio dependency '${DEPENDENCY}'.." - brew install ${DEPENDENCY} 2>/dev/null - fi -done +if [ -d /usr/local/opt/openssl@1.0.2t ]; then + brew uninstall openssl@1.0.2t + brew untap local/openssl +fi + +if [ -d /usr/local/opt/python@2.7.17 ]; then + brew uninstall python@2.7.17 + brew untap local/python2 +fi + +brew bundle --file ./CI/macos/Brewfile # Fetch and install Packages app # =!= NOTICE =!= @@ -45,13 +45,13 @@ if [ "${HAS_PACKAGES}" = "" ]; then sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target / fi +# OBS Deps +echo "[obs-websocket] Installing obs-websocket dependency 'OBS Deps ${OBS_DEPS_VERSION}'.." +wget --quiet --retry-connrefused --waitretry=1 https://github.com/obsproject/obs-deps/releases/download/${OBS_DEPS_VERSION}/macos-deps-${OBS_DEPS_VERSION}.tar.gz +tar -xf ./macos-deps-${OBS_DEPS_VERSION}.tar.gz -C /tmp + # Qt deps echo "[obs-websocket] Installing obs-websocket dependency 'Qt ${QT_VERSION}'.." curl -L -O https://github.com/obsproject/obs-deps/releases/download/${OBS_DEPS_VERSION}/macos-qt-${QT_VERSION}-${OBS_DEPS_VERSION}.tar.gz tar -xf ./macos-qt-${QT_VERSION}-${OBS_DEPS_VERSION}.tar.gz -C "/tmp" -xattr -r -d com.apple.quarantine /tmp/obsdeps - -# OBS Deps -echo "[obs-websocket] Downloading and unpacking OBS dependencies" -wget --quiet --retry-connrefused --waitretry=1 https://github.com/obsproject/obs-deps/releases/download/${OBS_DEPS_VERSION}/macos-deps-${OBS_DEPS_VERSION}.tar.gz -tar -xf ./macos-deps-${OBS_DEPS_VERSION}.tar.gz -C /tmp \ No newline at end of file +xattr -r -d com.apple.quarantine /tmp/obsdeps \ No newline at end of file diff --git a/CI/package-macos.sh b/CI/macos/package-macos.sh similarity index 96% rename from CI/package-macos.sh rename to CI/macos/package-macos.sh index 4363a8ab..db8193b4 100755 --- a/CI/package-macos.sh +++ b/CI/macos/package-macos.sh @@ -10,7 +10,6 @@ if [ "${OSTYPE}" != "Darwin" ]; then fi echo "[obs-websocket] Preparing package build" -export QT_CELLAR_PREFIX="$(/usr/bin/find /usr/local/Cellar/qt -d 1 | sort -t '.' -k 1,1n -k 2,2n -k 3,3n | tail -n 1)" GIT_HASH=$(git rev-parse --short HEAD) GIT_BRANCH_OR_TAG=$(git name-rev --name-only HEAD | awk -F/ '{print $NF}') diff --git a/CI/macos/qt.rb b/CI/macos/qt.rb deleted file mode 100644 index 4405a240..00000000 --- a/CI/macos/qt.rb +++ /dev/null @@ -1,145 +0,0 @@ -# Patches for Qt must be at the very least submitted to Qt's Gerrit codereview -# rather than their bug-report Jira. The latter is rarely reviewed by Qt. -class Qt < Formula - desc "Cross-platform application and UI framework" - homepage "https://www.qt.io/" - url "https://download.qt.io/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz" - mirror "https://www.mirrorservice.org/sites/download.qt-project.org/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz" - sha256 "05ffba7b811b854ed558abf2be2ddbd3bb6ddd0b60ea4b5da75d277ac15e740a" - head "https://code.qt.io/qt/qt5.git", :branch => "5.10", :shallow => false - - bottle do - sha256 "8b4bad005596a5f8790150fe455db998ac2406f4e0f04140d6656205d844d266" => :high_sierra - sha256 "9c488554935fb573554a4e36d36d3c81e47245b7fefc4b61edef894e67ba1740" => :sierra - sha256 "c0407afba5951df6cc4c6f6c1c315972bd41c99cecb4e029919c4c15ab6f7bdc" => :el_capitan - end - - keg_only "Qt 5 has CMake issues when linked" - - option "with-docs", "Build documentation" - option "with-examples", "Build examples" - option "without-proprietary-codecs", "Don't build with proprietary codecs (e.g. mp3)" - - # OS X 10.7 Lion is still supported in Qt 5.5, but is no longer a reference - # configuration and thus untested in practice. Builds on OS X 10.7 have been - # reported to fail: . - # depends_on :macos => :mountain_lion - - depends_on "pkg-config" => :build - depends_on :xcode => :build - depends_on "mysql" => :optional - depends_on "postgresql" => :optional - - # Restore `.pc` files for framework-based build of Qt 5 on OS X. This - # partially reverts merged - # between the 5.5.1 and 5.6.0 releases. (Remove this as soon as feasible!) - # - # Core formulae known to fail without this patch (as of 2016-10-15): - # * gnuplot (with `--with-qt` option) - # * mkvtoolnix (with `--with-qt` option, silent build failure) - # * poppler (with `--with-qt` option) - patch do - url "https://raw.githubusercontent.com/Homebrew/formula-patches/e8fe6567/qt5/restore-pc-files.patch" - sha256 "48ff18be2f4050de7288bddbae7f47e949512ac4bcd126c2f504be2ac701158b" - end - - def install - args = %W[ - -verbose - -prefix #{prefix} - -release - -opensource -confirm-license - -system-zlib - -qt-libpng - -qt-libjpeg - -qt-freetype - -qt-pcre - -nomake tests - -no-rpath - -pkg-config - -dbus-runtime - ] - - args << "-nomake" << "examples" if build.without? "examples" - - if build.with? "mysql" - args << "-plugin-sql-mysql" - (buildpath/"brew_shim/mysql_config").write <<~EOS - #!/bin/sh - if [ x"$1" = x"--libs" ]; then - mysql_config --libs | sed "s/-lssl -lcrypto//" - else - exec mysql_config "$@" - fi - EOS - chmod 0755, "brew_shim/mysql_config" - args << "-mysql_config" << buildpath/"brew_shim/mysql_config" - end - - args << "-plugin-sql-psql" if build.with? "postgresql" - args << "-proprietary-codecs" if build.with? "proprietary-codecs" - - system "./configure", *args - system "make" - ENV.deparallelize - system "make", "install" - - if build.with? "docs" - system "make", "docs" - system "make", "install_docs" - end - - # Some config scripts will only find Qt in a "Frameworks" folder - frameworks.install_symlink Dir["#{lib}/*.framework"] - - # The pkg-config files installed suggest that headers can be found in the - # `include` directory. Make this so by creating symlinks from `include` to - # the Frameworks' Headers folders. - Pathname.glob("#{lib}/*.framework/Headers") do |path| - include.install_symlink path => path.parent.basename(".framework") - end - - # Move `*.app` bundles into `libexec` to expose them to `brew linkapps` and - # because we don't like having them in `bin`. - # (Note: This move breaks invocation of Assistant via the Help menu - # of both Designer and Linguist as that relies on Assistant being in `bin`.) - libexec.mkpath - Pathname.glob("#{bin}/*.app") { |app| mv app, libexec } - end - - def caveats; <<~EOS - We agreed to the Qt opensource license for you. - If this is unacceptable you should uninstall. - EOS - end - - test do - (testpath/"hello.pro").write <<~EOS - QT += core - QT -= gui - TARGET = hello - CONFIG += console - CONFIG -= app_bundle - TEMPLATE = app - SOURCES += main.cpp - EOS - - (testpath/"main.cpp").write <<~EOS - #include - #include - - int main(int argc, char *argv[]) - { - QCoreApplication a(argc, argv); - qDebug() << "Hello World!"; - return 0; - } - EOS - - system bin/"qmake", testpath/"hello.pro" - system "make" - assert_predicate testpath/"hello", :exist? - assert_predicate testpath/"main.o", :exist? - system "./hello" - end -end \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d5da8c6f..4103d3af 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -156,7 +156,7 @@ jobs: - job: 'Build_macOS' pool: - vmImage: 'macos-10.14' + vmImage: 'macOS-10.15' variables: obsDepsVersion: '2020-12-22' qtVersion: '5.15.2' @@ -164,16 +164,16 @@ jobs: - checkout: self submodules: true - - script: ./CI/install-dependencies-macos.sh + - script: ./CI/macos/install-dependencies-macos.sh displayName: 'Install dependencies' env: OBS_DEPS_VERSION: $(obsDepsVersion) QT_VERSION: $(qtVersion) - - script: ./CI/install-build-obs-macos.sh + - script: ./CI/macos/install-build-obs-macos.sh displayName: 'Build OBS' - - script: ./CI/build-macos.sh + - script: ./CI/macos/build-plugin-macos.sh displayName: 'Build obs-websocket' - task: InstallAppleCertificate@2 @@ -183,7 +183,7 @@ jobs: certSecureFile: 'Certificates.p12' certPwd: $(secrets.macOS.certificatesImportPassword) - - script: ./CI/package-macos.sh + - script: ./CI/macos/package-macos.sh displayName: 'Package obs-websocket' env: RELEASE_MODE: $(isReleaseMode) From 415fb732454370a4dd7791b1370b4b4a8c92ff75 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 13 Apr 2021 05:35:13 -0700 Subject: [PATCH 272/278] CI: Update and improve Ubuntu build --- CI/{build-ubuntu.sh => linux/build-plugin-ubuntu.sh} | 3 +++ CI/{ => linux}/install-dependencies-ubuntu.sh | 9 ++++++++- CI/{package-ubuntu.sh => linux/package-plugin-ubuntu.sh} | 4 ++-- azure-pipelines.yml | 8 ++++---- 4 files changed, 17 insertions(+), 7 deletions(-) rename CI/{build-ubuntu.sh => linux/build-plugin-ubuntu.sh} (57%) rename CI/{ => linux}/install-dependencies-ubuntu.sh (53%) rename CI/{package-ubuntu.sh => linux/package-plugin-ubuntu.sh} (81%) diff --git a/CI/build-ubuntu.sh b/CI/linux/build-plugin-ubuntu.sh similarity index 57% rename from CI/build-ubuntu.sh rename to CI/linux/build-plugin-ubuntu.sh index 498840ef..584242f4 100755 --- a/CI/build-ubuntu.sh +++ b/CI/linux/build-plugin-ubuntu.sh @@ -1,6 +1,9 @@ #!/bin/sh set -ex +echo "[obs-websocket] Running CMake.." mkdir build && cd build cmake -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=true .. + +echo "[obs-websocket] Building plugin.." make -j4 diff --git a/CI/install-dependencies-ubuntu.sh b/CI/linux/install-dependencies-ubuntu.sh similarity index 53% rename from CI/install-dependencies-ubuntu.sh rename to CI/linux/install-dependencies-ubuntu.sh index 4ed044da..43cd58f1 100755 --- a/CI/install-dependencies-ubuntu.sh +++ b/CI/linux/install-dependencies-ubuntu.sh @@ -1,9 +1,11 @@ #!/bin/sh set -ex +echo "[obs-websocket] Installing obs-studio PPA and updates.." sudo add-apt-repository -y ppa:obsproject/obs-studio sudo apt-get -qq update +echo "[obs-websocket] Installing obs-studio and dependencies.." sudo apt-get install -y \ libc-dev-bin \ libc6-dev git \ @@ -13,7 +15,12 @@ sudo apt-get install -y \ obs-studio \ qtbase5-dev +echo "[obs-websocket] Installed OBS Version: $(obs --version)" + +ls /usr/include/ +ls /usr/include/obs/ + # Dirty hack -sudo wget -O /usr/include/obs/obs-frontend-api.h https://raw.githubusercontent.com/obsproject/obs-studio/26.0.0/UI/obs-frontend-api/obs-frontend-api.h +sudo wget -O /usr/include/obs/obs-frontend-api.h https://raw.githubusercontent.com/obsproject/obs-studio/26.1.2/UI/obs-frontend-api/obs-frontend-api.h sudo ldconfig diff --git a/CI/package-ubuntu.sh b/CI/linux/package-plugin-ubuntu.sh similarity index 81% rename from CI/package-ubuntu.sh rename to CI/linux/package-plugin-ubuntu.sh index 517e0054..fb3aac46 100755 --- a/CI/package-ubuntu.sh +++ b/CI/linux/package-plugin-ubuntu.sh @@ -7,7 +7,7 @@ export PKG_VERSION="1-$GIT_HASH-$BRANCH_SHORT_NAME-git" if [[ $BRANCH_FULL_NAME =~ ^refs/tags/ ]]; then export PKG_VERSION="$BRANCH_SHORT_NAME" - echo "[obs-websocket] Branch is a tag. Setting version to $PKG_VERSION." + echo "[obs-websocket] Branch is a tag. Setting version to $PKG_VERSION." fi cd ./build @@ -18,7 +18,7 @@ PAGER="cat" sudo checkinstall -y --type=debian --fstrans=no --nodoc \ --pkglicense="GPLv2.0" --maintainer="stephane.lepin@gmail.com" \ --pkggroup="video" \ --pkgsource="https://github.com/Palakis/obs-websocket" \ - --requires="obs-studio \(\>= 25.0.7\), libqt5core5a, libqt5widgets5, qt5-image-formats-plugins" \ + --requires="obs-studio \(\>= 26.1.0\), libqt5core5a, libqt5widgets5, qt5-image-formats-plugins" \ --pakdir="../package" sudo chmod ao+r ../package/* diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4103d3af..ff54ad50 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -131,7 +131,7 @@ jobs: - job: 'Build_Linux' pool: - vmImage: 'ubuntu-18.04' + vmImage: 'ubuntu-20.04' variables: BUILD_REASON: $(Build.Reason) BRANCH_SHORT_NAME: $(Build.SourceBranchName) @@ -140,13 +140,13 @@ jobs: - checkout: self submodules: true - - script: ./CI/install-dependencies-ubuntu.sh + - script: ./CI/linux/install-dependencies-ubuntu.sh displayName: 'Install dependencies' - - script: ./CI/build-ubuntu.sh + - script: ./CI/linux/build-plugin-ubuntu.sh displayName: 'Build obs-websocket' - - script: ./CI/package-ubuntu.sh + - script: ./CI/linux/package-plugin-ubuntu.sh displayName: 'Package obs-websocket' - task: PublishBuildArtifacts@1 From 1b61bd95519e65c8786ae3a03f39187c34c2a324 Mon Sep 17 00:00:00 2001 From: Dominik Nakamura Date: Tue, 13 Apr 2021 22:24:20 +0900 Subject: [PATCH 273/278] Remove unnecessary files and changes --- docs/generated/comments.json | 8 +- docs/generated/protocol.md | 4 +- docs/npm-shrinkwrap.json | 5168 ---------------------------------- 3 files changed, 6 insertions(+), 5174 deletions(-) delete mode 100644 docs/npm-shrinkwrap.json diff --git a/docs/generated/comments.json b/docs/generated/comments.json index 8f050ec7..f45b32cb 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -861,7 +861,7 @@ "{String} `name` Transition name.", "{String} `type` Transition type.", "{int} `duration` Transition duration (in milliseconds). Will be -1 for any transition with a fixed duration, such as a Stinger, due to limitations of the OBS API.", - "{String (optional)} `from-scene` Source scene of the transition", + "{String} `from-scene` Source scene of the transition", "{String} `to-scene` Destination scene of the transition" ], "api": "events", @@ -885,7 +885,7 @@ "description": "Transition duration (in milliseconds). Will be -1 for any transition with a fixed duration, such as a Stinger, due to limitations of the OBS API." }, { - "type": "String (optional)", + "type": "String", "name": "from-scene", "description": "Source scene of the transition" }, @@ -989,7 +989,7 @@ "{String} `name` Transition name.", "{String} `type` Transition type.", "{int} `duration` Transition duration (in milliseconds).", - "{String (optional)} `from-scene` Source scene of the transition", + "{String} `from-scene` Source scene of the transition", "{String} `to-scene` Destination scene of the transition" ], "api": "events", @@ -1013,7 +1013,7 @@ "description": "Transition duration (in milliseconds)." }, { - "type": "String (optional)", + "type": "String", "name": "from-scene", "description": "Source scene of the transition" }, diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index a1e3b500..bfcd2670 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -516,7 +516,7 @@ A transition (other than "cut") has begun. | `name` | _String_ | Transition name. | | `type` | _String_ | Transition type. | | `duration` | _int_ | Transition duration (in milliseconds). Will be -1 for any transition with a fixed duration, such as a Stinger, due to limitations of the OBS API. | -| `from-scene` | _String (optional)_ | Source scene of the transition | +| `from-scene` | _String_ | Source scene of the transition | | `to-scene` | _String_ | Destination scene of the transition | @@ -556,7 +556,7 @@ A stinger transition has finished playing its video. | `name` | _String_ | Transition name. | | `type` | _String_ | Transition type. | | `duration` | _int_ | Transition duration (in milliseconds). | -| `from-scene` | _String (optional)_ | Source scene of the transition | +| `from-scene` | _String_ | Source scene of the transition | | `to-scene` | _String_ | Destination scene of the transition | diff --git a/docs/npm-shrinkwrap.json b/docs/npm-shrinkwrap.json deleted file mode 100644 index 2e8c2392..00000000 --- a/docs/npm-shrinkwrap.json +++ /dev/null @@ -1,5168 +0,0 @@ -{ - "name": "obs-websocket-docs", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "obs-websocket-docs", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "glob": "^7.1.2", - "handlebars": "^4.0.10", - "handlebars-helpers": "^0.9.6", - "markdown-toc": "^1.1.0", - "parse-comments": "^0.4.3" - } - }, - "node_modules/ansi-bgblack": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgblack/-/ansi-bgblack-0.1.1.tgz", - "integrity": "sha1-poulAHiHcBtqr74/oNrf36juPKI=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-bgblue": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgblue/-/ansi-bgblue-0.1.1.tgz", - "integrity": "sha1-Z73ATtybm1J4lp2hlt6j11yMNhM=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-bgcyan": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgcyan/-/ansi-bgcyan-0.1.1.tgz", - "integrity": "sha1-WEiUJWAL3p9VBwaN2Wnr/bUP52g=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-bggreen": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bggreen/-/ansi-bggreen-0.1.1.tgz", - "integrity": "sha1-TjGRJIUplD9DIelr8THRwTgWr0k=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-bgmagenta": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgmagenta/-/ansi-bgmagenta-0.1.1.tgz", - "integrity": "sha1-myhDLAduqpmUGGcqPvvhk5HCx6E=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-bgred": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgred/-/ansi-bgred-0.1.1.tgz", - "integrity": "sha1-p2+Sg4OCukMpCmwXeEJPmE1vEEE=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-bgwhite": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgwhite/-/ansi-bgwhite-0.1.1.tgz", - "integrity": "sha1-ZQRlE3elim7OzQMxmU5IAljhG6g=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-bgyellow": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgyellow/-/ansi-bgyellow-0.1.1.tgz", - "integrity": "sha1-w/4usIzUdmSAKeaHTRWgs49h1E8=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-black": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-black/-/ansi-black-0.1.1.tgz", - "integrity": "sha1-9hheiJNgslRaHsUMC/Bj/EMDJFM=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-blue": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-blue/-/ansi-blue-0.1.1.tgz", - "integrity": "sha1-FbgEmQ6S/JyoxUds6PaZd3wh7b8=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-bold": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bold/-/ansi-bold-0.1.1.tgz", - "integrity": "sha1-PmOVCvWswq4uZw5vZ96xFdGl9QU=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-colors": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-0.2.0.tgz", - "integrity": "sha1-csMd4qDZoszQysMMyYI+6y9kNLU=", - "dependencies": { - "ansi-bgblack": "^0.1.1", - "ansi-bgblue": "^0.1.1", - "ansi-bgcyan": "^0.1.1", - "ansi-bggreen": "^0.1.1", - "ansi-bgmagenta": "^0.1.1", - "ansi-bgred": "^0.1.1", - "ansi-bgwhite": "^0.1.1", - "ansi-bgyellow": "^0.1.1", - "ansi-black": "^0.1.1", - "ansi-blue": "^0.1.1", - "ansi-bold": "^0.1.1", - "ansi-cyan": "^0.1.1", - "ansi-dim": "^0.1.1", - "ansi-gray": "^0.1.1", - "ansi-green": "^0.1.1", - "ansi-grey": "^0.1.1", - "ansi-hidden": "^0.1.1", - "ansi-inverse": "^0.1.1", - "ansi-italic": "^0.1.1", - "ansi-magenta": "^0.1.1", - "ansi-red": "^0.1.1", - "ansi-reset": "^0.1.1", - "ansi-strikethrough": "^0.1.1", - "ansi-underline": "^0.1.1", - "ansi-white": "^0.1.1", - "ansi-yellow": "^0.1.1", - "lazy-cache": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-cyan": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", - "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-dim": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-dim/-/ansi-dim-0.1.1.tgz", - "integrity": "sha1-QN5MYDqoCG2Oeoa4/5mNXDbu/Ww=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-green": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-green/-/ansi-green-0.1.1.tgz", - "integrity": "sha1-il2al55FjVfEDjNYCzc5C44Q0Pc=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-grey": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-grey/-/ansi-grey-0.1.1.tgz", - "integrity": "sha1-WdmLasK6GfilF5jphT+6eDOaM8E=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-hidden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-hidden/-/ansi-hidden-0.1.1.tgz", - "integrity": "sha1-7WpMSY0rt8uyidvyqNHcyFZ/rg8=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-inverse": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-inverse/-/ansi-inverse-0.1.1.tgz", - "integrity": "sha1-tq9Fgm/oJr+1KKbHmIV5Q1XM0mk=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-italic": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-italic/-/ansi-italic-0.1.1.tgz", - "integrity": "sha1-EEdDRj9iXBQqA2c5z4XtpoiYbyM=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-magenta": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-magenta/-/ansi-magenta-0.1.1.tgz", - "integrity": "sha1-BjtboW+z8j4c/aKwfAqJ3hHkMK4=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-red": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", - "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-reset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-reset/-/ansi-reset-0.1.1.tgz", - "integrity": "sha1-5+cSksPH3c1NYu9KbHwFmAkRw7c=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-strikethrough": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-strikethrough/-/ansi-strikethrough-0.1.1.tgz", - "integrity": "sha1-2Eh3FAss/wfRyT685pkE9oiF5Wg=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-underline": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-underline/-/ansi-underline-0.1.1.tgz", - "integrity": "sha1-38kg9Ml7WXfqFi34/7mIMIqqcaQ=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-white": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-white/-/ansi-white-0.1.1.tgz", - "integrity": "sha1-nHe3wZPF7pkuYBHTbsTJIbRXiUQ=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-yellow": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-yellow/-/ansi-yellow-0.1.1.tgz", - "integrity": "sha1-y5NW8vRscy8OMZnmEClVp32oPB0=", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" - }, - "node_modules/array-sort": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-0.1.4.tgz", - "integrity": "sha512-BNcM+RXxndPxiZ2rd76k6nyQLRZr2/B/sdi8pQ+Joafr5AH279L40dfokSUTp8O+AaqYjXWhblBWa2st2nc4fQ==", - "dependencies": { - "default-compare": "^1.0.0", - "get-value": "^2.0.6", - "kind-of": "^5.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arrayify-compact": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/arrayify-compact/-/arrayify-compact-0.1.1.tgz", - "integrity": "sha512-3R2V/8ixAhDXxyeWZ9aVzncUscrASg1TPrVsUgkxaMb0/WJLo5u769wYYf916OKqRCF+GKXaosouk1ixIfX89g==", - "dependencies": { - "array-flatten": "^2.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/autolinker": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz", - "integrity": "sha1-BlK0kYgYefB3XazgzcoyM5QqTkc=", - "dependencies": { - "gulp-header": "^1.7.1" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/braces/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/coffee-script": { - "version": "1.12.7", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", - "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==", - "deprecated": "CoffeeScript on NPM has moved to \"coffeescript\" (no hyphen)", - "bin": { - "cake": "bin/cake", - "coffee": "bin/coffee" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-with-sourcemaps": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", - "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", - "dependencies": { - "source-map": "^0.6.1" - } - }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "node_modules/create-frame": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/create-frame/-/create-frame-1.0.0.tgz", - "integrity": "sha1-i5XyaR4ySbYIBEPjPQutn49pdao=", - "dependencies": { - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "isobject": "^3.0.0", - "lazy-cache": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/create-frame/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/create-frame/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/create-frame/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/create-frame/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/create-frame/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/create-frame/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/date.js": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/date.js/-/date.js-0.3.3.tgz", - "integrity": "sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw==", - "dependencies": { - "debug": "~3.1.0" - } - }, - "node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/default-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", - "dependencies": { - "kind-of": "^5.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/diacritics-map": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz", - "integrity": "sha1-bfwP+dAQAKLt8oZTccrDFulJd68=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=" - }, - "node_modules/error-symbol": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/error-symbol/-/error-symbol-0.1.0.tgz", - "integrity": "sha1-Ck2uN9YA0VopukU9jvkg8YRDM/Y=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dependencies": { - "fill-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extract-comments": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/extract-comments/-/extract-comments-0.7.3.tgz", - "integrity": "sha1-HF3sFzAHLFsM36VVfl9X9pJ3/DQ=", - "dependencies": { - "is-whitespace": "^0.3.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/falsey": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/falsey/-/falsey-0.3.2.tgz", - "integrity": "sha512-lxEuefF5MBIVDmE6XeqCdM4BWk1+vYmGZtkbKZ/VFcg6uBBw6fXNEbWmxCjDdQlFc9hy450nkiWwM3VAW6G1qg==", - "dependencies": { - "kind-of": "^5.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "dependencies": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range/node_modules/is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dependencies": { - "for-in": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dependencies": { - "map-cache": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "node_modules/get-object": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/get-object/-/get-object-0.2.0.tgz", - "integrity": "sha1-2S/31RkMZFMM2gVD2sY6PUf+jAw=", - "dependencies": { - "is-number": "^2.0.2", - "isobject": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/get-object/node_modules/is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/get-object/node_modules/isobject": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-0.2.0.tgz", - "integrity": "sha1-o0MhkvObkQtfAsyYlIeDbscKqF4=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/get-object/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gfm-code-block-regex": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/gfm-code-block-regex/-/gfm-code-block-regex-0.2.3.tgz", - "integrity": "sha1-C5/mis8sWlAi/W0tQYpjT26XwRA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gfm-code-blocks": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/gfm-code-blocks/-/gfm-code-blocks-0.2.2.tgz", - "integrity": "sha1-KZnYFIxFG4ZjLHH6bevhFe4HH/4=", - "dependencies": { - "gfm-code-block-regex": "^0.2.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gray-matter": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz", - "integrity": "sha1-MELZrewqHe1qdwep7SOA+KF6Qw4=", - "dependencies": { - "ansi-red": "^0.1.1", - "coffee-script": "^1.12.4", - "extend-shallow": "^2.0.1", - "js-yaml": "^3.8.1", - "toml": "^2.3.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-header": { - "version": "1.8.12", - "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz", - "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==", - "deprecated": "Removed event-stream from gulp-header", - "dependencies": { - "concat-with-sourcemaps": "*", - "lodash.template": "^4.4.0", - "through2": "^2.0.0" - } - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars-helper-create-frame": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/handlebars-helper-create-frame/-/handlebars-helper-create-frame-0.1.0.tgz", - "integrity": "sha1-iqUdEK62QI/MZgXUDXc1YohIegM=", - "dependencies": { - "create-frame": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/handlebars-helpers": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/handlebars-helpers/-/handlebars-helpers-0.9.8.tgz", - "integrity": "sha512-N9MoNopXTOzNv9L2oDFUo1ZhWTzUd8YURVrksZaXVRybgs1JFnUXohCnFTOJL8m4t+jKn1xU6Vi7qxtCu4mRsg==", - "dependencies": { - "arr-flatten": "^1.0.3", - "array-sort": "^0.1.2", - "create-frame": "^1.0.0", - "define-property": "^1.0.0", - "falsey": "^0.3.0", - "for-in": "^1.0.2", - "for-own": "^1.0.0", - "get-object": "^0.2.0", - "get-value": "^2.0.6", - "handlebars": "^4.0.10", - "handlebars-helper-create-frame": "^0.1.0", - "handlebars-utils": "^1.0.1", - "has-value": "^1.0.0", - "helper-date": "^1.0.0", - "helper-markdown": "^0.2.2", - "helper-md": "^0.2.2", - "html-tag": "^1.0.0", - "is-even": "^1.0.0", - "is-glob": "^3.1.0", - "is-number": "^3.0.0", - "kind-of": "^5.0.0", - "lazy-cache": "^2.0.2", - "logging-helpers": "^1.0.0", - "micromatch": "^3.0.3", - "relative": "^3.0.2", - "striptags": "^3.0.1", - "to-gfm-code-block": "^0.1.1", - "year": "^0.2.1" - }, - "engines": { - "node": ">=5" - } - }, - "node_modules/handlebars-utils": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/handlebars-utils/-/handlebars-utils-1.0.6.tgz", - "integrity": "sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw==", - "dependencies": { - "kind-of": "^6.0.0", - "typeof-article": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/handlebars-utils/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/helper-date": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/helper-date/-/helper-date-1.0.1.tgz", - "integrity": "sha512-wU3VOwwTJvGr/w5rZr3cprPHO+hIhlblTJHD6aFBrKLuNbf4lAmkawd2iK3c6NbJEvY7HAmDpqjOFSI5/+Ey2w==", - "dependencies": { - "date.js": "^0.3.1", - "handlebars-utils": "^1.0.4", - "moment": "^2.18.1" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/helper-markdown": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/helper-markdown/-/helper-markdown-0.2.2.tgz", - "integrity": "sha1-ONt/dxhJ4wrpXJL8AhuutT8uMEA=", - "dependencies": { - "isobject": "^2.0.0", - "mixin-deep": "^1.1.3", - "remarkable": "^1.6.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/helper-markdown/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/helper-md": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/helper-md/-/helper-md-0.2.2.tgz", - "integrity": "sha1-wfWdflW7riM2L9ig6XFgeuxp1B8=", - "dependencies": { - "ent": "^2.2.0", - "extend-shallow": "^2.0.1", - "fs-exists-sync": "^0.1.0", - "remarkable": "^1.6.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/html-tag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/html-tag/-/html-tag-1.0.0.tgz", - "integrity": "sha1-leVhKuyCvqko7URZX4VBRen34LU=", - "dependencies": { - "isobject": "^3.0.0", - "void-elements": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inflection": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", - "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=", - "engines": [ - "node >= 0.4.0" - ] - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/info-symbol": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/info-symbol/-/info-symbol-0.1.0.tgz", - "integrity": "sha1-J4QdcoZ920JCzWEtecEGM4gcang=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-descriptor/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-even": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-even/-/is-even-1.0.0.tgz", - "integrity": "sha1-drUFX7rY0pSoa2qUkBXhyXtxfAY=", - "dependencies": { - "is-odd": "^0.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-odd": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-0.1.2.tgz", - "integrity": "sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc=", - "dependencies": { - "is-number": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-whitespace": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", - "integrity": "sha1-Fjnssb4DauxppUy7QBz77XEUq38=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lazy-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", - "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", - "dependencies": { - "set-getter": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/list-item": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz", - "integrity": "sha1-DGXQDih8tmPMs8s4Sad+iewmilY=", - "dependencies": { - "expand-range": "^1.8.1", - "extend-shallow": "^2.0.1", - "is-number": "^2.1.0", - "repeat-string": "^1.5.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/list-item/node_modules/is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/list-item/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" - }, - "node_modules/lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" - }, - "node_modules/lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "dependencies": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "node_modules/lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "dependencies": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "node_modules/log-ok": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/log-ok/-/log-ok-0.1.1.tgz", - "integrity": "sha1-vqPdNqzQuKckDXhza1uXxlREozQ=", - "dependencies": { - "ansi-green": "^0.1.1", - "success-symbol": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/log-utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/log-utils/-/log-utils-0.2.1.tgz", - "integrity": "sha1-pMIXoN2aUFFdm5ICBgkas9TgMc8=", - "dependencies": { - "ansi-colors": "^0.2.0", - "error-symbol": "^0.1.0", - "info-symbol": "^0.1.0", - "log-ok": "^0.1.1", - "success-symbol": "^0.1.0", - "time-stamp": "^1.0.1", - "warning-symbol": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/logging-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/logging-helpers/-/logging-helpers-1.0.0.tgz", - "integrity": "sha512-qyIh2goLt1sOgQQrrIWuwkRjUx4NUcEqEGAcYqD8VOnOC6ItwkrVE8/tA4smGpjzyp4Svhc6RodDp9IO5ghpyA==", - "dependencies": { - "isobject": "^3.0.0", - "log-utils": "^0.2.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/markdown-link": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz", - "integrity": "sha1-MsXGUZmmRXMWMi0eQinRNAfIx88=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/markdown-toc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz", - "integrity": "sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg==", - "dependencies": { - "concat-stream": "^1.5.2", - "diacritics-map": "^0.1.0", - "gray-matter": "^2.1.0", - "lazy-cache": "^2.0.2", - "list-item": "^1.1.1", - "markdown-link": "^0.1.1", - "minimist": "^1.2.0", - "mixin-deep": "^1.1.3", - "object.pick": "^1.2.0", - "remarkable": "^1.7.1", - "repeat-string": "^1.6.1", - "strip-color": "^0.1.0" - }, - "bin": { - "markdown-toc": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==" - }, - "node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-deep/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parse-code-context": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/parse-code-context/-/parse-code-context-0.1.3.tgz", - "integrity": "sha1-sMr+ZcNLkWQ0EAAz6zNOnSgstGE=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse-comments": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/parse-comments/-/parse-comments-0.4.3.tgz", - "integrity": "sha1-aMlV8eybZVpPJBsRJMedsTENZ70=", - "dependencies": { - "arrayify-compact": "^0.1.0", - "extract-comments": "^0.7.3", - "gfm-code-blocks": "^0.2.0", - "inflection": "^1.7.0", - "lodash": "^3.6.0", - "parse-code-context": "^0.1.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", - "dependencies": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/randomatic/node_modules/is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/randomatic/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regex-not/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regex-not/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/relative": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/relative/-/relative-3.0.2.tgz", - "integrity": "sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8=", - "dependencies": { - "isobject": "^2.0.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/relative/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/remarkable": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz", - "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==", - "dependencies": { - "argparse": "^1.0.10", - "autolinker": "~0.28.0" - }, - "bin": { - "remarkable": "bin/remarkable.js" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "deprecated": "https://github.com/lydell/resolve-url#deprecated" - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "engines": { - "node": ">=0.12" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dependencies": { - "ret": "~0.1.10" - } - }, - "node_modules/set-getter": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", - "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", - "dependencies": { - "to-object-path": "^0.3.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dependencies": { - "kind-of": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" - }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dependencies": { - "extend-shallow": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split-string/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split-string/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/strip-color": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz", - "integrity": "sha1-EG9l09PmotlAHKwOsM6LinArT3s=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/striptags": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/striptags/-/striptags-3.1.1.tgz", - "integrity": "sha1-yMPn/db7S7OjKjt1LltePjgJPr0=" - }, - "node_modules/success-symbol": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/success-symbol/-/success-symbol-0.1.0.tgz", - "integrity": "sha1-JAIuSG878c3KCUKDt2nEctO3KJc=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-gfm-code-block": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/to-gfm-code-block/-/to-gfm-code-block-0.1.1.tgz", - "integrity": "sha1-JdBFpfrlUxielje1kJANpzLYqoI=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex/node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/toml": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz", - "integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==" - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "node_modules/typeof-article": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/typeof-article/-/typeof-article-0.1.1.tgz", - "integrity": "sha1-nwfnM8P7tkb/qeYcCN66zUYOBq8=", - "dependencies": { - "kind-of": "^3.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/typeof-article/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uglify-js": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.1.tgz", - "integrity": "sha512-EWhx3fHy3M9JbaeTnO+rEqzCe1wtyQClv6q3YWq0voOj4E+bMZBErVS1GAHPDiRGONYq34M1/d8KuQMgvi6Gjw==", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "deprecated": "Please see https://github.com/lydell/urix#deprecated" - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/warning-symbol": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/warning-symbol/-/warning-symbol-0.1.0.tgz", - "integrity": "sha1-uzHdEbeg+dZ6su2V9Fe2WCW7rSE=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/year": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/year/-/year-0.2.1.tgz", - "integrity": "sha1-QIOuUgoxiyPshgN/MADLiSvfm7A=", - "engines": { - "node": ">=0.8" - } - } - }, - "dependencies": { - "ansi-bgblack": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgblack/-/ansi-bgblack-0.1.1.tgz", - "integrity": "sha1-poulAHiHcBtqr74/oNrf36juPKI=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-bgblue": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgblue/-/ansi-bgblue-0.1.1.tgz", - "integrity": "sha1-Z73ATtybm1J4lp2hlt6j11yMNhM=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-bgcyan": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgcyan/-/ansi-bgcyan-0.1.1.tgz", - "integrity": "sha1-WEiUJWAL3p9VBwaN2Wnr/bUP52g=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-bggreen": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bggreen/-/ansi-bggreen-0.1.1.tgz", - "integrity": "sha1-TjGRJIUplD9DIelr8THRwTgWr0k=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-bgmagenta": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgmagenta/-/ansi-bgmagenta-0.1.1.tgz", - "integrity": "sha1-myhDLAduqpmUGGcqPvvhk5HCx6E=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-bgred": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgred/-/ansi-bgred-0.1.1.tgz", - "integrity": "sha1-p2+Sg4OCukMpCmwXeEJPmE1vEEE=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-bgwhite": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgwhite/-/ansi-bgwhite-0.1.1.tgz", - "integrity": "sha1-ZQRlE3elim7OzQMxmU5IAljhG6g=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-bgyellow": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bgyellow/-/ansi-bgyellow-0.1.1.tgz", - "integrity": "sha1-w/4usIzUdmSAKeaHTRWgs49h1E8=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-black": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-black/-/ansi-black-0.1.1.tgz", - "integrity": "sha1-9hheiJNgslRaHsUMC/Bj/EMDJFM=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-blue": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-blue/-/ansi-blue-0.1.1.tgz", - "integrity": "sha1-FbgEmQ6S/JyoxUds6PaZd3wh7b8=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-bold": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-bold/-/ansi-bold-0.1.1.tgz", - "integrity": "sha1-PmOVCvWswq4uZw5vZ96xFdGl9QU=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-colors": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-0.2.0.tgz", - "integrity": "sha1-csMd4qDZoszQysMMyYI+6y9kNLU=", - "requires": { - "ansi-bgblack": "^0.1.1", - "ansi-bgblue": "^0.1.1", - "ansi-bgcyan": "^0.1.1", - "ansi-bggreen": "^0.1.1", - "ansi-bgmagenta": "^0.1.1", - "ansi-bgred": "^0.1.1", - "ansi-bgwhite": "^0.1.1", - "ansi-bgyellow": "^0.1.1", - "ansi-black": "^0.1.1", - "ansi-blue": "^0.1.1", - "ansi-bold": "^0.1.1", - "ansi-cyan": "^0.1.1", - "ansi-dim": "^0.1.1", - "ansi-gray": "^0.1.1", - "ansi-green": "^0.1.1", - "ansi-grey": "^0.1.1", - "ansi-hidden": "^0.1.1", - "ansi-inverse": "^0.1.1", - "ansi-italic": "^0.1.1", - "ansi-magenta": "^0.1.1", - "ansi-red": "^0.1.1", - "ansi-reset": "^0.1.1", - "ansi-strikethrough": "^0.1.1", - "ansi-underline": "^0.1.1", - "ansi-white": "^0.1.1", - "ansi-yellow": "^0.1.1", - "lazy-cache": "^2.0.1" - } - }, - "ansi-cyan": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", - "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-dim": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-dim/-/ansi-dim-0.1.1.tgz", - "integrity": "sha1-QN5MYDqoCG2Oeoa4/5mNXDbu/Ww=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-green": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-green/-/ansi-green-0.1.1.tgz", - "integrity": "sha1-il2al55FjVfEDjNYCzc5C44Q0Pc=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-grey": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-grey/-/ansi-grey-0.1.1.tgz", - "integrity": "sha1-WdmLasK6GfilF5jphT+6eDOaM8E=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-hidden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-hidden/-/ansi-hidden-0.1.1.tgz", - "integrity": "sha1-7WpMSY0rt8uyidvyqNHcyFZ/rg8=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-inverse": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-inverse/-/ansi-inverse-0.1.1.tgz", - "integrity": "sha1-tq9Fgm/oJr+1KKbHmIV5Q1XM0mk=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-italic": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-italic/-/ansi-italic-0.1.1.tgz", - "integrity": "sha1-EEdDRj9iXBQqA2c5z4XtpoiYbyM=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-magenta": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-magenta/-/ansi-magenta-0.1.1.tgz", - "integrity": "sha1-BjtboW+z8j4c/aKwfAqJ3hHkMK4=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-red": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", - "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-reset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-reset/-/ansi-reset-0.1.1.tgz", - "integrity": "sha1-5+cSksPH3c1NYu9KbHwFmAkRw7c=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-strikethrough": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-strikethrough/-/ansi-strikethrough-0.1.1.tgz", - "integrity": "sha1-2Eh3FAss/wfRyT685pkE9oiF5Wg=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-underline": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-underline/-/ansi-underline-0.1.1.tgz", - "integrity": "sha1-38kg9Ml7WXfqFi34/7mIMIqqcaQ=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-white": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-white/-/ansi-white-0.1.1.tgz", - "integrity": "sha1-nHe3wZPF7pkuYBHTbsTJIbRXiUQ=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" - }, - "ansi-yellow": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-yellow/-/ansi-yellow-0.1.1.tgz", - "integrity": "sha1-y5NW8vRscy8OMZnmEClVp32oPB0=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" - }, - "array-sort": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-0.1.4.tgz", - "integrity": "sha512-BNcM+RXxndPxiZ2rd76k6nyQLRZr2/B/sdi8pQ+Joafr5AH279L40dfokSUTp8O+AaqYjXWhblBWa2st2nc4fQ==", - "requires": { - "default-compare": "^1.0.0", - "get-value": "^2.0.6", - "kind-of": "^5.0.2" - } - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "arrayify-compact": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/arrayify-compact/-/arrayify-compact-0.1.1.tgz", - "integrity": "sha512-3R2V/8ixAhDXxyeWZ9aVzncUscrASg1TPrVsUgkxaMb0/WJLo5u769wYYf916OKqRCF+GKXaosouk1ixIfX89g==", - "requires": { - "array-flatten": "^2.1.2" - } - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - }, - "autolinker": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz", - "integrity": "sha1-BlK0kYgYefB3XazgzcoyM5QqTkc=", - "requires": { - "gulp-header": "^1.7.1" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - } - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - } - } - }, - "coffee-script": { - "version": "1.12.7", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", - "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==" - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "concat-with-sourcemaps": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", - "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", - "requires": { - "source-map": "^0.6.1" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "create-frame": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/create-frame/-/create-frame-1.0.0.tgz", - "integrity": "sha1-i5XyaR4ySbYIBEPjPQutn49pdao=", - "requires": { - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "isobject": "^3.0.0", - "lazy-cache": "^2.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - } - } - }, - "date.js": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/date.js/-/date.js-0.3.3.tgz", - "integrity": "sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw==", - "requires": { - "debug": "~3.1.0" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "default-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", - "requires": { - "kind-of": "^5.0.2" - } - }, - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "diacritics-map": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz", - "integrity": "sha1-bfwP+dAQAKLt8oZTccrDFulJd68=" - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=" - }, - "error-symbol": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/error-symbol/-/error-symbol-0.1.0.tgz", - "integrity": "sha1-Ck2uN9YA0VopukU9jvkg8YRDM/Y=" - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - } - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "requires": { - "fill-range": "^2.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "extract-comments": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/extract-comments/-/extract-comments-0.7.3.tgz", - "integrity": "sha1-HF3sFzAHLFsM36VVfl9X9pJ3/DQ=", - "requires": { - "is-whitespace": "^0.3.0" - } - }, - "falsey": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/falsey/-/falsey-0.3.2.tgz", - "integrity": "sha512-lxEuefF5MBIVDmE6XeqCdM4BWk1+vYmGZtkbKZ/VFcg6uBBw6fXNEbWmxCjDdQlFc9hy450nkiWwM3VAW6G1qg==", - "requires": { - "kind-of": "^5.0.2" - } - }, - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - }, - "dependencies": { - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "requires": { - "for-in": "^1.0.1" - } - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "requires": { - "map-cache": "^0.2.2" - } - }, - "fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "get-object": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/get-object/-/get-object-0.2.0.tgz", - "integrity": "sha1-2S/31RkMZFMM2gVD2sY6PUf+jAw=", - "requires": { - "is-number": "^2.0.2", - "isobject": "^0.2.0" - }, - "dependencies": { - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "isobject": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-0.2.0.tgz", - "integrity": "sha1-o0MhkvObkQtfAsyYlIeDbscKqF4=" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - }, - "gfm-code-block-regex": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/gfm-code-block-regex/-/gfm-code-block-regex-0.2.3.tgz", - "integrity": "sha1-C5/mis8sWlAi/W0tQYpjT26XwRA=" - }, - "gfm-code-blocks": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/gfm-code-blocks/-/gfm-code-blocks-0.2.2.tgz", - "integrity": "sha1-KZnYFIxFG4ZjLHH6bevhFe4HH/4=", - "requires": { - "gfm-code-block-regex": "^0.2.1" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "gray-matter": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz", - "integrity": "sha1-MELZrewqHe1qdwep7SOA+KF6Qw4=", - "requires": { - "ansi-red": "^0.1.1", - "coffee-script": "^1.12.4", - "extend-shallow": "^2.0.1", - "js-yaml": "^3.8.1", - "toml": "^2.3.2" - } - }, - "gulp-header": { - "version": "1.8.12", - "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz", - "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==", - "requires": { - "concat-with-sourcemaps": "*", - "lodash.template": "^4.4.0", - "through2": "^2.0.0" - } - }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - } - }, - "handlebars-helper-create-frame": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/handlebars-helper-create-frame/-/handlebars-helper-create-frame-0.1.0.tgz", - "integrity": "sha1-iqUdEK62QI/MZgXUDXc1YohIegM=", - "requires": { - "create-frame": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "handlebars-helpers": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/handlebars-helpers/-/handlebars-helpers-0.9.8.tgz", - "integrity": "sha512-N9MoNopXTOzNv9L2oDFUo1ZhWTzUd8YURVrksZaXVRybgs1JFnUXohCnFTOJL8m4t+jKn1xU6Vi7qxtCu4mRsg==", - "requires": { - "arr-flatten": "^1.0.3", - "array-sort": "^0.1.2", - "create-frame": "^1.0.0", - "define-property": "^1.0.0", - "falsey": "^0.3.0", - "for-in": "^1.0.2", - "for-own": "^1.0.0", - "get-object": "^0.2.0", - "get-value": "^2.0.6", - "handlebars": "^4.0.10", - "handlebars-helper-create-frame": "^0.1.0", - "handlebars-utils": "^1.0.1", - "has-value": "^1.0.0", - "helper-date": "^1.0.0", - "helper-markdown": "^0.2.2", - "helper-md": "^0.2.2", - "html-tag": "^1.0.0", - "is-even": "^1.0.0", - "is-glob": "^3.1.0", - "is-number": "^3.0.0", - "kind-of": "^5.0.0", - "lazy-cache": "^2.0.2", - "logging-helpers": "^1.0.0", - "micromatch": "^3.0.3", - "relative": "^3.0.2", - "striptags": "^3.0.1", - "to-gfm-code-block": "^0.1.1", - "year": "^0.2.1" - } - }, - "handlebars-utils": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/handlebars-utils/-/handlebars-utils-1.0.6.tgz", - "integrity": "sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw==", - "requires": { - "kind-of": "^6.0.0", - "typeof-article": "^0.1.1" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - } - } - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "helper-date": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/helper-date/-/helper-date-1.0.1.tgz", - "integrity": "sha512-wU3VOwwTJvGr/w5rZr3cprPHO+hIhlblTJHD6aFBrKLuNbf4lAmkawd2iK3c6NbJEvY7HAmDpqjOFSI5/+Ey2w==", - "requires": { - "date.js": "^0.3.1", - "handlebars-utils": "^1.0.4", - "moment": "^2.18.1" - } - }, - "helper-markdown": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/helper-markdown/-/helper-markdown-0.2.2.tgz", - "integrity": "sha1-ONt/dxhJ4wrpXJL8AhuutT8uMEA=", - "requires": { - "isobject": "^2.0.0", - "mixin-deep": "^1.1.3", - "remarkable": "^1.6.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "helper-md": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/helper-md/-/helper-md-0.2.2.tgz", - "integrity": "sha1-wfWdflW7riM2L9ig6XFgeuxp1B8=", - "requires": { - "ent": "^2.2.0", - "extend-shallow": "^2.0.1", - "fs-exists-sync": "^0.1.0", - "remarkable": "^1.6.2" - } - }, - "html-tag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/html-tag/-/html-tag-1.0.0.tgz", - "integrity": "sha1-leVhKuyCvqko7URZX4VBRen34LU=", - "requires": { - "isobject": "^3.0.0", - "void-elements": "^2.0.1" - } - }, - "inflection": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", - "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "info-symbol": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/info-symbol/-/info-symbol-0.1.0.tgz", - "integrity": "sha1-J4QdcoZ920JCzWEtecEGM4gcang=" - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - } - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - } - } - }, - "is-even": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-even/-/is-even-1.0.0.tgz", - "integrity": "sha1-drUFX7rY0pSoa2qUkBXhyXtxfAY=", - "requires": { - "is-odd": "^0.1.2" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "requires": { - "is-extglob": "^2.1.0" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-odd": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-0.1.2.tgz", - "integrity": "sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc=", - "requires": { - "is-number": "^3.0.0" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - }, - "is-whitespace": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", - "integrity": "sha1-Fjnssb4DauxppUy7QBz77XEUq38=" - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - }, - "lazy-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", - "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", - "requires": { - "set-getter": "^0.1.0" - } - }, - "list-item": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz", - "integrity": "sha1-DGXQDih8tmPMs8s4Sad+iewmilY=", - "requires": { - "expand-range": "^1.8.1", - "extend-shallow": "^2.0.1", - "is-number": "^2.1.0", - "repeat-string": "^1.5.2" - }, - "dependencies": { - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" - }, - "lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "requires": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "log-ok": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/log-ok/-/log-ok-0.1.1.tgz", - "integrity": "sha1-vqPdNqzQuKckDXhza1uXxlREozQ=", - "requires": { - "ansi-green": "^0.1.1", - "success-symbol": "^0.1.0" - } - }, - "log-utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/log-utils/-/log-utils-0.2.1.tgz", - "integrity": "sha1-pMIXoN2aUFFdm5ICBgkas9TgMc8=", - "requires": { - "ansi-colors": "^0.2.0", - "error-symbol": "^0.1.0", - "info-symbol": "^0.1.0", - "log-ok": "^0.1.1", - "success-symbol": "^0.1.0", - "time-stamp": "^1.0.1", - "warning-symbol": "^0.1.0" - } - }, - "logging-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/logging-helpers/-/logging-helpers-1.0.0.tgz", - "integrity": "sha512-qyIh2goLt1sOgQQrrIWuwkRjUx4NUcEqEGAcYqD8VOnOC6ItwkrVE8/tA4smGpjzyp4Svhc6RodDp9IO5ghpyA==", - "requires": { - "isobject": "^3.0.0", - "log-utils": "^0.2.1" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "requires": { - "object-visit": "^1.0.0" - } - }, - "markdown-link": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz", - "integrity": "sha1-MsXGUZmmRXMWMi0eQinRNAfIx88=" - }, - "markdown-toc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz", - "integrity": "sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg==", - "requires": { - "concat-stream": "^1.5.2", - "diacritics-map": "^0.1.0", - "gray-matter": "^2.1.0", - "lazy-cache": "^2.0.2", - "list-item": "^1.1.1", - "markdown-link": "^0.1.1", - "minimist": "^1.2.0", - "mixin-deep": "^1.1.3", - "object.pick": "^1.2.0", - "remarkable": "^1.7.1", - "repeat-string": "^1.6.1", - "strip-color": "^0.1.0" - } - }, - "math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==" - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - } - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - } - } - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "requires": { - "isobject": "^3.0.0" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "requires": { - "isobject": "^3.0.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "parse-code-context": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/parse-code-context/-/parse-code-context-0.1.3.tgz", - "integrity": "sha1-sMr+ZcNLkWQ0EAAz6zNOnSgstGE=" - }, - "parse-comments": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/parse-comments/-/parse-comments-0.4.3.tgz", - "integrity": "sha1-aMlV8eybZVpPJBsRJMedsTENZ70=", - "requires": { - "arrayify-compact": "^0.1.0", - "extract-comments": "^0.7.3", - "gfm-code-blocks": "^0.2.0", - "inflection": "^1.7.0", - "lodash": "^3.6.0", - "parse-code-context": "^0.1.3" - } - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - } - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "relative": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/relative/-/relative-3.0.2.tgz", - "integrity": "sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8=", - "requires": { - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "remarkable": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz", - "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==", - "requires": { - "argparse": "^1.0.10", - "autolinker": "~0.28.0" - } - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "requires": { - "ret": "~0.1.10" - } - }, - "set-getter": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", - "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", - "requires": { - "to-object-path": "^0.3.0" - } - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "requires": { - "extend-shallow": "^3.0.0" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-color": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz", - "integrity": "sha1-EG9l09PmotlAHKwOsM6LinArT3s=" - }, - "striptags": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/striptags/-/striptags-3.1.1.tgz", - "integrity": "sha1-yMPn/db7S7OjKjt1LltePjgJPr0=" - }, - "success-symbol": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/success-symbol/-/success-symbol-0.1.0.tgz", - "integrity": "sha1-JAIuSG878c3KCUKDt2nEctO3KJc=" - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" - }, - "to-gfm-code-block": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/to-gfm-code-block/-/to-gfm-code-block-0.1.1.tgz", - "integrity": "sha1-JdBFpfrlUxielje1kJANpzLYqoI=" - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "dependencies": { - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "toml": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz", - "integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==" - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "typeof-article": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/typeof-article/-/typeof-article-0.1.1.tgz", - "integrity": "sha1-nwfnM8P7tkb/qeYcCN66zUYOBq8=", - "requires": { - "kind-of": "^3.1.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "uglify-js": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.1.tgz", - "integrity": "sha512-EWhx3fHy3M9JbaeTnO+rEqzCe1wtyQClv6q3YWq0voOj4E+bMZBErVS1GAHPDiRGONYq34M1/d8KuQMgvi6Gjw==", - "optional": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - } - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" - }, - "warning-symbol": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/warning-symbol/-/warning-symbol-0.1.0.tgz", - "integrity": "sha1-uzHdEbeg+dZ6su2V9Fe2WCW7rSE=" - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "year": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/year/-/year-0.2.1.tgz", - "integrity": "sha1-QIOuUgoxiyPshgN/MADLiSvfm7A=" - } - } -} From 381745dbf6f895349537990458f5c388ea40f8fe Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 13 Apr 2021 06:34:39 -0700 Subject: [PATCH 274/278] CI: Various improvements --- BUILDING.md | 10 +-- CI/download-obs-deps.cmd | 7 -- CI/macos/build-plugin-macos.sh | 10 +-- CI/macos/install-build-obs-macos.sh | 16 ++--- CI/macos/install-dependencies-macos.sh | 16 ++--- ...ckage-macos.sh => package-plugin-macos.sh} | 4 +- CI/prepare-obs-windows.cmd | 37 ---------- CI/prepare-windows.cmd | 7 -- CI/windows/download-obs-deps.cmd | 7 ++ CI/{ => windows}/install-qt-win.cmd | 6 +- .../package-plugin-windows.cmd} | 0 CI/windows/prepare-obs-windows.cmd | 37 ++++++++++ CI/windows/prepare-plugin-windows.cmd | 7 ++ azure-pipelines.yml | 67 +++++++++---------- 14 files changed, 115 insertions(+), 116 deletions(-) delete mode 100644 CI/download-obs-deps.cmd rename CI/macos/{package-macos.sh => package-plugin-macos.sh} (96%) delete mode 100644 CI/prepare-obs-windows.cmd delete mode 100644 CI/prepare-windows.cmd create mode 100644 CI/windows/download-obs-deps.cmd rename CI/{ => windows}/install-qt-win.cmd (68%) rename CI/{package-windows.cmd => windows/package-plugin-windows.cmd} (100%) create mode 100644 CI/windows/prepare-obs-windows.cmd create mode 100644 CI/windows/prepare-plugin-windows.cmd diff --git a/BUILDING.md b/BUILDING.md index bec47951..f3a5ca54 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -2,7 +2,7 @@ ## Prerequisites -You'll need [Qt 5.10.x](https://download.qt.io/official_releases/qt/5.10/), +You'll need [Qt 5.15.2](https://download.qt.io/official_releases/qt/5.15/5.15.2/), [CMake](https://cmake.org/download/) and a working [OBS Studio development environment](https://obsproject.com/wiki/install-instructions) installed on your computer. @@ -53,10 +53,10 @@ look for issues or specificities. ```shell git clone --recursive https://github.com/Palakis/obs-websocket.git cd obs-websocket -./CI/install-dependencies-macos.sh -./CI/install-build-obs-macos.sh -./CI/build-macos.sh -./CI/package-macos.sh +./CI/macos/install-dependencies-macos.sh +./CI/macos/install-build-obs-macos.sh +./CI/macos/build-plugin-macos.sh +./CI/macos/package-plugin-macos.sh ``` This will result in a ready-to-use `obs-websocket.pkg` installer in the `release` subfolder. diff --git a/CI/download-obs-deps.cmd b/CI/download-obs-deps.cmd deleted file mode 100644 index 2063e297..00000000 --- a/CI/download-obs-deps.cmd +++ /dev/null @@ -1,7 +0,0 @@ -if exist %DepsBasePath% ( - echo "OBS dependencies found. Download skipped." -) else ( - echo "OBS dependencies not found. Downloading..." - curl -o %DepsBasePath%.zip -kLO https://cdn-fastly.obsproject.com/downloads/dependencies2019.zip -f --retry 5 -C - - 7z x %DepsBasePath%.zip -o%DepsBasePath% -) diff --git a/CI/macos/build-plugin-macos.sh b/CI/macos/build-plugin-macos.sh index 260fe50f..13c15806 100755 --- a/CI/macos/build-plugin-macos.sh +++ b/CI/macos/build-plugin-macos.sh @@ -3,21 +3,21 @@ OSTYPE=$(uname) if [ "${OSTYPE}" != "Darwin" ]; then - echo "[obs-websocket - Error] macOS build script can be run on Darwin-type OS only." - exit 1 + echo "[obs-websocket - Error] macOS build script can be run on Darwin-type OS only." + exit 1 fi HAS_CMAKE=$(type cmake 2>/dev/null) if [ "${HAS_CMAKE}" = "" ]; then - echo "[obs-websocket - Error] CMake not installed - please run 'install-dependencies-macos.sh' first." - exit 1 + echo "[obs-websocket - Error] CMake not installed - please run 'install-dependencies-macos.sh' first." + exit 1 fi echo "[obs-websocket] Building 'obs-websocket' for macOS." mkdir -p build && cd build cmake .. \ - -DQTDIR=/tmp/obsdeps \ + -DQTDIR=/tmp/obsdeps \ -DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \ -DLIBOBS_LIB=../../obs-studio/libobs \ -DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \ diff --git a/CI/macos/install-build-obs-macos.sh b/CI/macos/install-build-obs-macos.sh index a6e8ca82..580c1dfd 100755 --- a/CI/macos/install-build-obs-macos.sh +++ b/CI/macos/install-build-obs-macos.sh @@ -3,21 +3,21 @@ OSTYPE=$(uname) if [ "${OSTYPE}" != "Darwin" ]; then - echo "[obs-websocket - Error] macOS obs-studio build script can be run on Darwin-type OS only." - exit 1 + echo "[obs-websocket - Error] macOS obs-studio build script can be run on Darwin-type OS only." + exit 1 fi HAS_CMAKE=$(type cmake 2>/dev/null) HAS_GIT=$(type git 2>/dev/null) if [ "${HAS_CMAKE}" = "" ]; then - echo "[obs-websocket - Error] CMake not installed - please run 'install-dependencies-macos.sh' first." - exit 1 + echo "[obs-websocket - Error] CMake not installed - please run 'install-dependencies-macos.sh' first." + exit 1 fi if [ "${HAS_GIT}" = "" ]; then - echo "[obs-websocket - Error] Git not installed - please install Xcode developer tools or via Homebrew." - exit 1 + echo "[obs-websocket - Error] Git not installed - please install Xcode developer tools or via Homebrew." + exit 1 fi # Build obs-studio @@ -30,8 +30,8 @@ git checkout $OBSLatestTag mkdir build && cd build echo "[obs-websocket] Building obs-studio.." cmake .. \ - -DQTDIR=/tmp/obsdeps \ - -DDepsPath=/tmp/obsdeps \ + -DQTDIR=/tmp/obsdeps \ + -DDepsPath=/tmp/obsdeps \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \ -DDISABLE_PLUGINS=true \ -DENABLE_SCRIPTING=0 \ diff --git a/CI/macos/install-dependencies-macos.sh b/CI/macos/install-dependencies-macos.sh index 872dcca5..8c734b8a 100755 --- a/CI/macos/install-dependencies-macos.sh +++ b/CI/macos/install-dependencies-macos.sh @@ -3,15 +3,15 @@ OSTYPE=$(uname) if [ "${OSTYPE}" != "Darwin" ]; then - echo "[obs-websocket - Error] macOS install dependencies script can be run on Darwin-type OS only." - exit 1 + echo "[obs-websocket - Error] macOS install dependencies script can be run on Darwin-type OS only." + exit 1 fi HAS_BREW=$(type brew 2>/dev/null) if [ "${HAS_BREW}" = "" ]; then - echo "[obs-websocket - Error] Please install Homebrew (https://www.brew.sh/) to build obs-websocket on macOS." - exit 1 + echo "[obs-websocket - Error] Please install Homebrew (https://www.brew.sh/) to build obs-websocket on macOS." + exit 1 fi # OBS Studio Brew Deps @@ -39,10 +39,10 @@ brew bundle --file ./CI/macos/Brewfile HAS_PACKAGES=$(type packagesbuild 2>/dev/null) if [ "${HAS_PACKAGES}" = "" ]; then - echo "[obs-websocket] Installing Packaging app (might require password due to 'sudo').." - curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg - sudo hdiutil attach ./Packages.dmg - sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target / + echo "[obs-websocket] Installing Packaging app (might require password due to 'sudo').." + curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg + sudo hdiutil attach ./Packages.dmg + sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target / fi # OBS Deps diff --git a/CI/macos/package-macos.sh b/CI/macos/package-plugin-macos.sh similarity index 96% rename from CI/macos/package-macos.sh rename to CI/macos/package-plugin-macos.sh index db8193b4..6af9cd97 100755 --- a/CI/macos/package-macos.sh +++ b/CI/macos/package-plugin-macos.sh @@ -5,8 +5,8 @@ set -e OSTYPE=$(uname) if [ "${OSTYPE}" != "Darwin" ]; then - echo "[obs-websocket - Error] macOS package script can be run on Darwin-type OS only." - exit 1 + echo "[obs-websocket - Error] macOS package script can be run on Darwin-type OS only." + exit 1 fi echo "[obs-websocket] Preparing package build" diff --git a/CI/prepare-obs-windows.cmd b/CI/prepare-obs-windows.cmd deleted file mode 100644 index 43fc1c32..00000000 --- a/CI/prepare-obs-windows.cmd +++ /dev/null @@ -1,37 +0,0 @@ - -@echo off -SETLOCAL EnableDelayedExpansion - -REM If obs-studio directory does not exist, clone the git repo -if not exist %OBSPath% ( - echo obs-studio directory does not exist - git clone https://github.com/obsproject/obs-studio %OBSPath% - cd /D %OBSPath%\ - git describe --tags --abbrev=0 --exclude="*-rc*" > "%OBSPath%\obs-studio-latest-tag.txt" - set /p OBSLatestTag=<"%OBSPath%\obs-studio-latest-tag.txt" -) - -REM Prepare OBS Studio builds - -echo Running CMake... -cd /D %OBSPath% -echo git checkout %OBSLatestTag% -git checkout %OBSLatestTag% -echo: - -if not exist build32 mkdir build32 -if not exist build64 mkdir build64 - -echo Running cmake for obs-studio %OBSLatestTag% 32-bit... -cd build32 -cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DepsPath32%" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. -echo: -echo: - -echo Running cmake for obs-studio %OBSLatestTag% 64-bit... -cd ..\build64 -cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DepsPath64%" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. -echo: -echo: - -dir "%OBSPath%\libobs" diff --git a/CI/prepare-windows.cmd b/CI/prepare-windows.cmd deleted file mode 100644 index 09057149..00000000 --- a/CI/prepare-windows.cmd +++ /dev/null @@ -1,7 +0,0 @@ -mkdir build32 -mkdir build64 - -cd build32 -cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DLibObs_DIR="%OBSPath%\build32\libobs" -DLIBOBS_INCLUDE_DIR="%OBSPath%\libobs" -DLIBOBS_LIB="%OBSPath%\build32\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="%OBSPath%\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" .. -cd ..\build64 -cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DLibObs_DIR="%OBSPath%\build64\libobs" -DLIBOBS_INCLUDE_DIR="%OBSPath%\libobs" -DLIBOBS_LIB="%OBSPath%\build64\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="%OBSPath%\build64\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" .. diff --git a/CI/windows/download-obs-deps.cmd b/CI/windows/download-obs-deps.cmd new file mode 100644 index 00000000..2baf5ec4 --- /dev/null +++ b/CI/windows/download-obs-deps.cmd @@ -0,0 +1,7 @@ +if exist %DEPS_BASE_PATH% ( + echo "OBS dependencies found. Download skipped." +) else ( + echo "OBS dependencies not found. Downloading..." + curl -o %DEPS_BASE_PATH%.zip -kLO https://cdn-fastly.obsproject.com/downloads/dependencies2019.zip -f --retry 5 -C - + 7z x %DEPS_BASE_PATH%.zip -o%DEPS_BASE_PATH% +) diff --git a/CI/install-qt-win.cmd b/CI/windows/install-qt-win.cmd similarity index 68% rename from CI/install-qt-win.cmd rename to CI/windows/install-qt-win.cmd index 2fa20c8f..39abd6ba 100644 --- a/CI/install-qt-win.cmd +++ b/CI/windows/install-qt-win.cmd @@ -1,8 +1,8 @@ -if exist %QtBaseDir% ( +if exist %QT_BASE_DIR% ( echo "Qt directory found. Download skipped." ) else ( echo "Qt directory not found. Downloading..." curl -kLO https://tt2468.net/dl/Qt_5.15.2.7z -f --retry 5 -C - - 7z x Qt_5.15.2.7z -o%QtBaseDir% + 7z x Qt_5.15.2.7z -o%QT_BASE_DIR% ) -dir %QtBaseDir% +dir %QT_BASE_DIR% diff --git a/CI/package-windows.cmd b/CI/windows/package-plugin-windows.cmd similarity index 100% rename from CI/package-windows.cmd rename to CI/windows/package-plugin-windows.cmd diff --git a/CI/windows/prepare-obs-windows.cmd b/CI/windows/prepare-obs-windows.cmd new file mode 100644 index 00000000..1795517c --- /dev/null +++ b/CI/windows/prepare-obs-windows.cmd @@ -0,0 +1,37 @@ + +@echo off +SETLOCAL EnableDelayedExpansion + +REM If obs-studio directory does not exist, clone the git repo +if not exist %OBS_PATH% ( + echo obs-studio directory does not exist + git clone https://github.com/obsproject/obs-studio %OBS_PATH% + cd /D %OBS_PATH%\ + git describe --tags --abbrev=0 --exclude="*-rc*" > "%OBS_PATH%\obs-studio-latest-tag.txt" + set /p OBS_LATEST_TAG=<"%OBS_PATH%\obs-studio-latest-tag.txt" +) + +REM Prepare OBS Studio builds + +echo Running CMake... +cd /D %OBS_PATH% +echo git checkout %OBS_LATEST_TAG% +git checkout %OBS_LATEST_TAG% +echo: + +if not exist build32 mkdir build32 +if not exist build64 mkdir build64 + +echo Running cmake for obs-studio %OBS_LATEST_TAG% 32-bit... +cd build32 +cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DEPS_PATH_32%" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. +echo: +echo: + +echo Running cmake for obs-studio %OBS_LATEST_TAG% 64-bit... +cd ..\build64 +cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DEPS_PATH_64%" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. +echo: +echo: + +dir "%OBS_PATH%\libobs" diff --git a/CI/windows/prepare-plugin-windows.cmd b/CI/windows/prepare-plugin-windows.cmd new file mode 100644 index 00000000..08bfdafd --- /dev/null +++ b/CI/windows/prepare-plugin-windows.cmd @@ -0,0 +1,7 @@ +mkdir build32 +mkdir build64 + +cd build32 +cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DLibObs_DIR="%OBS_PATH%\build32\libobs" -DLIBOBS_INCLUDE_DIR="%OBS_PATH%\libobs" -DLIBOBS_LIB="%OBS_PATH%\build32\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="%OBS_PATH%\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" .. +cd ..\build64 +cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DLibObs_DIR="%OBS_PATH%\build64\libobs" -DLIBOBS_INCLUDE_DIR="%OBS_PATH%\libobs" -DLIBOBS_LIB="%OBS_PATH%\build64\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="%OBS_PATH%\build64\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" .. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ff54ad50..d08cb5e4 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -33,15 +33,15 @@ jobs: vmImage: 'windows-2019' variables: build_config: RelWithDebInfo - DepsCacheVersion: '1' # Change whenever updating OBS dependencies URL, in order to force a cache reset - DepsBasePath: 'D:\obsdependencies' - DepsPath32: '$(DepsBasePath)\win32' - DepsPath64: '$(DepsBasePath)\win64' - QtCacheVersion: '1' # Change whenever updating Qt dependency URL, in order to force a cache reset - QtBaseDir: 'D:\QtDep' - QTDIR32: '$(QtBaseDir)\5.15.2\msvc2019' - QTDIR64: '$(QtBaseDir)\5.15.2\msvc2019_64' - OBSPath: 'D:\obs-studio' + DEPS_CACHE_VERSION: '1' # Change whenever updating OBS dependencies URL, in order to force a cache reset + DEPS_BASE_PATH: 'D:\obsdependencies' + DEPS_PATH_32: '$(DEPS_BASE_PATH)\win32' + DEPS_PATH_64: '$(DEPS_BASE_PATH)\win64' + QT_CACHE_VERSION: '1' # Change whenever updating Qt dependency URL, in order to force a cache reset + QT_BASE_DIR: 'D:\QtDep' + QTDIR32: '$(QT_BASE_DIR)\5.15.2\msvc2019' + QTDIR64: '$(QT_BASE_DIR)\5.15.2\msvc2019_64' + OBS_PATH: 'D:\obs-studio' steps: - checkout: self submodules: true @@ -49,25 +49,25 @@ jobs: - task: Cache@2 displayName: Restore cached Qt archive file inputs: - key: 'qtdep-"$(QtCacheVersion)" | "$(Agent.OS)"' + key: 'qtdep-"$(QT_CACHE_VERSION)" | "$(Agent.OS)"' restoreKeys: | - qtdep-"$(QtCacheVersion)" | "$(Agent.OS)" - path: $(QtBaseDir) + qtdep-"$(QT_CACHE_VERSION)" | "$(Agent.OS)" + path: $(QT_BASE_DIR) - - script: ./CI/install-qt-win.cmd + - script: ./CI/windows/install-qt-win.cmd displayName: 'Install Qt' env: - QtBaseDir: $(QtBaseDir) + QT_BASE_DIR: $(QT_BASE_DIR) - task: Cache@2 displayName: Restore cached OBS Studio dependencies inputs: - key: 'obsdeps-"$(DepsCacheVersion)" | "$(Agent.OS)"' + key: 'obsdeps-"$(DEPS_CACHE_VERSION)" | "$(Agent.OS)"' restoreKeys: | - obsdeps-"$(DepsCacheVersion)" | "$(Agent.OS)" - path: $(DepsBasePath) + obsdeps-"$(DEPS_CACHE_VERSION)" | "$(Agent.OS)" + path: $(DEPS_BASE_PATH) - - script: ./CI/download-obs-deps.cmd + - script: ./CI/windows/download-obs-deps.cmd displayName: 'Download OBS Studio dependencies' - task: Cache@2 @@ -76,37 +76,37 @@ jobs: key: 'obs | "$(Agent.OS)"' restoreKeys: | obs | "$(Agent.OS)" - path: $(OBSPath) + path: $(OBS_PATH) - - script: ./CI/prepare-obs-windows.cmd + - script: ./CI/windows/prepare-obs-windows.cmd displayName: 'Checkout & CMake OBS Studio' env: build_config: $(build_config) - DepsPath32: $(DepsPath32) - DepsPath64: $(DepsPath64) + DEPS_PATH_32: $(DEPS_PATH_32) + DEPS_PATH_64: $(DEPS_PATH_64) QTDIR32: $(QTDIR32) QTDIR64: $(QTDIR64) - OBSPath: $(OBSPath) + OBS_PATH: $(OBS_PATH) - task: MSBuild@1 displayName: 'Build OBS Studio 32-bit' inputs: msbuildArguments: '/m /p:Configuration=$(build_config)' - solution: '$(OBSPath)\build32\obs-studio.sln' + solution: '$(OBS_PATH)\build32\obs-studio.sln' - task: MSBuild@1 displayName: 'Build OBS Studio 64-bit' inputs: msbuildArguments: '/m /p:Configuration=$(build_config)' - solution: '$(OBSPath)\build64\obs-studio.sln' + solution: '$(OBS_PATH)\build64\obs-studio.sln' - - script: ./CI/prepare-windows.cmd + - script: ./CI/windows/prepare-plugin-windows.cmd displayName: 'CMake obs-websocket' env: build_config: $(build_config) QTDIR32: $(QTDIR32) QTDIR64: $(QTDIR64) - OBSPath: $(OBSPath) + OBS_PATH: $(OBS_PATH) - task: MSBuild@1 displayName: 'Build obs-websocket 32-bit' @@ -120,7 +120,7 @@ jobs: msbuildArguments: '/m /p:Configuration=$(build_config)' solution: '.\build64\obs-websocket.sln' - - script: ./CI/package-windows.cmd + - script: ./CI/windows/package-plugin-windows.cmd displayName: 'Package obs-websocket' - task: PublishBuildArtifacts@1 @@ -133,7 +133,6 @@ jobs: pool: vmImage: 'ubuntu-20.04' variables: - BUILD_REASON: $(Build.Reason) BRANCH_SHORT_NAME: $(Build.SourceBranchName) BRANCH_FULL_NAME: $(Build.SourceBranch) steps: @@ -158,8 +157,8 @@ jobs: pool: vmImage: 'macOS-10.15' variables: - obsDepsVersion: '2020-12-22' - qtVersion: '5.15.2' + OBS_DEPS_VERSION: '2020-12-22' + QT_VERSION: '5.15.2' steps: - checkout: self submodules: true @@ -167,8 +166,8 @@ jobs: - script: ./CI/macos/install-dependencies-macos.sh displayName: 'Install dependencies' env: - OBS_DEPS_VERSION: $(obsDepsVersion) - QT_VERSION: $(qtVersion) + OBS_DEPS_VERSION: $(OBS_DEPS_VERSION) + QT_VERSION: $(QT_VERSION) - script: ./CI/macos/install-build-obs-macos.sh displayName: 'Build OBS' @@ -183,7 +182,7 @@ jobs: certSecureFile: 'Certificates.p12' certPwd: $(secrets.macOS.certificatesImportPassword) - - script: ./CI/macos/package-macos.sh + - script: ./CI/macos/package-plugin-macos.sh displayName: 'Package obs-websocket' env: RELEASE_MODE: $(isReleaseMode) From ff548589bcd49b1d0d2b553812f7b12dd4aee7cb Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Tue, 13 Apr 2021 14:04:38 +0000 Subject: [PATCH 275/278] docs(ci): Update protocol.md - f2f80c4 [skip ci] --- docs/generated/comments.json | 8 ++++---- docs/generated/protocol.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index c6aaf88b..eb55391a 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -861,7 +861,7 @@ "{String} `name` Transition name.", "{String} `type` Transition type.", "{int} `duration` Transition duration (in milliseconds). Will be -1 for any transition with a fixed duration, such as a Stinger, due to limitations of the OBS API.", - "{String} `from-scene` Source scene of the transition", + "{String (optional)} `from-scene` Source scene of the transition", "{String} `to-scene` Destination scene of the transition" ], "api": "events", @@ -885,7 +885,7 @@ "description": "Transition duration (in milliseconds). Will be -1 for any transition with a fixed duration, such as a Stinger, due to limitations of the OBS API." }, { - "type": "String", + "type": "String (optional)", "name": "from-scene", "description": "Source scene of the transition" }, @@ -989,7 +989,7 @@ "{String} `name` Transition name.", "{String} `type` Transition type.", "{int} `duration` Transition duration (in milliseconds).", - "{String} `from-scene` Source scene of the transition", + "{String (optional)} `from-scene` Source scene of the transition", "{String} `to-scene` Destination scene of the transition" ], "api": "events", @@ -1013,7 +1013,7 @@ "description": "Transition duration (in milliseconds)." }, { - "type": "String", + "type": "String (optional)", "name": "from-scene", "description": "Source scene of the transition" }, diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index 86e79b90..e4c7da27 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -516,7 +516,7 @@ A transition (other than "cut") has begun. | `name` | _String_ | Transition name. | | `type` | _String_ | Transition type. | | `duration` | _int_ | Transition duration (in milliseconds). Will be -1 for any transition with a fixed duration, such as a Stinger, due to limitations of the OBS API. | -| `from-scene` | _String_ | Source scene of the transition | +| `from-scene` | _String (optional)_ | Source scene of the transition | | `to-scene` | _String_ | Destination scene of the transition | @@ -556,7 +556,7 @@ A stinger transition has finished playing its video. | `name` | _String_ | Transition name. | | `type` | _String_ | Transition type. | | `duration` | _int_ | Transition duration (in milliseconds). | -| `from-scene` | _String_ | Source scene of the transition | +| `from-scene` | _String (optional)_ | Source scene of the transition | | `to-scene` | _String_ | Destination scene of the transition | From 39454bc13ec8811f5da39e48ef064241e52185c7 Mon Sep 17 00:00:00 2001 From: Azure CI <> Date: Sun, 18 Apr 2021 19:53:39 +0000 Subject: [PATCH 276/278] docs(ci): Update protocol.md - fcffa55 [skip ci] --- docs/generated/comments.json | 4 ++-- docs/generated/protocol.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/generated/comments.json b/docs/generated/comments.json index eb55391a..95fb966e 100644 --- a/docs/generated/comments.json +++ b/docs/generated/comments.json @@ -9584,7 +9584,7 @@ "param": [ "{String} `sceneName` Name of the scene to create the scene item in", "{String} `sourceName` Name of the source to be added", - "{boolean} `setVisible` Whether to make the sceneitem visible on creation or not. Default `true`" + "{boolean (optional)} `setVisible` Whether to make the sceneitem visible on creation or not. Default `true`" ], "return": "{int} `itemId` Numerical ID of the created scene item", "api": "requests", @@ -9610,7 +9610,7 @@ "description": "Name of the source to be added" }, { - "type": "boolean", + "type": "boolean (optional)", "name": "setVisible", "description": "Whether to make the sceneitem visible on creation or not. Default `true`" } diff --git a/docs/generated/protocol.md b/docs/generated/protocol.md index e4c7da27..c537330b 100644 --- a/docs/generated/protocol.md +++ b/docs/generated/protocol.md @@ -3776,7 +3776,7 @@ Creates a scene item in a scene. In other words, this is how you add a source in | ---- | :---: | ------------| | `sceneName` | _String_ | Name of the scene to create the scene item in | | `sourceName` | _String_ | Name of the source to be added | -| `setVisible` | _boolean_ | Whether to make the sceneitem visible on creation or not. Default `true` | +| `setVisible` | _boolean (optional)_ | Whether to make the sceneitem visible on creation or not. Default `true` | **Response Items:** From 50eedd11e04188bafb459dfcd81629493bc94459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 18 Apr 2021 22:19:20 +0200 Subject: [PATCH 277/278] cmake: fix target directory path of QImage plugins --- CMakeLists.txt | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed7c93d3..4849d965 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,8 +130,11 @@ if(WIN32) "$" "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}") - # In Release mode, copy Qt image format plugins - COMMAND if $==1 ( + # In Release or RelWithDebInfo mode, copy Qt image format plugins + COMMAND if $,$>==1 ( + "${CMAKE_COMMAND}" -E make_directory + "${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats") + COMMAND if $,$>==1 ( "${CMAKE_COMMAND}" -E copy "${QTDIR}/plugins/imageformats/qicns.dll" "${QTDIR}/plugins/imageformats/qico.dll" @@ -139,16 +142,7 @@ if(WIN32) "${QTDIR}/plugins/imageformats/qtiff.dll" "${QTDIR}/plugins/imageformats/qwbmp.dll" "${QTDIR}/plugins/imageformats/qwebp.dll" - "${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats/qjpeg.dll") - COMMAND if $==1 ( - "${CMAKE_COMMAND}" -E copy - "${QTDIR}/plugins/imageformats/qicns.dll" - "${QTDIR}/plugins/imageformats/qico.dll" - "${QTDIR}/plugins/imageformats/qjpeg.dll" - "${QTDIR}/plugins/imageformats/qtiff.dll" - "${QTDIR}/plugins/imageformats/qwbmp.dll" - "${QTDIR}/plugins/imageformats/qwebp.dll" - "${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats/qjpeg.dll") + "${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats") # If config is RelWithDebInfo, package release files COMMAND if $==1 ( From 1f0c1691be68f09adc24f9ab6d3296964ffa9676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lepin?= Date: Sun, 18 Apr 2021 23:00:26 +0200 Subject: [PATCH 278/278] cmake: simplify package step --- CMakeLists.txt | 63 +++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4849d965..06855862 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,24 +116,29 @@ if(WIN32) set(RELEASE_DIR "${PROJECT_SOURCE_DIR}/release") add_custom_command(TARGET obs-websocket POST_BUILD - # If config is Release, package release files - COMMAND if $==1 ( + # If config is Release or RelWithDebInfo, package release files + COMMAND if $,$>==1 ( "${CMAKE_COMMAND}" -E make_directory - "${RELEASE_DIR}/data/obs-plugins/obs-websocket" - "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}") + "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}" + ) - COMMAND if $==1 ("${CMAKE_COMMAND}" -E copy_directory + COMMAND if $,$>==1 ( + "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/data" - "${RELEASE_DIR}/data/obs-plugins/obs-websocket") + "${RELEASE_DIR}/data/obs-plugins/obs-websocket" + ) - COMMAND if $==1 ("${CMAKE_COMMAND}" -E copy + COMMAND if $,$>==1 ( + "${CMAKE_COMMAND}" -E copy "$" - "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}") + "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}" + ) # In Release or RelWithDebInfo mode, copy Qt image format plugins COMMAND if $,$>==1 ( "${CMAKE_COMMAND}" -E make_directory - "${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats") + "${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats" + ) COMMAND if $,$>==1 ( "${CMAKE_COMMAND}" -E copy "${QTDIR}/plugins/imageformats/qicns.dll" @@ -142,45 +147,39 @@ if(WIN32) "${QTDIR}/plugins/imageformats/qtiff.dll" "${QTDIR}/plugins/imageformats/qwbmp.dll" "${QTDIR}/plugins/imageformats/qwebp.dll" - "${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats") + "${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats" + ) - # If config is RelWithDebInfo, package release files + # If config is RelWithDebInfo, package PDB file for target COMMAND if $==1 ( - "${CMAKE_COMMAND}" -E make_directory - "${RELEASE_DIR}/data/obs-plugins/obs-websocket" - "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}") - - COMMAND if $==1 ("${CMAKE_COMMAND}" -E copy_directory - "${PROJECT_SOURCE_DIR}/data" - "${RELEASE_DIR}/data/obs-plugins/obs-websocket") - - COMMAND if $==1 ("${CMAKE_COMMAND}" -E copy - "$" - "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}") - - COMMAND if $==1 ("${CMAKE_COMMAND}" -E copy + "${CMAKE_COMMAND}" -E copy "$" - "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}") + "${RELEASE_DIR}/obs-plugins/${ARCH_NAME}" + ) - # Copy to obs-studio dev environment for immediate testing + # In the Debug configuration, copy to obs-studio dev environment for immediate testing COMMAND if $==1 ( "${CMAKE_COMMAND}" -E copy "$" - "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/obs-plugins/${ARCH_NAME}") + "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/obs-plugins/${ARCH_NAME}" + ) COMMAND if $==1 ( "${CMAKE_COMMAND}" -E copy - "$" - "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/obs-plugins/${ARCH_NAME}") + "$" + "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/obs-plugins/${ARCH_NAME}" + ) COMMAND if $==1 ( "${CMAKE_COMMAND}" -E make_directory - "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/data/obs-plugins/obs-websocket") + "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/data/obs-plugins/obs-websocket" + ) COMMAND if $==1 ( "${CMAKE_COMMAND}" -E copy_directory - "${PROJECT_SOURCE_DIR}/data" - "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/data/obs-plugins/obs-websocket") + "${PROJECT_SOURCE_DIR}/data" + "${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$/data/obs-plugins/obs-websocket" + ) ) # --- End of sub-section ---