mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
a25427c7cc | |||
99f93c4bdd | |||
6af3af61d5 | |||
14a1547e11 | |||
60ade46481 | |||
7c21e5732e | |||
f5db53f217 | |||
3400f88665 | |||
ad3cb5dcfd | |||
5ecda806bd | |||
af3f29169c | |||
af97978841 | |||
4f89378c45 | |||
4fc8a3aecc | |||
1626ae5546 | |||
8b7fd3dd46 | |||
73848a7370 | |||
2e48dd24c4 | |||
ad1f28480c | |||
91cabe1202 | |||
f869f3df76 |
107
.clang-format
Normal file
107
.clang-format
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
# please use clang-format version 8 or later
|
||||||
|
|
||||||
|
Standard: Cpp11
|
||||||
|
AccessModifierOffset: -8
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: true
|
||||||
|
#AllowAllArgumentsOnNextLine: false # requires clang-format 9
|
||||||
|
#AllowAllConstructorInitializersOnNextLine: false # requires clang-format 9
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
#AllowShortLambdasOnASingleLine: Inline # requires clang-format 9
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: false
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakStringLiterals: false # apparently unpredictable
|
||||||
|
ColumnLimit: 132
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
ConstructorInitializerIndentWidth: 8
|
||||||
|
ContinuationIndentWidth: 8
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
FixNamespaceComments: false
|
||||||
|
ForEachMacros:
|
||||||
|
- 'json_object_foreach'
|
||||||
|
- 'json_object_foreach_safe'
|
||||||
|
- 'json_array_foreach'
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentWidth: 8
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: All
|
||||||
|
#ObjCBinPackProtocolList: Auto # requires clang-format 7
|
||||||
|
ObjCBlockIndentWidth: 8
|
||||||
|
ObjCSpaceAfterProperty: true
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
|
||||||
|
PenaltyBreakAssignment: 10
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 30
|
||||||
|
PenaltyBreakComment: 10
|
||||||
|
PenaltyBreakFirstLessLess: 0
|
||||||
|
PenaltyBreakString: 10
|
||||||
|
PenaltyExcessCharacter: 100
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: false
|
||||||
|
SortIncludes: false
|
||||||
|
SortUsingDeclarations: false
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
#SpaceAfterLogicalNot: false # requires clang-format 9
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
#SpaceBeforeCtorInitializerColon: true # requires clang-format 7
|
||||||
|
#SpaceBeforeInheritanceColon: true # requires clang-format 7
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
#SpaceBeforeRangeBasedForLoopColon: true # requires clang-format 7
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
#StatementMacros: # requires clang-format 8
|
||||||
|
# - 'Q_OBJECT'
|
||||||
|
TabWidth: 8
|
||||||
|
#TypenameMacros: # requires clang-format 9
|
||||||
|
# - 'DARRAY'
|
||||||
|
UseTab: ForContinuationAndIndentation
|
||||||
|
---
|
||||||
|
Language: ObjC
|
11
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
11
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -27,10 +27,11 @@ body:
|
|||||||
- macOS 10.15
|
- macOS 10.15
|
||||||
- macOS 10.14
|
- macOS 10.14
|
||||||
- macOS 10.13
|
- macOS 10.13
|
||||||
|
- Ubuntu 22.04 LTS
|
||||||
- Ubuntu 21.04
|
- Ubuntu 21.04
|
||||||
- Ubuntu 20.10
|
- Ubuntu 20.10
|
||||||
- Ubuntu 20.04
|
- Ubuntu 20.04 LTS
|
||||||
- Ubuntu 18.04
|
- Ubuntu 18.04 LTS
|
||||||
- Other
|
- Other
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
@ -48,6 +49,11 @@ body:
|
|||||||
label: OBS Studio Version
|
label: OBS Studio Version
|
||||||
description: What version of OBS Studio are you using?
|
description: What version of OBS Studio are you using?
|
||||||
options:
|
options:
|
||||||
|
- 27.2.4
|
||||||
|
- 27.2.3
|
||||||
|
- 27.2.2
|
||||||
|
- 27.2.1
|
||||||
|
- 27.2.0
|
||||||
- 27.1.3
|
- 27.1.3
|
||||||
- 27.1.1
|
- 27.1.1
|
||||||
- 27.1.0
|
- 27.1.0
|
||||||
@ -69,6 +75,7 @@ body:
|
|||||||
label: obs-websocket Version
|
label: obs-websocket Version
|
||||||
description: What version of obs-websocket are you using?
|
description: What version of obs-websocket are you using?
|
||||||
options:
|
options:
|
||||||
|
- 5.0.0
|
||||||
- 5.0.0-beta1
|
- 5.0.0-beta1
|
||||||
- 5.0.0-alpha3
|
- 5.0.0-alpha3
|
||||||
- 5.0.0-alpha2
|
- 5.0.0-alpha2
|
||||||
|
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@ -29,6 +29,6 @@ Tested OS(s):
|
|||||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||||
- [ ] I have read the [Contributing Guidelines](https://github.com/obsproject/obs-websocket/wiki/Contributing-Guidelines).
|
- [ ] I have read the [Contributing Guidelines](https://github.com/obsproject/obs-websocket/wiki/Contributing-Guidelines).
|
||||||
- [ ] All commit messages are properly formatted and commits squashed where appropriate.
|
- [ ] All commit messages are properly formatted and commits squashed where appropriate.
|
||||||
- [ ] My code is not on the `master` branch.
|
- [ ] My code is not on `master` or a `release/*` branch.
|
||||||
- [ ] The code has been tested.
|
- [ ] The code has been tested.
|
||||||
- [ ] I have included updates to all appropriate documentation.
|
- [ ] I have included updates to all appropriate documentation.
|
||||||
|
2
.github/workflows/generate_docs.yml
vendored
2
.github/workflows/generate_docs.yml
vendored
@ -5,7 +5,7 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'docs/generated/**'
|
- 'docs/generated/**'
|
||||||
branches:
|
branches:
|
||||||
- master
|
- release/5.0.0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docs-build:
|
docs-build:
|
||||||
|
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@ -3,10 +3,10 @@ name: Code Quality
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- release/5.0.0
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- release/5.0.0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
markdown:
|
markdown:
|
||||||
|
8
.github/workflows/main.yml
vendored
8
.github/workflows/main.yml
vendored
@ -5,7 +5,7 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
branches:
|
branches:
|
||||||
- master
|
- release/5.0.0
|
||||||
tags:
|
tags:
|
||||||
- '[45].[0-9]+.[0-9]+*'
|
- '[45].[0-9]+.[0-9]+*'
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -13,7 +13,7 @@ on:
|
|||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
- '**.md'
|
- '**.md'
|
||||||
branches:
|
branches:
|
||||||
- master
|
- release/5.0.0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
windows:
|
windows:
|
||||||
@ -334,9 +334,9 @@ jobs:
|
|||||||
cd ./build
|
cd ./build
|
||||||
sudo checkinstall -y --type=debian --fstrans=no -nodoc \
|
sudo checkinstall -y --type=debian --fstrans=no -nodoc \
|
||||||
--backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion="$CHECKINSTALL_VERSION" \
|
--backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion="$CHECKINSTALL_VERSION" \
|
||||||
--pkglicense="GPLv2.0" --maintainer="${{ github.event.pusher.email }}" --pkggroup="video" \
|
--pkglicense="GPLv2.0" --maintainer="tt2468@irltoolkit.com" --pkggroup="video" \
|
||||||
--pkgsource="${{ github.event.repository.html_url }}" \
|
--pkgsource="${{ github.event.repository.html_url }}" \
|
||||||
--requires="obs-studio,libqt5network5,libqt5concurrent5,qt5-image-formats-plugins" \
|
--requires="obs-studio,libqt5network5,qt5-image-formats-plugins" \
|
||||||
--pakdir="../package"
|
--pakdir="../package"
|
||||||
sudo chmod ao+r ../package/*
|
sudo chmod ao+r ../package/*
|
||||||
sudo mv ../package/* ../package/${{ env.LINUX_FILENAME }}
|
sudo mv ../package/* ../package/${{ env.LINUX_FILENAME }}
|
||||||
|
57
CI/check-format.sh
Executable file
57
CI/check-format.sh
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Original source https://github.com/Project-OSRM/osrm-backend/blob/master/scripts/format.sh
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
if [ ${#} -eq 1 ]; then
|
||||||
|
VERBOSITY="--verbose"
|
||||||
|
else
|
||||||
|
VERBOSITY=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Runs the Clang Formatter in parallel on the code base.
|
||||||
|
# Return codes:
|
||||||
|
# - 1 there are files to be formatted
|
||||||
|
# - 0 everything looks fine
|
||||||
|
|
||||||
|
# Get CPU count
|
||||||
|
OS=$(uname)
|
||||||
|
NPROC=1
|
||||||
|
if [[ ${OS} = "Linux" ]] ; then
|
||||||
|
NPROC=$(nproc)
|
||||||
|
elif [[ ${OS} = "Darwin" ]] ; then
|
||||||
|
NPROC=$(sysctl -n hw.physicalcpu)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Discover clang-format
|
||||||
|
if type clang-format-12 2> /dev/null ; then
|
||||||
|
CLANG_FORMAT=clang-format-12
|
||||||
|
elif type clang-format 2> /dev/null ; then
|
||||||
|
# Clang format found, but need to check version
|
||||||
|
CLANG_FORMAT=clang-format
|
||||||
|
V=$(clang-format --version)
|
||||||
|
if [[ $V != *"version 12.0"* ]]; then
|
||||||
|
echo "clang-format is not 12.0 (returned ${V})"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No appropriate clang-format found (expected clang-format-12.0.0, or clang-format)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
find . -type d \( \
|
||||||
|
-path ./\*build\* -o \
|
||||||
|
-path ./deps/websocketpp -o \
|
||||||
|
-path ./deps/asio -o \
|
||||||
|
-path ./deps/json -o \
|
||||||
|
-path ./deps/qr \
|
||||||
|
\) -prune -false -type f -o \
|
||||||
|
-name '*.h' -or \
|
||||||
|
-name '*.hpp' -or \
|
||||||
|
-name '*.m' -or \
|
||||||
|
-name '*.m,' -or \
|
||||||
|
-name '*.c' -or \
|
||||||
|
-name '*.cpp' \
|
||||||
|
| xargs -L100 -P ${NPROC} ${CLANG_FORMAT} ${VERBOSITY} -i -style=file -fallback-style=none
|
@ -85,7 +85,7 @@ configure_file(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Inlude sources
|
# Include sources
|
||||||
set(obs-websocket_SOURCES
|
set(obs-websocket_SOURCES
|
||||||
src/obs-websocket.cpp
|
src/obs-websocket.cpp
|
||||||
src/Config.cpp
|
src/Config.cpp
|
||||||
@ -129,7 +129,6 @@ set(obs-websocket_SOURCES
|
|||||||
src/utils/Json.cpp
|
src/utils/Json.cpp
|
||||||
src/utils/Obs.cpp
|
src/utils/Obs.cpp
|
||||||
src/utils/Obs_StringHelper.cpp
|
src/utils/Obs_StringHelper.cpp
|
||||||
src/utils/Obs_EnumHelper.cpp
|
|
||||||
src/utils/Obs_NumberHelper.cpp
|
src/utils/Obs_NumberHelper.cpp
|
||||||
src/utils/Obs_ArrayHelper.cpp
|
src/utils/Obs_ArrayHelper.cpp
|
||||||
src/utils/Obs_ObjectHelper.cpp
|
src/utils/Obs_ObjectHelper.cpp
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
WebSocket API for OBS Studio.
|
WebSocket API for OBS Studio.
|
||||||
|
|
||||||
[](https://github.com/obsproject/obs-websocket/actions/workflows/main.yml)
|
|
||||||
[](https://discord.gg/WBaSQ3A)
|
[](https://discord.gg/WBaSQ3A)
|
||||||
[](https://opencollective.com/obs-websocket-dev)
|
[](https://opencollective.com/obs-websocket-dev)
|
||||||
|
|
||||||
|
@ -9,14 +9,11 @@ OBSWebSocket.Settings.DebugEnable="Enable Debug Logging"
|
|||||||
OBSWebSocket.Settings.DebugEnableHoverText="Enables debug logging for the current instance of OBS. Does not persist on load.\nUse --websocket_debug to enable on load."
|
OBSWebSocket.Settings.DebugEnableHoverText="Enables debug logging for the current instance of OBS. Does not persist on load.\nUse --websocket_debug to enable on load."
|
||||||
|
|
||||||
OBSWebSocket.Settings.ServerSettingsTitle="Server Settings"
|
OBSWebSocket.Settings.ServerSettingsTitle="Server Settings"
|
||||||
OBSWebSocket.Settings.ServerPort="Server Port"
|
|
||||||
OBSWebSocket.Settings.AllowExternal="Allow External Access"
|
|
||||||
OBSWebSocket.Settings.AllowExternalHoverText="Allows clients from outside this computer to connect to obs-websocket."
|
|
||||||
OBSWebSocket.Settings.AuthRequired="Enable Authentication"
|
OBSWebSocket.Settings.AuthRequired="Enable Authentication"
|
||||||
OBSWebSocket.Settings.Password="Server Password"
|
OBSWebSocket.Settings.Password="Server Password"
|
||||||
OBSWebSocket.Settings.GeneratePassword="Generate Password"
|
OBSWebSocket.Settings.GeneratePassword="Generate Password"
|
||||||
|
OBSWebSocket.Settings.ServerPort="Server Port"
|
||||||
OBSWebSocket.Settings.ShowConnectInfo="Show Connect Info"
|
OBSWebSocket.Settings.ShowConnectInfo="Show Connect Info"
|
||||||
OBSWebSocket.Settings.ShowConnectInfoHoverText="Connect Info is not available if external connections are disabled."
|
|
||||||
OBSWebSocket.Settings.ShowConnectInfoWarningTitle="Warning: Currently Live"
|
OBSWebSocket.Settings.ShowConnectInfoWarningTitle="Warning: Currently Live"
|
||||||
OBSWebSocket.Settings.ShowConnectInfoWarningMessage="It appears that an output (stream, recording, etc.) is currently active."
|
OBSWebSocket.Settings.ShowConnectInfoWarningMessage="It appears that an output (stream, recording, etc.) is currently active."
|
||||||
OBSWebSocket.Settings.ShowConnectInfoWarningInfoText="Are you sure that you want to show your connect info?"
|
OBSWebSocket.Settings.ShowConnectInfoWarningInfoText="Are you sure that you want to show your connect info?"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#define MyAppName "obs-websocket"
|
#define MyAppName "obs-websocket"
|
||||||
#define MyAppVersion "@OBS_WEBSOCKET_VERSION@"
|
#define MyAppVersion "@OBS_WEBSOCKET_VERSION@"
|
||||||
#define MyAppPublisher "obs-websocket"
|
#define MyAppPublisher "OBS Project"
|
||||||
#define MyAppURL "http://github.com/obsproject/obs-websocket"
|
#define MyAppURL "http://github.com/obsproject/obs-websocket"
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
|
@ -60,7 +60,8 @@ void obs_module_post_load(void)
|
|||||||
blog(LOG_ERROR, "Failed to call GetVersion due to obs-websocket not being installed.");
|
blog(LOG_ERROR, "Failed to call GetVersion due to obs-websocket not being installed.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s", response->status_code, response->comment, response->response_data);
|
blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
|
||||||
|
response->status_code, response->comment, response->response_data);
|
||||||
obs_websocket_request_response_free(response);
|
obs_websocket_request_response_free(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +161,8 @@ static inline obs_websocket_vendor obs_websocket_register_vendor(const char *ven
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Registers a new request for a vendor
|
// Registers a new request for a vendor
|
||||||
static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor vendor, const char *request_type, obs_websocket_request_callback_function request_callback, void* priv_data)
|
static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor vendor, const char *request_type,
|
||||||
|
obs_websocket_request_callback_function request_callback, void *priv_data)
|
||||||
{
|
{
|
||||||
calldata_t cd = {0};
|
calldata_t cd = {0};
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#define PARAM_FIRSTLOAD "FirstLoad"
|
#define PARAM_FIRSTLOAD "FirstLoad"
|
||||||
#define PARAM_ENABLED "ServerEnabled"
|
#define PARAM_ENABLED "ServerEnabled"
|
||||||
#define PARAM_PORT "ServerPort"
|
#define PARAM_PORT "ServerPort"
|
||||||
#define PARAM_BINDLOOPBACK "BindLoopback"
|
|
||||||
#define PARAM_ALERTS "AlertsEnabled"
|
#define PARAM_ALERTS "AlertsEnabled"
|
||||||
#define PARAM_AUTHREQUIRED "AuthRequired"
|
#define PARAM_AUTHREQUIRED "AuthRequired"
|
||||||
#define PARAM_PASSWORD "ServerPassword"
|
#define PARAM_PASSWORD "ServerPassword"
|
||||||
@ -38,13 +37,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#define CMDLINE_WEBSOCKET_PASSWORD "websocket_password"
|
#define CMDLINE_WEBSOCKET_PASSWORD "websocket_password"
|
||||||
#define CMDLINE_WEBSOCKET_DEBUG "websocket_debug"
|
#define CMDLINE_WEBSOCKET_DEBUG "websocket_debug"
|
||||||
|
|
||||||
Config::Config() :
|
Config::Config()
|
||||||
PortOverridden(false),
|
: PortOverridden(false),
|
||||||
PasswordOverridden(false),
|
PasswordOverridden(false),
|
||||||
FirstLoad(true),
|
FirstLoad(true),
|
||||||
ServerEnabled(true),
|
ServerEnabled(true),
|
||||||
ServerPort(4455),
|
ServerPort(4455),
|
||||||
BindLoopback(true),
|
|
||||||
Ipv4Only(false),
|
Ipv4Only(false),
|
||||||
DebugEnabled(false),
|
DebugEnabled(false),
|
||||||
AlertsEnabled(false),
|
AlertsEnabled(false),
|
||||||
@ -66,7 +64,6 @@ void Config::Load()
|
|||||||
ServerEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED);
|
ServerEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED);
|
||||||
AlertsEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS);
|
AlertsEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS);
|
||||||
ServerPort = config_get_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT);
|
ServerPort = config_get_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT);
|
||||||
BindLoopback = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK);
|
|
||||||
AuthRequired = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED);
|
AuthRequired = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED);
|
||||||
ServerPassword = config_get_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD);
|
ServerPassword = config_get_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD);
|
||||||
|
|
||||||
@ -134,7 +131,6 @@ void Config::Save()
|
|||||||
if (!PortOverridden) {
|
if (!PortOverridden) {
|
||||||
config_set_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
|
config_set_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
}
|
}
|
||||||
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK, BindLoopback);
|
|
||||||
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
|
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
|
||||||
if (!PasswordOverridden) {
|
if (!PasswordOverridden) {
|
||||||
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
||||||
@ -155,7 +151,6 @@ void Config::SetDefaultsToGlobalStore()
|
|||||||
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, FirstLoad);
|
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, FirstLoad);
|
||||||
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, ServerEnabled);
|
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, ServerEnabled);
|
||||||
config_set_default_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
|
config_set_default_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_BINDLOOPBACK, BindLoopback);
|
|
||||||
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
|
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
|
||||||
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
config_set_default_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
||||||
config_set_default_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
|
config_set_default_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
|
||||||
|
@ -38,7 +38,6 @@ struct Config {
|
|||||||
std::atomic<bool> FirstLoad;
|
std::atomic<bool> FirstLoad;
|
||||||
std::atomic<bool> ServerEnabled;
|
std::atomic<bool> ServerEnabled;
|
||||||
std::atomic<uint16_t> ServerPort;
|
std::atomic<uint16_t> ServerPort;
|
||||||
std::atomic<bool> BindLoopback;
|
|
||||||
std::atomic<bool> Ipv4Only;
|
std::atomic<bool> Ipv4Only;
|
||||||
std::atomic<bool> DebugEnabled;
|
std::atomic<bool> DebugEnabled;
|
||||||
std::atomic<bool> AlertsEnabled;
|
std::atomic<bool> AlertsEnabled;
|
||||||
|
@ -3,7 +3,11 @@
|
|||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
#include "utils/Json.h"
|
#include "utils/Json.h"
|
||||||
|
|
||||||
#define RETURN_STATUS(status) { calldata_set_bool(cd, "success", status); return; }
|
#define RETURN_STATUS(status) \
|
||||||
|
{ \
|
||||||
|
calldata_set_bool(cd, "success", status); \
|
||||||
|
return; \
|
||||||
|
}
|
||||||
#define RETURN_SUCCESS() RETURN_STATUS(true);
|
#define RETURN_SUCCESS() RETURN_STATUS(true);
|
||||||
#define RETURN_FAILURE() RETURN_STATUS(false);
|
#define RETURN_FAILURE() RETURN_STATUS(false);
|
||||||
|
|
||||||
@ -25,11 +29,15 @@ WebSocketApi::WebSocketApi()
|
|||||||
_procHandler = proc_handler_create();
|
_procHandler = proc_handler_create();
|
||||||
|
|
||||||
proc_handler_add(_procHandler, "bool get_api_version(out int version)", &get_api_version, nullptr);
|
proc_handler_add(_procHandler, "bool get_api_version(out int version)", &get_api_version, nullptr);
|
||||||
proc_handler_add(_procHandler, "bool call_request(in string request_type, in string request_data, out ptr response)", &call_request, nullptr);
|
proc_handler_add(_procHandler, "bool call_request(in string request_type, in string request_data, out ptr response)",
|
||||||
|
&call_request, nullptr);
|
||||||
proc_handler_add(_procHandler, "bool vendor_register(in string name, out ptr vendor)", &vendor_register_cb, this);
|
proc_handler_add(_procHandler, "bool vendor_register(in string name, out ptr vendor)", &vendor_register_cb, this);
|
||||||
proc_handler_add(_procHandler, "bool vendor_request_register(in ptr vendor, in string type, in ptr callback)", &vendor_request_register_cb, this);
|
proc_handler_add(_procHandler, "bool vendor_request_register(in ptr vendor, in string type, in ptr callback)",
|
||||||
proc_handler_add(_procHandler, "bool vendor_request_unregister(in ptr vendor, in string type)", &vendor_request_unregister_cb, this);
|
&vendor_request_register_cb, this);
|
||||||
proc_handler_add(_procHandler, "bool vendor_event_emit(in ptr vendor, in string type, in ptr data)", &vendor_event_emit_cb, this);
|
proc_handler_add(_procHandler, "bool vendor_request_unregister(in ptr vendor, in string type)",
|
||||||
|
&vendor_request_unregister_cb, this);
|
||||||
|
proc_handler_add(_procHandler, "bool vendor_event_emit(in ptr vendor, in string type, in ptr data)", &vendor_event_emit_cb,
|
||||||
|
this);
|
||||||
|
|
||||||
proc_handler_t *ph = obs_get_proc_handler();
|
proc_handler_t *ph = obs_get_proc_handler();
|
||||||
assert(ph != NULL);
|
assert(ph != NULL);
|
||||||
@ -58,7 +66,8 @@ void WebSocketApi::SetEventCallback(EventCallback cb)
|
|||||||
_eventCallback = cb;
|
_eventCallback = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType, obs_data_t *requestData, obs_data_t *responseData)
|
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType,
|
||||||
|
obs_data_t *requestData, obs_data_t *responseData)
|
||||||
{
|
{
|
||||||
std::shared_lock l(_mutex);
|
std::shared_lock l(_mutex);
|
||||||
|
|
||||||
@ -119,7 +128,7 @@ void WebSocketApi::call_request(void *, calldata_t *cd)
|
|||||||
Request request(request_type, requestData);
|
Request request(request_type, requestData);
|
||||||
RequestResult requestResult = requestHandler.ProcessRequest(request);
|
RequestResult requestResult = requestHandler.ProcessRequest(request);
|
||||||
|
|
||||||
response->status_code = (uint)requestResult.StatusCode;
|
response->status_code = (unsigned int)requestResult.StatusCode;
|
||||||
if (!requestResult.Comment.empty())
|
if (!requestResult.Comment.empty())
|
||||||
response->comment = bstrdup(requestResult.Comment.c_str());
|
response->comment = bstrdup(requestResult.Comment.c_str());
|
||||||
if (requestResult.ResponseData.is_object()) {
|
if (requestResult.ResponseData.is_object()) {
|
||||||
@ -129,7 +138,8 @@ void WebSocketApi::call_request(void *, calldata_t *cd)
|
|||||||
|
|
||||||
calldata_set_ptr(cd, "response", response);
|
calldata_set_ptr(cd, "response", response);
|
||||||
|
|
||||||
blog_debug("[WebSocketApi::call_request] Request %s called, response status code is %u", request_type, response->status_code);
|
blog_debug("[WebSocketApi::call_request] Request %s called, response status code is %u", request_type,
|
||||||
|
response->status_code);
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
@ -148,7 +158,8 @@ void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
|
|||||||
std::unique_lock l(c->_mutex);
|
std::unique_lock l(c->_mutex);
|
||||||
|
|
||||||
if (c->_vendors.count(vendorName)) {
|
if (c->_vendors.count(vendorName)) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed because `%s` is already a registered vendor.", vendorName);
|
blog(LOG_WARNING, "[WebSocketApi::vendor_register_cb] Failed because `%s` is already a registered vendor.",
|
||||||
|
vendorName);
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,13 +183,17 @@ void WebSocketApi::vendor_request_register_cb(void *, calldata_t *cd)
|
|||||||
|
|
||||||
const char *requestType;
|
const char *requestType;
|
||||||
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
|
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing or empty `type` string.", v->_name.c_str());
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing or empty `type` string.",
|
||||||
|
v->_name.c_str());
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *voidCallback;
|
void *voidCallback;
|
||||||
if (!calldata_get_ptr(cd, "callback", &voidCallback) || !voidCallback) {
|
if (!calldata_get_ptr(cd, "callback", &voidCallback) || !voidCallback) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing `callback` pointer.", v->_name.c_str());
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed due to missing `callback` pointer.",
|
||||||
|
v->_name.c_str());
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,13 +202,16 @@ void WebSocketApi::vendor_request_register_cb(void *, calldata_t *cd)
|
|||||||
std::unique_lock l(v->_mutex);
|
std::unique_lock l(v->_mutex);
|
||||||
|
|
||||||
if (v->_requests.count(requestType)) {
|
if (v->_requests.count(requestType)) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is already a registered request.", v->_name.c_str(), requestType);
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is already a registered request.",
|
||||||
|
v->_name.c_str(), requestType);
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
v->_requests[requestType] = *cb;
|
v->_requests[requestType] = *cb;
|
||||||
|
|
||||||
blog_debug("[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Registered new vendor request: %s", v->_name.c_str(), requestType);
|
blog_debug("[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Registered new vendor request: %s",
|
||||||
|
v->_name.c_str(), requestType);
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
@ -206,20 +224,25 @@ void WebSocketApi::vendor_request_unregister_cb(void *, calldata_t *cd)
|
|||||||
|
|
||||||
const char *requestType;
|
const char *requestType;
|
||||||
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
|
if (!calldata_get_string(cd, "type", &requestType) || strlen(requestType) == 0) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Failed due to missing `type` string.", v->_name.c_str());
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Failed due to missing `type` string.",
|
||||||
|
v->_name.c_str());
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_lock l(v->_mutex);
|
std::unique_lock l(v->_mutex);
|
||||||
|
|
||||||
if (!v->_requests.count(requestType)) {
|
if (!v->_requests.count(requestType)) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is not a registered request.", v->_name.c_str(), requestType);
|
blog(LOG_WARNING,
|
||||||
|
"[WebSocketApi::vendor_request_register_cb] [vendorName: %s] Failed because `%s` is not a registered request.",
|
||||||
|
v->_name.c_str(), requestType);
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
v->_requests.erase(requestType);
|
v->_requests.erase(requestType);
|
||||||
|
|
||||||
blog_debug("[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Unregistered vendor request: %s", v->_name.c_str(), requestType);
|
blog_debug("[WebSocketApi::vendor_request_unregister_cb] [vendorName: %s] Unregistered vendor request: %s",
|
||||||
|
v->_name.c_str(), requestType);
|
||||||
|
|
||||||
RETURN_SUCCESS();
|
RETURN_SUCCESS();
|
||||||
}
|
}
|
||||||
@ -234,13 +257,15 @@ void WebSocketApi::vendor_event_emit_cb(void *priv_data, calldata_t *cd)
|
|||||||
|
|
||||||
const char *eventType;
|
const char *eventType;
|
||||||
if (!calldata_get_string(cd, "type", &eventType) || strlen(eventType) == 0) {
|
if (!calldata_get_string(cd, "type", &eventType) || strlen(eventType) == 0) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `type` string.", v->_name.c_str());
|
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `type` string.",
|
||||||
|
v->_name.c_str());
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *voidEventData;
|
void *voidEventData;
|
||||||
if (!calldata_get_ptr(cd, "data", &voidEventData)) {
|
if (!calldata_get_ptr(cd, "data", &voidEventData)) {
|
||||||
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `data` pointer.", v->_name.c_str());
|
blog(LOG_WARNING, "[WebSocketApi::vendor_event_emit_cb] [vendorName: %s] Failed due to missing `data` pointer.",
|
||||||
|
v->_name.c_str());
|
||||||
RETURN_FAILURE();
|
RETURN_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,8 @@ class WebSocketApi {
|
|||||||
|
|
||||||
void SetEventCallback(EventCallback cb);
|
void SetEventCallback(EventCallback cb);
|
||||||
|
|
||||||
enum RequestReturnCode PerformVendorRequest(std::string vendorName, std::string requestName, obs_data_t *requestData, obs_data_t *responseData);
|
enum RequestReturnCode PerformVendorRequest(std::string vendorName, std::string requestName, obs_data_t *requestData,
|
||||||
|
obs_data_t *responseData);
|
||||||
|
|
||||||
static void get_ph_cb(void *priv_data, calldata_t *cd);
|
static void get_ph_cb(void *priv_data, calldata_t *cd);
|
||||||
static void get_api_version(void *, calldata_t *cd);
|
static void get_api_version(void *, calldata_t *cd);
|
||||||
|
@ -19,8 +19,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
EventHandler::EventHandler() :
|
EventHandler::EventHandler()
|
||||||
_obsLoaded(false),
|
: _obsLoaded(false),
|
||||||
_inputVolumeMetersRef(0),
|
_inputVolumeMetersRef(0),
|
||||||
_inputActiveStateChangedRef(0),
|
_inputActiveStateChangedRef(0),
|
||||||
_inputShowStateChangedRef(0),
|
_inputShowStateChangedRef(0),
|
||||||
@ -80,7 +80,8 @@ void EventHandler::ProcessSubscription(uint64_t eventSubscriptions)
|
|||||||
if (_inputVolumeMetersHandler)
|
if (_inputVolumeMetersHandler)
|
||||||
blog(LOG_WARNING, "[EventHandler::ProcessSubscription] Input volume meter handler already exists!");
|
blog(LOG_WARNING, "[EventHandler::ProcessSubscription] Input volume meter handler already exists!");
|
||||||
else
|
else
|
||||||
_inputVolumeMetersHandler = std::make_unique<Utils::Obs::VolumeMeter::Handler>(std::bind(&EventHandler::HandleInputVolumeMeters, this, std::placeholders::_1));
|
_inputVolumeMetersHandler = std::make_unique<Utils::Obs::VolumeMeter::Handler>(
|
||||||
|
std::bind(&EventHandler::HandleInputVolumeMeters, this, std::placeholders::_1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0)
|
if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0)
|
||||||
@ -266,7 +267,8 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
|||||||
switch (event) {
|
switch (event) {
|
||||||
// General
|
// General
|
||||||
case OBS_FRONTEND_EVENT_FINISHED_LOADING:
|
case OBS_FRONTEND_EVENT_FINISHED_LOADING:
|
||||||
blog_debug("[EventHandler::OnFrontendEvent] OBS has finished loading. Connecting final handlers and enabling events...");
|
blog_debug(
|
||||||
|
"[EventHandler::OnFrontendEvent] OBS has finished loading. Connecting final handlers and enabling events...");
|
||||||
|
|
||||||
// Connect source signals and enable events only after OBS has fully loaded (to reduce extra logging).
|
// Connect source signals and enable events only after OBS has fully loaded (to reduce extra logging).
|
||||||
eventHandler->_obsLoaded.store(true);
|
eventHandler->_obsLoaded.store(true);
|
||||||
@ -359,8 +361,7 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:
|
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING: {
|
||||||
{
|
|
||||||
obs_frontend_source_list transitions = {};
|
obs_frontend_source_list transitions = {};
|
||||||
obs_frontend_get_transitions(&transitions);
|
obs_frontend_get_transitions(&transitions);
|
||||||
for (size_t i = 0; i < transitions.sources.num; i++) {
|
for (size_t i = 0; i < transitions.sources.num; i++) {
|
||||||
@ -371,8 +372,7 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
|||||||
}
|
}
|
||||||
eventHandler->HandleCurrentSceneCollectionChanging();
|
eventHandler->HandleCurrentSceneCollectionChanging();
|
||||||
break;
|
break;
|
||||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED:
|
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED: {
|
||||||
{
|
|
||||||
obs_frontend_source_list transitions = {};
|
obs_frontend_source_list transitions = {};
|
||||||
obs_frontend_get_transitions(&transitions);
|
obs_frontend_get_transitions(&transitions);
|
||||||
for (size_t i = 0; i < transitions.sources.num; i++) {
|
for (size_t i = 0; i < transitions.sources.num; i++) {
|
||||||
@ -411,8 +411,7 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
|||||||
case OBS_FRONTEND_EVENT_TRANSITION_CHANGED:
|
case OBS_FRONTEND_EVENT_TRANSITION_CHANGED:
|
||||||
eventHandler->HandleCurrentSceneTransitionChanged();
|
eventHandler->HandleCurrentSceneTransitionChanged();
|
||||||
break;
|
break;
|
||||||
case OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED:
|
case OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED: {
|
||||||
{
|
|
||||||
obs_frontend_source_list transitions = {};
|
obs_frontend_source_list transitions = {};
|
||||||
obs_frontend_get_transitions(&transitions);
|
obs_frontend_get_transitions(&transitions);
|
||||||
for (size_t i = 0; i < transitions.sources.num; i++) {
|
for (size_t i = 0; i < transitions.sources.num; i++) {
|
||||||
@ -420,8 +419,7 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
|
|||||||
eventHandler->ConnectSourceSignals(transition);
|
eventHandler->ConnectSourceSignals(transition);
|
||||||
}
|
}
|
||||||
obs_frontend_source_list_free(&transitions);
|
obs_frontend_source_list_free(&transitions);
|
||||||
}
|
} break;
|
||||||
break;
|
|
||||||
case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED:
|
case OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED:
|
||||||
eventHandler->HandleCurrentSceneTransitionDurationChanged();
|
eventHandler->HandleCurrentSceneTransitionDurationChanged();
|
||||||
break;
|
break;
|
||||||
|
@ -29,8 +29,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../utils/Obs_VolumeMeter.h"
|
#include "../utils/Obs_VolumeMeter.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
class EventHandler
|
class EventHandler {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
EventHandler();
|
EventHandler();
|
||||||
~EventHandler();
|
~EventHandler();
|
||||||
@ -77,7 +76,6 @@ class EventHandler
|
|||||||
static void SourceMediaNextMultiHandler(void *param, calldata_t *data);
|
static void SourceMediaNextMultiHandler(void *param, calldata_t *data);
|
||||||
static void SourceMediaPreviousMultiHandler(void *param, calldata_t *data);
|
static void SourceMediaPreviousMultiHandler(void *param, calldata_t *data);
|
||||||
|
|
||||||
|
|
||||||
// General
|
// General
|
||||||
void HandleExitStarted();
|
void HandleExitStarted();
|
||||||
void HandleStudioModeStateChanged(bool enabled);
|
void HandleStudioModeStateChanged(bool enabled);
|
||||||
|
@ -373,11 +373,9 @@ void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *d
|
|||||||
|
|
||||||
enum obs_monitoring_type monitorType = (obs_monitoring_type)calldata_int(data, "type");
|
enum obs_monitoring_type monitorType = (obs_monitoring_type)calldata_int(data, "type");
|
||||||
|
|
||||||
std::string monitorTypeString = Utils::Obs::StringHelper::GetInputMonitorType(monitorType);
|
|
||||||
|
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["inputName"] = obs_source_get_name(source);
|
eventData["inputName"] = obs_source_get_name(source);
|
||||||
eventData["monitorType"] = monitorTypeString;
|
eventData["monitorType"] = monitorType;
|
||||||
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioMonitorTypeChanged", eventData);
|
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioMonitorTypeChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,9 +19,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
#define CASE(x) case x: return #x;
|
#define CASE(x) \
|
||||||
|
case x: \
|
||||||
|
return #x;
|
||||||
|
|
||||||
std::string GetMediaInputActionString(ObsMediaInputAction action) {
|
std::string GetMediaInputActionString(ObsMediaInputAction action)
|
||||||
|
{
|
||||||
switch (action) {
|
switch (action) {
|
||||||
default:
|
default:
|
||||||
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE)
|
CASE(OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE)
|
||||||
|
@ -19,7 +19,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
|
|
||||||
static bool GetOutputStateActive(ObsOutputState state) {
|
static bool GetOutputStateActive(ObsOutputState state)
|
||||||
|
{
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case OBS_WEBSOCKET_OUTPUT_STARTED:
|
case OBS_WEBSOCKET_OUTPUT_STARTED:
|
||||||
case OBS_WEBSOCKET_OUTPUT_RESUMED:
|
case OBS_WEBSOCKET_OUTPUT_RESUMED:
|
||||||
@ -52,7 +53,7 @@ void EventHandler::HandleStreamStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
eventData["outputState"] = state;
|
||||||
BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "StreamStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +62,7 @@ void EventHandler::HandleStreamStateChanged(ObsOutputState state)
|
|||||||
*
|
*
|
||||||
* @dataField outputActive | Boolean | Whether the output is active
|
* @dataField outputActive | Boolean | Whether the output is active
|
||||||
* @dataField outputState | String | The specific state of the output
|
* @dataField outputState | String | The specific state of the output
|
||||||
|
* @dataField outputPath | String | File name for the saved recording, if record stopped. `null` otherwise
|
||||||
*
|
*
|
||||||
* @eventType RecordStateChanged
|
* @eventType RecordStateChanged
|
||||||
* @eventSubscription Outputs
|
* @eventSubscription Outputs
|
||||||
@ -74,7 +76,12 @@ void EventHandler::HandleRecordStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
eventData["outputState"] = state;
|
||||||
|
if (state == OBS_WEBSOCKET_OUTPUT_STOPPED) {
|
||||||
|
eventData["outputPath"] = Utils::Obs::StringHelper::GetLastRecordFileName();
|
||||||
|
} else {
|
||||||
|
eventData["outputPath"] = nullptr;
|
||||||
|
}
|
||||||
BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +103,7 @@ void EventHandler::HandleReplayBufferStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
eventData["outputState"] = state;
|
||||||
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +125,7 @@ void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
|
|||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["outputActive"] = GetOutputStateActive(state);
|
eventData["outputActive"] = GetOutputStateActive(state);
|
||||||
eventData["outputState"] = Utils::Obs::StringHelper::GetOutputState(state);
|
eventData["outputState"] = state;
|
||||||
BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "VirtualcamStateChanged", eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,6 +145,6 @@ void EventHandler::HandleVirtualcamStateChanged(ObsOutputState state)
|
|||||||
void EventHandler::HandleReplayBufferSaved()
|
void EventHandler::HandleReplayBufferSaved()
|
||||||
{
|
{
|
||||||
json eventData;
|
json eventData;
|
||||||
eventData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFilePath();
|
eventData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFileName();
|
||||||
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferSaved", eventData);
|
BroadcastEvent(EventSubscription::Outputs, "ReplayBufferSaved", eventData);
|
||||||
}
|
}
|
||||||
|
@ -157,13 +157,15 @@ namespace EventSubscription {
|
|||||||
* Helper to receive all non-high-volume events.
|
* Helper to receive all non-high-volume events.
|
||||||
*
|
*
|
||||||
* @enumIdentifier All
|
* @enumIdentifier All
|
||||||
* @enumValue (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Vendors)
|
* @enumValue (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Vendors | Ui)
|
||||||
* @enumType EventSubscription
|
* @enumType EventSubscription
|
||||||
* @rpcVersion -1
|
* @rpcVersion -1
|
||||||
* @initialVersion 5.0.0
|
* @initialVersion 5.0.0
|
||||||
* @api enums
|
* @api enums
|
||||||
*/
|
*/
|
||||||
All = (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Ui | Vendors),
|
All = (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Vendors |
|
||||||
|
Ui),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription value to receive the `InputVolumeMeters` high-volume event.
|
* Subscription value to receive the `InputVolumeMeters` high-volume event.
|
||||||
*
|
*
|
||||||
|
@ -28,18 +28,13 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../Config.h"
|
#include "../Config.h"
|
||||||
#include "../utils/Platform.h"
|
#include "../utils/Platform.h"
|
||||||
|
|
||||||
ConnectInfo::ConnectInfo(QWidget* parent) :
|
ConnectInfo::ConnectInfo(QWidget *parent) : QDialog(parent, Qt::Dialog), ui(new Ui::ConnectInfo)
|
||||||
QDialog(parent, Qt::Dialog),
|
|
||||||
ui(new Ui::ConnectInfo)
|
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
connect(ui->copyServerIpButton, &QPushButton::clicked,
|
connect(ui->copyServerIpButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerIpButtonClicked);
|
||||||
this, &ConnectInfo::CopyServerIpButtonClicked);
|
connect(ui->copyServerPortButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerPortButtonClicked);
|
||||||
connect(ui->copyServerPortButton, &QPushButton::clicked,
|
connect(ui->copyServerPasswordButton, &QPushButton::clicked, this, &ConnectInfo::CopyServerPasswordButtonClicked);
|
||||||
this, &ConnectInfo::CopyServerPortButtonClicked);
|
|
||||||
connect(ui->copyServerPasswordButton, &QPushButton::clicked,
|
|
||||||
this, &ConnectInfo::CopyServerPasswordButtonClicked);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectInfo::~ConnectInfo()
|
ConnectInfo::~ConnectInfo()
|
||||||
|
@ -25,8 +25,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "ui_ConnectInfo.h"
|
#include "ui_ConnectInfo.h"
|
||||||
|
|
||||||
class ConnectInfo : public QDialog
|
class ConnectInfo : public QDialog {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -37,8 +37,8 @@ QString GetToolTipIconHtml()
|
|||||||
return iconTemplate.arg(iconFile);
|
return iconTemplate.arg(iconFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::SettingsDialog(QWidget* parent) :
|
SettingsDialog::SettingsDialog(QWidget *parent)
|
||||||
QDialog(parent, Qt::Dialog),
|
: QDialog(parent, Qt::Dialog),
|
||||||
ui(new Ui::SettingsDialog),
|
ui(new Ui::SettingsDialog),
|
||||||
connectInfo(new ConnectInfo),
|
connectInfo(new ConnectInfo),
|
||||||
sessionTableTimer(new QTimer),
|
sessionTableTimer(new QTimer),
|
||||||
@ -52,22 +52,15 @@ SettingsDialog::SettingsDialog(QWidget* parent) :
|
|||||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
|
||||||
// Set the appropriate tooltip icon for the theme
|
// Set the appropriate tooltip icon for the theme
|
||||||
QString toolTipHtml = GetToolTipIconHtml();
|
ui->enableDebugLoggingToolTipLabel->setText(GetToolTipIconHtml());
|
||||||
ui->enableDebugLoggingToolTipLabel->setText(toolTipHtml);
|
|
||||||
ui->allowExternalToolTipLabel->setText(toolTipHtml);
|
|
||||||
|
|
||||||
connect(sessionTableTimer, &QTimer::timeout,
|
connect(sessionTableTimer, &QTimer::timeout, this, &SettingsDialog::FillSessionTable);
|
||||||
this, &SettingsDialog::FillSessionTable);
|
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &SettingsDialog::DialogButtonClicked);
|
||||||
connect(ui->buttonBox, &QDialogButtonBox::clicked,
|
connect(ui->enableAuthenticationCheckBox, &QCheckBox::stateChanged, this,
|
||||||
this, &SettingsDialog::DialogButtonClicked);
|
&SettingsDialog::EnableAuthenticationCheckBoxChanged);
|
||||||
connect(ui->enableAuthenticationCheckBox, &QCheckBox::stateChanged,
|
connect(ui->generatePasswordButton, &QPushButton::clicked, this, &SettingsDialog::GeneratePasswordButtonClicked);
|
||||||
this, &SettingsDialog::EnableAuthenticationCheckBoxChanged);
|
connect(ui->showConnectInfoButton, &QPushButton::clicked, this, &SettingsDialog::ShowConnectInfoButtonClicked);
|
||||||
connect(ui->generatePasswordButton, &QPushButton::clicked,
|
connect(ui->serverPasswordLineEdit, &QLineEdit::textEdited, this, &SettingsDialog::PasswordEdited);
|
||||||
this, &SettingsDialog::GeneratePasswordButtonClicked);
|
|
||||||
connect(ui->showConnectInfoButton, &QPushButton::clicked,
|
|
||||||
this, &SettingsDialog::ShowConnectInfoButtonClicked);
|
|
||||||
connect(ui->serverPasswordLineEdit, &QLineEdit::textEdited,
|
|
||||||
this, &SettingsDialog::PasswordEdited);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::~SettingsDialog()
|
SettingsDialog::~SettingsDialog()
|
||||||
@ -129,16 +122,12 @@ void SettingsDialog::RefreshData()
|
|||||||
ui->enableSystemTrayAlertsCheckBox->setChecked(conf->AlertsEnabled);
|
ui->enableSystemTrayAlertsCheckBox->setChecked(conf->AlertsEnabled);
|
||||||
ui->enableDebugLoggingCheckBox->setChecked(conf->DebugEnabled);
|
ui->enableDebugLoggingCheckBox->setChecked(conf->DebugEnabled);
|
||||||
ui->serverPortSpinBox->setValue(conf->ServerPort);
|
ui->serverPortSpinBox->setValue(conf->ServerPort);
|
||||||
ui->allowExternalCheckBox->setChecked(!conf->BindLoopback);
|
|
||||||
ui->enableAuthenticationCheckBox->setChecked(conf->AuthRequired);
|
ui->enableAuthenticationCheckBox->setChecked(conf->AuthRequired);
|
||||||
ui->serverPasswordLineEdit->setText(conf->ServerPassword);
|
ui->serverPasswordLineEdit->setText(conf->ServerPassword);
|
||||||
|
|
||||||
ui->showConnectInfoButton->setEnabled(!conf->BindLoopback);
|
|
||||||
ui->serverPasswordLineEdit->setEnabled(conf->AuthRequired);
|
ui->serverPasswordLineEdit->setEnabled(conf->AuthRequired);
|
||||||
ui->generatePasswordButton->setEnabled(conf->AuthRequired);
|
ui->generatePasswordButton->setEnabled(conf->AuthRequired);
|
||||||
|
|
||||||
ui->showConnectInfoButton->setToolTip(ui->allowExternalCheckBox->isChecked() ? "" : obs_module_text("OBSWebSocket.Settings.ShowConnectInfoHoverText"));
|
|
||||||
|
|
||||||
FillSessionTable();
|
FillSessionTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,16 +177,15 @@ void SettingsDialog::SaveFormData()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needsRestart = (conf->ServerEnabled != ui->enableWebSocketServerCheckBox->isChecked()) ||
|
bool needsRestart =
|
||||||
(ui->enableAuthenticationCheckBox->isChecked() && conf->ServerPassword != ui->serverPasswordLineEdit->text()) ||
|
(conf->ServerEnabled != ui->enableWebSocketServerCheckBox->isChecked()) ||
|
||||||
(conf->BindLoopback == ui->allowExternalCheckBox->isChecked()) ||
|
(conf->ServerPort != ui->serverPortSpinBox->value()) ||
|
||||||
(conf->ServerPort != ui->serverPortSpinBox->value());
|
(ui->enableAuthenticationCheckBox->isChecked() && conf->ServerPassword != ui->serverPasswordLineEdit->text());
|
||||||
|
|
||||||
conf->ServerEnabled = ui->enableWebSocketServerCheckBox->isChecked();
|
conf->ServerEnabled = ui->enableWebSocketServerCheckBox->isChecked();
|
||||||
conf->AlertsEnabled = ui->enableSystemTrayAlertsCheckBox->isChecked();
|
conf->AlertsEnabled = ui->enableSystemTrayAlertsCheckBox->isChecked();
|
||||||
conf->DebugEnabled = ui->enableDebugLoggingCheckBox->isChecked();
|
conf->DebugEnabled = ui->enableDebugLoggingCheckBox->isChecked();
|
||||||
conf->ServerPort = ui->serverPortSpinBox->value();
|
conf->ServerPort = ui->serverPortSpinBox->value();
|
||||||
conf->BindLoopback = !ui->allowExternalCheckBox->isChecked();
|
|
||||||
conf->AuthRequired = ui->enableAuthenticationCheckBox->isChecked();
|
conf->AuthRequired = ui->enableAuthenticationCheckBox->isChecked();
|
||||||
conf->ServerPassword = ui->serverPasswordLineEdit->text();
|
conf->ServerPassword = ui->serverPasswordLineEdit->text();
|
||||||
|
|
||||||
@ -246,7 +234,8 @@ void SettingsDialog::FillSessionTable()
|
|||||||
QTableWidgetItem *durationItem = new QTableWidgetItem(QTime(0, 0, sessionDuration).toString("hh:mm:ss"));
|
QTableWidgetItem *durationItem = new QTableWidgetItem(QTime(0, 0, sessionDuration).toString("hh:mm:ss"));
|
||||||
ui->websocketSessionTable->setItem(i, 1, durationItem);
|
ui->websocketSessionTable->setItem(i, 1, durationItem);
|
||||||
|
|
||||||
QTableWidgetItem *statsItem = new QTableWidgetItem(QString("%1/%2").arg(session.incomingMessages).arg(session.outgoingMessages));
|
QTableWidgetItem *statsItem =
|
||||||
|
new QTableWidgetItem(QString("%1/%2").arg(session.incomingMessages).arg(session.outgoingMessages));
|
||||||
ui->websocketSessionTable->setItem(i, 2, statsItem);
|
ui->websocketSessionTable->setItem(i, 2, statsItem);
|
||||||
|
|
||||||
QLabel *identifiedLabel = new QLabel();
|
QLabel *identifiedLabel = new QLabel();
|
||||||
@ -266,9 +255,7 @@ void SettingsDialog::FillSessionTable()
|
|||||||
invalidateButtonLayout->setContentsMargins(0, 0, 0, 0);
|
invalidateButtonLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
invalidateButtonWidget->setLayout(invalidateButtonLayout);
|
invalidateButtonWidget->setLayout(invalidateButtonLayout);
|
||||||
ui->websocketSessionTable->setCellWidget(i, 4, invalidateButtonWidget);
|
ui->websocketSessionTable->setCellWidget(i, 4, invalidateButtonWidget);
|
||||||
connect(invalidateButton, &QPushButton::clicked, [=]() {
|
connect(invalidateButton, &QPushButton::clicked, [=]() { webSocketServer->InvalidateSession(session.hdl); });
|
||||||
webSocketServer->InvalidateSession(session.hdl);
|
|
||||||
});
|
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "ui_SettingsDialog.h"
|
#include "ui_SettingsDialog.h"
|
||||||
|
|
||||||
class SettingsDialog : public QDialog
|
class SettingsDialog : public QDialog {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -155,21 +155,21 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="enableAuthenticationCheckBox">
|
<widget class="QCheckBox" name="enableAuthenticationCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>OBSWebSocket.Settings.AuthRequired</string>
|
<string>OBSWebSocket.Settings.AuthRequired</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="serverPasswordLabel">
|
<widget class="QLabel" name="serverPasswordLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>OBSWebSocket.Settings.Password</string>
|
<string>OBSWebSocket.Settings.Password</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="2" column="1">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="serverPasswordLineEdit">
|
<widget class="QLineEdit" name="serverPasswordLineEdit">
|
||||||
@ -187,7 +187,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="3" column="0">
|
||||||
<spacer name="horizontalSpacer_2">
|
<spacer name="horizontalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
@ -203,47 +203,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QPushButton" name="showConnectInfoButton">
|
<widget class="QPushButton" name="showConnectInfoButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>OBSWebSocket.Settings.ShowConnectInfo</string>
|
<string>OBSWebSocket.Settings.ShowConnectInfo</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="allowExternalCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>OBSWebSocket.Settings.AllowExternal</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="allowExternalToolTipLabel">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>OBSWebSocket.Settings.AllowExternalHoverText</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_4">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -32,8 +32,14 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
OBS_DECLARE_MODULE()
|
OBS_DECLARE_MODULE()
|
||||||
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
||||||
OBS_MODULE_AUTHOR("OBSProject")
|
OBS_MODULE_AUTHOR("OBSProject")
|
||||||
const char *obs_module_name(void) { return "obs-websocket"; }
|
const char *obs_module_name(void)
|
||||||
const char *obs_module_description(void) { return obs_module_text("OBSWebSocket.Plugin.Description"); }
|
{
|
||||||
|
return "obs-websocket";
|
||||||
|
}
|
||||||
|
const char *obs_module_description(void)
|
||||||
|
{
|
||||||
|
return obs_module_text("OBSWebSocket.Plugin.Description");
|
||||||
|
}
|
||||||
|
|
||||||
os_cpu_usage_info_t *_cpuUsageInfo;
|
os_cpu_usage_info_t *_cpuUsageInfo;
|
||||||
ConfigPtr _config;
|
ConfigPtr _config;
|
||||||
@ -46,7 +52,8 @@ void WebSocketApiEventCallback(std::string vendorName, std::string eventType, ob
|
|||||||
|
|
||||||
bool obs_module_load(void)
|
bool obs_module_load(void)
|
||||||
{
|
{
|
||||||
blog(LOG_INFO, "[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_VERSION, OBS_WEBSOCKET_RPC_VERSION);
|
blog(LOG_INFO, "[obs_module_load] you can haz websockets (Version: %s | RPC Version: %d)", OBS_WEBSOCKET_VERSION,
|
||||||
|
OBS_WEBSOCKET_RPC_VERSION);
|
||||||
blog(LOG_INFO, "[obs_module_load] Qt version (compile-time): %s | Qt version (run-time): %s", QT_VERSION_STR, qVersion());
|
blog(LOG_INFO, "[obs_module_load] Qt version (compile-time): %s | Qt version (run-time): %s", QT_VERSION_STR, qVersion());
|
||||||
blog(LOG_INFO, "[obs_module_load] Linked ASIO Version: %d", ASIO_VERSION);
|
blog(LOG_INFO, "[obs_module_load] Linked ASIO Version: %d", ASIO_VERSION);
|
||||||
|
|
||||||
@ -197,7 +204,8 @@ void obs_module_post_load()
|
|||||||
// Test calling obs-websocket requests
|
// Test calling obs-websocket requests
|
||||||
struct obs_websocket_request_response *response = obs_websocket_call_request("GetVersion");
|
struct obs_websocket_request_response *response = obs_websocket_call_request("GetVersion");
|
||||||
if (response) {
|
if (response) {
|
||||||
blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s", response->status_code, response->comment, response->response_data);
|
blog(LOG_INFO, "[obs_module_post_load] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
|
||||||
|
response->status_code, response->comment, response->response_data);
|
||||||
obs_websocket_request_response_free(response);
|
obs_websocket_request_response_free(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,8 +24,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../utils/Compat.h"
|
#include "../utils/Compat.h"
|
||||||
#include "../obs-websocket.h"
|
#include "../obs-websocket.h"
|
||||||
|
|
||||||
struct SerialFrameBatch
|
struct SerialFrameBatch {
|
||||||
{
|
|
||||||
RequestHandler &requestHandler;
|
RequestHandler &requestHandler;
|
||||||
std::queue<RequestBatchRequest> requests;
|
std::queue<RequestBatchRequest> requests;
|
||||||
std::vector<RequestResult> results;
|
std::vector<RequestResult> results;
|
||||||
@ -37,43 +36,46 @@ struct SerialFrameBatch
|
|||||||
std::mutex conditionMutex;
|
std::mutex conditionMutex;
|
||||||
std::condition_variable condition;
|
std::condition_variable condition;
|
||||||
|
|
||||||
SerialFrameBatch(RequestHandler &requestHandler, json &variables, bool haltOnFailure) :
|
SerialFrameBatch(RequestHandler &requestHandler, json &variables, bool haltOnFailure)
|
||||||
requestHandler(requestHandler),
|
: requestHandler(requestHandler),
|
||||||
variables(variables),
|
variables(variables),
|
||||||
haltOnFailure(haltOnFailure),
|
haltOnFailure(haltOnFailure),
|
||||||
frameCount(0),
|
frameCount(0),
|
||||||
sleepUntilFrame(0)
|
sleepUntilFrame(0)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParallelBatchResults
|
struct ParallelBatchResults {
|
||||||
{
|
|
||||||
RequestHandler &requestHandler;
|
RequestHandler &requestHandler;
|
||||||
std::vector<RequestResult> results;
|
std::vector<RequestResult> results;
|
||||||
|
|
||||||
std::mutex conditionMutex;
|
std::mutex conditionMutex;
|
||||||
std::condition_variable condition;
|
std::condition_variable condition;
|
||||||
|
|
||||||
ParallelBatchResults(RequestHandler &requestHandler) :
|
ParallelBatchResults(RequestHandler &requestHandler) : requestHandler(requestHandler) {}
|
||||||
requestHandler(requestHandler)
|
|
||||||
{}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// `{"inputName": "inputNameVariable"}` is essentially `inputName = inputNameVariable`
|
// `{"inputName": "inputNameVariable"}` is essentially `inputName = inputNameVariable`
|
||||||
static void PreProcessVariables(const json &variables, RequestBatchRequest &request)
|
static void PreProcessVariables(const json &variables, RequestBatchRequest &request)
|
||||||
{
|
{
|
||||||
if (variables.empty() || !request.InputVariables.is_object() || request.InputVariables.empty() || !request.RequestData.is_object())
|
if (variables.empty() || !request.InputVariables.is_object() || request.InputVariables.empty() ||
|
||||||
|
!request.RequestData.is_object())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto &[key, value] : request.InputVariables.items()) {
|
for (auto &[key, value] : request.InputVariables.items()) {
|
||||||
if (!value.is_string()) {
|
if (!value.is_string()) {
|
||||||
blog_debug("[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `inputVariables `is not a string. Skipping!", key.c_str());
|
blog_debug(
|
||||||
|
"[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `inputVariables `is not a string. Skipping!",
|
||||||
|
key.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string valueString = value;
|
std::string valueString = value;
|
||||||
if (!variables.contains(valueString)) {
|
if (!variables.contains(valueString)) {
|
||||||
blog_debug("[WebSocketServer::ProcessRequestBatch] `inputVariables` requested variable `%s`, but it does not exist. Skipping!", valueString.c_str());
|
blog_debug(
|
||||||
|
"[WebSocketServer::ProcessRequestBatch] `inputVariables` requested variable `%s`, but it does not exist. Skipping!",
|
||||||
|
valueString.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,13 +93,17 @@ static void PostProcessVariables(json &variables, const RequestBatchRequest &req
|
|||||||
|
|
||||||
for (auto &[key, value] : request.OutputVariables.items()) {
|
for (auto &[key, value] : request.OutputVariables.items()) {
|
||||||
if (!value.is_string()) {
|
if (!value.is_string()) {
|
||||||
blog_debug("[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `outputVariables` is not a string. Skipping!", key.c_str());
|
blog_debug(
|
||||||
|
"[WebSocketServer::ProcessRequestBatch] Value of field `%s` in `outputVariables` is not a string. Skipping!",
|
||||||
|
key.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string valueString = value;
|
std::string valueString = value;
|
||||||
if (!requestResult.ResponseData.contains(valueString)) {
|
if (!requestResult.ResponseData.contains(valueString)) {
|
||||||
blog_debug("[WebSocketServer::ProcessRequestBatch] `outputVariables` requested responseData field `%s`, but it does not exist. Skipping!", valueString.c_str());
|
blog_debug(
|
||||||
|
"[WebSocketServer::ProcessRequestBatch] `outputVariables` requested responseData field `%s`, but it does not exist. Skipping!",
|
||||||
|
valueString.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +162,10 @@ static void ObsTickCallback(void *param, float)
|
|||||||
serialFrameBatch->condition.notify_one();
|
serialFrameBatch->condition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session, RequestBatchExecutionType::RequestBatchExecutionType executionType, std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure)
|
std::vector<RequestResult>
|
||||||
|
RequestBatchHandler::ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session,
|
||||||
|
RequestBatchExecutionType::RequestBatchExecutionType executionType,
|
||||||
|
std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure)
|
||||||
{
|
{
|
||||||
RequestHandler requestHandler(session);
|
RequestHandler requestHandler(session);
|
||||||
if (executionType == RequestBatchExecutionType::SerialRealtime) {
|
if (executionType == RequestBatchExecutionType::SerialRealtime) {
|
||||||
@ -215,7 +224,9 @@ std::vector<RequestResult> RequestBatchHandler::ProcessRequestBatch(QThreadPool
|
|||||||
|
|
||||||
// Wait for the last request to finish processing
|
// Wait for the last request to finish processing
|
||||||
size_t requestCount = requests.size();
|
size_t requestCount = requests.size();
|
||||||
parallelResults.condition.wait(lock, [¶llelResults, requestCount]{return parallelResults.results.size() == requestCount;});
|
parallelResults.condition.wait(lock, [¶llelResults, requestCount] {
|
||||||
|
return parallelResults.results.size() == requestCount;
|
||||||
|
});
|
||||||
|
|
||||||
return parallelResults.results;
|
return parallelResults.results;
|
||||||
}
|
}
|
||||||
|
@ -24,5 +24,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "rpc/RequestBatchRequest.h"
|
#include "rpc/RequestBatchRequest.h"
|
||||||
|
|
||||||
namespace RequestBatchHandler {
|
namespace RequestBatchHandler {
|
||||||
std::vector<RequestResult> ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session, RequestBatchExecutionType::RequestBatchExecutionType executionType, std::vector<RequestBatchRequest> &requests, json &variables, bool haltOnFailure);
|
std::vector<RequestResult> ProcessRequestBatch(QThreadPool &threadPool, SessionPtr session,
|
||||||
|
RequestBatchExecutionType::RequestBatchExecutionType executionType,
|
||||||
|
std::vector<RequestBatchRequest> &requests, json &variables,
|
||||||
|
bool haltOnFailure);
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
|
|
||||||
const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_handlerMap
|
const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_handlerMap{
|
||||||
{
|
|
||||||
// General
|
// General
|
||||||
{"GetVersion", &RequestHandler::GetVersion},
|
{"GetVersion", &RequestHandler::GetVersion},
|
||||||
{"GetStats", &RequestHandler::GetStats},
|
{"GetStats", &RequestHandler::GetStats},
|
||||||
@ -152,6 +151,13 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
|
|||||||
{"StopReplayBuffer", &RequestHandler::StopReplayBuffer},
|
{"StopReplayBuffer", &RequestHandler::StopReplayBuffer},
|
||||||
{"SaveReplayBuffer", &RequestHandler::SaveReplayBuffer},
|
{"SaveReplayBuffer", &RequestHandler::SaveReplayBuffer},
|
||||||
{"GetLastReplayBufferReplay", &RequestHandler::GetLastReplayBufferReplay},
|
{"GetLastReplayBufferReplay", &RequestHandler::GetLastReplayBufferReplay},
|
||||||
|
{"GetOutputList", &RequestHandler::GetOutputList},
|
||||||
|
{"GetOutputStatus", &RequestHandler::GetOutputStatus},
|
||||||
|
{"ToggleOutput", &RequestHandler::ToggleOutput},
|
||||||
|
{"StartOutput", &RequestHandler::StartOutput},
|
||||||
|
{"StopOutput", &RequestHandler::StopOutput},
|
||||||
|
{"GetOutputSettings", &RequestHandler::GetOutputSettings},
|
||||||
|
{"SetOutputSettings", &RequestHandler::SetOutputSettings},
|
||||||
|
|
||||||
// Stream
|
// Stream
|
||||||
{"GetStreamStatus", &RequestHandler::GetStreamStatus},
|
{"GetStreamStatus", &RequestHandler::GetStreamStatus},
|
||||||
@ -182,12 +188,11 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
|
|||||||
{"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog},
|
{"OpenInputFiltersDialog", &RequestHandler::OpenInputFiltersDialog},
|
||||||
{"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog},
|
{"OpenInputInteractDialog", &RequestHandler::OpenInputInteractDialog},
|
||||||
{"GetMonitorList", &RequestHandler::GetMonitorList},
|
{"GetMonitorList", &RequestHandler::GetMonitorList},
|
||||||
|
{"OpenVideoMixProjector", &RequestHandler::OpenVideoMixProjector},
|
||||||
|
{"OpenSourceProjector", &RequestHandler::OpenSourceProjector},
|
||||||
};
|
};
|
||||||
|
|
||||||
RequestHandler::RequestHandler(SessionPtr session) :
|
RequestHandler::RequestHandler(SessionPtr session) : _session(session) {}
|
||||||
_session(session)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestResult RequestHandler::ProcessRequest(const Request &request)
|
RequestResult RequestHandler::ProcessRequest(const Request &request)
|
||||||
{
|
{
|
||||||
@ -199,7 +204,7 @@ RequestResult RequestHandler::ProcessRequest(const Request& request)
|
|||||||
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object.");
|
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "Your request data is not an object.");
|
||||||
|
|
||||||
if (request.RequestType.empty())
|
if (request.RequestType.empty())
|
||||||
return RequestResult::Error(RequestStatus::MissingRequestType, "Your request is missing a `requestType`");
|
return RequestResult::Error(RequestStatus::MissingRequestType, "Your request's `requestType` may not be empty.");
|
||||||
|
|
||||||
RequestMethodHandler handler;
|
RequestMethodHandler handler;
|
||||||
try {
|
try {
|
||||||
|
@ -170,6 +170,13 @@ class RequestHandler {
|
|||||||
RequestResult StopReplayBuffer(const Request &);
|
RequestResult StopReplayBuffer(const Request &);
|
||||||
RequestResult SaveReplayBuffer(const Request &);
|
RequestResult SaveReplayBuffer(const Request &);
|
||||||
RequestResult GetLastReplayBufferReplay(const Request &);
|
RequestResult GetLastReplayBufferReplay(const Request &);
|
||||||
|
RequestResult GetOutputList(const Request &);
|
||||||
|
RequestResult GetOutputStatus(const Request &);
|
||||||
|
RequestResult ToggleOutput(const Request &);
|
||||||
|
RequestResult StartOutput(const Request &);
|
||||||
|
RequestResult StopOutput(const Request &);
|
||||||
|
RequestResult GetOutputSettings(const Request &);
|
||||||
|
RequestResult SetOutputSettings(const Request &);
|
||||||
|
|
||||||
// Stream
|
// Stream
|
||||||
RequestResult GetStreamStatus(const Request &);
|
RequestResult GetStreamStatus(const Request &);
|
||||||
@ -200,6 +207,8 @@ class RequestHandler {
|
|||||||
RequestResult OpenInputFiltersDialog(const Request &);
|
RequestResult OpenInputFiltersDialog(const Request &);
|
||||||
RequestResult OpenInputInteractDialog(const Request &);
|
RequestResult OpenInputInteractDialog(const Request &);
|
||||||
RequestResult GetMonitorList(const Request &);
|
RequestResult GetMonitorList(const Request &);
|
||||||
|
RequestResult OpenVideoMixProjector(const Request &);
|
||||||
|
RequestResult OpenSourceProjector(const Request &);
|
||||||
|
|
||||||
SessionPtr _session;
|
SessionPtr _session;
|
||||||
static const std::unordered_map<std::string, RequestMethodHandler> _handlerMap;
|
static const std::unordered_map<std::string, RequestMethodHandler> _handlerMap;
|
||||||
|
@ -53,7 +53,8 @@ RequestResult RequestHandler::GetPersistentData(const Request& request)
|
|||||||
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
|
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
|
||||||
persistentDataPath += "/obsWebSocketPersistentData.json";
|
persistentDataPath += "/obsWebSocketPersistentData.json";
|
||||||
else
|
else
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "You have specified an invalid persistent data realm.");
|
return RequestResult::Error(RequestStatus::ResourceNotFound,
|
||||||
|
"You have specified an invalid persistent data realm.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
json persistentData;
|
json persistentData;
|
||||||
@ -83,7 +84,8 @@ RequestResult RequestHandler::SetPersistentData(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!(request.ValidateString("realm", statusCode, comment) && request.ValidateString("slotName", statusCode, comment) && request.ValidateBasic("slotValue", statusCode, comment)))
|
if (!(request.ValidateString("realm", statusCode, comment) && request.ValidateString("slotName", statusCode, comment) &&
|
||||||
|
request.ValidateBasic("slotValue", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string realm = request.RequestData["realm"];
|
std::string realm = request.RequestData["realm"];
|
||||||
@ -96,13 +98,15 @@ RequestResult RequestHandler::SetPersistentData(const Request& request)
|
|||||||
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
|
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
|
||||||
persistentDataPath += "/obsWebSocketPersistentData.json";
|
persistentDataPath += "/obsWebSocketPersistentData.json";
|
||||||
else
|
else
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "You have specified an invalid persistent data realm.");
|
return RequestResult::Error(RequestStatus::ResourceNotFound,
|
||||||
|
"You have specified an invalid persistent data realm.");
|
||||||
|
|
||||||
json persistentData = json::object();
|
json persistentData = json::object();
|
||||||
Utils::Json::GetJsonFileContent(persistentDataPath, persistentData);
|
Utils::Json::GetJsonFileContent(persistentDataPath, persistentData);
|
||||||
persistentData[slotName] = slotValue;
|
persistentData[slotName] = slotValue;
|
||||||
if (!Utils::Json::SetJsonFileContent(persistentDataPath, persistentData))
|
if (!Utils::Json::SetJsonFileContent(persistentDataPath, persistentData))
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to write persistent data. No permissions?");
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
||||||
|
"Unable to write persistent data. No permissions?");
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -158,9 +162,10 @@ RequestResult RequestHandler::SetCurrentSceneCollection(const Request& request)
|
|||||||
std::string currentSceneCollectionName = Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
std::string currentSceneCollectionName = Utils::Obs::StringHelper::GetCurrentSceneCollection();
|
||||||
// Avoid queueing tasks if nothing will change
|
// Avoid queueing tasks if nothing will change
|
||||||
if (currentSceneCollectionName != sceneCollectionName) {
|
if (currentSceneCollectionName != sceneCollectionName) {
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(
|
||||||
obs_frontend_set_current_scene_collection(static_cast<const char*>(param));
|
OBS_TASK_UI,
|
||||||
}, (void*)sceneCollectionName.c_str(), true);
|
[](void *param) { obs_frontend_set_current_scene_collection(static_cast<const char *>(param)); },
|
||||||
|
(void *)sceneCollectionName.c_str(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
@ -195,7 +200,8 @@ RequestResult RequestHandler::CreateSceneCollection(const Request& request)
|
|||||||
|
|
||||||
QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
||||||
bool success = false;
|
bool success = false;
|
||||||
QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
|
QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success),
|
||||||
|
Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
|
||||||
if (!success)
|
if (!success)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene collection.");
|
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene collection.");
|
||||||
|
|
||||||
@ -251,9 +257,9 @@ RequestResult RequestHandler::SetCurrentProfile(const Request& request)
|
|||||||
std::string currentProfileName = Utils::Obs::StringHelper::GetCurrentProfile();
|
std::string currentProfileName = Utils::Obs::StringHelper::GetCurrentProfile();
|
||||||
// Avoid queueing tasks if nothing will change
|
// Avoid queueing tasks if nothing will change
|
||||||
if (currentProfileName != profileName) {
|
if (currentProfileName != profileName) {
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(
|
||||||
obs_frontend_set_current_profile(static_cast<const char*>(param));
|
OBS_TASK_UI, [](void *param) { obs_frontend_set_current_profile(static_cast<const char *>(param)); },
|
||||||
}, (void*)profileName.c_str(), true);
|
(void *)profileName.c_str(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
@ -285,7 +291,8 @@ RequestResult RequestHandler::CreateProfile(const Request& request)
|
|||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
|
return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
|
||||||
|
|
||||||
QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
||||||
QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
|
QMetaObject::invokeMethod(mainWindow, "NewProfile", Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(QString, QString::fromStdString(profileName)));
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -319,7 +326,8 @@ RequestResult RequestHandler::RemoveProfile(const Request& request)
|
|||||||
return RequestResult::Error(RequestStatus::NotEnoughResources);
|
return RequestResult::Error(RequestStatus::NotEnoughResources);
|
||||||
|
|
||||||
QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
|
||||||
QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromStdString(profileName)));
|
QMetaObject::invokeMethod(mainWindow, "DeleteProfile", Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(QString, QString::fromStdString(profileName)));
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
}
|
}
|
||||||
@ -344,7 +352,8 @@ RequestResult RequestHandler::GetProfileParameter(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!(request.ValidateString("parameterCategory", statusCode, comment) && request.ValidateString("parameterName", statusCode, comment)))
|
if (!(request.ValidateString("parameterCategory", statusCode, comment) &&
|
||||||
|
request.ValidateString("parameterName", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string parameterCategory = request.RequestData["parameterCategory"];
|
std::string parameterCategory = request.RequestData["parameterCategory"];
|
||||||
@ -358,7 +367,8 @@ RequestResult RequestHandler::GetProfileParameter(const Request& request)
|
|||||||
json responseData;
|
json responseData;
|
||||||
if (config_has_default_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
|
if (config_has_default_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
|
||||||
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
||||||
responseData["defaultParameterValue"] = config_get_default_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
responseData["defaultParameterValue"] =
|
||||||
|
config_get_default_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
||||||
} else if (config_has_user_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
|
} else if (config_has_user_value(profile, parameterCategory.c_str(), parameterName.c_str())) {
|
||||||
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
responseData["parameterValue"] = config_get_string(profile, parameterCategory.c_str(), parameterName.c_str());
|
||||||
responseData["defaultParameterValue"] = nullptr;
|
responseData["defaultParameterValue"] = nullptr;
|
||||||
@ -400,7 +410,8 @@ RequestResult RequestHandler::SetProfileParameter(const Request& request)
|
|||||||
// Using check helpers here would just make the logic more complicated
|
// Using check helpers here would just make the logic more complicated
|
||||||
if (!request.RequestData.contains("parameterValue") || request.RequestData["parameterValue"].is_null()) {
|
if (!request.RequestData.contains("parameterValue") || request.RequestData["parameterValue"].is_null()) {
|
||||||
if (!config_remove_value(profile, parameterCategory.c_str(), parameterName.c_str()))
|
if (!config_remove_value(profile, parameterCategory.c_str(), parameterName.c_str()))
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "There are no existing instances of that profile parameter.");
|
return RequestResult::Error(RequestStatus::ResourceNotFound,
|
||||||
|
"There are no existing instances of that profile parameter.");
|
||||||
} else if (request.RequestData["parameterValue"].is_string()) {
|
} else if (request.RequestData["parameterValue"].is_string()) {
|
||||||
std::string parameterValue = request.RequestData["parameterValue"];
|
std::string parameterValue = request.RequestData["parameterValue"];
|
||||||
config_set_string(profile, parameterCategory.c_str(), parameterName.c_str(), parameterValue.c_str());
|
config_set_string(profile, parameterCategory.c_str(), parameterName.c_str(), parameterValue.c_str());
|
||||||
@ -471,20 +482,24 @@ RequestResult RequestHandler::GetVideoSettings(const Request&)
|
|||||||
RequestResult RequestHandler::SetVideoSettings(const Request &request)
|
RequestResult RequestHandler::SetVideoSettings(const Request &request)
|
||||||
{
|
{
|
||||||
if (obs_video_active())
|
if (obs_video_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning, "Video settings cannot be changed while an output is active.");
|
return RequestResult::Error(RequestStatus::OutputRunning,
|
||||||
|
"Video settings cannot be changed while an output is active.");
|
||||||
|
|
||||||
RequestStatus::RequestStatus statusCode = RequestStatus::NoError;
|
RequestStatus::RequestStatus statusCode = RequestStatus::NoError;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
bool changeFps = (request.Contains("fpsNumerator") && request.Contains("fpsDenominator"));
|
bool changeFps = (request.Contains("fpsNumerator") && request.Contains("fpsDenominator"));
|
||||||
if (changeFps && !(request.ValidateOptionalNumber("fpsNumerator", statusCode, comment, 1) && request.ValidateOptionalNumber("fpsDenominator", statusCode, comment, 1)))
|
if (changeFps && !(request.ValidateOptionalNumber("fpsNumerator", statusCode, comment, 1) &&
|
||||||
|
request.ValidateOptionalNumber("fpsDenominator", statusCode, comment, 1)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
bool changeBaseRes = (request.Contains("baseWidth") && request.Contains("baseHeight"));
|
bool changeBaseRes = (request.Contains("baseWidth") && request.Contains("baseHeight"));
|
||||||
if (changeBaseRes && !(request.ValidateOptionalNumber("baseWidth", statusCode, comment, 8, 4096) && request.ValidateOptionalNumber("baseHeight", statusCode, comment, 8, 4096)))
|
if (changeBaseRes && !(request.ValidateOptionalNumber("baseWidth", statusCode, comment, 8, 4096) &&
|
||||||
|
request.ValidateOptionalNumber("baseHeight", statusCode, comment, 8, 4096)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
bool changeOutputRes = (request.Contains("outputWidth") && request.Contains("outputHeight"));
|
bool changeOutputRes = (request.Contains("outputWidth") && request.Contains("outputHeight"));
|
||||||
if (changeOutputRes && !(request.ValidateOptionalNumber("outputWidth", statusCode, comment, 8, 4096) && request.ValidateOptionalNumber("outputHeight", statusCode, comment, 8, 4096)))
|
if (changeOutputRes && !(request.ValidateOptionalNumber("outputWidth", statusCode, comment, 8, 4096) &&
|
||||||
|
request.ValidateOptionalNumber("outputHeight", statusCode, comment, 8, 4096)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
config_t *config = obs_frontend_get_profile_config();
|
config_t *config = obs_frontend_get_profile_config();
|
||||||
@ -557,18 +572,21 @@ RequestResult RequestHandler::GetStreamServiceSettings(const Request&)
|
|||||||
RequestResult RequestHandler::SetStreamServiceSettings(const Request &request)
|
RequestResult RequestHandler::SetStreamServiceSettings(const Request &request)
|
||||||
{
|
{
|
||||||
if (obs_frontend_streaming_active())
|
if (obs_frontend_streaming_active())
|
||||||
return RequestResult::Error(RequestStatus::OutputRunning, "You cannot change stream service settings while streaming.");
|
return RequestResult::Error(RequestStatus::OutputRunning,
|
||||||
|
"You cannot change stream service settings while streaming.");
|
||||||
|
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!(request.ValidateString("streamServiceType", statusCode, comment) && request.ValidateObject("streamServiceSettings", statusCode, comment)))
|
if (!(request.ValidateString("streamServiceType", statusCode, comment) &&
|
||||||
|
request.ValidateObject("streamServiceSettings", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
OBSService currentStreamService = obs_frontend_get_streaming_service();
|
OBSService currentStreamService = obs_frontend_get_streaming_service();
|
||||||
|
|
||||||
std::string streamServiceType = obs_service_get_type(currentStreamService);
|
std::string streamServiceType = obs_service_get_type(currentStreamService);
|
||||||
std::string requestedStreamServiceType = request.RequestData["streamServiceType"];
|
std::string requestedStreamServiceType = request.RequestData["streamServiceType"];
|
||||||
OBSDataAutoRelease requestedStreamServiceSettings = Utils::Json::JsonToObsData(request.RequestData["streamServiceSettings"]);
|
OBSDataAutoRelease requestedStreamServiceSettings =
|
||||||
|
Utils::Json::JsonToObsData(request.RequestData["streamServiceSettings"]);
|
||||||
|
|
||||||
// Don't create a new service if the current service is the same type.
|
// Don't create a new service if the current service is the same type.
|
||||||
if (streamServiceType == requestedStreamServiceType) {
|
if (streamServiceType == requestedStreamServiceType) {
|
||||||
@ -582,10 +600,13 @@ RequestResult RequestHandler::SetStreamServiceSettings(const Request& request)
|
|||||||
obs_service_update(currentStreamService, newStreamServiceSettings);
|
obs_service_update(currentStreamService, newStreamServiceSettings);
|
||||||
} else {
|
} else {
|
||||||
// TODO: This leaks memory. I have no idea why.
|
// TODO: This leaks memory. I have no idea why.
|
||||||
OBSService newStreamService = obs_service_create(requestedStreamServiceType.c_str(), "obs_websocket_custom_service", requestedStreamServiceSettings, nullptr);
|
OBSService newStreamService = obs_service_create(requestedStreamServiceType.c_str(), "obs_websocket_custom_service",
|
||||||
|
requestedStreamServiceSettings, nullptr);
|
||||||
// TODO: Check service type here, instead of relying on service creation to fail.
|
// TODO: Check service type here, instead of relying on service creation to fail.
|
||||||
if (!newStreamService)
|
if (!newStreamService)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the stream service with the requested streamServiceType. It may be an invalid type.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::ResourceCreationFailed,
|
||||||
|
"Failed to create the stream service with the requested streamServiceType. It may be an invalid type.");
|
||||||
|
|
||||||
obs_frontend_set_streaming_service(newStreamService);
|
obs_frontend_set_streaming_service(newStreamService);
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,8 @@ RequestResult RequestHandler::CreateSourceFilter(const Request& request)
|
|||||||
std::string comment;
|
std::string comment;
|
||||||
|
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
||||||
if (!(source && request.ValidateString("filterName", statusCode, comment) && request.ValidateString("filterKind", statusCode, comment)))
|
if (!(source && request.ValidateString("filterName", statusCode, comment) &&
|
||||||
|
request.ValidateString("filterKind", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string filterName = request.RequestData["filterName"];
|
std::string filterName = request.RequestData["filterName"];
|
||||||
@ -114,7 +115,9 @@ RequestResult RequestHandler::CreateSourceFilter(const Request& request)
|
|||||||
std::string filterKind = request.RequestData["filterKind"];
|
std::string filterKind = request.RequestData["filterKind"];
|
||||||
auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList();
|
auto kinds = Utils::Obs::ArrayHelper::GetFilterKindList();
|
||||||
if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end())
|
if (std::find(kinds.begin(), kinds.end(), filterKind) == kinds.end())
|
||||||
return RequestResult::Error(RequestStatus::InvalidFilterKind, "Your specified filter kind is not supported by OBS. Check that any necessary plugins are loaded.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidFilterKind,
|
||||||
|
"Your specified filter kind is not supported by OBS. Check that any necessary plugins are loaded.");
|
||||||
|
|
||||||
OBSDataAutoRelease filterSettings = nullptr;
|
OBSDataAutoRelease filterSettings = nullptr;
|
||||||
if (request.Contains("filterSettings")) {
|
if (request.Contains("filterSettings")) {
|
||||||
@ -219,7 +222,8 @@ RequestResult RequestHandler::GetSourceFilter(const Request& request)
|
|||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["filterEnabled"] = obs_source_enabled(pair.filter);
|
responseData["filterEnabled"] = obs_source_enabled(pair.filter);
|
||||||
responseData["filterIndex"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(pair.source, pair.filter); // Todo: Use `GetSourceFilterlist` to select this filter maybe
|
responseData["filterIndex"] = Utils::Obs::NumberHelper::GetSourceFilterIndex(
|
||||||
|
pair.source, pair.filter); // Todo: Use `GetSourceFilterlist` to select this filter maybe
|
||||||
responseData["filterKind"] = obs_source_get_id(pair.filter);
|
responseData["filterKind"] = obs_source_get_id(pair.filter);
|
||||||
|
|
||||||
OBSDataAutoRelease filterSettings = obs_source_get_settings(pair.filter);
|
OBSDataAutoRelease filterSettings = obs_source_get_settings(pair.filter);
|
||||||
@ -292,7 +296,8 @@ RequestResult RequestHandler::SetSourceFilterSettings(const Request& request)
|
|||||||
|
|
||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
|
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["filterSettings"]);
|
||||||
if (!newSettings)
|
if (!newSettings)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
||||||
|
"An internal data conversion operation failed. Please report this!");
|
||||||
|
|
||||||
if (overlay)
|
if (overlay)
|
||||||
obs_source_update(pair.filter, newSettings);
|
obs_source_update(pair.filter, newSettings);
|
||||||
|
@ -26,7 +26,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../WebSocketApi.h"
|
#include "../WebSocketApi.h"
|
||||||
#include "../obs-websocket.h"
|
#include "../obs-websocket.h"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets data about the current plugin and RPC version.
|
* Gets data about the current plugin and RPC version.
|
||||||
*
|
*
|
||||||
@ -141,6 +140,8 @@ RequestResult RequestHandler::BroadcastCustomEvent(const Request& request)
|
|||||||
* @requestField requestType | String | The request type to call
|
* @requestField requestType | String | The request type to call
|
||||||
* @requestField ?requestData | Object | Object containing appropriate request data | {}
|
* @requestField ?requestData | Object | Object containing appropriate request data | {}
|
||||||
*
|
*
|
||||||
|
* @responseField vendorName | String | Echoed of `vendorName`
|
||||||
|
* @responseField requestType | String | Echoed of `requestType`
|
||||||
* @responseField responseData | Object | Object containing appropriate response data. {} if request does not provide any response data
|
* @responseField responseData | Object | Object containing appropriate response data. {} if request does not provide any response data
|
||||||
*
|
*
|
||||||
* @requestType CallVendorRequest
|
* @requestType CallVendorRequest
|
||||||
@ -154,7 +155,8 @@ RequestResult RequestHandler::CallVendorRequest(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
if (!request.ValidateString("vendorName", statusCode, comment) || !request.ValidateString("requestType", statusCode, comment))
|
if (!request.ValidateString("vendorName", statusCode, comment) ||
|
||||||
|
!request.ValidateString("requestType", statusCode, comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string vendorName = request.RequestData["vendorName"];
|
std::string vendorName = request.RequestData["vendorName"];
|
||||||
@ -172,7 +174,8 @@ RequestResult RequestHandler::CallVendorRequest(const Request& request)
|
|||||||
|
|
||||||
auto webSocketApi = GetWebSocketApi();
|
auto webSocketApi = GetWebSocketApi();
|
||||||
if (!webSocketApi)
|
if (!webSocketApi)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to call request due to internal error.");
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
||||||
|
"Unable to call request due to internal error.");
|
||||||
|
|
||||||
auto ret = webSocketApi->PerformVendorRequest(vendorName, requestType, requestData, obsResponseData);
|
auto ret = webSocketApi->PerformVendorRequest(vendorName, requestType, requestData, obsResponseData);
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
@ -186,6 +189,8 @@ RequestResult RequestHandler::CallVendorRequest(const Request& request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
|
responseData["vendorName"] = vendorName;
|
||||||
|
responseData["requestType"] = requestType;
|
||||||
responseData["responseData"] = Utils::Json::ObsDataToJson(obsResponseData);
|
responseData["responseData"] = Utils::Json::ObsDataToJson(obsResponseData);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
@ -277,19 +282,23 @@ RequestResult RequestHandler::TriggerHotkeyByKeySequence(const Request& request)
|
|||||||
|
|
||||||
const json keyModifiersJson = request.RequestData["keyModifiers"];
|
const json keyModifiersJson = request.RequestData["keyModifiers"];
|
||||||
uint32_t keyModifiers = 0;
|
uint32_t keyModifiers = 0;
|
||||||
if (keyModifiersJson.contains("shift") && keyModifiersJson["shift"].is_boolean() && keyModifiersJson["shift"].get<bool>())
|
if (keyModifiersJson.contains("shift") && keyModifiersJson["shift"].is_boolean() &&
|
||||||
|
keyModifiersJson["shift"].get<bool>())
|
||||||
keyModifiers |= INTERACT_SHIFT_KEY;
|
keyModifiers |= INTERACT_SHIFT_KEY;
|
||||||
if (keyModifiersJson.contains("control") && keyModifiersJson["control"].is_boolean() && keyModifiersJson["control"].get<bool>())
|
if (keyModifiersJson.contains("control") && keyModifiersJson["control"].is_boolean() &&
|
||||||
|
keyModifiersJson["control"].get<bool>())
|
||||||
keyModifiers |= INTERACT_CONTROL_KEY;
|
keyModifiers |= INTERACT_CONTROL_KEY;
|
||||||
if (keyModifiersJson.contains("alt") && keyModifiersJson["alt"].is_boolean() && keyModifiersJson["alt"].get<bool>())
|
if (keyModifiersJson.contains("alt") && keyModifiersJson["alt"].is_boolean() && keyModifiersJson["alt"].get<bool>())
|
||||||
keyModifiers |= INTERACT_ALT_KEY;
|
keyModifiers |= INTERACT_ALT_KEY;
|
||||||
if (keyModifiersJson.contains("command") && keyModifiersJson["command"].is_boolean() && keyModifiersJson["command"].get<bool>())
|
if (keyModifiersJson.contains("command") && keyModifiersJson["command"].is_boolean() &&
|
||||||
|
keyModifiersJson["command"].get<bool>())
|
||||||
keyModifiers |= INTERACT_COMMAND_KEY;
|
keyModifiers |= INTERACT_COMMAND_KEY;
|
||||||
combo.modifiers = keyModifiers;
|
combo.modifiers = keyModifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!combo.modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE))
|
if (!combo.modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE))
|
||||||
return RequestResult::Error(RequestStatus::CannotAct, "Your provided request fields cannot be used to trigger a hotkey.");
|
return RequestResult::Error(RequestStatus::CannotAct,
|
||||||
|
"Your provided request fields cannot be used to trigger a hotkey.");
|
||||||
|
|
||||||
// Apparently things break when you don't start by setting the combo to false
|
// Apparently things break when you don't start by setting the combo to false
|
||||||
obs_hotkey_inject_event(combo, false);
|
obs_hotkey_inject_event(combo, false);
|
||||||
|
@ -143,7 +143,8 @@ RequestResult RequestHandler::CreateInput(const Request& request)
|
|||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment);
|
OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment);
|
||||||
if (!(sceneSource && request.ValidateString("inputName", statusCode, comment) && request.ValidateString("inputKind", statusCode, comment)))
|
if (!(sceneSource && request.ValidateString("inputName", statusCode, comment) &&
|
||||||
|
request.ValidateString("inputKind", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string inputName = request.RequestData["inputName"];
|
std::string inputName = request.RequestData["inputName"];
|
||||||
@ -154,7 +155,9 @@ RequestResult RequestHandler::CreateInput(const Request& request)
|
|||||||
std::string inputKind = request.RequestData["inputKind"];
|
std::string inputKind = request.RequestData["inputKind"];
|
||||||
auto kinds = Utils::Obs::ArrayHelper::GetInputKindList();
|
auto kinds = Utils::Obs::ArrayHelper::GetInputKindList();
|
||||||
if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end())
|
if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end())
|
||||||
return RequestResult::Error(RequestStatus::InvalidInputKind, "Your specified input kind is not supported by OBS. Check that your specified kind is properly versioned and that any necessary plugins are loaded.");
|
return RequestResult::Error(
|
||||||
|
RequestStatus::InvalidInputKind,
|
||||||
|
"Your specified input kind is not supported by OBS. Check that your specified kind is properly versioned and that any necessary plugins are loaded.");
|
||||||
|
|
||||||
OBSDataAutoRelease inputSettings = nullptr;
|
OBSDataAutoRelease inputSettings = nullptr;
|
||||||
if (request.Contains("inputSettings")) {
|
if (request.Contains("inputSettings")) {
|
||||||
@ -175,7 +178,8 @@ RequestResult RequestHandler::CreateInput(const Request& request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the input and add it as a scene item to the destination scene
|
// Create the input and add it as a scene item to the destination scene
|
||||||
OBSSceneItemAutoRelease sceneItem = Utils::Obs::ActionHelper::CreateInput(inputName, inputKind, inputSettings, scene, sceneItemEnabled);
|
OBSSceneItemAutoRelease sceneItem =
|
||||||
|
Utils::Obs::ActionHelper::CreateInput(inputName, inputKind, inputSettings, scene, sceneItemEnabled);
|
||||||
|
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the input or scene item failed.");
|
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the input or scene item failed.");
|
||||||
@ -240,7 +244,8 @@ RequestResult RequestHandler::SetInputName(const Request& request)
|
|||||||
|
|
||||||
OBSSourceAutoRelease existingSource = obs_get_source_by_name(newInputName.c_str());
|
OBSSourceAutoRelease existingSource = obs_get_source_by_name(newInputName.c_str());
|
||||||
if (existingSource)
|
if (existingSource)
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that new input name.");
|
return RequestResult::Error(RequestStatus::ResourceAlreadyExists,
|
||||||
|
"A source already exists by that new input name.");
|
||||||
|
|
||||||
obs_source_set_name(input, newInputName.c_str());
|
obs_source_set_name(input, newInputName.c_str());
|
||||||
|
|
||||||
@ -349,7 +354,8 @@ RequestResult RequestHandler::SetInputSettings(const Request& request)
|
|||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["inputSettings"]);
|
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["inputSettings"]);
|
||||||
if (!newSettings)
|
if (!newSettings)
|
||||||
// This should never happen
|
// This should never happen
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
||||||
|
"An internal data conversion operation failed. Please report this!");
|
||||||
|
|
||||||
if (overlay)
|
if (overlay)
|
||||||
// Applies the new settings on top of the existing user settings
|
// Applies the new settings on top of the existing user settings
|
||||||
@ -700,7 +706,7 @@ RequestResult RequestHandler::GetInputAudioMonitorType(const Request& request)
|
|||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["monitorType"] = Utils::Obs::StringHelper::GetInputMonitorType(input);
|
responseData["monitorType"] = obs_source_get_monitoring_type(input);
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -730,7 +736,8 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
|
|||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio.");
|
||||||
|
|
||||||
if (!obs_audio_monitoring_available())
|
if (!obs_audio_monitoring_available())
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "Audio monitoring is not available on this platform.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"Audio monitoring is not available on this platform.");
|
||||||
|
|
||||||
enum obs_monitoring_type monitorType;
|
enum obs_monitoring_type monitorType;
|
||||||
std::string monitorTypeString = request.RequestData["monitorType"];
|
std::string monitorTypeString = request.RequestData["monitorType"];
|
||||||
@ -741,7 +748,8 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request& request)
|
|||||||
else if (monitorTypeString == "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT")
|
else if (monitorTypeString == "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT")
|
||||||
monitorType = OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT;
|
monitorType = OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT;
|
||||||
else
|
else
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField, std::string("Unknown monitor type: ") + monitorTypeString);
|
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
||||||
|
std::string("Unknown monitor type: ") + monitorTypeString);
|
||||||
|
|
||||||
obs_source_set_monitoring_type(input, monitorType);
|
obs_source_set_monitoring_type(input, monitorType);
|
||||||
|
|
||||||
@ -821,7 +829,8 @@ RequestResult RequestHandler::SetInputAudioTracks(const Request& request)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!inputAudioTracks[track].is_boolean())
|
if (!inputAudioTracks[track].is_boolean())
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The value of one of your tracks is not a boolean.");
|
return RequestResult::Error(RequestStatus::InvalidRequestFieldType,
|
||||||
|
"The value of one of your tracks is not a boolean.");
|
||||||
|
|
||||||
bool enabled = inputAudioTracks[track];
|
bool enabled = inputAudioTracks[track];
|
||||||
|
|
||||||
|
@ -61,7 +61,8 @@ RequestResult RequestHandler::GetMediaInputStatus(const Request& request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["mediaState"] = Utils::Obs::StringHelper::GetMediaInputState(input);
|
responseData["mediaState"] = obs_source_media_get_state(input);
|
||||||
|
;
|
||||||
|
|
||||||
if (IsMediaTimeValid(input)) {
|
if (IsMediaTimeValid(input)) {
|
||||||
responseData["mediaDuration"] = obs_source_media_get_duration(input);
|
responseData["mediaDuration"] = obs_source_media_get_duration(input);
|
||||||
@ -98,7 +99,8 @@ RequestResult RequestHandler::SetMediaInputCursor(const Request& request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!IsMediaTimeValid(input))
|
if (!IsMediaTimeValid(input))
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The media input must be playing or paused in order to set the cursor position.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"The media input must be playing or paused in order to set the cursor position.");
|
||||||
|
|
||||||
int64_t mediaCursor = request.RequestData["mediaCursor"];
|
int64_t mediaCursor = request.RequestData["mediaCursor"];
|
||||||
|
|
||||||
@ -132,7 +134,8 @@ RequestResult RequestHandler::OffsetMediaInputCursor(const Request& request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!IsMediaTimeValid(input))
|
if (!IsMediaTimeValid(input))
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The media input must be playing or paused in order to set the cursor position.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"The media input must be playing or paused in order to set the cursor position.");
|
||||||
|
|
||||||
int64_t mediaCursorOffset = request.RequestData["mediaCursorOffset"];
|
int64_t mediaCursorOffset = request.RequestData["mediaCursorOffset"];
|
||||||
int64_t mediaCursor = obs_source_media_get_time(input) + mediaCursorOffset;
|
int64_t mediaCursor = obs_source_media_get_time(input) + mediaCursorOffset;
|
||||||
@ -166,13 +169,13 @@ RequestResult RequestHandler::TriggerMediaInputAction(const Request& request)
|
|||||||
if (!(input && request.ValidateString("mediaAction", statusCode, comment)))
|
if (!(input && request.ValidateString("mediaAction", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string mediaActionString = request.RequestData["mediaAction"];
|
enum ObsMediaInputAction mediaAction = request.RequestData["mediaAction"];
|
||||||
auto mediaAction = Utils::Obs::EnumHelper::GetMediaInputAction(mediaActionString);
|
|
||||||
|
|
||||||
switch (mediaAction) {
|
switch (mediaAction) {
|
||||||
default:
|
default:
|
||||||
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE:
|
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE:
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField, "You have specified an invalid media input action.");
|
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
||||||
|
"You have specified an invalid media input action.");
|
||||||
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY:
|
case OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY:
|
||||||
// Shoutout to whoever implemented this API call like this
|
// Shoutout to whoever implemented this API call like this
|
||||||
obs_source_media_play_pause(input, false);
|
obs_source_media_play_pause(input, false);
|
||||||
|
@ -272,6 +272,217 @@ RequestResult RequestHandler::GetLastReplayBufferReplay(const Request&)
|
|||||||
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFilePath();
|
responseData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFileName();
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of available outputs.
|
||||||
|
*
|
||||||
|
* @requestType GetOutputList
|
||||||
|
* @complexity 4
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::GetOutputList(const Request &)
|
||||||
|
{
|
||||||
|
json responseData;
|
||||||
|
responseData["outputs"] = Utils::Obs::ArrayHelper::GetOutputList();
|
||||||
|
return RequestResult::Success(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the status of an output.
|
||||||
|
*
|
||||||
|
* @requestField outputName | String | Output name
|
||||||
|
*
|
||||||
|
* @responseField outputActive | Boolean | Whether the output is active
|
||||||
|
* @responseField outputReconnecting | Boolean | Whether the output is reconnecting
|
||||||
|
* @responseField outputTimecode | String | Current formatted timecode string for the output
|
||||||
|
* @responseField outputDuration | Number | Current duration in milliseconds for the output
|
||||||
|
* @responseField outputCongestion | Number | Congestion of the output
|
||||||
|
* @responseField outputBytes | Number | Number of bytes sent by the output
|
||||||
|
* @responseField outputSkippedFrames | Number | Number of frames skipped by the output's process
|
||||||
|
* @responseField outputTotalFrames | Number | Total number of frames delivered by the output's process
|
||||||
|
*
|
||||||
|
* @requestType GetOutputStatus
|
||||||
|
* @complexity 4
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::GetOutputStatus(const Request &request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
||||||
|
if (!output)
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
uint64_t outputDuration = Utils::Obs::NumberHelper::GetOutputDuration(output);
|
||||||
|
|
||||||
|
json responseData;
|
||||||
|
responseData["outputActive"] = obs_output_active(output);
|
||||||
|
responseData["outputReconnecting"] = obs_output_reconnecting(output);
|
||||||
|
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
|
||||||
|
responseData["outputDuration"] = outputDuration;
|
||||||
|
responseData["outputCongestion"] = obs_output_get_congestion(output);
|
||||||
|
responseData["outputBytes"] = obs_output_get_total_bytes(output);
|
||||||
|
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(output);
|
||||||
|
responseData["outputTotalFrames"] = obs_output_get_total_frames(output);
|
||||||
|
|
||||||
|
return RequestResult::Success(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the status of an output.
|
||||||
|
*
|
||||||
|
* @requestField outputName | String | Output name
|
||||||
|
*
|
||||||
|
* @responseField outputActive | Boolean | Whether the output is active
|
||||||
|
*
|
||||||
|
* @requestType ToggleOutput
|
||||||
|
* @complexity 4
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::ToggleOutput(const Request &request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
||||||
|
if (!output)
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
bool outputActive = obs_output_active(output);
|
||||||
|
if (outputActive)
|
||||||
|
obs_output_stop(output);
|
||||||
|
else
|
||||||
|
obs_output_start(output);
|
||||||
|
|
||||||
|
json responseData;
|
||||||
|
responseData["outputActive"] = !outputActive;
|
||||||
|
return RequestResult::Success(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an output.
|
||||||
|
*
|
||||||
|
* @requestField outputName | String | Output name
|
||||||
|
*
|
||||||
|
* @requestType StartOutput
|
||||||
|
* @complexity 4
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::StartOutput(const Request &request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
||||||
|
if (!output)
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
if (obs_output_active(output))
|
||||||
|
return RequestResult::Error(RequestStatus::OutputRunning);
|
||||||
|
|
||||||
|
obs_output_start(output);
|
||||||
|
|
||||||
|
return RequestResult::Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops an output.
|
||||||
|
*
|
||||||
|
* @requestField outputName | String | Output name
|
||||||
|
*
|
||||||
|
* @requestType StopOutput
|
||||||
|
* @complexity 4
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::StopOutput(const Request &request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
||||||
|
if (!output)
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
if (!obs_output_active(output))
|
||||||
|
return RequestResult::Error(RequestStatus::OutputNotRunning);
|
||||||
|
|
||||||
|
obs_output_stop(output);
|
||||||
|
|
||||||
|
return RequestResult::Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the settings of an output.
|
||||||
|
*
|
||||||
|
* @requestField outputName | String | Output name
|
||||||
|
*
|
||||||
|
* @responseField outputSettings | Object | Output settings
|
||||||
|
*
|
||||||
|
* @requestType GetOutputSettings
|
||||||
|
* @complexity 4
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::GetOutputSettings(const Request &request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
||||||
|
if (!output)
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
OBSDataAutoRelease outputSettings = obs_output_get_settings(output);
|
||||||
|
|
||||||
|
json responseData;
|
||||||
|
responseData["outputSettings"] = Utils::Json::ObsDataToJson(outputSettings);
|
||||||
|
return RequestResult::Success(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the settings of an output.
|
||||||
|
*
|
||||||
|
* @requestField outputName | String | Output name
|
||||||
|
* @requestField outputSettings | Object | Output settings
|
||||||
|
*
|
||||||
|
* @requestType SetOutputSettings
|
||||||
|
* @complexity 4
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @api requests
|
||||||
|
* @category outputs
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::SetOutputSettings(const Request &request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
|
||||||
|
if (!(output && request.ValidateObject("outputSettings", statusCode, comment, true)))
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["outputSettings"]);
|
||||||
|
if (!newSettings)
|
||||||
|
// This should never happen
|
||||||
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
||||||
|
"An internal data conversion operation failed. Please report this!");
|
||||||
|
|
||||||
|
obs_output_update(output, newSettings);
|
||||||
|
|
||||||
|
return RequestResult::Success();
|
||||||
|
}
|
||||||
|
@ -99,6 +99,8 @@ RequestResult RequestHandler::StartRecord(const Request&)
|
|||||||
/**
|
/**
|
||||||
* Stops the record output.
|
* Stops the record output.
|
||||||
*
|
*
|
||||||
|
* @responseField outputPath | String | File name for the saved recording
|
||||||
|
*
|
||||||
* @requestType StopRecord
|
* @requestType StopRecord
|
||||||
* @complexity 1
|
* @complexity 1
|
||||||
* @rpcVersion -1
|
* @rpcVersion -1
|
||||||
@ -114,7 +116,10 @@ RequestResult RequestHandler::StopRecord(const Request&)
|
|||||||
// TODO: Call signal directly to perform blocking wait
|
// TODO: Call signal directly to perform blocking wait
|
||||||
obs_frontend_recording_stop();
|
obs_frontend_recording_stop();
|
||||||
|
|
||||||
return RequestResult::Success();
|
json responseData;
|
||||||
|
responseData["outputPath"] = Utils::Obs::StringHelper::GetLastRecordFileName();
|
||||||
|
|
||||||
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,7 +103,8 @@ RequestResult RequestHandler::GetSceneItemId(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneAutoRelease scene = request.ValidateScene2("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneAutoRelease scene =
|
||||||
|
request.ValidateScene2("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!(scene && request.ValidateString("sourceName", statusCode, comment)))
|
if (!(scene && request.ValidateString("sourceName", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -118,7 +119,8 @@ RequestResult RequestHandler::GetSceneItemId(const Request& request)
|
|||||||
|
|
||||||
OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName, offset);
|
OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName, offset);
|
||||||
if (!item)
|
if (!item)
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene items were found in the specified scene by that name or offset.");
|
return RequestResult::Error(RequestStatus::ResourceNotFound,
|
||||||
|
"No scene items were found in the specified scene by that name or offset.");
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItemId"] = obs_sceneitem_get_id(item);
|
responseData["sceneItemId"] = obs_sceneitem_get_id(item);
|
||||||
@ -242,7 +244,8 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
|
|||||||
} else {
|
} else {
|
||||||
destinationScene = obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem));
|
destinationScene = obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem));
|
||||||
if (!destinationScene)
|
if (!destinationScene)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Internal error: Failed to get ref for scene of scene item.");
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
||||||
|
"Internal error: Failed to get ref for scene of scene item.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obs_sceneitem_is_group(sceneItem) && obs_sceneitem_get_scene(sceneItem) == destinationScene) {
|
if (obs_sceneitem_is_group(sceneItem) && obs_sceneitem_get_scene(sceneItem) == destinationScene) {
|
||||||
@ -259,7 +262,8 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request& request)
|
|||||||
obs_sceneitem_get_crop(sceneItem, &sceneItemCrop);
|
obs_sceneitem_get_crop(sceneItem, &sceneItemCrop);
|
||||||
|
|
||||||
// Create the new item
|
// Create the new item
|
||||||
OBSSceneItemAutoRelease newSceneItem = Utils::Obs::ActionHelper::CreateSceneItem(sceneItemSource, destinationScene, sceneItemEnabled, &sceneItemTransform, &sceneItemCrop);
|
OBSSceneItemAutoRelease newSceneItem = Utils::Obs::ActionHelper::CreateSceneItem(
|
||||||
|
sceneItemSource, destinationScene, sceneItemEnabled, &sceneItemTransform, &sceneItemCrop);
|
||||||
obs_scene_release(destinationScene);
|
obs_scene_release(destinationScene);
|
||||||
if (!newSceneItem)
|
if (!newSceneItem)
|
||||||
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene item.");
|
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene item.");
|
||||||
@ -291,7 +295,8 @@ RequestResult RequestHandler::GetSceneItemTransform(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -319,7 +324,8 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!(sceneItem && request.ValidateObject("sceneItemTransform", statusCode, comment)))
|
if (!(sceneItem && request.ValidateObject("sceneItemTransform", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -363,7 +369,8 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
|
|||||||
float scaleX = r.RequestData["scaleX"];
|
float scaleX = r.RequestData["scaleX"];
|
||||||
float finalWidth = scaleX * sourceWidth;
|
float finalWidth = scaleX * sourceWidth;
|
||||||
if (!(finalWidth > -90001.0 && finalWidth < 90001.0))
|
if (!(finalWidth > -90001.0 && finalWidth < 90001.0))
|
||||||
return RequestResult::Error(RequestStatus::RequestFieldOutOfRange, "The field scaleX is too small or large for the current source resolution.");
|
return RequestResult::Error(RequestStatus::RequestFieldOutOfRange,
|
||||||
|
"The field `scaleX` is too small or large for the current source resolution.");
|
||||||
sceneItemTransform.scale.x = scaleX;
|
sceneItemTransform.scale.x = scaleX;
|
||||||
transformChanged = true;
|
transformChanged = true;
|
||||||
}
|
}
|
||||||
@ -373,7 +380,8 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
|
|||||||
float scaleY = r.RequestData["scaleY"];
|
float scaleY = r.RequestData["scaleY"];
|
||||||
float finalHeight = scaleY * sourceHeight;
|
float finalHeight = scaleY * sourceHeight;
|
||||||
if (!(finalHeight > -90001.0 && finalHeight < 90001.0))
|
if (!(finalHeight > -90001.0 && finalHeight < 90001.0))
|
||||||
return RequestResult::Error(RequestStatus::RequestFieldOutOfRange, "The field scaleY is too small or large for the current source resolution.");
|
return RequestResult::Error(RequestStatus::RequestFieldOutOfRange,
|
||||||
|
"The field `scaleY` is too small or large for the current source resolution.");
|
||||||
sceneItemTransform.scale.y = scaleY;
|
sceneItemTransform.scale.y = scaleY;
|
||||||
transformChanged = true;
|
transformChanged = true;
|
||||||
}
|
}
|
||||||
@ -388,10 +396,10 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request& request)
|
|||||||
if (r.Contains("boundsType")) {
|
if (r.Contains("boundsType")) {
|
||||||
if (!r.ValidateOptionalString("boundsType", statusCode, comment))
|
if (!r.ValidateOptionalString("boundsType", statusCode, comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
std::string boundsTypeString = r.RequestData["boundsType"];
|
enum obs_bounds_type boundsType = r.RequestData["boundsType"];
|
||||||
enum obs_bounds_type boundsType = Utils::Obs::EnumHelper::GetSceneItemBoundsType(boundsTypeString);
|
if (boundsType == OBS_BOUNDS_NONE && r.RequestData["boundsType"] != "OBS_BOUNDS_NONE")
|
||||||
if (boundsType == OBS_BOUNDS_NONE && boundsTypeString != "OBS_BOUNDS_NONE")
|
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField, "The field boundsType has an invalid value.");
|
"The field `boundsType` has an invalid value.");
|
||||||
sceneItemTransform.bounds_type = boundsType;
|
sceneItemTransform.bounds_type = boundsType;
|
||||||
transformChanged = true;
|
transformChanged = true;
|
||||||
}
|
}
|
||||||
@ -474,7 +482,8 @@ RequestResult RequestHandler::GetSceneItemEnabled(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -504,7 +513,8 @@ RequestResult RequestHandler::SetSceneItemEnabled(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!(sceneItem && request.ValidateBoolean("sceneItemEnabled", statusCode, comment)))
|
if (!(sceneItem && request.ValidateBoolean("sceneItemEnabled", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -536,7 +546,8 @@ RequestResult RequestHandler::GetSceneItemLocked(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -566,7 +577,8 @@ RequestResult RequestHandler::SetSceneItemLocked(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!(sceneItem && request.ValidateBoolean("sceneItemLocked", statusCode, comment)))
|
if (!(sceneItem && request.ValidateBoolean("sceneItemLocked", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -600,7 +612,8 @@ RequestResult RequestHandler::GetSceneItemIndex(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -630,7 +643,8 @@ RequestResult RequestHandler::SetSceneItemIndex(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!(sceneItem && request.ValidateNumber("sceneItemIndex", statusCode, comment, 0, 8192)))
|
if (!(sceneItem && request.ValidateNumber("sceneItemIndex", statusCode, comment, 0, 8192)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -672,14 +686,15 @@ RequestResult RequestHandler::GetSceneItemBlendMode(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
auto blendMode = obs_sceneitem_get_blending_mode(sceneItem);
|
auto blendMode = obs_sceneitem_get_blending_mode(sceneItem);
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["sceneItemBlendMode"] = Utils::Obs::StringHelper::GetSceneItemBlendMode(blendMode);
|
responseData["sceneItemBlendMode"] = blendMode;
|
||||||
|
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
@ -704,15 +719,15 @@ RequestResult RequestHandler::SetSceneItemBlendMode(const Request& request)
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!(sceneItem && request.ValidateString("sceneItemBlendMode", statusCode, comment)))
|
if (!(sceneItem && request.ValidateString("sceneItemBlendMode", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
std::string blendModeString = request.RequestData["sceneItemBlendMode"];
|
enum obs_blending_type blendMode = request.RequestData["sceneItemBlendMode"];
|
||||||
|
if (blendMode == OBS_BLEND_NORMAL && request.RequestData["sceneItemBlendMode"] != "OBS_BLEND_NORMAL")
|
||||||
auto blendMode = Utils::Obs::EnumHelper::GetSceneItemBlendMode(blendModeString);
|
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
||||||
if (blendMode == OBS_BLEND_NORMAL && blendModeString != "OBS_BLEND_NORMAL")
|
"The field sceneItemBlendMode has an invalid value.");
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField, "The field sceneItemBlendMode has an invalid value.");
|
|
||||||
|
|
||||||
obs_sceneitem_set_blending_mode(sceneItem, blendMode);
|
obs_sceneitem_set_blending_mode(sceneItem, blendMode);
|
||||||
|
|
||||||
@ -724,7 +739,8 @@ RequestResult RequestHandler::GetSceneItemPrivateSettings(const Request& request
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem)
|
if (!sceneItem)
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
@ -741,7 +757,8 @@ RequestResult RequestHandler::SetSceneItemPrivateSettings(const Request& request
|
|||||||
{
|
{
|
||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
|
||||||
|
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
|
||||||
if (!sceneItem || !request.ValidateObject("sceneItemSettings", statusCode, comment))
|
if (!sceneItem || !request.ValidateObject("sceneItemSettings", statusCode, comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
@ -235,7 +235,8 @@ RequestResult RequestHandler::RemoveScene(const Request& request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (Utils::Obs::NumberHelper::GetSceneCount() < 2)
|
if (Utils::Obs::NumberHelper::GetSceneCount() < 2)
|
||||||
return RequestResult::Error(RequestStatus::NotEnoughResources, "You cannot remove the last scene in the collection.");
|
return RequestResult::Error(RequestStatus::NotEnoughResources,
|
||||||
|
"You cannot remove the last scene in the collection.");
|
||||||
|
|
||||||
obs_source_remove(scene);
|
obs_source_remove(scene);
|
||||||
|
|
||||||
@ -267,7 +268,8 @@ RequestResult RequestHandler::SetSceneName(const Request& request)
|
|||||||
|
|
||||||
OBSSourceAutoRelease existingSource = obs_get_source_by_name(newSceneName.c_str());
|
OBSSourceAutoRelease existingSource = obs_get_source_by_name(newSceneName.c_str());
|
||||||
if (existingSource)
|
if (existingSource)
|
||||||
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that new scene name.");
|
return RequestResult::Error(RequestStatus::ResourceAlreadyExists,
|
||||||
|
"A source already exists by that new scene name.");
|
||||||
|
|
||||||
obs_source_set_name(scene, newSceneName.c_str());
|
obs_source_set_name(scene, newSceneName.c_str());
|
||||||
|
|
||||||
@ -342,7 +344,8 @@ RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request& req
|
|||||||
if (hasName && !request.RequestData["transitionName"].is_null()) {
|
if (hasName && !request.RequestData["transitionName"].is_null()) {
|
||||||
if (!request.ValidateOptionalString("transitionName", statusCode, comment))
|
if (!request.ValidateOptionalString("transitionName", statusCode, comment))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
OBSSourceAutoRelease transition = Utils::Obs::SearchHelper::GetSceneTransitionByName(request.RequestData["transitionName"]);
|
OBSSourceAutoRelease transition =
|
||||||
|
Utils::Obs::SearchHelper::GetSceneTransitionByName(request.RequestData["transitionName"]);
|
||||||
if (!transition)
|
if (!transition)
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene transition was found by that name.");
|
return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene transition was found by that name.");
|
||||||
}
|
}
|
||||||
@ -354,7 +357,8 @@ RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request& req
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!hasName && !hasDuration)
|
if (!hasName && !hasDuration)
|
||||||
return RequestResult::Error(RequestStatus::MissingRequestField, "Your request data must include either `transitionName` or `transitionDuration`.");
|
return RequestResult::Error(RequestStatus::MissingRequestField,
|
||||||
|
"Your request data must include either `transitionName` or `transitionDuration`.");
|
||||||
|
|
||||||
if (hasName) {
|
if (hasName) {
|
||||||
if (request.RequestData["transitionName"].is_null()) {
|
if (request.RequestData["transitionName"].is_null()) {
|
||||||
|
@ -180,7 +180,8 @@ RequestResult RequestHandler::GetSourceScreenshot(const Request& request)
|
|||||||
std::string imageFormat = request.RequestData["imageFormat"];
|
std::string imageFormat = request.RequestData["imageFormat"];
|
||||||
|
|
||||||
if (!IsImageFormatValid(imageFormat))
|
if (!IsImageFormatValid(imageFormat))
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField, "Your specified image format is invalid or not supported by this system.");
|
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
||||||
|
"Your specified image format is invalid or not supported by this system.");
|
||||||
|
|
||||||
uint32_t requestedWidth{0};
|
uint32_t requestedWidth{0};
|
||||||
uint32_t requestedHeight{0};
|
uint32_t requestedHeight{0};
|
||||||
@ -258,7 +259,8 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request& request)
|
|||||||
RequestStatus::RequestStatus statusCode;
|
RequestStatus::RequestStatus statusCode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
||||||
if (!(source && request.ValidateString("imageFormat", statusCode, comment) && request.ValidateString("imageFilePath", statusCode, comment)))
|
if (!(source && request.ValidateString("imageFormat", statusCode, comment) &&
|
||||||
|
request.ValidateString("imageFilePath", statusCode, comment)))
|
||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE)
|
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT && obs_source_get_type(source) != OBS_SOURCE_TYPE_SCENE)
|
||||||
@ -268,7 +270,8 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request& request)
|
|||||||
std::string imageFilePath = request.RequestData["imageFilePath"];
|
std::string imageFilePath = request.RequestData["imageFilePath"];
|
||||||
|
|
||||||
if (!IsImageFormatValid(imageFormat))
|
if (!IsImageFormatValid(imageFormat))
|
||||||
return RequestResult::Error(RequestStatus::InvalidRequestField, "Your specified image format is invalid or not supported by this system.");
|
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
||||||
|
"Your specified image format is invalid or not supported by this system.");
|
||||||
|
|
||||||
QFileInfo filePathInfo(QString::fromStdString(imageFilePath));
|
QFileInfo filePathInfo(QString::fromStdString(imageFilePath));
|
||||||
if (!filePathInfo.absoluteDir().exists())
|
if (!filePathInfo.absoluteDir().exists())
|
||||||
|
@ -26,6 +26,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
* @responseField outputReconnecting | Boolean | Whether the output is currently reconnecting
|
* @responseField outputReconnecting | Boolean | Whether the output is currently reconnecting
|
||||||
* @responseField outputTimecode | String | Current formatted timecode string for the output
|
* @responseField outputTimecode | String | Current formatted timecode string for the output
|
||||||
* @responseField outputDuration | Number | Current duration in milliseconds for the output
|
* @responseField outputDuration | Number | Current duration in milliseconds for the output
|
||||||
|
* @responseField outputCongestion | Number | Congestion of the output
|
||||||
* @responseField outputBytes | Number | Number of bytes sent by the output
|
* @responseField outputBytes | Number | Number of bytes sent by the output
|
||||||
* @responseField outputSkippedFrames | Number | Number of frames skipped by the output's process
|
* @responseField outputSkippedFrames | Number | Number of frames skipped by the output's process
|
||||||
* @responseField outputTotalFrames | Number | Total number of frames delivered by the output's process
|
* @responseField outputTotalFrames | Number | Total number of frames delivered by the output's process
|
||||||
@ -48,6 +49,7 @@ RequestResult RequestHandler::GetStreamStatus(const Request&)
|
|||||||
responseData["outputReconnecting"] = obs_output_reconnecting(streamOutput);
|
responseData["outputReconnecting"] = obs_output_reconnecting(streamOutput);
|
||||||
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
|
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
|
||||||
responseData["outputDuration"] = outputDuration;
|
responseData["outputDuration"] = outputDuration;
|
||||||
|
responseData["outputCongestion"] = obs_output_get_congestion(streamOutput);
|
||||||
responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(streamOutput);
|
responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(streamOutput);
|
||||||
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(streamOutput);
|
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(streamOutput);
|
||||||
responseData["outputTotalFrames"] = obs_output_get_total_frames(streamOutput);
|
responseData["outputTotalFrames"] = obs_output_get_total_frames(streamOutput);
|
||||||
|
@ -95,7 +95,8 @@ RequestResult RequestHandler::GetCurrentSceneTransition(const Request&)
|
|||||||
{
|
{
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
||||||
if (!transition)
|
if (!transition)
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"OBS does not currently have a scene transition set."); // This should not happen!
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["transitionName"] = obs_source_get_name(transition);
|
responseData["transitionName"] = obs_source_get_name(transition);
|
||||||
@ -201,10 +202,12 @@ RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request& r
|
|||||||
|
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
||||||
if (!transition)
|
if (!transition)
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"OBS does not currently have a scene transition set."); // This should not happen!
|
||||||
|
|
||||||
if (!obs_source_configurable(transition))
|
if (!obs_source_configurable(transition))
|
||||||
return RequestResult::Error(RequestStatus::ResourceNotConfigurable, "The current transition does not support custom settings.");
|
return RequestResult::Error(RequestStatus::ResourceNotConfigurable,
|
||||||
|
"The current transition does not support custom settings.");
|
||||||
|
|
||||||
bool overlay = true;
|
bool overlay = true;
|
||||||
if (request.Contains("overlay")) {
|
if (request.Contains("overlay")) {
|
||||||
@ -216,7 +219,8 @@ RequestResult RequestHandler::SetCurrentSceneTransitionSettings(const Request& r
|
|||||||
|
|
||||||
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["transitionSettings"]);
|
OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["transitionSettings"]);
|
||||||
if (!newSettings)
|
if (!newSettings)
|
||||||
return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!");
|
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
|
||||||
|
"An internal data conversion operation failed. Please report this!");
|
||||||
|
|
||||||
if (overlay)
|
if (overlay)
|
||||||
obs_source_update(transition, newSettings);
|
obs_source_update(transition, newSettings);
|
||||||
@ -246,7 +250,8 @@ RequestResult RequestHandler::GetCurrentSceneTransitionCursor(const Request&)
|
|||||||
{
|
{
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
||||||
if (!transition)
|
if (!transition)
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"OBS does not currently have a scene transition set."); // This should not happen!
|
||||||
|
|
||||||
json responseData;
|
json responseData;
|
||||||
responseData["transitionCursor"] = obs_transition_get_time(transition);
|
responseData["transitionCursor"] = obs_transition_get_time(transition);
|
||||||
@ -309,7 +314,8 @@ RequestResult RequestHandler::SetTBarPosition(const Request& request)
|
|||||||
|
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
||||||
if (!transition)
|
if (!transition)
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "OBS does not currently have a scene transition set."); // This should not happen!
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"OBS does not currently have a scene transition set."); // This should not happen!
|
||||||
|
|
||||||
float position = request.RequestData["position"];
|
float position = request.RequestData["position"];
|
||||||
|
|
||||||
|
@ -67,10 +67,13 @@ RequestResult RequestHandler::SetStudioModeEnabled(const Request& request)
|
|||||||
// (Bad) Create a boolean then pass it as a reference to the task. Requires `wait` in obs_queue_task() to be true, else undefined behavior
|
// (Bad) Create a boolean then pass it as a reference to the task. Requires `wait` in obs_queue_task() to be true, else undefined behavior
|
||||||
bool studioModeEnabled = request.RequestData["studioModeEnabled"];
|
bool studioModeEnabled = request.RequestData["studioModeEnabled"];
|
||||||
// Queue the task inside of the UI thread to prevent race conditions
|
// Queue the task inside of the UI thread to prevent race conditions
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(
|
||||||
|
OBS_TASK_UI,
|
||||||
|
[](void *param) {
|
||||||
auto studioModeEnabled = (bool *)param;
|
auto studioModeEnabled = (bool *)param;
|
||||||
obs_frontend_set_preview_program_mode(*studioModeEnabled);
|
obs_frontend_set_preview_program_mode(*studioModeEnabled);
|
||||||
}, &studioModeEnabled, true);
|
},
|
||||||
|
&studioModeEnabled, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestResult::Success();
|
return RequestResult::Success();
|
||||||
@ -147,7 +150,8 @@ RequestResult RequestHandler::OpenInputInteractDialog(const Request& request)
|
|||||||
return RequestResult::Error(statusCode, comment);
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_INTERACTION))
|
if (!(obs_source_get_output_flags(input) & OBS_SOURCE_INTERACTION))
|
||||||
return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support interaction.");
|
return RequestResult::Error(RequestStatus::InvalidResourceState,
|
||||||
|
"The specified input does not support interaction.");
|
||||||
|
|
||||||
obs_frontend_open_source_interaction(input);
|
obs_frontend_open_source_interaction(input);
|
||||||
|
|
||||||
@ -171,14 +175,14 @@ RequestResult RequestHandler::GetMonitorList(const Request&)
|
|||||||
json responseData;
|
json responseData;
|
||||||
std::vector<json> monitorsData;
|
std::vector<json> monitorsData;
|
||||||
QList<QScreen *> screensList = QGuiApplication::screens();
|
QList<QScreen *> screensList = QGuiApplication::screens();
|
||||||
for (int screenIndex = 0; screenIndex < screensList.size(); screenIndex++)
|
for (int screenIndex = 0; screenIndex < screensList.size(); screenIndex++) {
|
||||||
{
|
|
||||||
json screenData;
|
json screenData;
|
||||||
QScreen const *screen = screensList[screenIndex];
|
QScreen const *screen = screensList[screenIndex];
|
||||||
std::stringstream nameAndIndex;
|
std::stringstream nameAndIndex;
|
||||||
nameAndIndex << screen->name().toStdString();
|
nameAndIndex << screen->name().toStdString();
|
||||||
nameAndIndex << '(' << screenIndex << ')';
|
nameAndIndex << '(' << screenIndex << ')';
|
||||||
screenData["monitorName"] = nameAndIndex.str();
|
screenData["monitorName"] = nameAndIndex.str();
|
||||||
|
screenData["monitorIndex"] = screenIndex;
|
||||||
const QRect screenGeometry = screen->geometry();
|
const QRect screenGeometry = screen->geometry();
|
||||||
screenData["monitorWidth"] = screenGeometry.width();
|
screenData["monitorWidth"] = screenGeometry.width();
|
||||||
screenData["monitorHeight"] = screenGeometry.height();
|
screenData["monitorHeight"] = screenGeometry.height();
|
||||||
@ -189,3 +193,112 @@ RequestResult RequestHandler::GetMonitorList(const Request&)
|
|||||||
responseData["monitors"] = monitorsData;
|
responseData["monitors"] = monitorsData;
|
||||||
return RequestResult::Success(responseData);
|
return RequestResult::Success(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a projector for a specific output video mix.
|
||||||
|
*
|
||||||
|
* Mix types:
|
||||||
|
*
|
||||||
|
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_PREVIEW`
|
||||||
|
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_PROGRAM`
|
||||||
|
* - `OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW`
|
||||||
|
*
|
||||||
|
* Note: This request serves to provide feature parity with 4.x. It is very likely to be changed/deprecated in a future release.
|
||||||
|
*
|
||||||
|
* @requestField videoMixType | String | Type of mix to open
|
||||||
|
* @requestField ?monitorIndex | Number | Monitor index, use `GetMonitorList` to obtain index | None | -1: Opens projector in windowed mode
|
||||||
|
* @requestField ?projectorGeometry | String | Size/Position data for a windowed projector, in Qt Base64 encoded format. Mutually exclusive with `monitorIndex` | N/A
|
||||||
|
*
|
||||||
|
* @requestType OpenVideoMixProjector
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @category ui
|
||||||
|
* @api requests
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::OpenVideoMixProjector(const Request &request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
if (!request.ValidateString("videoMixType", statusCode, comment))
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
std::string videoMixType = request.RequestData["videoMixType"];
|
||||||
|
const char *projectorType;
|
||||||
|
if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PREVIEW")
|
||||||
|
projectorType = "Preview";
|
||||||
|
else if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PROGRAM")
|
||||||
|
projectorType = "Program";
|
||||||
|
else if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW")
|
||||||
|
projectorType = "Multiview";
|
||||||
|
else
|
||||||
|
return RequestResult::Error(RequestStatus::InvalidRequestField,
|
||||||
|
"The field `videoMixType` has an invalid enum value.");
|
||||||
|
|
||||||
|
int monitorIndex = -1;
|
||||||
|
if (request.Contains("monitorIndex")) {
|
||||||
|
if (!request.ValidateOptionalNumber("monitorIndex", statusCode, comment, -1, 9))
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
monitorIndex = request.RequestData["monitorIndex"];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string projectorGeometry;
|
||||||
|
if (request.Contains("projectorGeometry")) {
|
||||||
|
if (!request.ValidateOptionalString("projectorGeometry", statusCode, comment))
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
if (monitorIndex != -1)
|
||||||
|
return RequestResult::Error(RequestStatus::TooManyRequestFields,
|
||||||
|
"`monitorIndex` and `projectorGeometry` are mutually exclusive.");
|
||||||
|
projectorGeometry = request.RequestData["projectorGeometry"];
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_frontend_open_projector(projectorType, monitorIndex, projectorGeometry.c_str(), nullptr);
|
||||||
|
|
||||||
|
return RequestResult::Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a projector for a source.
|
||||||
|
*
|
||||||
|
* Note: This request serves to provide feature parity with 4.x. It is very likely to be changed/deprecated in a future release.
|
||||||
|
*
|
||||||
|
* @requestField sourceName | String | Name of the source to open a projector for
|
||||||
|
* @requestField ?monitorIndex | Number | Monitor index, use `GetMonitorList` to obtain index | None | -1: Opens projector in windowed mode
|
||||||
|
* @requestField ?projectorGeometry | String | Size/Position data for a windowed projector, in Qt Base64 encoded format. Mutually exclusive with `monitorIndex` | N/A
|
||||||
|
*
|
||||||
|
* @requestType OpenSourceProjector
|
||||||
|
* @complexity 3
|
||||||
|
* @rpcVersion -1
|
||||||
|
* @initialVersion 5.0.0
|
||||||
|
* @category ui
|
||||||
|
* @api requests
|
||||||
|
*/
|
||||||
|
RequestResult RequestHandler::OpenSourceProjector(const Request &request)
|
||||||
|
{
|
||||||
|
RequestStatus::RequestStatus statusCode;
|
||||||
|
std::string comment;
|
||||||
|
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
|
||||||
|
if (!source)
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
|
||||||
|
int monitorIndex = -1;
|
||||||
|
if (request.Contains("monitorIndex")) {
|
||||||
|
if (!request.ValidateOptionalNumber("monitorIndex", statusCode, comment, -1, 9))
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
monitorIndex = request.RequestData["monitorIndex"];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string projectorGeometry;
|
||||||
|
if (request.Contains("projectorGeometry")) {
|
||||||
|
if (!request.ValidateOptionalString("projectorGeometry", statusCode, comment))
|
||||||
|
return RequestResult::Error(statusCode, comment);
|
||||||
|
if (monitorIndex != -1)
|
||||||
|
return RequestResult::Error(RequestStatus::TooManyRequestFields,
|
||||||
|
"`monitorIndex` and `projectorGeometry` are mutually exclusive.");
|
||||||
|
projectorGeometry = request.RequestData["projectorGeometry"];
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_frontend_open_projector("Source", monitorIndex, projectorGeometry.c_str(), obs_source_get_name(source));
|
||||||
|
|
||||||
|
return RequestResult::Success();
|
||||||
|
}
|
||||||
|
@ -29,8 +29,9 @@ json GetDefaultJsonObject(const json &requestData)
|
|||||||
return requestData;
|
return requestData;
|
||||||
}
|
}
|
||||||
|
|
||||||
Request::Request(const std::string &requestType, const json &requestData, const RequestBatchExecutionType::RequestBatchExecutionType executionType) :
|
Request::Request(const std::string &requestType, const json &requestData,
|
||||||
RequestType(requestType),
|
const RequestBatchExecutionType::RequestBatchExecutionType executionType)
|
||||||
|
: RequestType(requestType),
|
||||||
HasRequestData(requestData.is_object()),
|
HasRequestData(requestData.is_object()),
|
||||||
RequestData(GetDefaultJsonObject(requestData)),
|
RequestData(GetDefaultJsonObject(requestData)),
|
||||||
ExecutionType(executionType)
|
ExecutionType(executionType)
|
||||||
@ -59,7 +60,8 @@ bool Request::ValidateBasic(const std::string &keyName, RequestStatus::RequestSt
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue, const double maxValue) const
|
bool Request::ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const double minValue, const double maxValue) const
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_number()) {
|
if (!RequestData[keyName].is_number()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
@ -70,19 +72,22 @@ bool Request::ValidateOptionalNumber(const std::string &keyName, RequestStatus::
|
|||||||
double value = RequestData[keyName];
|
double value = RequestData[keyName];
|
||||||
if (value < minValue) {
|
if (value < minValue) {
|
||||||
statusCode = RequestStatus::RequestFieldOutOfRange;
|
statusCode = RequestStatus::RequestFieldOutOfRange;
|
||||||
comment = std::string("The field value of `") + keyName + "` is below the minimum of `" + std::to_string(minValue) + "`";
|
comment = std::string("The field value of `") + keyName + "` is below the minimum of `" + std::to_string(minValue) +
|
||||||
|
"`";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (value > maxValue) {
|
if (value > maxValue) {
|
||||||
statusCode = RequestStatus::RequestFieldOutOfRange;
|
statusCode = RequestStatus::RequestFieldOutOfRange;
|
||||||
comment = std::string("The field value of `") + keyName + "` is above the maximum of `" + std::to_string(maxValue) + "`";
|
comment = std::string("The field value of `") + keyName + "` is above the maximum of `" + std::to_string(maxValue) +
|
||||||
|
"`";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue, const double maxValue) const
|
bool Request::ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const double minValue, const double maxValue) const
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -93,7 +98,8 @@ bool Request::ValidateNumber(const std::string &keyName, RequestStatus::RequestS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_string()) {
|
if (!RequestData[keyName].is_string()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
@ -110,7 +116,8 @@ bool Request::ValidateOptionalString(const std::string &keyName, RequestStatus::
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -121,7 +128,8 @@ bool Request::ValidateString(const std::string &keyName, RequestStatus::RequestS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
bool Request::ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_boolean()) {
|
if (!RequestData[keyName].is_boolean()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
@ -143,7 +151,8 @@ bool Request::ValidateBoolean(const std::string &keyName, RequestStatus::Request
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_object()) {
|
if (!RequestData[keyName].is_object()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
@ -160,7 +169,8 @@ bool Request::ValidateOptionalObject(const std::string &keyName, RequestStatus::
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -171,7 +181,8 @@ bool Request::ValidateObject(const std::string &keyName, RequestStatus::RequestS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!RequestData[keyName].is_array()) {
|
if (!RequestData[keyName].is_array()) {
|
||||||
statusCode = RequestStatus::InvalidRequestFieldType;
|
statusCode = RequestStatus::InvalidRequestFieldType;
|
||||||
@ -188,7 +199,8 @@ bool Request::ValidateOptionalArray(const std::string &keyName, RequestStatus::R
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const
|
bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const bool allowEmpty) const
|
||||||
{
|
{
|
||||||
if (!ValidateBasic(keyName, statusCode, comment))
|
if (!ValidateBasic(keyName, statusCode, comment))
|
||||||
return false;
|
return false;
|
||||||
@ -199,7 +211,8 @@ bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestSt
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const
|
||||||
{
|
{
|
||||||
if (!ValidateString(keyName, statusCode, comment))
|
if (!ValidateString(keyName, statusCode, comment))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -216,7 +229,8 @@ obs_source_t *Request::ValidateSource(const std::string &keyName, RequestStatus:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
|
obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const ObsWebSocketSceneFilter filter) const
|
||||||
{
|
{
|
||||||
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
@ -245,7 +259,8 @@ obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
|
obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const ObsWebSocketSceneFilter filter) const
|
||||||
{
|
{
|
||||||
OBSSourceAutoRelease sceneSource = ValidateSource(keyName, statusCode, comment);
|
OBSSourceAutoRelease sceneSource = ValidateSource(keyName, statusCode, comment);
|
||||||
if (!sceneSource)
|
if (!sceneSource)
|
||||||
@ -275,7 +290,8 @@ obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const
|
||||||
{
|
{
|
||||||
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
@ -291,7 +307,8 @@ obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode, std::string &comment) const
|
||||||
{
|
{
|
||||||
obs_source_t *source = ValidateSource(sourceKeyName, statusCode, comment);
|
obs_source_t *source = ValidateSource(sourceKeyName, statusCode, comment);
|
||||||
if (!source)
|
if (!source)
|
||||||
@ -305,14 +322,17 @@ FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::
|
|||||||
obs_source_t *filter = obs_source_get_filter_by_name(source, filterName.c_str());
|
obs_source_t *filter = obs_source_get_filter_by_name(source, filterName.c_str());
|
||||||
if (!filter) {
|
if (!filter) {
|
||||||
statusCode = RequestStatus::ResourceNotFound;
|
statusCode = RequestStatus::ResourceNotFound;
|
||||||
comment = std::string("No filter was found in the source `") + RequestData[sourceKeyName].get<std::string>() + "` with the name `" + filterName + "`.";
|
comment = std::string("No filter was found in the source `") + RequestData[sourceKeyName].get<std::string>() +
|
||||||
|
"` with the name `" + filterName + "`.";
|
||||||
return FilterPair{source, nullptr};
|
return FilterPair{source, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
return FilterPair{source, filter};
|
return FilterPair{source, filter};
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter) const
|
obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const ObsWebSocketSceneFilter filter) const
|
||||||
{
|
{
|
||||||
OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter);
|
OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter);
|
||||||
if (!scene)
|
if (!scene)
|
||||||
@ -326,10 +346,29 @@ obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, con
|
|||||||
OBSSceneItem sceneItem = obs_scene_find_sceneitem_by_id(scene, sceneItemId);
|
OBSSceneItem sceneItem = obs_scene_find_sceneitem_by_id(scene, sceneItemId);
|
||||||
if (!sceneItem) {
|
if (!sceneItem) {
|
||||||
statusCode = RequestStatus::ResourceNotFound;
|
statusCode = RequestStatus::ResourceNotFound;
|
||||||
comment = std::string("No scene items were found in scene `") + RequestData[sceneKeyName].get<std::string>() + "` with the ID `" + std::to_string(sceneItemId) + "`.";
|
comment = std::string("No scene items were found in scene `") + RequestData[sceneKeyName].get<std::string>() +
|
||||||
|
"` with the ID `" + std::to_string(sceneItemId) + "`.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_addref(sceneItem);
|
obs_sceneitem_addref(sceneItem);
|
||||||
return sceneItem;
|
return sceneItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obs_output_t *Request::ValidateOutput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const
|
||||||
|
{
|
||||||
|
if (!ValidateString(keyName, statusCode, comment))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
std::string outputName = RequestData[keyName];
|
||||||
|
|
||||||
|
obs_output_t *ret = obs_get_output_by_name(outputName.c_str());
|
||||||
|
if (!ret) {
|
||||||
|
statusCode = RequestStatus::ResourceNotFound;
|
||||||
|
comment = std::string("No output was found with the name `") + outputName + "`.";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -35,32 +35,50 @@ struct FilterPair {
|
|||||||
OBSSourceAutoRelease filter;
|
OBSSourceAutoRelease filter;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Request
|
struct Request {
|
||||||
{
|
Request(const std::string &requestType, const json &requestData = nullptr,
|
||||||
Request(const std::string &requestType, const json &requestData = nullptr, const RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::None);
|
const RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::None);
|
||||||
|
|
||||||
// Contains the key and is not null
|
// Contains the key and is not null
|
||||||
bool Contains(const std::string &keyName) const;
|
bool Contains(const std::string &keyName) const;
|
||||||
|
|
||||||
bool ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
bool ValidateBasic(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
||||||
bool ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const;
|
bool ValidateOptionalNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
bool ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const;
|
const double minValue = -INFINITY, const double maxValue = INFINITY) const;
|
||||||
bool ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
bool ValidateNumber(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
bool ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
const double minValue = -INFINITY, const double maxValue = INFINITY) const;
|
||||||
bool ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
bool ValidateOptionalString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const bool allowEmpty = false) const;
|
||||||
|
bool ValidateString(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const bool allowEmpty = false) const;
|
||||||
|
bool ValidateOptionalBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const;
|
||||||
bool ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
bool ValidateBoolean(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
||||||
bool ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
bool ValidateOptionalObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
bool ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
const bool allowEmpty = false) const;
|
||||||
bool ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
bool ValidateObject(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
bool ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const;
|
const bool allowEmpty = false) const;
|
||||||
|
bool ValidateOptionalArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const bool allowEmpty = false) const;
|
||||||
|
bool ValidateArray(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const bool allowEmpty = false) const;
|
||||||
|
|
||||||
// All return values have incremented refcounts
|
// All return values have incremented refcounts
|
||||||
obs_source_t *ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
obs_source_t *ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
||||||
obs_source_t *ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
std::string &comment) const;
|
||||||
obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
obs_source_t *ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
obs_source_t *ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
||||||
FilterPair ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
||||||
|
obs_source_t *ValidateInput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const;
|
||||||
|
FilterPair ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode, std::string &comment) const;
|
||||||
|
obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName,
|
||||||
|
RequestStatus::RequestStatus &statusCode, std::string &comment,
|
||||||
|
const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
|
||||||
|
obs_output_t *ValidateOutput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
|
||||||
|
std::string &comment) const;
|
||||||
|
|
||||||
std::string RequestType;
|
std::string RequestType;
|
||||||
bool HasRequestData;
|
bool HasRequestData;
|
||||||
|
@ -18,9 +18,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "RequestBatchRequest.h"
|
#include "RequestBatchRequest.h"
|
||||||
|
|
||||||
RequestBatchRequest::RequestBatchRequest(const std::string &requestType, const json &requestData, RequestBatchExecutionType::RequestBatchExecutionType executionType, const json &inputVariables, const json &outputVariables) :
|
RequestBatchRequest::RequestBatchRequest(const std::string &requestType, const json &requestData,
|
||||||
Request(requestType, requestData, executionType),
|
RequestBatchExecutionType::RequestBatchExecutionType executionType,
|
||||||
InputVariables(inputVariables),
|
const json &inputVariables, const json &outputVariables)
|
||||||
OutputVariables(outputVariables)
|
: Request(requestType, requestData, executionType), InputVariables(inputVariables), OutputVariables(outputVariables)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Request.h"
|
#include "Request.h"
|
||||||
|
|
||||||
struct RequestBatchRequest : Request {
|
struct RequestBatchRequest : Request {
|
||||||
RequestBatchRequest(const std::string &requestType, const json &requestData, RequestBatchExecutionType::RequestBatchExecutionType executionType, const json &inputVariables = nullptr, const json &outputVariables = nullptr);
|
RequestBatchRequest(const std::string &requestType, const json &requestData,
|
||||||
|
RequestBatchExecutionType::RequestBatchExecutionType executionType,
|
||||||
|
const json &inputVariables = nullptr, const json &outputVariables = nullptr);
|
||||||
|
|
||||||
json InputVariables;
|
json InputVariables;
|
||||||
json OutputVariables;
|
json OutputVariables;
|
||||||
|
@ -19,11 +19,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "RequestResult.h"
|
#include "RequestResult.h"
|
||||||
|
|
||||||
RequestResult::RequestResult(RequestStatus::RequestStatus statusCode, json responseData, std::string comment) :
|
RequestResult::RequestResult(RequestStatus::RequestStatus statusCode, json responseData, std::string comment)
|
||||||
StatusCode(statusCode),
|
: StatusCode(statusCode), ResponseData(responseData), Comment(comment), SleepFrames(0)
|
||||||
ResponseData(responseData),
|
|
||||||
Comment(comment),
|
|
||||||
SleepFrames(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../types/RequestStatus.h"
|
#include "../types/RequestStatus.h"
|
||||||
#include "../../utils/Json.h"
|
#include "../../utils/Json.h"
|
||||||
|
|
||||||
struct RequestResult
|
struct RequestResult {
|
||||||
{
|
RequestResult(RequestStatus::RequestStatus statusCode = RequestStatus::Success, json responseData = nullptr,
|
||||||
RequestResult(RequestStatus::RequestStatus statusCode = RequestStatus::Success, json responseData = nullptr, std::string comment = "");
|
std::string comment = "");
|
||||||
static RequestResult Success(json responseData = nullptr);
|
static RequestResult Success(json responseData = nullptr);
|
||||||
static RequestResult Error(RequestStatus::RequestStatus statusCode, std::string comment = "");
|
static RequestResult Error(RequestStatus::RequestStatus statusCode, std::string comment = "");
|
||||||
RequestStatus::RequestStatus StatusCode;
|
RequestStatus::RequestStatus StatusCode;
|
||||||
|
@ -77,8 +77,5 @@ namespace RequestBatchExecutionType {
|
|||||||
Parallel = 2,
|
Parallel = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool IsValid(int8_t executionType)
|
inline bool IsValid(int8_t executionType) { return executionType >= None && executionType <= Parallel; }
|
||||||
{
|
|
||||||
return executionType >= None && executionType <= Parallel;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -263,7 +263,6 @@ namespace RequestStatus {
|
|||||||
*/
|
*/
|
||||||
StudioModeNotActive = 506,
|
StudioModeNotActive = 506,
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The resource was not found.
|
* The resource was not found.
|
||||||
*
|
*
|
||||||
|
@ -19,10 +19,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "Compat.h"
|
#include "Compat.h"
|
||||||
|
|
||||||
Utils::Compat::StdFunctionRunnable::StdFunctionRunnable(std::function<void()> func) :
|
Utils::Compat::StdFunctionRunnable::StdFunctionRunnable(std::function<void()> func) : cb(std::move(func)) {}
|
||||||
cb(std::move(func))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Utils::Compat::StdFunctionRunnable::run()
|
void Utils::Compat::StdFunctionRunnable::run()
|
||||||
{
|
{
|
||||||
|
@ -27,6 +27,7 @@ namespace Utils {
|
|||||||
// Reimplement QRunnable for std::function. Retrocompatability for Qt < 5.15
|
// Reimplement QRunnable for std::function. Retrocompatability for Qt < 5.15
|
||||||
class StdFunctionRunnable : public QRunnable {
|
class StdFunctionRunnable : public QRunnable {
|
||||||
std::function<void()> cb;
|
std::function<void()> cb;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StdFunctionRunnable(std::function<void()> func);
|
StdFunctionRunnable(std::function<void()> func);
|
||||||
void run() override;
|
void run() override;
|
||||||
|
@ -63,10 +63,7 @@ bool Utils::Crypto::CheckAuthenticationString(std::string secret, std::string ch
|
|||||||
secretAndChallenge += QString::fromStdString(challenge);
|
secretAndChallenge += QString::fromStdString(challenge);
|
||||||
|
|
||||||
// Generate a SHA256 hash of secretAndChallenge
|
// Generate a SHA256 hash of secretAndChallenge
|
||||||
auto hash = QCryptographicHash::hash(
|
auto hash = QCryptographicHash::hash(secretAndChallenge.toUtf8(), QCryptographicHash::Algorithm::Sha256);
|
||||||
secretAndChallenge.toUtf8(),
|
|
||||||
QCryptographicHash::Algorithm::Sha256
|
|
||||||
);
|
|
||||||
|
|
||||||
// Encode the SHA256 hash to Base64
|
// Encode the SHA256 hash to Base64
|
||||||
std::string expectedAuthenticationString = hash.toBase64().toStdString();
|
std::string expectedAuthenticationString = hash.toBase64().toStdString();
|
||||||
|
@ -168,8 +168,7 @@ json Utils::Json::ObsDataToJson(obs_data_t *d, bool includeDefault)
|
|||||||
case OBS_DATA_ARRAY:
|
case OBS_DATA_ARRAY:
|
||||||
set_json_array(&j, name, item, includeDefault);
|
set_json_array(&j, name, item, includeDefault);
|
||||||
break;
|
break;
|
||||||
default:
|
default:;
|
||||||
;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,51 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(obs_source_type, {
|
||||||
|
{OBS_SOURCE_TYPE_INPUT, "OBS_SOURCE_TYPE_INPUT"},
|
||||||
|
{OBS_SOURCE_TYPE_FILTER, "OBS_SOURCE_TYPE_FILTER"},
|
||||||
|
{OBS_SOURCE_TYPE_TRANSITION, "OBS_SOURCE_TYPE_TRANSITION"},
|
||||||
|
{OBS_SOURCE_TYPE_SCENE, "OBS_SOURCE_TYPE_SCENE"},
|
||||||
|
})
|
||||||
|
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(obs_monitoring_type,
|
||||||
|
{
|
||||||
|
{OBS_MONITORING_TYPE_NONE, "OBS_MONITORING_TYPE_NONE"},
|
||||||
|
{OBS_MONITORING_TYPE_MONITOR_ONLY, "OBS_MONITORING_TYPE_MONITOR_ONLY"},
|
||||||
|
{OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT, "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT"},
|
||||||
|
})
|
||||||
|
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(obs_media_state, {
|
||||||
|
{OBS_MEDIA_STATE_NONE, "OBS_MEDIA_STATE_NONE"},
|
||||||
|
{OBS_MEDIA_STATE_PLAYING, "OBS_MEDIA_STATE_PLAYING"},
|
||||||
|
{OBS_MEDIA_STATE_OPENING, "OBS_MEDIA_STATE_OPENING"},
|
||||||
|
{OBS_MEDIA_STATE_BUFFERING, "OBS_MEDIA_STATE_BUFFERING"},
|
||||||
|
{OBS_MEDIA_STATE_PAUSED, "OBS_MEDIA_STATE_PAUSED"},
|
||||||
|
{OBS_MEDIA_STATE_STOPPED, "OBS_MEDIA_STATE_STOPPED"},
|
||||||
|
{OBS_MEDIA_STATE_ENDED, "OBS_MEDIA_STATE_ENDED"},
|
||||||
|
{OBS_MEDIA_STATE_ERROR, "OBS_MEDIA_STATE_ERROR"},
|
||||||
|
})
|
||||||
|
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(obs_bounds_type, {
|
||||||
|
{OBS_BOUNDS_NONE, "OBS_BOUNDS_NONE"},
|
||||||
|
{OBS_BOUNDS_STRETCH, "OBS_BOUNDS_STRETCH"},
|
||||||
|
{OBS_BOUNDS_SCALE_INNER, "OBS_BOUNDS_SCALE_INNER"},
|
||||||
|
{OBS_BOUNDS_SCALE_OUTER, "OBS_BOUNDS_SCALE_OUTER"},
|
||||||
|
{OBS_BOUNDS_SCALE_TO_WIDTH, "OBS_BOUNDS_SCALE_TO_WIDTH"},
|
||||||
|
{OBS_BOUNDS_SCALE_TO_HEIGHT, "OBS_BOUNDS_SCALE_TO_HEIGHT"},
|
||||||
|
{OBS_BOUNDS_MAX_ONLY, "OBS_BOUNDS_MAX_ONLY"},
|
||||||
|
})
|
||||||
|
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(obs_blending_type, {
|
||||||
|
{OBS_BLEND_NORMAL, "OBS_BLEND_NORMAL"},
|
||||||
|
{OBS_BLEND_ADDITIVE, "OBS_BLEND_ADDITIVE"},
|
||||||
|
{OBS_BLEND_SUBTRACT, "OBS_BLEND_SUBTRACT"},
|
||||||
|
{OBS_BLEND_SCREEN, "OBS_BLEND_SCREEN"},
|
||||||
|
{OBS_BLEND_MULTIPLY, "OBS_BLEND_MULTIPLY"},
|
||||||
|
{OBS_BLEND_LIGHTEN, "OBS_BLEND_LIGHTEN"},
|
||||||
|
{OBS_BLEND_DARKEN, "OBS_BLEND_DARKEN"},
|
||||||
|
})
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Json {
|
namespace Json {
|
||||||
bool JsonArrayIsValidObsArray(const json &j);
|
bool JsonArrayIsValidObsArray(const json &j);
|
||||||
|
@ -59,7 +59,8 @@ using OBSWeakEncoderAutoRelease = OBSRef<obs_weak_encoder_t *, ___weak_encoder_d
|
|||||||
using OBSWeakServiceAutoRelease = OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref, obs_weak_service_release>;
|
using OBSWeakServiceAutoRelease = OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref, obs_weak_service_release>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename T> T* GetCalldataPointer(const calldata_t *data, const char* name) {
|
template<typename T> T *GetCalldataPointer(const calldata_t *data, const char *name)
|
||||||
|
{
|
||||||
void *ptr = nullptr;
|
void *ptr = nullptr;
|
||||||
calldata_get_ptr(data, name, &ptr);
|
calldata_get_ptr(data, name, &ptr);
|
||||||
return static_cast<T *>(ptr);
|
return static_cast<T *>(ptr);
|
||||||
@ -76,6 +77,16 @@ enum ObsOutputState {
|
|||||||
OBS_WEBSOCKET_OUTPUT_RESUMED,
|
OBS_WEBSOCKET_OUTPUT_RESUMED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(ObsOutputState, {
|
||||||
|
{OBS_WEBSOCKET_OUTPUT_UNKNOWN, "OBS_WEBSOCKET_OUTPUT_UNKNOWN"},
|
||||||
|
{OBS_WEBSOCKET_OUTPUT_STARTING, "OBS_WEBSOCKET_OUTPUT_STARTING"},
|
||||||
|
{OBS_WEBSOCKET_OUTPUT_STARTED, "OBS_WEBSOCKET_OUTPUT_STARTED"},
|
||||||
|
{OBS_WEBSOCKET_OUTPUT_STOPPING, "OBS_WEBSOCKET_OUTPUT_STOPPING"},
|
||||||
|
{OBS_WEBSOCKET_OUTPUT_STOPPED, "OBS_WEBSOCKET_OUTPUT_STOPPED"},
|
||||||
|
{OBS_WEBSOCKET_OUTPUT_PAUSED, "OBS_WEBSOCKET_OUTPUT_PAUSED"},
|
||||||
|
{OBS_WEBSOCKET_OUTPUT_RESUMED, "OBS_WEBSOCKET_OUTPUT_RESUMED"},
|
||||||
|
})
|
||||||
|
|
||||||
enum ObsMediaInputAction {
|
enum ObsMediaInputAction {
|
||||||
/**
|
/**
|
||||||
* No action.
|
* No action.
|
||||||
@ -149,6 +160,17 @@ enum ObsMediaInputAction {
|
|||||||
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS,
|
OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(ObsMediaInputAction,
|
||||||
|
{
|
||||||
|
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE"},
|
||||||
|
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY"},
|
||||||
|
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE"},
|
||||||
|
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP"},
|
||||||
|
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART"},
|
||||||
|
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT"},
|
||||||
|
{OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS, "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS"},
|
||||||
|
})
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Obs {
|
namespace Obs {
|
||||||
namespace StringHelper {
|
namespace StringHelper {
|
||||||
@ -157,21 +179,9 @@ namespace Utils {
|
|||||||
std::string GetCurrentProfile();
|
std::string GetCurrentProfile();
|
||||||
std::string GetCurrentProfilePath();
|
std::string GetCurrentProfilePath();
|
||||||
std::string GetCurrentRecordOutputPath();
|
std::string GetCurrentRecordOutputPath();
|
||||||
std::string GetSourceType(obs_source_t *source);
|
std::string GetLastRecordFileName();
|
||||||
std::string GetInputMonitorType(enum obs_monitoring_type monitorType);
|
std::string GetLastReplayBufferFileName();
|
||||||
std::string GetInputMonitorType(obs_source_t *input);
|
|
||||||
std::string GetMediaInputState(obs_source_t *input);
|
|
||||||
std::string GetLastReplayBufferFilePath();
|
|
||||||
std::string GetSceneItemBoundsType(enum obs_bounds_type type);
|
|
||||||
std::string GetSceneItemBlendMode(enum obs_blending_type mode);
|
|
||||||
std::string DurationToTimecode(uint64_t);
|
std::string DurationToTimecode(uint64_t);
|
||||||
std::string GetOutputState(ObsOutputState state);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace EnumHelper {
|
|
||||||
enum obs_bounds_type GetSceneItemBoundsType(std::string boundsType);
|
|
||||||
enum ObsMediaInputAction GetMediaInputAction(std::string mediaAction);
|
|
||||||
enum obs_blending_type GetSceneItemBlendMode(std::string mode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace NumberHelper {
|
namespace NumberHelper {
|
||||||
@ -195,6 +205,7 @@ namespace Utils {
|
|||||||
std::vector<json> GetSceneTransitionList();
|
std::vector<json> GetSceneTransitionList();
|
||||||
std::vector<json> GetSourceFilterList(obs_source_t *source);
|
std::vector<json> GetSourceFilterList(obs_source_t *source);
|
||||||
std::vector<std::string> GetFilterKindList();
|
std::vector<std::string> GetFilterKindList();
|
||||||
|
std::vector<json> GetOutputList();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ObjectHelper {
|
namespace ObjectHelper {
|
||||||
@ -205,13 +216,21 @@ namespace Utils {
|
|||||||
namespace SearchHelper {
|
namespace SearchHelper {
|
||||||
obs_hotkey_t *GetHotkeyByName(std::string name);
|
obs_hotkey_t *GetHotkeyByName(std::string name);
|
||||||
obs_source_t *GetSceneTransitionByName(std::string name); // Increments source ref. Use OBSSourceAutoRelease
|
obs_source_t *GetSceneTransitionByName(std::string name); // Increments source ref. Use OBSSourceAutoRelease
|
||||||
obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name, int offset = 0); // Increments ref. Use OBSSceneItemAutoRelease
|
obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name,
|
||||||
|
int offset = 0); // Increments ref. Use OBSSceneItemAutoRelease
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ActionHelper {
|
namespace ActionHelper {
|
||||||
obs_sceneitem_t *CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled = true, obs_transform_info *sceneItemTransform = nullptr, obs_sceneitem_crop *sceneItemCrop = nullptr); // Increments ref. Use OBSSceneItemAutoRelease
|
obs_sceneitem_t *
|
||||||
obs_sceneitem_t *CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled = true); // Increments ref. Use OBSSceneItemAutoRelease
|
CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled = true,
|
||||||
obs_source_t *CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind, obs_data_t *filterSettings); // Increments source ref. Use OBSSourceAutoRelease
|
obs_transform_info *sceneItemTransform = nullptr,
|
||||||
|
obs_sceneitem_crop *sceneItemCrop = nullptr); // Increments ref. Use OBSSceneItemAutoRelease
|
||||||
|
obs_sceneitem_t *CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings,
|
||||||
|
obs_scene_t *scene,
|
||||||
|
bool sceneItemEnabled = true); // Increments ref. Use OBSSceneItemAutoRelease
|
||||||
|
obs_source_t *
|
||||||
|
CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind,
|
||||||
|
obs_data_t *filterSettings); // Increments source ref. Use OBSSourceAutoRelease
|
||||||
void SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index);
|
void SetSourceFilterIndex(obs_source_t *source, obs_source_t *filter, size_t index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ struct CreateSceneItemData {
|
|||||||
OBSSceneItem sceneItem; // Out
|
OBSSceneItem sceneItem; // Out
|
||||||
};
|
};
|
||||||
|
|
||||||
void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
static void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
||||||
{
|
{
|
||||||
auto *data = static_cast<CreateSceneItemData *>(_data);
|
auto *data = static_cast<CreateSceneItemData *>(_data);
|
||||||
data->sceneItem = obs_scene_add(scene, data->source);
|
data->sceneItem = obs_scene_add(scene, data->source);
|
||||||
@ -41,7 +41,9 @@ void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
|
|||||||
obs_sceneitem_set_visible(data->sceneItem, data->sceneItemEnabled);
|
obs_sceneitem_set_visible(data->sceneItem, data->sceneItemEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled, obs_transform_info *sceneItemTransform, obs_sceneitem_crop *sceneItemCrop)
|
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled,
|
||||||
|
obs_transform_info *sceneItemTransform,
|
||||||
|
obs_sceneitem_crop *sceneItemCrop)
|
||||||
{
|
{
|
||||||
// Sanity check for valid scene
|
// Sanity check for valid scene
|
||||||
if (!(source && scene))
|
if (!(source && scene))
|
||||||
@ -64,7 +66,8 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source,
|
|||||||
return data.sceneItem;
|
return data.sceneItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled)
|
obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings,
|
||||||
|
obs_scene_t *scene, bool sceneItemEnabled)
|
||||||
{
|
{
|
||||||
// Create the input
|
// Create the input
|
||||||
OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
|
OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
|
||||||
@ -88,7 +91,8 @@ obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, st
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind, obs_data_t *filterSettings)
|
obs_source_t *Utils::Obs::ActionHelper::CreateSourceFilter(obs_source_t *source, std::string filterName, std::string filterKind,
|
||||||
|
obs_data_t *filterSettings)
|
||||||
{
|
{
|
||||||
obs_source_t *filter = obs_source_create_private(filterKind.c_str(), filterName.c_str(), filterSettings);
|
obs_source_t *filter = obs_source_create_private(filterKind.c_str(), filterName.c_str(), filterSettings);
|
||||||
|
|
||||||
|
@ -60,13 +60,15 @@ std::vector<obs_hotkey_t *> Utils::Obs::ArrayHelper::GetHotkeyList()
|
|||||||
{
|
{
|
||||||
std::vector<obs_hotkey_t *> ret;
|
std::vector<obs_hotkey_t *> ret;
|
||||||
|
|
||||||
obs_enum_hotkeys([](void* data, obs_hotkey_id, obs_hotkey_t* hotkey) {
|
auto cb = [](void *data, obs_hotkey_id, obs_hotkey_t *hotkey) {
|
||||||
auto ret = static_cast<std::vector<obs_hotkey_t *> *>(data);
|
auto ret = static_cast<std::vector<obs_hotkey_t *> *>(data);
|
||||||
|
|
||||||
ret->push_back(hotkey);
|
ret->push_back(hotkey);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, &ret);
|
};
|
||||||
|
|
||||||
|
obs_enum_hotkeys(cb, &ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -132,17 +134,23 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene,
|
|||||||
std::pair<std::vector<json>, bool> enumData;
|
std::pair<std::vector<json>, bool> enumData;
|
||||||
enumData.second = basic;
|
enumData.second = basic;
|
||||||
|
|
||||||
obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) {
|
auto cb = [](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
|
||||||
auto enumData = static_cast<std::pair<std::vector<json>, bool> *>(param);
|
auto enumData = static_cast<std::pair<std::vector<json>, bool> *>(param);
|
||||||
|
|
||||||
|
// TODO: Make ObjectHelper util for scene items
|
||||||
|
|
||||||
json item;
|
json item;
|
||||||
item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
|
||||||
// Should be slightly faster than calling obs_sceneitem_get_order_position()
|
item["sceneItemIndex"] =
|
||||||
item["sceneItemIndex"] = enumData->first.size();
|
enumData->first.size(); // Should be slightly faster than calling obs_sceneitem_get_order_position()
|
||||||
if (!enumData->second) {
|
if (!enumData->second) {
|
||||||
|
item["sceneItemEnabled"] = obs_sceneitem_visible(sceneItem);
|
||||||
|
item["sceneItemLocked"] = obs_sceneitem_locked(sceneItem);
|
||||||
|
item["sceneItemTransform"] = ObjectHelper::GetSceneItemTransform(sceneItem);
|
||||||
|
item["sceneItemBlendMode"] = obs_sceneitem_get_blending_mode(sceneItem);
|
||||||
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
||||||
item["sourceName"] = obs_source_get_name(itemSource);
|
item["sourceName"] = obs_source_get_name(itemSource);
|
||||||
item["sourceType"] = StringHelper::GetSourceType(itemSource);
|
item["sourceType"] = obs_source_get_type(itemSource);
|
||||||
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
|
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
|
||||||
item["inputKind"] = obs_source_get_id(itemSource);
|
item["inputKind"] = obs_source_get_id(itemSource);
|
||||||
else
|
else
|
||||||
@ -156,7 +164,9 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene,
|
|||||||
enumData->first.push_back(item);
|
enumData->first.push_back(item);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, &enumData);
|
};
|
||||||
|
|
||||||
|
obs_scene_enum_items(scene, cb, &enumData);
|
||||||
|
|
||||||
return enumData.first;
|
return enumData.first;
|
||||||
}
|
}
|
||||||
@ -171,7 +181,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
|
|||||||
EnumInputInfo inputInfo;
|
EnumInputInfo inputInfo;
|
||||||
inputInfo.inputKind = inputKind;
|
inputInfo.inputKind = inputKind;
|
||||||
|
|
||||||
auto inputEnumProc = [](void *param, obs_source_t *input) {
|
auto cb = [](void *param, obs_source_t *input) {
|
||||||
// Sanity check in case the API changes
|
// Sanity check in case the API changes
|
||||||
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
|
||||||
return true;
|
return true;
|
||||||
@ -191,8 +201,9 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
|
|||||||
inputInfo->inputs.push_back(inputJson);
|
inputInfo->inputs.push_back(inputJson);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Actually enumerates only public inputs, despite the name
|
// Actually enumerates only public inputs, despite the name
|
||||||
obs_enum_sources(inputEnumProc, &inputInfo);
|
obs_enum_sources(cb, &inputInfo);
|
||||||
|
|
||||||
return inputInfo.inputs;
|
return inputInfo.inputs;
|
||||||
}
|
}
|
||||||
@ -297,7 +308,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *sou
|
|||||||
{
|
{
|
||||||
std::vector<json> filters;
|
std::vector<json> filters;
|
||||||
|
|
||||||
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *param) {
|
auto cb = [](obs_source_t *, obs_source_t *filter, void *param) {
|
||||||
auto filters = reinterpret_cast<std::vector<json> *>(param);
|
auto filters = reinterpret_cast<std::vector<json> *>(param);
|
||||||
|
|
||||||
json filterJson;
|
json filterJson;
|
||||||
@ -311,7 +322,40 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *sou
|
|||||||
|
|
||||||
filters->push_back(filterJson);
|
filters->push_back(filterJson);
|
||||||
};
|
};
|
||||||
obs_source_enum_filters(source, enumFilters, &filters);
|
|
||||||
|
obs_source_enum_filters(source, cb, &filters);
|
||||||
|
|
||||||
return filters;
|
return filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<json> Utils::Obs::ArrayHelper::GetOutputList()
|
||||||
|
{
|
||||||
|
std::vector<json> outputs;
|
||||||
|
|
||||||
|
auto cb = [](void *param, obs_output_t *output) {
|
||||||
|
auto outputs = reinterpret_cast<std::vector<json> *>(param);
|
||||||
|
|
||||||
|
auto rawFlags = obs_output_get_flags(output);
|
||||||
|
json flags;
|
||||||
|
flags["OBS_OUTPUT_AUDIO"] = !!(rawFlags & OBS_OUTPUT_AUDIO);
|
||||||
|
flags["OBS_OUTPUT_VIDEO"] = !!(rawFlags & OBS_OUTPUT_VIDEO);
|
||||||
|
flags["OBS_OUTPUT_ENCODED"] = !!(rawFlags & OBS_OUTPUT_ENCODED);
|
||||||
|
flags["OBS_OUTPUT_MULTI_TRACK"] = !!(rawFlags & OBS_OUTPUT_MULTI_TRACK);
|
||||||
|
flags["OBS_OUTPUT_SERVICE"] = !!(rawFlags & OBS_OUTPUT_SERVICE);
|
||||||
|
|
||||||
|
json outputJson;
|
||||||
|
outputJson["outputName"] = obs_output_get_name(output);
|
||||||
|
outputJson["outputKind"] = obs_output_get_id(output);
|
||||||
|
outputJson["outputWidth"] = obs_output_get_width(output);
|
||||||
|
outputJson["outputHeight"] = obs_output_get_height(output);
|
||||||
|
outputJson["outputActive"] = obs_output_active(output);
|
||||||
|
outputJson["outputFlags"] = flags;
|
||||||
|
|
||||||
|
outputs->push_back(outputJson);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
obs_enum_outputs(cb, &outputs);
|
||||||
|
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
obs-websocket
|
|
||||||
Copyright (C) 2020-2021 Kyle Manning <tt2468@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Obs.h"
|
|
||||||
#include "../plugin-macros.generated.h"
|
|
||||||
|
|
||||||
#define RET_COMPARE(str, x) if (str == #x) return x;
|
|
||||||
|
|
||||||
enum obs_bounds_type Utils::Obs::EnumHelper::GetSceneItemBoundsType(std::string boundsType)
|
|
||||||
{
|
|
||||||
RET_COMPARE(boundsType, OBS_BOUNDS_NONE)
|
|
||||||
RET_COMPARE(boundsType, OBS_BOUNDS_STRETCH)
|
|
||||||
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_INNER)
|
|
||||||
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_OUTER)
|
|
||||||
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_WIDTH)
|
|
||||||
RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_HEIGHT)
|
|
||||||
RET_COMPARE(boundsType, OBS_BOUNDS_MAX_ONLY)
|
|
||||||
|
|
||||||
return OBS_BOUNDS_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ObsMediaInputAction Utils::Obs::EnumHelper::GetMediaInputAction(std::string mediaAction)
|
|
||||||
{
|
|
||||||
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY)
|
|
||||||
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE)
|
|
||||||
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP)
|
|
||||||
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART)
|
|
||||||
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT)
|
|
||||||
RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS)
|
|
||||||
|
|
||||||
return OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum obs_blending_type Utils::Obs::EnumHelper::GetSceneItemBlendMode(std::string mode)
|
|
||||||
{
|
|
||||||
RET_COMPARE(mode, OBS_BLEND_NORMAL)
|
|
||||||
RET_COMPARE(mode, OBS_BLEND_ADDITIVE)
|
|
||||||
RET_COMPARE(mode, OBS_BLEND_SUBTRACT)
|
|
||||||
RET_COMPARE(mode, OBS_BLEND_SCREEN)
|
|
||||||
RET_COMPARE(mode, OBS_BLEND_MULTIPLY)
|
|
||||||
RET_COMPARE(mode, OBS_BLEND_LIGHTEN)
|
|
||||||
RET_COMPARE(mode, OBS_BLEND_DARKEN)
|
|
||||||
|
|
||||||
return OBS_BLEND_NORMAL;
|
|
||||||
}
|
|
@ -54,8 +54,8 @@ json Utils::Obs::ObjectHelper::GetSceneItemTransform(obs_sceneitem_t *item)
|
|||||||
obs_sceneitem_get_crop(item, &crop);
|
obs_sceneitem_get_crop(item, &crop);
|
||||||
|
|
||||||
OBSSource source = obs_sceneitem_get_source(item);
|
OBSSource source = obs_sceneitem_get_source(item);
|
||||||
float sourceWidth = float(obs_source_get_width(source));
|
float sourceWidth = (float)obs_source_get_width(source);
|
||||||
float sourceHeight = float(obs_source_get_height(source));
|
float sourceHeight = (float)obs_source_get_height(source);
|
||||||
|
|
||||||
ret["sourceWidth"] = sourceWidth;
|
ret["sourceWidth"] = sourceWidth;
|
||||||
ret["sourceHeight"] = sourceHeight;
|
ret["sourceHeight"] = sourceHeight;
|
||||||
@ -73,15 +73,15 @@ json Utils::Obs::ObjectHelper::GetSceneItemTransform(obs_sceneitem_t *item)
|
|||||||
|
|
||||||
ret["alignment"] = osi.alignment;
|
ret["alignment"] = osi.alignment;
|
||||||
|
|
||||||
ret["boundsType"] = StringHelper::GetSceneItemBoundsType(osi.bounds_type);
|
ret["boundsType"] = osi.bounds_type;
|
||||||
ret["boundsAlignment"] = osi.bounds_alignment;
|
ret["boundsAlignment"] = osi.bounds_alignment;
|
||||||
ret["boundsWidth"] = osi.bounds.x;
|
ret["boundsWidth"] = osi.bounds.x;
|
||||||
ret["boundsHeight"] = osi.bounds.y;
|
ret["boundsHeight"] = osi.bounds.y;
|
||||||
|
|
||||||
ret["cropLeft"] = int(crop.left);
|
ret["cropLeft"] = (int)crop.left;
|
||||||
ret["cropRight"] = int(crop.right);
|
ret["cropRight"] = (int)crop.right;
|
||||||
ret["cropTop"] = int(crop.top);
|
ret["cropTop"] = (int)crop.top;
|
||||||
ret["cropBottom"] = int(crop.bottom);
|
ret["cropBottom"] = (int)crop.bottom;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene
|
|||||||
enumData.name = name;
|
enumData.name = name;
|
||||||
enumData.offset = offset;
|
enumData.offset = offset;
|
||||||
|
|
||||||
obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) {
|
auto cb = [](obs_scene_t *, obs_sceneitem_t *sceneItem, void *param) {
|
||||||
auto enumData = static_cast<SceneItemSearchData *>(param);
|
auto enumData = static_cast<SceneItemSearchData *>(param);
|
||||||
|
|
||||||
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
|
||||||
@ -89,7 +89,9 @@ obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, &enumData);
|
};
|
||||||
|
|
||||||
|
obs_scene_enum_items(scene, cb, &enumData);
|
||||||
|
|
||||||
return enumData.ret;
|
return enumData.ret;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Obs.h"
|
#include "Obs.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
#define CASE(x) case x: return #x;
|
#define CASE(x) \
|
||||||
|
case x: \
|
||||||
|
return #x;
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetObsVersion()
|
std::string Utils::Obs::StringHelper::GetObsVersion()
|
||||||
{
|
{
|
||||||
@ -70,54 +72,27 @@ std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetSourceType(obs_source_t *source)
|
std::string Utils::Obs::StringHelper::GetLastRecordFileName()
|
||||||
{
|
{
|
||||||
obs_source_type sourceType = obs_source_get_type(source);
|
OBSOutputAutoRelease output = obs_frontend_get_recording_output();
|
||||||
|
if (!output)
|
||||||
|
return "";
|
||||||
|
|
||||||
switch (sourceType) {
|
OBSDataAutoRelease outputSettings = obs_output_get_settings(output);
|
||||||
default:
|
|
||||||
CASE(OBS_SOURCE_TYPE_INPUT)
|
obs_data_item_t *item = obs_data_item_byname(outputSettings, "url");
|
||||||
CASE(OBS_SOURCE_TYPE_FILTER)
|
if (!item) {
|
||||||
CASE(OBS_SOURCE_TYPE_TRANSITION)
|
item = obs_data_item_byname(outputSettings, "path");
|
||||||
CASE(OBS_SOURCE_TYPE_SCENE)
|
if (!item)
|
||||||
}
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetInputMonitorType(enum obs_monitoring_type monitorType)
|
std::string ret = obs_data_item_get_string(item);
|
||||||
{
|
obs_data_item_release(&item);
|
||||||
switch (monitorType) {
|
return ret;
|
||||||
default:
|
|
||||||
CASE(OBS_MONITORING_TYPE_NONE)
|
|
||||||
CASE(OBS_MONITORING_TYPE_MONITOR_ONLY)
|
|
||||||
CASE(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetInputMonitorType(obs_source_t *input)
|
std::string Utils::Obs::StringHelper::GetLastReplayBufferFileName()
|
||||||
{
|
|
||||||
obs_monitoring_type monitorType = obs_source_get_monitoring_type(input);
|
|
||||||
|
|
||||||
return GetInputMonitorType(monitorType);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetMediaInputState(obs_source_t *input)
|
|
||||||
{
|
|
||||||
obs_media_state mediaState = obs_source_media_get_state(input);
|
|
||||||
|
|
||||||
switch (mediaState) {
|
|
||||||
default:
|
|
||||||
CASE(OBS_MEDIA_STATE_NONE)
|
|
||||||
CASE(OBS_MEDIA_STATE_PLAYING)
|
|
||||||
CASE(OBS_MEDIA_STATE_OPENING)
|
|
||||||
CASE(OBS_MEDIA_STATE_BUFFERING)
|
|
||||||
CASE(OBS_MEDIA_STATE_PAUSED)
|
|
||||||
CASE(OBS_MEDIA_STATE_STOPPED)
|
|
||||||
CASE(OBS_MEDIA_STATE_ENDED)
|
|
||||||
CASE(OBS_MEDIA_STATE_ERROR)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetLastReplayBufferFilePath()
|
|
||||||
{
|
{
|
||||||
OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output();
|
OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output();
|
||||||
if (!output)
|
if (!output)
|
||||||
@ -135,34 +110,6 @@ std::string Utils::Obs::StringHelper::GetLastReplayBufferFilePath()
|
|||||||
return savedReplayPath;
|
return savedReplayPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
default:
|
|
||||||
CASE(OBS_BOUNDS_NONE)
|
|
||||||
CASE(OBS_BOUNDS_STRETCH)
|
|
||||||
CASE(OBS_BOUNDS_SCALE_INNER)
|
|
||||||
CASE(OBS_BOUNDS_SCALE_OUTER)
|
|
||||||
CASE(OBS_BOUNDS_SCALE_TO_WIDTH)
|
|
||||||
CASE(OBS_BOUNDS_SCALE_TO_HEIGHT)
|
|
||||||
CASE(OBS_BOUNDS_MAX_ONLY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetSceneItemBlendMode(enum obs_blending_type mode)
|
|
||||||
{
|
|
||||||
switch (mode) {
|
|
||||||
default:
|
|
||||||
CASE(OBS_BLEND_NORMAL)
|
|
||||||
CASE(OBS_BLEND_ADDITIVE)
|
|
||||||
CASE(OBS_BLEND_SUBTRACT)
|
|
||||||
CASE(OBS_BLEND_SCREEN)
|
|
||||||
CASE(OBS_BLEND_MULTIPLY)
|
|
||||||
CASE(OBS_BLEND_LIGHTEN)
|
|
||||||
CASE(OBS_BLEND_DARKEN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
|
std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
|
||||||
{
|
{
|
||||||
uint64_t secs = ms / 1000ULL;
|
uint64_t secs = ms / 1000ULL;
|
||||||
@ -173,20 +120,7 @@ std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
|
|||||||
uint64_t secsPart = secs % 60ULL;
|
uint64_t secsPart = secs % 60ULL;
|
||||||
uint64_t msPart = ms % 1000ULL;
|
uint64_t msPart = ms % 1000ULL;
|
||||||
|
|
||||||
QString formatted = QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
|
QString formatted =
|
||||||
|
QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
|
||||||
return formatted.toStdString();
|
return formatted.toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Obs::StringHelper::GetOutputState(ObsOutputState state)
|
|
||||||
{
|
|
||||||
switch (state) {
|
|
||||||
default:
|
|
||||||
CASE(OBS_WEBSOCKET_OUTPUT_UNKNOWN)
|
|
||||||
CASE(OBS_WEBSOCKET_OUTPUT_STARTING)
|
|
||||||
CASE(OBS_WEBSOCKET_OUTPUT_STARTED)
|
|
||||||
CASE(OBS_WEBSOCKET_OUTPUT_STOPPING)
|
|
||||||
CASE(OBS_WEBSOCKET_OUTPUT_STOPPED)
|
|
||||||
CASE(OBS_WEBSOCKET_OUTPUT_PAUSED)
|
|
||||||
CASE(OBS_WEBSOCKET_OUTPUT_RESUMED)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -26,8 +26,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "Obs_VolumeMeter_Helpers.h"
|
#include "Obs_VolumeMeter_Helpers.h"
|
||||||
#include "../obs-websocket.h"
|
#include "../obs-websocket.h"
|
||||||
|
|
||||||
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) :
|
Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input)
|
||||||
PeakMeterType(SAMPLE_PEAK_METER),
|
: PeakMeterType(SAMPLE_PEAK_METER),
|
||||||
_input(obs_source_get_weak_source(input)),
|
_input(obs_source_get_weak_source(input)),
|
||||||
_channels(0),
|
_channels(0),
|
||||||
_lastUpdate(0),
|
_lastUpdate(0),
|
||||||
@ -45,7 +45,8 @@ Utils::Obs::VolumeMeter::Meter::~Meter()
|
|||||||
{
|
{
|
||||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||||
if (!input) {
|
if (!input) {
|
||||||
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?");
|
blog(LOG_WARNING,
|
||||||
|
"[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +69,8 @@ json Utils::Obs::VolumeMeter::Meter::GetMeterData()
|
|||||||
|
|
||||||
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
OBSSourceAutoRelease input = obs_weak_source_get_source(_input);
|
||||||
if (!input) {
|
if (!input) {
|
||||||
blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
|
blog(LOG_WARNING,
|
||||||
|
"[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +214,8 @@ void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data, bool muted)
|
void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data,
|
||||||
|
bool muted)
|
||||||
{
|
{
|
||||||
auto c = static_cast<Meter *>(priv_data);
|
auto c = static_cast<Meter *>(priv_data);
|
||||||
|
|
||||||
@ -233,10 +236,8 @@ void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, callda
|
|||||||
c->_volume = (float)calldata_float(cd, "volume");
|
c->_volume = (float)calldata_float(cd, "volume");
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod) :
|
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod)
|
||||||
_updateCallback(cb),
|
: _updateCallback(cb), _updatePeriod(updatePeriod), _running(false)
|
||||||
_updatePeriod(updatePeriod),
|
|
||||||
_running(false)
|
|
||||||
{
|
{
|
||||||
signal_handler_t *sh = obs_get_signal_handler();
|
signal_handler_t *sh = obs_get_signal_handler();
|
||||||
if (!sh)
|
if (!sh)
|
||||||
|
@ -65,7 +65,8 @@ namespace Utils {
|
|||||||
void ProcessPeak(const struct audio_data *data);
|
void ProcessPeak(const struct audio_data *data);
|
||||||
void ProcessMagnitude(const struct audio_data *data);
|
void ProcessMagnitude(const struct audio_data *data);
|
||||||
|
|
||||||
static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source, const struct audio_data *data, bool muted);
|
static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source,
|
||||||
|
const struct audio_data *data, bool muted);
|
||||||
static void InputVolumeCallback(void *priv_data, calldata_t *cd);
|
static void InputVolumeCallback(void *priv_data, calldata_t *cd);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,8 +24,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#define SHIFT_RIGHT_2PS(msb, lsb) \
|
#define SHIFT_RIGHT_2PS(msb, lsb) \
|
||||||
{ \
|
{ \
|
||||||
__m128 tmp = \
|
__m128 tmp = _mm_shuffle_ps(lsb, msb, _MM_SHUFFLE(0, 0, 3, 3)); \
|
||||||
_mm_shuffle_ps(lsb, msb, _MM_SHUFFLE(0, 0, 3, 3)); \
|
|
||||||
lsb = _mm_shuffle_ps(lsb, tmp, _MM_SHUFFLE(2, 1, 2, 1)); \
|
lsb = _mm_shuffle_ps(lsb, tmp, _MM_SHUFFLE(2, 1, 2, 1)); \
|
||||||
msb = _mm_shuffle_ps(msb, msb, _MM_SHUFFLE(3, 3, 2, 1)); \
|
msb = _mm_shuffle_ps(msb, msb, _MM_SHUFFLE(3, 3, 2, 1)); \
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,8 @@ std::string Utils::Platform::GetLocalAddress()
|
|||||||
std::vector<std::pair<QString, uint8_t>> preferredAddresses;
|
std::vector<std::pair<QString, uint8_t>> preferredAddresses;
|
||||||
for (auto address : validAddresses) {
|
for (auto address : validAddresses) {
|
||||||
// Attribute a priority (0 is best) to the address to choose the best picks
|
// Attribute a priority (0 is best) to the address to choose the best picks
|
||||||
if (address.startsWith("192.168.1.") || address.startsWith("192.168.0.")) { // Prefer common consumer router network prefixes
|
if (address.startsWith("192.168.1.") ||
|
||||||
|
address.startsWith("192.168.0.")) { // Prefer common consumer router network prefixes
|
||||||
if (address.startsWith("192.168.56."))
|
if (address.startsWith("192.168.56."))
|
||||||
preferredAddresses.push_back(std::make_pair(address, 255)); // Ignore virtualbox default
|
preferredAddresses.push_back(std::make_pair(address, 255)); // Ignore virtualbox default
|
||||||
else
|
else
|
||||||
@ -68,29 +69,13 @@ std::string Utils::Platform::GetLocalAddress()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sort by priority
|
// Sort by priority
|
||||||
std::sort(preferredAddresses.begin(), preferredAddresses.end(), [=](std::pair<QString, uint8_t> a, std::pair<QString, uint8_t> b) {
|
std::sort(preferredAddresses.begin(), preferredAddresses.end(),
|
||||||
return a.second < b.second;
|
[=](std::pair<QString, uint8_t> a, std::pair<QString, uint8_t> b) { return a.second < b.second; });
|
||||||
});
|
|
||||||
|
|
||||||
// Return highest priority address
|
// Return highest priority address
|
||||||
return preferredAddresses[0].first.toStdString();
|
return preferredAddresses[0].first.toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Utils::Platform::GetLoopbackAddress(bool allowIpv6)
|
|
||||||
{
|
|
||||||
std::vector<QString> validAddresses;
|
|
||||||
for (auto address : QNetworkInterface::allAddresses()) {
|
|
||||||
if (address == QHostAddress::LocalHost)
|
|
||||||
return address.toString().toStdString();
|
|
||||||
else if (address == QHostAddress::LocalHostIPv6 && allowIpv6)
|
|
||||||
return address.toString().toStdString();
|
|
||||||
else if (address.isLoopback())
|
|
||||||
return address.toString().toStdString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Utils::Platform::GetCommandLineArgument(QString arg)
|
QString Utils::Platform::GetCommandLineArgument(QString arg)
|
||||||
{
|
{
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
@ -127,14 +112,17 @@ void Utils::Platform::SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QS
|
|||||||
|
|
||||||
SystemTrayNotification *notification = new SystemTrayNotification{icon, title, body};
|
SystemTrayNotification *notification = new SystemTrayNotification{icon, title, body};
|
||||||
|
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
obs_queue_task(
|
||||||
|
OBS_TASK_UI,
|
||||||
|
[](void *param) {
|
||||||
void *systemTrayPtr = obs_frontend_get_system_tray();
|
void *systemTrayPtr = obs_frontend_get_system_tray();
|
||||||
auto systemTray = static_cast<QSystemTrayIcon *>(systemTrayPtr);
|
auto systemTray = static_cast<QSystemTrayIcon *>(systemTrayPtr);
|
||||||
|
|
||||||
auto notification = static_cast<SystemTrayNotification *>(param);
|
auto notification = static_cast<SystemTrayNotification *>(param);
|
||||||
systemTray->showMessage(notification->title, notification->body, notification->icon);
|
systemTray->showMessage(notification->title, notification->body, notification->icon);
|
||||||
delete notification;
|
delete notification;
|
||||||
}, (void*)notification, false);
|
},
|
||||||
|
(void *)notification, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::Platform::GetTextFileContent(std::string fileName, std::string &content)
|
bool Utils::Platform::GetTextFileContent(std::string fileName, std::string &content)
|
||||||
|
@ -26,7 +26,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
namespace Utils {
|
namespace Utils {
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
std::string GetLocalAddress();
|
std::string GetLocalAddress();
|
||||||
std::string GetLoopbackAddress(bool allowIpv6 = true);
|
|
||||||
QString GetCommandLineArgument(QString arg);
|
QString GetCommandLineArgument(QString arg);
|
||||||
bool GetCommandLineFlagSet(QString arg);
|
bool GetCommandLineFlagSet(QString arg);
|
||||||
void SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QString title, QString body);
|
void SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QString title, QString body);
|
||||||
|
@ -31,9 +31,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../utils/Platform.h"
|
#include "../utils/Platform.h"
|
||||||
#include "../utils/Compat.h"
|
#include "../utils/Compat.h"
|
||||||
|
|
||||||
WebSocketServer::WebSocketServer() :
|
WebSocketServer::WebSocketServer() : QObject(nullptr), _sessions()
|
||||||
QObject(nullptr),
|
|
||||||
_sessions()
|
|
||||||
{
|
{
|
||||||
_server.get_alog().clear_channels(websocketpp::log::alevel::all);
|
_server.get_alog().clear_channels(websocketpp::log::alevel::all);
|
||||||
_server.get_elog().clear_channels(websocketpp::log::elevel::all);
|
_server.get_elog().clear_channels(websocketpp::log::elevel::all);
|
||||||
@ -44,38 +42,17 @@ WebSocketServer::WebSocketServer() :
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
_server.set_validate_handler(
|
_server.set_validate_handler(
|
||||||
websocketpp::lib::bind(
|
websocketpp::lib::bind(&WebSocketServer::onValidate, this, websocketpp::lib::placeholders::_1));
|
||||||
&WebSocketServer::onValidate, this, websocketpp::lib::placeholders::_1
|
_server.set_open_handler(websocketpp::lib::bind(&WebSocketServer::onOpen, this, websocketpp::lib::placeholders::_1));
|
||||||
)
|
_server.set_close_handler(websocketpp::lib::bind(&WebSocketServer::onClose, this, websocketpp::lib::placeholders::_1));
|
||||||
);
|
_server.set_message_handler(websocketpp::lib::bind(&WebSocketServer::onMessage, this, websocketpp::lib::placeholders::_1,
|
||||||
_server.set_open_handler(
|
websocketpp::lib::placeholders::_2));
|
||||||
websocketpp::lib::bind(
|
|
||||||
&WebSocketServer::onOpen, this, websocketpp::lib::placeholders::_1
|
|
||||||
)
|
|
||||||
);
|
|
||||||
_server.set_close_handler(
|
|
||||||
websocketpp::lib::bind(
|
|
||||||
&WebSocketServer::onClose, this, websocketpp::lib::placeholders::_1
|
|
||||||
)
|
|
||||||
);
|
|
||||||
_server.set_message_handler(
|
|
||||||
websocketpp::lib::bind(
|
|
||||||
&WebSocketServer::onMessage, this, websocketpp::lib::placeholders::_1, websocketpp::lib::placeholders::_2
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
auto eventHandler = GetEventHandler();
|
auto eventHandler = GetEventHandler();
|
||||||
eventHandler->SetBroadcastCallback(
|
eventHandler->SetBroadcastCallback(std::bind(&WebSocketServer::BroadcastEvent, this, std::placeholders::_1,
|
||||||
std::bind(
|
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||||
&WebSocketServer::BroadcastEvent, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
eventHandler->SetObsLoadedCallback(
|
eventHandler->SetObsLoadedCallback(std::bind(&WebSocketServer::onObsLoaded, this));
|
||||||
std::bind(
|
|
||||||
&WebSocketServer::onObsLoaded, this
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketServer::~WebSocketServer()
|
WebSocketServer::~WebSocketServer()
|
||||||
@ -118,7 +95,8 @@ void WebSocketServer::Start()
|
|||||||
// Set log levels if debug is enabled
|
// Set log levels if debug is enabled
|
||||||
if (IsDebugEnabled()) {
|
if (IsDebugEnabled()) {
|
||||||
_server.get_alog().set_channels(websocketpp::log::alevel::all);
|
_server.get_alog().set_channels(websocketpp::log::alevel::all);
|
||||||
_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.get_elog().set_channels(websocketpp::log::elevel::all);
|
_server.get_elog().set_channels(websocketpp::log::elevel::all);
|
||||||
_server.get_alog().clear_channels(websocketpp::log::elevel::devel | websocketpp::log::elevel::library);
|
_server.get_alog().clear_channels(websocketpp::log::elevel::devel | websocketpp::log::elevel::library);
|
||||||
} else {
|
} else {
|
||||||
@ -129,25 +107,17 @@ void WebSocketServer::Start()
|
|||||||
_server.reset();
|
_server.reset();
|
||||||
|
|
||||||
websocketpp::lib::error_code errorCode;
|
websocketpp::lib::error_code errorCode;
|
||||||
if (conf->BindLoopback) {
|
if (conf->Ipv4Only) {
|
||||||
std::string addr = Utils::Platform::GetLoopbackAddress(!conf->Ipv4Only);
|
blog(LOG_INFO, "[WebSocketServer::Start] Locked to IPv4 bindings");
|
||||||
if (addr.empty()) {
|
|
||||||
blog(LOG_ERROR, "[WebSocketServer::Start] Failed to find loopback interface. Server not started.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_server.listen(addr, std::to_string(conf->ServerPort), errorCode);
|
|
||||||
blog(LOG_INFO, "[WebSocketServer::Start] Locked to loopback interface.");
|
|
||||||
} else if (conf->Ipv4Only) {
|
|
||||||
_server.listen(websocketpp::lib::asio::ip::tcp::v4(), conf->ServerPort, errorCode);
|
_server.listen(websocketpp::lib::asio::ip::tcp::v4(), conf->ServerPort, errorCode);
|
||||||
blog(LOG_INFO, "[WebSocketServer::Start] Locked to IPv4 bindings.");
|
|
||||||
} else {
|
} else {
|
||||||
|
blog(LOG_INFO, "[WebSocketServer::Start] Not locked to IPv4 bindings");
|
||||||
_server.listen(conf->ServerPort, errorCode);
|
_server.listen(conf->ServerPort, errorCode);
|
||||||
blog(LOG_INFO, "[WebSocketServer::Start] Bound to all interfaces.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorCode) {
|
if (errorCode) {
|
||||||
std::string errorCodeMessage = errorCode.message();
|
std::string errorCodeMessage = errorCode.message();
|
||||||
blog(LOG_ERROR, "[WebSocketServer::Start] Listen failed: %s", errorCodeMessage.c_str());
|
blog(LOG_INFO, "[WebSocketServer::Start] Listen failed: %s", errorCodeMessage.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +125,8 @@ void WebSocketServer::Start()
|
|||||||
|
|
||||||
_serverThread = std::thread(&WebSocketServer::ServerRunner, this);
|
_serverThread = std::thread(&WebSocketServer::ServerRunner, this);
|
||||||
|
|
||||||
blog(LOG_INFO, "[WebSocketServer::Start] Server started successfully on port %d. Possible connect address: %s", conf->ServerPort.load(), Utils::Platform::GetLocalAddress().c_str());
|
blog(LOG_INFO, "[WebSocketServer::Start] Server started successfully on port %d. Possible connect address: %s",
|
||||||
|
conf->ServerPort.load(), Utils::Platform::GetLocalAddress().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::Stop()
|
void WebSocketServer::Stop()
|
||||||
@ -226,7 +197,8 @@ std::vector<WebSocketServer::WebSocketSessionState> WebSocketServer::GetWebSocke
|
|||||||
std::string remoteAddress = session->RemoteAddress();
|
std::string remoteAddress = session->RemoteAddress();
|
||||||
bool isIdentified = session->IsIdentified();
|
bool isIdentified = session->IsIdentified();
|
||||||
|
|
||||||
webSocketSessions.emplace_back(WebSocketSessionState{hdl, remoteAddress, connectedAt, incomingMessages, outgoingMessages, isIdentified});
|
webSocketSessions.emplace_back(
|
||||||
|
WebSocketSessionState{hdl, remoteAddress, connectedAt, incomingMessages, outgoingMessages, isIdentified});
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
@ -372,7 +344,8 @@ void WebSocketServer::onClose(websocketpp::connection_hdl hdl)
|
|||||||
emit ClientDisconnected(state, conn->get_local_close_code());
|
emit ClientDisconnected(state, conn->get_local_close_code());
|
||||||
|
|
||||||
// Log disconnection
|
// Log disconnection
|
||||||
blog(LOG_INFO, "[WebSocketServer::onClose] WebSocket client %s has disconnected", remoteAddress.c_str());
|
blog(LOG_INFO, "[WebSocketServer::onClose] WebSocket client `%s` has disconnected with code `%d` and reason: %s",
|
||||||
|
remoteAddress.c_str(), conn->get_local_close_code(), conn->get_local_close_reason().c_str());
|
||||||
|
|
||||||
// Get config for tray notification
|
// Get config for tray notification
|
||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
@ -384,12 +357,14 @@ void WebSocketServer::onClose(websocketpp::connection_hdl hdl)
|
|||||||
// If previously identified, not going away, and notifications enabled, send a tray notification
|
// If previously identified, not going away, and notifications enabled, send a tray notification
|
||||||
if (isIdentified && (conn->get_local_close_code() != websocketpp::close::status::going_away) && conf->AlertsEnabled) {
|
if (isIdentified && (conn->get_local_close_code() != websocketpp::close::status::going_away) && conf->AlertsEnabled) {
|
||||||
QString title = obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Title");
|
QString title = obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Title");
|
||||||
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Body")).arg(QString::fromStdString(remoteAddress));
|
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Disconnected.Body"))
|
||||||
|
.arg(QString::fromStdString(remoteAddress));
|
||||||
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
|
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::server<websocketpp::config::asio>::message_ptr message)
|
void WebSocketServer::onMessage(websocketpp::connection_hdl hdl,
|
||||||
|
websocketpp::server<websocketpp::config::asio>::message_ptr message)
|
||||||
{
|
{
|
||||||
auto opCode = message->get_opcode();
|
auto opCode = message->get_opcode();
|
||||||
std::string payload = message->get_payload();
|
std::string payload = message->get_payload();
|
||||||
@ -412,26 +387,32 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se
|
|||||||
uint8_t sessionEncoding = session->Encoding();
|
uint8_t sessionEncoding = session->Encoding();
|
||||||
if (sessionEncoding == WebSocketEncoding::Json) {
|
if (sessionEncoding == WebSocketEncoding::Json) {
|
||||||
if (opCode != websocketpp::frame::opcode::text) {
|
if (opCode != websocketpp::frame::opcode::text) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, "Your session encoding is set to Json, but a binary message was received.", errorCode);
|
_server.close(hdl, WebSocketCloseCode::MessageDecodeError,
|
||||||
|
"Your session encoding is set to Json, but a binary message was received.",
|
||||||
|
errorCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
incomingMessage = json::parse(payload);
|
incomingMessage = json::parse(payload);
|
||||||
} catch (json::parse_error &e) {
|
} catch (json::parse_error &e) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, std::string("Unable to decode Json: ") + e.what(), errorCode);
|
_server.close(hdl, WebSocketCloseCode::MessageDecodeError,
|
||||||
|
std::string("Unable to decode Json: ") + e.what(), errorCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (sessionEncoding == WebSocketEncoding::MsgPack) {
|
} else if (sessionEncoding == WebSocketEncoding::MsgPack) {
|
||||||
if (opCode != websocketpp::frame::opcode::binary) {
|
if (opCode != websocketpp::frame::opcode::binary) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, "Your session encoding is set to MsgPack, but a text message was received.", errorCode);
|
_server.close(hdl, WebSocketCloseCode::MessageDecodeError,
|
||||||
|
"Your session encoding is set to MsgPack, but a text message was received.",
|
||||||
|
errorCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
incomingMessage = json::from_msgpack(payload);
|
incomingMessage = json::from_msgpack(payload);
|
||||||
} catch (json::parse_error &e) {
|
} catch (json::parse_error &e) {
|
||||||
_server.close(hdl, WebSocketCloseCode::MessageDecodeError, std::string("Unable to decode MsgPack: ") + e.what(), errorCode);
|
_server.close(hdl, WebSocketCloseCode::MessageDecodeError,
|
||||||
|
std::string("Unable to decode MsgPack: ") + e.what(), errorCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -449,9 +430,11 @@ void WebSocketServer::onMessage(websocketpp::connection_hdl hdl, websocketpp::se
|
|||||||
|
|
||||||
// Disconnect client if 4.x protocol is detected
|
// Disconnect client if 4.x protocol is detected
|
||||||
if (!session->IsIdentified() && incomingMessage.contains("request-type")) {
|
if (!session->IsIdentified() && incomingMessage.contains("request-type")) {
|
||||||
blog(LOG_WARNING, "[WebSocketServer::onMessage] Client %s appears to be running a pre-5.0.0 protocol.", session->RemoteAddress().c_str());
|
blog(LOG_WARNING, "[WebSocketServer::onMessage] Client %s appears to be running a pre-5.0.0 protocol.",
|
||||||
|
session->RemoteAddress().c_str());
|
||||||
ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion;
|
ret.closeCode = WebSocketCloseCode::UnsupportedRpcVersion;
|
||||||
ret.closeReason = "You appear to be attempting to connect with the pre-5.0.0 plugin protocol. Check to make sure your client is updated.";
|
ret.closeReason =
|
||||||
|
"You appear to be attempting to connect with the pre-5.0.0 plugin protocol. Check to make sure your client is updated.";
|
||||||
goto skipProcessing;
|
goto skipProcessing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,7 +469,8 @@ skipProcessing:
|
|||||||
blog_debug("[WebSocketServer::onMessage] Outgoing message:\n%s", ret.result.dump(2).c_str());
|
blog_debug("[WebSocketServer::onMessage] Outgoing message:\n%s", ret.result.dump(2).c_str());
|
||||||
|
|
||||||
if (errorCode)
|
if (errorCode)
|
||||||
blog(LOG_WARNING, "[WebSocketServer::onMessage] Sending message to client failed: %s", errorCode.message().c_str());
|
blog(LOG_WARNING, "[WebSocketServer::onMessage] Sending message to client failed: %s",
|
||||||
|
errorCode.message().c_str());
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -34,15 +34,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../requesthandler/rpc/Request.h"
|
#include "../requesthandler/rpc/Request.h"
|
||||||
#include "../plugin-macros.generated.h"
|
#include "../plugin-macros.generated.h"
|
||||||
|
|
||||||
class WebSocketServer : QObject
|
class WebSocketServer : QObject {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum WebSocketEncoding {
|
enum WebSocketEncoding { Json, MsgPack };
|
||||||
Json,
|
|
||||||
MsgPack
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WebSocketSessionState {
|
struct WebSocketSessionState {
|
||||||
websocketpp::connection_hdl hdl;
|
websocketpp::connection_hdl hdl;
|
||||||
@ -59,17 +55,14 @@ class WebSocketServer : QObject
|
|||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
void InvalidateSession(websocketpp::connection_hdl hdl);
|
void InvalidateSession(websocketpp::connection_hdl hdl);
|
||||||
void BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData = nullptr, uint8_t rpcVersion = 0);
|
void BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData = nullptr,
|
||||||
|
uint8_t rpcVersion = 0);
|
||||||
|
|
||||||
bool IsListening() {
|
bool IsListening() { return _server.is_listening(); }
|
||||||
return _server.is_listening();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<WebSocketSessionState> GetWebSocketSessions();
|
std::vector<WebSocketSessionState> GetWebSocketSessions();
|
||||||
|
|
||||||
QThreadPool *GetThreadPool() {
|
QThreadPool *GetThreadPool() { return &_threadPool; }
|
||||||
return &_threadPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void ClientConnected(WebSocketSessionState state);
|
void ClientConnected(WebSocketSessionState state);
|
||||||
|
@ -44,10 +44,7 @@ static json ConstructRequestResult(RequestResult requestResult, const json &requ
|
|||||||
if (requestJson.contains("requestId") && !requestJson["requestId"].is_null())
|
if (requestJson.contains("requestId") && !requestJson["requestId"].is_null())
|
||||||
ret["requestId"] = requestJson["requestId"];
|
ret["requestId"] = requestJson["requestId"];
|
||||||
|
|
||||||
ret["requestStatus"] = {
|
ret["requestStatus"] = {{"result", requestResult.StatusCode == RequestStatus::Success}, {"code", requestResult.StatusCode}};
|
||||||
{"result", requestResult.StatusCode == RequestStatus::Success},
|
|
||||||
{"code", requestResult.StatusCode}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!requestResult.Comment.empty())
|
if (!requestResult.Comment.empty())
|
||||||
ret["requestStatus"]["comment"] = requestResult.Comment;
|
ret["requestStatus"]["comment"] = requestResult.Comment;
|
||||||
@ -70,7 +67,8 @@ void WebSocketServer::SetSessionParameters(SessionPtr session, ProcessResult &re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::ProcessResult &ret, WebSocketOpCode::WebSocketOpCode opCode, json &payloadData)
|
void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::ProcessResult &ret,
|
||||||
|
WebSocketOpCode::WebSocketOpCode opCode, json &payloadData)
|
||||||
{
|
{
|
||||||
if (!payloadData.is_object()) {
|
if (!payloadData.is_object()) {
|
||||||
if (payloadData.is_null()) {
|
if (payloadData.is_null()) {
|
||||||
@ -102,14 +100,18 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
if (session->AuthenticationRequired()) {
|
if (session->AuthenticationRequired()) {
|
||||||
if (!payloadData.contains("authentication")) {
|
if (!payloadData.contains("authentication")) {
|
||||||
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
|
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
|
||||||
ret.closeReason = "Your payload's data is missing an `authentication` string, however authentication is required.";
|
ret.closeReason =
|
||||||
|
"Your payload's data is missing an `authentication` string, however authentication is required.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!Utils::Crypto::CheckAuthenticationString(session->Secret(), session->Challenge(), payloadData["authentication"])) {
|
if (!Utils::Crypto::CheckAuthenticationString(session->Secret(), session->Challenge(),
|
||||||
|
payloadData["authentication"])) {
|
||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
if (conf && conf->AlertsEnabled) {
|
if (conf && conf->AlertsEnabled) {
|
||||||
QString title = obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Title");
|
QString title = obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Title");
|
||||||
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Body")).arg(QString::fromStdString(session->RemoteAddress()));
|
QString body =
|
||||||
|
QString(obs_module_text("OBSWebSocket.TrayNotification.AuthenticationFailed.Body"))
|
||||||
|
.arg(QString::fromStdString(session->RemoteAddress()));
|
||||||
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Warning, title, body);
|
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Warning, title, body);
|
||||||
}
|
}
|
||||||
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
|
ret.closeCode = WebSocketCloseCode::AuthenticationFailed;
|
||||||
@ -127,6 +129,7 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
if (!payloadData["rpcVersion"].is_number_unsigned()) {
|
if (!payloadData["rpcVersion"].is_number_unsigned()) {
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
||||||
ret.closeReason = "Your `rpcVersion` is not an unsigned number.";
|
ret.closeReason = "Your `rpcVersion` is not an unsigned number.";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t requestedRpcVersion = payloadData["rpcVersion"];
|
uint8_t requestedRpcVersion = payloadData["rpcVersion"];
|
||||||
@ -153,13 +156,15 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
auto conf = GetConfig();
|
auto conf = GetConfig();
|
||||||
if (conf && conf->AlertsEnabled) {
|
if (conf && conf->AlertsEnabled) {
|
||||||
QString title = obs_module_text("OBSWebSocket.TrayNotification.Identified.Title");
|
QString title = obs_module_text("OBSWebSocket.TrayNotification.Identified.Title");
|
||||||
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Identified.Body")).arg(QString::fromStdString(session->RemoteAddress()));
|
QString body = QString(obs_module_text("OBSWebSocket.TrayNotification.Identified.Body"))
|
||||||
|
.arg(QString::fromStdString(session->RemoteAddress()));
|
||||||
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
|
Utils::Platform::SendTrayNotification(QSystemTrayIcon::Information, title, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.result["op"] = WebSocketOpCode::Identified;
|
ret.result["op"] = WebSocketOpCode::Identified;
|
||||||
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
||||||
} return;
|
}
|
||||||
|
return;
|
||||||
case WebSocketOpCode::Reidentify: { // Reidentify
|
case WebSocketOpCode::Reidentify: { // Reidentify
|
||||||
std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
|
std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
|
||||||
|
|
||||||
@ -177,7 +182,8 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
|
|
||||||
ret.result["op"] = WebSocketOpCode::Identified;
|
ret.result["op"] = WebSocketOpCode::Identified;
|
||||||
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();
|
||||||
} return;
|
}
|
||||||
|
return;
|
||||||
case WebSocketOpCode::Request: { // Request
|
case WebSocketOpCode::Request: { // Request
|
||||||
// RequestID checking has to be done here where we are able to close the connection.
|
// RequestID checking has to be done here where we are able to close the connection.
|
||||||
if (!payloadData.contains("requestId")) {
|
if (!payloadData.contains("requestId")) {
|
||||||
@ -186,25 +192,39 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!payloadData.contains("requestType")) {
|
||||||
|
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
||||||
|
ret.closeReason = "Your payload's data is missing an `requestType`.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payloadData["requestType"].is_string()) {
|
||||||
|
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
||||||
|
ret.closeReason = "Your `requestType` is not a string.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
RequestHandler requestHandler(session);
|
RequestHandler requestHandler(session);
|
||||||
Request request(payloadData["requestType"], payloadData["requestData"]);
|
|
||||||
|
std::string requestType = payloadData["requestType"];
|
||||||
|
json requestData = payloadData["requestData"];
|
||||||
|
Request request(requestType, requestData);
|
||||||
|
|
||||||
RequestResult requestResult = requestHandler.ProcessRequest(request);
|
RequestResult requestResult = requestHandler.ProcessRequest(request);
|
||||||
|
|
||||||
json resultPayloadData;
|
json resultPayloadData;
|
||||||
resultPayloadData["requestType"] = payloadData["requestType"];
|
resultPayloadData["requestType"] = requestType;
|
||||||
resultPayloadData["requestId"] = payloadData["requestId"];
|
resultPayloadData["requestId"] = payloadData["requestId"];
|
||||||
resultPayloadData["requestStatus"] = {
|
resultPayloadData["requestStatus"] = {{"result", requestResult.StatusCode == RequestStatus::Success},
|
||||||
{"result", requestResult.StatusCode == RequestStatus::Success},
|
{"code", requestResult.StatusCode}};
|
||||||
{"code", requestResult.StatusCode}
|
|
||||||
};
|
|
||||||
if (!requestResult.Comment.empty())
|
if (!requestResult.Comment.empty())
|
||||||
resultPayloadData["requestStatus"]["comment"] = requestResult.Comment;
|
resultPayloadData["requestStatus"]["comment"] = requestResult.Comment;
|
||||||
if (requestResult.ResponseData.is_object())
|
if (requestResult.ResponseData.is_object())
|
||||||
resultPayloadData["responseData"] = requestResult.ResponseData;
|
resultPayloadData["responseData"] = requestResult.ResponseData;
|
||||||
ret.result["op"] = WebSocketOpCode::RequestResponse;
|
ret.result["op"] = WebSocketOpCode::RequestResponse;
|
||||||
ret.result["d"] = resultPayloadData;
|
ret.result["d"] = resultPayloadData;
|
||||||
} return;
|
}
|
||||||
|
return;
|
||||||
case WebSocketOpCode::RequestBatch: { // RequestBatch
|
case WebSocketOpCode::RequestBatch: { // RequestBatch
|
||||||
// RequestID checking has to be done here where we are able to close the connection.
|
// RequestID checking has to be done here where we are able to close the connection.
|
||||||
if (!payloadData.contains("requestId")) {
|
if (!payloadData.contains("requestId")) {
|
||||||
@ -213,18 +233,6 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!payloadData.contains("requests")) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
|
||||||
ret.closeReason = "Your payload data is missing a `requests`.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!payloadData["requests"].is_array()) {
|
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
|
||||||
ret.closeReason = "Your `requests` is not an array.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::SerialRealtime;
|
RequestBatchExecutionType::RequestBatchExecutionType executionType = RequestBatchExecutionType::SerialRealtime;
|
||||||
if (payloadData.contains("executionType") && !payloadData["executionType"].is_null()) {
|
if (payloadData.contains("executionType") && !payloadData["executionType"].is_null()) {
|
||||||
if (!payloadData["executionType"].is_number_unsigned()) {
|
if (!payloadData["executionType"].is_number_unsigned()) {
|
||||||
@ -234,7 +242,8 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
}
|
}
|
||||||
|
|
||||||
int8_t requestedExecutionType = payloadData["executionType"];
|
int8_t requestedExecutionType = payloadData["executionType"];
|
||||||
if (!RequestBatchExecutionType::IsValid(requestedExecutionType) || requestedExecutionType == RequestBatchExecutionType::None) {
|
if (!RequestBatchExecutionType::IsValid(requestedExecutionType) ||
|
||||||
|
requestedExecutionType == RequestBatchExecutionType::None) {
|
||||||
ret.closeCode = WebSocketCloseCode::InvalidDataFieldValue;
|
ret.closeCode = WebSocketCloseCode::InvalidDataFieldValue;
|
||||||
ret.closeReason = "Your `executionType` has an invalid value.";
|
ret.closeReason = "Your `executionType` has an invalid value.";
|
||||||
return;
|
return;
|
||||||
@ -243,7 +252,8 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
// The thread pool must support 2 or more threads else parallel requests will deadlock.
|
// The thread pool must support 2 or more threads else parallel requests will deadlock.
|
||||||
if (requestedExecutionType == RequestBatchExecutionType::Parallel && _threadPool.maxThreadCount() < 2) {
|
if (requestedExecutionType == RequestBatchExecutionType::Parallel && _threadPool.maxThreadCount() < 2) {
|
||||||
ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
|
ret.closeCode = WebSocketCloseCode::UnsupportedFeature;
|
||||||
ret.closeReason = "Parallel request batch processing is not available on this system due to limited core count.";
|
ret.closeReason =
|
||||||
|
"Parallel request batch processing is not available on this system due to limited core count.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,13 +285,34 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
haltOnFailure = payloadData["haltOnFailure"];
|
haltOnFailure = payloadData["haltOnFailure"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!payloadData.contains("requests")) {
|
||||||
|
ret.closeCode = WebSocketCloseCode::MissingDataField;
|
||||||
|
ret.closeReason = "Your payload data is missing a `requests`.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payloadData["requests"].is_array()) {
|
||||||
|
ret.closeCode = WebSocketCloseCode::InvalidDataFieldType;
|
||||||
|
ret.closeReason = "Your `requests` is not an array.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<json> requests = payloadData["requests"];
|
std::vector<json> requests = payloadData["requests"];
|
||||||
|
|
||||||
std::vector<RequestBatchRequest> requestsVector;
|
std::vector<RequestBatchRequest> requestsVector;
|
||||||
for (auto &requestJson : requests)
|
for (auto &requestJson : requests) {
|
||||||
requestsVector.emplace_back(requestJson["requestType"], requestJson["requestData"], executionType, requestJson["inputVariables"], requestJson["outputVariables"]);
|
if (!requestJson["requestType"].is_string())
|
||||||
|
requestJson["requestType"] =
|
||||||
|
""; // Workaround for what would otherwise be extensive additional logic for a rare edge case
|
||||||
|
std::string requestType = requestJson["requestType"];
|
||||||
|
json requestData = requestJson["requestData"];
|
||||||
|
json inputVariables = requestJson["inputVariables"];
|
||||||
|
json outputVariables = requestJson["outputVariables"];
|
||||||
|
requestsVector.emplace_back(requestType, requestData, executionType, inputVariables, outputVariables);
|
||||||
|
}
|
||||||
|
|
||||||
auto resultsVector = RequestBatchHandler::ProcessRequestBatch(_threadPool, session, executionType, requestsVector, payloadData["variables"], haltOnFailure);
|
auto resultsVector = RequestBatchHandler::ProcessRequestBatch(_threadPool, session, executionType, requestsVector,
|
||||||
|
payloadData["variables"], haltOnFailure);
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
std::vector<json> results;
|
std::vector<json> results;
|
||||||
@ -293,7 +324,8 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
ret.result["op"] = WebSocketOpCode::RequestBatchResponse;
|
ret.result["op"] = WebSocketOpCode::RequestBatchResponse;
|
||||||
ret.result["d"]["requestId"] = payloadData["requestId"];
|
ret.result["d"]["requestId"] = payloadData["requestId"];
|
||||||
ret.result["d"]["results"] = results;
|
ret.result["d"]["results"] = results;
|
||||||
} return;
|
}
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
ret.closeCode = WebSocketCloseCode::UnknownOpCode;
|
ret.closeCode = WebSocketCloseCode::UnknownOpCode;
|
||||||
ret.closeReason = std::string("Unknown OpCode: ") + std::to_string(opCode);
|
ret.closeReason = std::string("Unknown OpCode: ") + std::to_string(opCode);
|
||||||
@ -302,7 +334,8 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
|
|||||||
}
|
}
|
||||||
|
|
||||||
// It isn't consistent to directly call the WebSocketServer from the events system, but it would also be dumb to make it unnecessarily complicated.
|
// It isn't consistent to directly call the WebSocketServer from the events system, but it would also be dumb to make it unnecessarily complicated.
|
||||||
void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData, uint8_t rpcVersion)
|
void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData,
|
||||||
|
uint8_t rpcVersion)
|
||||||
{
|
{
|
||||||
if (!_server.is_listening())
|
if (!_server.is_listening())
|
||||||
return;
|
return;
|
||||||
@ -336,7 +369,8 @@ void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string
|
|||||||
if (messageJson.empty()) {
|
if (messageJson.empty()) {
|
||||||
messageJson = eventMessage.dump();
|
messageJson = eventMessage.dump();
|
||||||
}
|
}
|
||||||
_server.send((websocketpp::connection_hdl)it.first, messageJson, websocketpp::frame::opcode::text, errorCode);
|
_server.send((websocketpp::connection_hdl)it.first, messageJson,
|
||||||
|
websocketpp::frame::opcode::text, errorCode);
|
||||||
it.second->IncrementOutgoingMessages();
|
it.second->IncrementOutgoingMessages();
|
||||||
break;
|
break;
|
||||||
case WebSocketEncoding::MsgPack:
|
case WebSocketEncoding::MsgPack:
|
||||||
@ -344,12 +378,14 @@ void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, const std::string
|
|||||||
auto msgPackData = json::to_msgpack(eventMessage);
|
auto msgPackData = json::to_msgpack(eventMessage);
|
||||||
messageMsgPack = std::string(msgPackData.begin(), msgPackData.end());
|
messageMsgPack = std::string(msgPackData.begin(), msgPackData.end());
|
||||||
}
|
}
|
||||||
_server.send((websocketpp::connection_hdl)it.first, messageMsgPack, websocketpp::frame::opcode::binary, errorCode);
|
_server.send((websocketpp::connection_hdl)it.first, messageMsgPack,
|
||||||
|
websocketpp::frame::opcode::binary, errorCode);
|
||||||
it.second->IncrementOutgoingMessages();
|
it.second->IncrementOutgoingMessages();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (errorCode)
|
if (errorCode)
|
||||||
blog(LOG_ERROR, "[WebSocketServer::BroadcastEvent] Error sending event message: %s", errorCode.message().c_str());
|
blog(LOG_ERROR, "[WebSocketServer::BroadcastEvent] Error sending event message: %s",
|
||||||
|
errorCode.message().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
@ -20,8 +20,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "WebSocketSession.h"
|
#include "WebSocketSession.h"
|
||||||
#include "../../eventhandler/types/EventSubscription.h"
|
#include "../../eventhandler/types/EventSubscription.h"
|
||||||
|
|
||||||
WebSocketSession::WebSocketSession() :
|
WebSocketSession::WebSocketSession()
|
||||||
_remoteAddress(""),
|
: _remoteAddress(""),
|
||||||
_connectedAt(0),
|
_connectedAt(0),
|
||||||
_incomingMessages(0),
|
_incomingMessages(0),
|
||||||
_outgoingMessages(0),
|
_outgoingMessages(0),
|
||||||
|
@ -29,8 +29,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
class WebSocketSession;
|
class WebSocketSession;
|
||||||
typedef std::shared_ptr<WebSocketSession> SessionPtr;
|
typedef std::shared_ptr<WebSocketSession> SessionPtr;
|
||||||
|
|
||||||
class WebSocketSession
|
class WebSocketSession {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
WebSocketSession();
|
WebSocketSession();
|
||||||
|
|
||||||
|
@ -122,8 +122,5 @@ namespace WebSocketOpCode {
|
|||||||
RequestBatchResponse = 9,
|
RequestBatchResponse = 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool IsValid(uint8_t opCode)
|
inline bool IsValid(uint8_t opCode) { return opCode >= Hello && opCode <= RequestBatchResponse; }
|
||||||
{
|
|
||||||
return opCode >= Hello && opCode <= RequestBatchResponse;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user