Compare commits

...

78 Commits

Author SHA1 Message Date
PatTheMav
faf0866f1b cmake: Downgrade deprecation error for MSVC compilations
Error has already been downgraded for Clang, AppleClang, and GCC.
2024-08-01 14:24:00 -07:00
PatTheMav
228afd3405 cmake: Downgrade deprecation error for GCC compilations
Error has already been downgraded for Clang and AppleClang, not doing
so for GCC was an oversight.
2024-08-01 10:26:50 -07:00
tt2468
0548c7798a base: Update version to 5.5.2
Bug Fixes:
- Fix an issue where the virtualcam requests would report that the
virtualcam is not available.
- Fix an issue with the config migration where the migrated settings
were not being persisted to disk.
2024-07-18 12:45:53 -07:00
tt2468
6c9fd55c63 config: Always write config when migrating
Fixes an issue where OBS 30.1.2 migrations would work on the first
30.2.0 load, but the settings would not persist to disk for further
loads.
2024-07-17 22:58:54 -07:00
Translation Updater
7e3f2a82f0 Update translations from Crowdin 2024-07-17 09:34:11 +00:00
tt2468
65396e1db7 requesthandler: Use existence of virtualcam output to test availability
An upstream commit removed the `vcamEnabled` private data field from
being set, so we need to use a new method now.
2024-07-16 11:44:02 -07:00
tt2468
f8bc7c4f59 base: Update version to 5.5.1
Enhancements:
- Updated translation strings

Bug Fixes:
- Fixed a potential crash with the migration on systems set to
non-english languages
2024-06-11 15:41:13 -07:00
derrod
9e48274617 Config: Ensure conversion to filesystem::path uses utf-8 2024-06-11 13:43:10 -07:00
Translation Updater
3b7c1c5381 Update translations from Crowdin 2024-06-07 09:47:56 +00:00
tt2468
20551043f9 base: Update version to 5.5.0
New Features:
- Added `CreateRecordChapter` request for the new native MP4 muxer
[tt2468]
- Added `SplitRecordFile` request to create a file split [tt2468]
- Added `RecordFileChanged` request for when the current recording is
split [tt2468]
- Added obs-websocket-api as a Cmake target, allowing plugins to build
against it without directly including the header file in their source
tree. [tytan652]
- Added the ability to subscribe to obs-websocket events via the
obs-websocket-api header file. [tt2468]

Enhancements:
- Added `cropToBounds` boolean value to Get/SetSceneItemTransform
[exeldro]

Bug Fixes:
- Fixed screenshot behavior of sources with a crop filter not
respecting the cropped size (#1132) [tt2468]
- Fixed an issue with `TriggerHotkeyByName` not releasing keys
correctly when multiple keys are specified. [exeldro]

Other Notes:
- Fixed a few enums showing as deprecated in the documentation
- The location of the obs-websocket global settings data has changed!
Settings located in `global.ini` have moved to the
`plugin_config/obs-websocket` directory. This includes the `global`
realm for the `*PersistentData` requests. Upon loading with an
un-migrated configuration, obs-websocket will perform a migration and
delete the old configurations.
As such, **migration is not reversible**
2024-06-07 01:30:46 -07:00
Github Actions
086bf06008 docs(ci): Update generated docs - 6483dca [skip ci] 2024-06-07 08:29:25 +00:00
tt2468
6483dcaef0 requesthandler: Add CreateRecordChapter
The new `Hybrid MP4 [BETA]` output added in OBS adds support for
writing chapter markers to the file.
2024-06-07 01:29:10 -07:00
tt2468
71920c484b eventhandler: Add RecordFileChanged event
When a file split happens, this will fire with the new file name
2024-06-07 01:29:10 -07:00
tt2468
0eda8f9406 requesthandler: Add SplitRecordFile request 2024-06-07 01:29:10 -07:00
Exeldro
3b873ceb30 requesthandler: Fix releasing hotkeys triggered by name 2024-06-06 00:04:39 -07:00
Exeldro
36f50adf8a requesthandler: Add cropToBounds to scene item 2024-06-06 00:03:26 -07:00
Github Actions
acd1af12a1 docs(ci): Update generated docs - eb28825 [skip ci] 2024-06-06 06:53:33 +00:00
tt2468
eb2882515f docs: Fix some enums showing up as deprecated
Closes #1141
2024-06-05 23:50:48 -07:00
tt2468
5c3c4c76c8 requesthandler: Fix resolution of screenshots of cropped sources
This applies the same fix found in obsproject/obs-studio#10077 to get
the target source's real width and height, not the width and height
values from the pre-filter stage.

Closes #1213
2024-06-05 23:34:04 -07:00
tt2468
8c80e0745a Config: Fix plugin startup for fresh installs
The commit to migrate data from global.ini to the plugin_config folder
accidentally broke plugin startup for fresh configurations. Instead of
returning early if no configuration is found, simply generate a new one
from defaults.

Closes #1225
2024-06-05 23:26:36 -07:00
tt2468
5b4aa9dabd WebSocketApi: Implement backend for obs-websocket event listening 2024-04-23 01:50:51 -07:00
tt2468
ee283c7141 lib: Implement obs-websocket event callback access
This allows plugins to listen directly for obs-websocket events.
2024-04-23 01:50:11 -07:00
tt2468
179e197bd5 base: Many random fixups preparing for WebSocketApi event callbacks 2024-04-23 00:28:00 -07:00
tt2468
5fc39ef054 base: Apply latest clang-format changes from upstream
Minus, some customizations, of course
2024-04-22 23:44:04 -07:00
tt2468
74719ce502 base: Move some direct crosstalk to callback system 2024-04-22 23:35:16 -07:00
tt2468
9123879c76 Config: Use std::string for ServerPassword instead of QString
Less Qt leeching into things is better.
2024-04-22 22:50:10 -07:00
tt2468
9db7464faa base: Use std::make_shared when allocating classes
Follows c++ recommendations.
2024-04-22 22:42:55 -07:00
tt2468
e2b8a06d94 requesthandler: Use new global realm path in persistent data requests
The `MigratePersistentData()` function handles migrating persistent
data on module load, and will fail if the data cannot be migrated.
2024-04-22 22:36:12 -07:00
tt2468
af31f1adca Config: Migrate config/persistent data to plugin_config directory
This commit moves the Config value storage from `global.ini` to a new
`config.json` file in the `plugin_config/obs-websocket` directory. This
comes after some internal discussion about plugins not using the
`plugin_config` directory, and that obs-websocket was offending.

Settings are currently stored as a JSON object, and field names have
been changed from using PascalCase to snake_case, to better align
with how JSON is stored elsewhere in OBS.
2024-04-22 22:30:44 -07:00
tt2468
4410e30684 utils: Pass fileName by value in GetModuleConfigPath()
Lots of `const char *` values, preventing usage of passing by reference
2024-04-22 22:29:21 -07:00
tt2468
2c884ca690 utils: Make SetJsonFileContent() create directories by default 2024-04-22 22:28:45 -07:00
tt2468
f72f23a9d7 utils: Minor code fixups 2024-04-22 18:40:50 -07:00
tt2468
a589e80bdb utils: Implement helper to get current module config path 2024-04-22 18:38:19 -07:00
tt2468
305afd763d utils: Remove old *AutoRelease definitions
Now that OBS has been out with the upstream AutoRelease definitions,
and obs-websocket is also in-tree-only, these are no longer necessary.
2024-04-22 18:32:51 -07:00
tt2468
42e7eb6c34 utils: Remove text file Get/Set methods from Platform
No longer needed, and using Qt isn't good anyway
2024-04-22 18:23:39 -07:00
tt2468
bdf812dc09 utils: Reimplement Get/SetJsonFileContent helpers
Uses <fstream> instead of the text helpers
2024-04-22 18:22:27 -07:00
tytan652
c8cf2d94ac cmake,lib,base: Export obs-websocket-api as a target
This enables the installation of the header in the include directory
2024-03-30 17:04:37 -07:00
Translation Updater
d2d4bfb3e7 Update translations from Crowdin 2024-03-12 18:11:05 +00:00
Lain
d5077fca03 base: Update to version 5.4.2
Bug Fixes:
- Fixes version update to use both legacy and main CMake files
2024-02-21 11:21:17 -06:00
Lain
4a647c5262 base: Update to version 5.4.1
Bug Fixes:
- Updated scene item transform API to latest version to prevent
  deprecation warnings (obs_sceneitem_set_info2 and
  obs_sceneitem_get_info2)
2024-02-20 21:44:33 -06:00
Exeldro
3ea3d3228b requesthandler: Update scene item transform API
Updates:
obs_sceneitem_get_info to obs_sceneitem_get_info2
obs_sceneitem_set_info to obs_sceneitem_set_info2

Ensures that we're using the latest versions of these functions in order
to prevent future deprecation
2024-02-20 21:44:33 -06:00
Ryan Foster
e94f9194a2 CI: Update first-party GitHub Actions to v4
GitHub Actions has deprecated actions based on node16. The v4 actions
are based on node20. Replace first-party v2/v3 actions with their v4
counterparts.

GitHub Actions has deprecated actions based on node12 and forces them to
run on node16, which is also deprecated. Update to v4 actions to avoid
warnings on CI.

See:
https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/
https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/
2024-01-30 16:25:38 -08:00
Translation Updater
9ee6e2ff2a Update translations from Crowdin 2024-01-29 20:47:37 +00:00
tt2468
b61a5c2431 base: Update version to 5.4.0
New Features:
- Added `GetSourceFilterKindList` request
- Added `GetSceneItemSource` request
- Added `InputSettingsChanged` event
- Added `SourceFilterSettingsChanged` event
- Added UUID support to Sources (Inputs/Scenes), Inputs, Scenes,
Transitions
  - The `Source` requests/events use `sourceUuid`
  - The `Input` requests/events use `inputUuid`
  - The `Scene` requests/events use `sceneUuid`
  - The `Transition` requests/events use `transitionUuid`
  - Filters do not have support for UUIDs at this time.

Enhancements:
- Added `contextName` field to `TriggerHotkeyByName` (exeldro)

Bug Fixes:
- Fixed a crash on shutdown with notifications enabled (r1ch)
- Added safety check to prevent `null` `outputCongestion` values in
`GetStreamStatus`
- Fixed a memory leak when switching service kinds via
`SetStreamServiceSettings`

Other Notes:
- Documented missing `outputActive` `ToggleRecord` response field
- Added a few new client softwares to the README list
- Removed a mis-documented `imageData` field
- Added a note to the hotkey requests that they are as-is and we
will not provide support for them
2024-01-24 15:51:27 -08:00
Github Actions
fbd4cfb4af docs(ci): Update generated docs - e5aa4c2 [skip ci] 2024-01-19 03:35:08 +00:00
tt2468
e5aa4c2f69 requesthandler: Rename GetSceneItemSourceName to GetSceneItemSource
Not an API break, this was introduced just a few commits ago.
2024-01-18 19:34:51 -08:00
Github Actions
4cf8de8382 docs(ci): Update generated docs - 7adfb58 [skip ci] 2024-01-19 03:30:20 +00:00
tt2468
7adfb5874c requesthandler: Implement input, scene, and transition UUID support
Transition UUID support is partial due to the current state of the OBS
frontend API.

Most requests which accepted things like `sourceName` now allow
`sourceUuid` (or equivalent) to be specified instead. While both fields
on the various requests may be marked as optional, at least one field
will still be required.
2024-01-18 19:27:57 -08:00
tt2468
f18f46543b eventhandler: Implement input, scene, and transition UUID support
Adds `inputUuid` next to `inputName` etc.
2024-01-18 16:58:40 -08:00
tt2468
f40426efa1 utils: Implement input, scene, and transition UUID support 2024-01-18 16:56:20 -08:00
tt2468
830f7eb931 utils: Use BPtr for strings instead of manual bfree() 2024-01-18 16:17:48 -08:00
Github Actions
5e3fff78f3 docs(ci): Update generated docs - b53527c [skip ci] 2024-01-19 00:06:25 +00:00
tt2468
b53527cba8 requesthandler: Add note about as-is status of hotkey requests
Too much trouble for too little gain. Please stop using these requests.
2024-01-18 16:06:07 -08:00
Github Actions
b806a0cfb1 docs(ci): Update generated docs - 1d0db34 [skip ci] 2024-01-19 00:02:16 +00:00
tt2468
1d0db34bb2 requesthandler: Add GetSceneItemSourceName request
Closes #1122
2024-01-18 16:01:41 -08:00
Github Actions
690726d281 docs(ci): Update generated docs - ef4142f [skip ci] 2024-01-18 23:54:27 +00:00
tt2468
ef4142fe75 eventhandler: Add SourceFilterSettingsChanged event
Closes #1059
2024-01-18 15:54:08 -08:00
gxalpha
caaec5d97f requesthandler: Use Frontend API for CreateSceneCollection 2024-01-18 15:43:46 -08:00
Norihiro Kamae
6c67b276a7 lib: Fix compile errors on C and warnings on C++ 2024-01-16 01:39:27 -08:00
Github Actions
fad7dfd55c docs(ci): Update generated docs - a5c459b [skip ci] 2024-01-16 09:37:45 +00:00
aki-954315
a5c459b6d4 docs: Fix SaveSourceScreenshot's Response Field 2024-01-16 01:37:29 -08:00
Tim Yuen
899a9b3801 README: Bump Godot client library to 4.0.x 2024-01-16 01:36:49 -08:00
tt2468
939e503736
README: Add MATRIC to client software list
Closes #1177
2024-01-16 01:35:11 -08:00
Norihiro Kamae
5b149add99 RequestHandler: Fix memory leak when setting streaming service 2024-01-16 01:33:29 -08:00
jdudetv
50b57d38d0 README: Add Macrograph to Client Software 2024-01-16 01:23:59 -08:00
Github Actions
9b58dd1627 docs(ci): Update generated docs - 81b307e [skip ci] 2024-01-16 08:41:23 +00:00
tt2468
81b307e5ad eventhandler: Add InputSettingsChanged
Fired when an input's settings change, like via the properties dialog
or via `SetInputSettings`.

Closes #1157
2024-01-16 00:41:03 -08:00
Github Actions
52733ddce7 docs(ci): Update generated docs - 444caeb [skip ci] 2024-01-16 08:19:11 +00:00
tt2468
444caeb1d7 requesthandler: Add GetSourceFilterKindList
Closes #1198
2024-01-16 00:18:52 -08:00
Github Actions
f03e82c3f8 docs(ci): Update generated docs - bbdc5bc [skip ci] 2024-01-16 07:46:34 +00:00
tt2468
bbdc5bc823 requesthandler: Prevent NaN outputCongestion values (null) 2024-01-15 23:45:33 -08:00
tt2468
9ecc9532e8 requesthandler: Document ToggleRecord outputActive response field 2024-01-15 23:44:50 -08:00
Richard Stanway
0189c3a3f5 Utils: Check system tray exists before trying to use it
Fixes https://github.com/obsproject/obs-studio/issues/9991
2024-01-15 23:19:09 -08:00
Github Actions
f48fcc06ec docs(ci): Update generated docs - f43ef8e [skip ci] 2024-01-09 07:06:29 +00:00
Exeldro
f43ef8e2da requesthandler: Add optional context to TriggerHotkeyByName 2024-01-08 23:06:12 -08:00
Ryan Foster
7a1c71bb96 base: Update version to 5.3.5
- CMake update, no functional changes
2023-12-22 16:38:27 -05:00
PatTheMav
cf285b3761 cmake: Update formatting and switch to native find_package call for Qt6 2023-12-20 15:09:11 -05:00
Translation Updater
ede66a68cb Update translations from Crowdin 2023-12-05 22:04:30 +00:00
79 changed files with 3033 additions and 952 deletions

View File

@ -1,6 +1,6 @@
# please use clang-format version 8 or later
# please use clang-format version 16 or later
Standard: Cpp11
Standard: c++17
AccessModifierOffset: -8
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
@ -8,14 +8,14 @@ AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
#AllowAllArgumentsOnNextLine: false # requires clang-format 9
#AllowAllConstructorInitializersOnNextLine: false # requires clang-format 9
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
#AllowShortLambdasOnASingleLine: Inline # requires clang-format 9
AllowShortLambdasOnASingleLine: Inline
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
@ -53,10 +53,11 @@ Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
FixNamespaceComments: false
ForEachMacros:
ForEachMacros:
- 'json_object_foreach'
- 'json_object_foreach_safe'
- 'json_array_foreach'
- 'HASH_ITER'
IncludeBlocks: Preserve
IndentCaseLabels: false
IndentPPDirectives: None
@ -65,7 +66,7 @@ IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
#ObjCBinPackProtocolList: Auto # requires clang-format 7
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
@ -83,13 +84,13 @@ ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
#SpaceAfterLogicalNot: false # requires clang-format 9
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
#SpaceBeforeCtorInitializerColon: true # requires clang-format 7
#SpaceBeforeInheritanceColon: true # requires clang-format 7
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
#SpaceBeforeRangeBasedForLoopColon: true # requires clang-format 7
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
@ -97,11 +98,111 @@ SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
#StatementMacros: # requires clang-format 8
# - 'Q_OBJECT'
StatementMacros:
- 'Q_OBJECT'
TabWidth: 8
#TypenameMacros: # requires clang-format 9
# - 'DARRAY'
TypenameMacros:
- 'DARRAY'
UseTab: ForContinuationAndIndentation
---
Language: ObjC
AccessModifierOffset: 2
AlignArrayOfStructures: Right
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AllowShortBlocksOnASingleLine: Never
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: None
AttributeMacros: ['__unused', '__autoreleasing', '_Nonnull', '__bridge']
BitFieldColonSpacing: Both
#BreakBeforeBraces: Webkit
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: true
BreakAfterAttributes: Never
BreakArrays: false
BreakBeforeConceptDeclarations: Allowed
BreakBeforeInlineASMColon: OnlyMultiline
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterComma
ColumnLimit: 120
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExternBlock: Indent
IndentGotoLabels: false
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: true
InsertBraces: false
InsertNewlineAtEOF: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: false
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PPIndentWidth: -1
PackConstructorInitializers: NextLine
QualifierAlignment: Leave
ReferenceAlignment: Right
RemoveSemicolon: false
RequiresClausePosition: WithPreceding
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Always
ShortNamespaceLines: 1
SortIncludes: false
#SortUsingDeclarations: LexicographicNumeric
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInConditionalStatement: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
Standard: c++17
TabWidth: 4
UseTab: Never

View File

@ -11,7 +11,7 @@ jobs:
if: github.repository_owner == 'obsproject'
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 100
- name: Upload US English Language Files 🇺🇸

View File

@ -18,7 +18,7 @@ jobs:
IS_CI: "true"
steps:
- name: 'Checkout'
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
path: ${{ github.workspace }}/obs-websocket
- name: 'Generate docs'

View File

@ -15,7 +15,7 @@ jobs:
if: contains(github.event.head_commit.message, '[skip ci]') != true
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Generate docs
run: cd docs && ./build_docs.sh
- name: Run markdownlint-cli

View File

@ -2,9 +2,11 @@ cmake_minimum_required(VERSION 3.16...3.25)
legacy_check()
set(obs-websocket_VERSION 5.3.4)
set(obs-websocket_VERSION 5.5.2)
set(OBS_WEBSOCKET_RPC_VERSION 1)
include(cmake/obs-websocket-api.cmake)
option(ENABLE_WEBSOCKET "Enable building OBS with websocket plugin" ON)
if(NOT ENABLE_WEBSOCKET)
target_disable(obs-websocket)
@ -12,7 +14,7 @@ if(NOT ENABLE_WEBSOCKET)
endif()
# Find Qt
find_qt(COMPONENTS Core Widgets Svg Network)
find_package(Qt6 REQUIRED Core Widgets Svg Network)
# Find nlohmann JSON
find_package(nlohmann_json 3 REQUIRED)
@ -31,51 +33,52 @@ find_package(Asio 1.12.1 REQUIRED)
add_library(obs-websocket MODULE)
add_library(OBS::websocket ALIAS obs-websocket)
target_sources(obs-websocket PRIVATE)
target_sources(
obs-websocket
PRIVATE src/obs-websocket.cpp
src/obs-websocket.h
PRIVATE # cmake-format: sortable
src/Config.cpp
src/Config.h
lib/obs-websocket-api.h
src/forms/ConnectInfo.cpp
src/forms/ConnectInfo.h
src/forms/resources.qrc
src/forms/SettingsDialog.cpp
src/forms/SettingsDialog.h
src/obs-websocket.cpp
src/obs-websocket.h
src/WebSocketApi.cpp
src/WebSocketApi.h)
target_sources(
obs-websocket
PRIVATE src/websocketserver/WebSocketServer.cpp
src/websocketserver/WebSocketServer_Protocol.cpp
src/websocketserver/WebSocketServer.h
PRIVATE # cmake-format: sortable
src/websocketserver/rpc/WebSocketSession.h
src/websocketserver/types/WebSocketCloseCode.h
src/websocketserver/types/WebSocketOpCode.h)
src/websocketserver/types/WebSocketOpCode.h
src/websocketserver/WebSocketServer.cpp
src/websocketserver/WebSocketServer.h
src/websocketserver/WebSocketServer_Protocol.cpp)
target_sources(
obs-websocket
PRIVATE src/eventhandler/EventHandler.cpp
PRIVATE # cmake-format: sortable
src/eventhandler/EventHandler.cpp
src/eventhandler/EventHandler.h
src/eventhandler/EventHandler_Config.cpp
src/eventhandler/EventHandler_General.cpp
src/eventhandler/EventHandler_Filters.cpp
src/eventhandler/EventHandler_General.cpp
src/eventhandler/EventHandler_Inputs.cpp
src/eventhandler/EventHandler_MediaInputs.cpp
src/eventhandler/EventHandler_Outputs.cpp
src/eventhandler/EventHandler_Scenes.cpp
src/eventhandler/EventHandler_SceneItems.cpp
src/eventhandler/EventHandler_Scenes.cpp
src/eventhandler/EventHandler_Transitions.cpp
src/eventhandler/EventHandler_Ui.cpp
src/eventhandler/types/EventSubscription.h)
target_sources(
obs-websocket
PRIVATE src/requesthandler/RequestBatchHandler.cpp
PRIVATE # cmake-format: sortable
src/requesthandler/RequestBatchHandler.cpp
src/requesthandler/RequestBatchHandler.h
src/requesthandler/RequestHandler.cpp
src/requesthandler/RequestHandler.h
@ -85,10 +88,10 @@ target_sources(
src/requesthandler/RequestHandler_Inputs.cpp
src/requesthandler/RequestHandler_MediaInputs.cpp
src/requesthandler/RequestHandler_Outputs.cpp
src/requesthandler/RequestHandler_Sources.cpp
src/requesthandler/RequestHandler_Record.cpp
src/requesthandler/RequestHandler_Scenes.cpp
src/requesthandler/RequestHandler_SceneItems.cpp
src/requesthandler/RequestHandler_Scenes.cpp
src/requesthandler/RequestHandler_Sources.cpp
src/requesthandler/RequestHandler_Stream.cpp
src/requesthandler/RequestHandler_Transitions.cpp
src/requesthandler/RequestHandler_Ui.cpp
@ -98,12 +101,13 @@ target_sources(
src/requesthandler/rpc/RequestBatchRequest.h
src/requesthandler/rpc/RequestResult.cpp
src/requesthandler/rpc/RequestResult.h
src/requesthandler/types/RequestStatus.h
src/requesthandler/types/RequestBatchExecutionType.h)
src/requesthandler/types/RequestBatchExecutionType.h
src/requesthandler/types/RequestStatus.h)
target_sources(
obs-websocket
PRIVATE src/utils/Compat.cpp
PRIVATE # cmake-format: sortable
src/utils/Compat.cpp
src/utils/Compat.h
src/utils/Crypto.cpp
src/utils/Crypto.h
@ -115,8 +119,8 @@ target_sources(
src/utils/Obs_ArrayHelper.cpp
src/utils/Obs_NumberHelper.cpp
src/utils/Obs_ObjectHelper.cpp
src/utils/Obs_StringHelper.cpp
src/utils/Obs_SearchHelper.cpp
src/utils/Obs_StringHelper.cpp
src/utils/Obs_VolumeMeter.cpp
src/utils/Obs_VolumeMeter.h
src/utils/Obs_VolumeMeter_Helpers.h
@ -133,19 +137,27 @@ target_compile_definitions(
target_compile_options(
obs-websocket
PRIVATE
$<$<PLATFORM_ID:Windows>:/wd4267>
$<$<PLATFORM_ID:Windows>:/wd4996>
$<$<PLATFORM_ID:Darwin,Linux,FreeBSD>:-Wall>
$<$<COMPILE_LANG_AND_ID:CXX,GNU,AppleClang,Clang>:-Wno-error=float-conversion;-Wno-error=shadow>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wno-error=format-overflow;-Wno-error=int-conversion;-Wno-error=comment>
$<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:-Wno-error=null-pointer-subtraction;-Wno-error=deprecated-declarations;-Wno-error=implicit-int-conversion;-Wno-error=shorten-64-to-32;-Wno-comma;-Wno-quoted-include-in-framework-header>
)
PRIVATE $<$<PLATFORM_ID:Windows>:/wd4267>
$<$<PLATFORM_ID:Windows>:/wd4996>
$<$<COMPILE_LANG_AND_ID:CXX,GNU,AppleClang,Clang>:-Wall>
$<$<COMPILE_LANG_AND_ID:CXX,GNU,AppleClang,Clang>:-Wno-error=float-conversion>
$<$<COMPILE_LANG_AND_ID:CXX,GNU,AppleClang,Clang>:-Wno-error=shadow>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wno-error=format-overflow>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wno-error=int-conversion>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wno-error=comment>
$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wno-error=deprecated-declarations>
$<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:-Wno-error=null-pointer-subtraction>
$<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:-Wno-error=deprecated-declarations>
$<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:-Wno-error=implicit-int-conversion>
$<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:-Wno-error=shorten-64-to-32>
$<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:-Wno-comma>
$<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:-Wno-quoted-include-in-framework-header>)
target_link_libraries(
obs-websocket
PRIVATE OBS::libobs
OBS::frontend-api
OBS::websocket-api
Qt::Core
Qt::Widgets
Qt::Svg
@ -155,6 +167,8 @@ target_link_libraries(
Asio::Asio
qrcodegencpp::qrcodegencpp)
target_link_options(obs-websocket PRIVATE $<$<PLATFORM_ID:Windows>:/IGNORE:4099>)
set_target_properties_obs(
obs-websocket
PROPERTIES FOLDER plugins
@ -168,6 +182,4 @@ if(OS_WINDOWS)
TARGET obs-websocket
APPEND
PROPERTY AUTORCC_OPTIONS --format-version 1)
target_link_options(obs-websocket PRIVATE /IGNORE:4099)
endif()

View File

@ -41,6 +41,8 @@ It is **highly recommended** to keep obs-websocket protected with a password aga
- [Aitum](https://aitum.tv/)
- [Kruiz Control](https://github.com/Kruiser8/Kruiz-Control)
- [Bitfocus Companion Module](https://bitfocus.io/companion/)
- [MacroGraph](https://github.com/Brendonovich/macrograph) - hosted client [here](https://macrograph.brendonovich.dev/)
- [MATRIC](https://matricapp.com/)
### Client libraries (for developers)
@ -49,7 +51,7 @@ Here's a list of available language APIs for obs-websocket:
- Python 3.7+ (Asyncio): [simpleobsws](https://github.com/IRLToolkit/simpleobsws/tree/master) by IRLToolkit
- Python 3.10+ (Non-Asyncio): [obsws-python](https://pypi.org/project/obsws-python) by aatikturk and onyx-and-iris
- Rust: [obws](https://github.com/dnaka91/obws) by dnaka91
- Godot 3.4.x: [obs-websocket-gd](https://github.com/you-win/obs-websocket-gd) by you-win
- Godot 4.0.x: [obs-websocket-gd](https://github.com/you-win/obs-websocket-gd) by you-win
- Javascript (Node and web): [obs-websocket-js](https://github.com/obs-websocket-community-projects/obs-websocket-js) by OBS Websocket Community
- C (uses obs-websocket-js): [v8-libwebsocket-obs-websocket](https://github.com/dgatwood/v8-libwebsocket-obs-websocket)
- Go: [goobs](https://github.com/andreykaipov/goobs) by andreykaipov

View File

@ -1,8 +1,23 @@
project(obs-websocket VERSION 5.3.4)
project(obs-websocket VERSION 5.5.2)
set(OBS_WEBSOCKET_RPC_VERSION 1)
option(ENABLE_WEBSOCKET "Enable building OBS with websocket plugin" ON)
add_library(obs-websocket-api INTERFACE)
add_library(OBS::websocket-api ALIAS obs-websocket-api)
target_sources(obs-websocket-api INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/obs-websocket-api.h>
$<INSTALL_INTERFACE:${OBS_INCLUDE_DESTINATION}/obs-websocket-api.h>)
target_link_libraries(obs-websocket-api INTERFACE OBS::libobs)
target_include_directories(obs-websocket-api INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib>
$<INSTALL_INTERFACE:${OBS_INCLUDE_DESTINATION}>)
set_target_properties(obs-websocket-api PROPERTIES PUBLIC_HEADER lib/obs-websocket-api.h)
export_target(obs-websocket-api)
if(NOT ENABLE_WEBSOCKET OR NOT ENABLE_UI)
message(STATUS "OBS: DISABLED obs-websocket")
return()
@ -56,7 +71,6 @@ target_sources(
src/obs-websocket.h
src/Config.cpp
src/Config.h
lib/obs-websocket-api.h
src/forms/SettingsDialog.cpp
src/forms/SettingsDialog.h
src/forms/ConnectInfo.cpp
@ -133,6 +147,7 @@ target_link_libraries(
obs-websocket
PRIVATE OBS::libobs
OBS::frontend-api
OBS::websocket-api
Qt::Core
Qt::Widgets
Qt::Svg

View File

@ -0,0 +1,14 @@
add_library(obs-websocket-api INTERFACE)
add_library(OBS::websocket-api ALIAS obs-websocket-api)
target_sources(obs-websocket-api INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib/obs-websocket-api.h>
$<INSTALL_INTERFACE:${OBS_INCLUDE_DESTINATION}/obs-websocket-api.h>)
target_link_libraries(obs-websocket-api INTERFACE OBS::libobs)
target_include_directories(obs-websocket-api INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib>"
"$<INSTALL_INTERFACE:${OBS_INCLUDE_DESTINATION}>")
set_target_properties(obs-websocket-api PROPERTIES PREFIX "" PUBLIC_HEADER lib/obs-websocket-api.h)
target_export(obs-websocket-api)

View File

@ -0,0 +1,8 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(libobs REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake")
check_required_components("@PROJECT_NAME@")

View File

@ -10,6 +10,7 @@ OBSWebSocket.Settings.ShowConnectInfoWarningTitle="Waarskuwing: Tans regstreeks"
OBSWebSocket.Settings.ShowConnectInfoWarningMessage="Dit lyk of n afvoer (stroom, opname, ens.) tans aktief is."
OBSWebSocket.Settings.ShowConnectInfoWarningInfoText="Is u seker u wil u verbindingsinligting laat sien?"
OBSWebSocket.Settings.Save.UserPasswordWarningTitle="Waarskuwing: potensiële beveiligingsprobleem"
OBSWebSocket.Settings.Save.UserPasswordWarningMessage="obs-websok bewaar die bedienerwagwoord as platteks. Dit word ten sterkste aanbeveel om n wagwoord wat deur obs-websok geskep is te gebruik."
OBSWebSocket.Settings.Save.UserPasswordWarningInfoText="Is u seker u wil u eie wagwoord gebruik?"
OBSWebSocket.Settings.Save.PasswordInvalidErrorTitle="Fout: Ongeldige opstalling"
OBSWebSocket.Settings.Save.PasswordInvalidErrorMessage="U moet n wagwoord van meet as 6 karakters gebruik."
@ -18,6 +19,9 @@ OBSWebSocket.SessionTable.RemoteAddressColumnTitle="Afstandsadres"
OBSWebSocket.SessionTable.SessionDurationColumnTitle="Sessieduur"
OBSWebSocket.SessionTable.MessagesInOutColumnTitle="Boodskappe In/Uit"
OBSWebSocket.SessionTable.IdentifiedTitle="Geïdentifiseer"
OBSWebSocket.SessionTable.KickButtonColumnTitle="Verwyder?"
OBSWebSocket.SessionTable.KickButtonText="Verwyder"
OBSWebSocket.ConnectInfo.DialogTitle="WebSocket-verbindingsinligting"
OBSWebSocket.ConnectInfo.CopyText="Kopieer"
OBSWebSocket.ConnectInfo.ServerIp="Bediener-IP (beste skatting)"
OBSWebSocket.ConnectInfo.ServerPort="Bedienerpoort"

41
data/locale/be-BY.ini Normal file
View File

@ -0,0 +1,41 @@
OBSWebSocket.Plugin.Description="Аддаленае кіраванне OBS Studio праз WebSocket"
OBSWebSocket.Settings.DialogTitle="Налады сервера WebSocket"
OBSWebSocket.Settings.PluginSettingsTitle="Налады плагіна"
OBSWebSocket.Settings.ServerEnable="Уключыць сервер WebSocket"
OBSWebSocket.Settings.AlertsEnable="Уключыць апавяшчэнні ў вобласці апавяшчэнняў"
OBSWebSocket.Settings.DebugEnable="Уключыць журнал адладкі"
OBSWebSocket.Settings.DebugEnableHoverText="Уключае журнал адладкі толькі для бягучага сеансу OBS. Пасля перазапуску будзе выключана.\nКаб праграма запускалася з уключанай наладай, выкарыстайце параметр --websocket_debug"
OBSWebSocket.Settings.ServerSettingsTitle="Налады сервера"
OBSWebSocket.Settings.AuthRequired="Уключыць аўтэнтыфікацыю"
OBSWebSocket.Settings.Password="Пароль сервера"
OBSWebSocket.Settings.GeneratePassword="Згенераваць"
OBSWebSocket.Settings.ServerPort="Порт сервера"
OBSWebSocket.Settings.ShowConnectInfo="Паказаць звесткі пра злучэнне"
OBSWebSocket.Settings.ShowConnectInfoWarningTitle="Увага: ідзе трансляцыя"
OBSWebSocket.Settings.ShowConnectInfoWarningMessage="Выглядае, што ў бягучы момант ідзе вывад (стрым, запіс і г. д.)."
OBSWebSocket.Settings.ShowConnectInfoWarningInfoText="Ці вы ўпэўненыя, што хочаце паказаць вашы звесткі пра злучэнне?"
OBSWebSocket.Settings.Save.UserPasswordWarningTitle="Увага: магчымая небяспека"
OBSWebSocket.Settings.Save.UserPasswordWarningMessage="obs-websocket захоўвае пароль сервера ў выглядзе звычайнага тэксту. Настойліва рэкамендуецца выкарыстоўваць пароль, які згенеруе obs-websocket."
OBSWebSocket.Settings.Save.UserPasswordWarningInfoText="Ці вы ўпэўненыя, што хочаце карыстацца сваім паролем?"
OBSWebSocket.Settings.Save.PasswordInvalidErrorTitle="Увага: памылковая канфігурацыя"
OBSWebSocket.Settings.Save.PasswordInvalidErrorMessage="Пароль павінен утрымліваць 6 або больш сімвалаў."
OBSWebSocket.SessionTable.Title="Злучаныя сеансы WebSocket"
OBSWebSocket.SessionTable.RemoteAddressColumnTitle="Аддалены адрас"
OBSWebSocket.SessionTable.SessionDurationColumnTitle="Даўжыня сеансу"
OBSWebSocket.SessionTable.MessagesInOutColumnTitle="Паведамленні I/O"
OBSWebSocket.SessionTable.IdentifiedTitle="Ідэнтыфікавана"
OBSWebSocket.SessionTable.KickButtonColumnTitle="Выгнаць?"
OBSWebSocket.SessionTable.KickButtonText="Выгнаць"
OBSWebSocket.ConnectInfo.DialogTitle="Звесткі пра злучэнне WebSocket"
OBSWebSocket.ConnectInfo.CopyText="Скапіяваць"
OBSWebSocket.ConnectInfo.ServerIp="IP сервера (найлепшая здагадка)"
OBSWebSocket.ConnectInfo.ServerPort="Порт сервера"
OBSWebSocket.ConnectInfo.ServerPassword="Пароль сервера"
OBSWebSocket.ConnectInfo.ServerPasswordPlaceholderText="[аўтэнтыфікацыя адкл.]"
OBSWebSocket.ConnectInfo.QrTitle="QR-код злучэння"
OBSWebSocket.TrayNotification.Identified.Title="Новае злучэнне WebSocket"
OBSWebSocket.TrayNotification.Identified.Body="Кліент %1 ідэнтыфікаваны."
OBSWebSocket.TrayNotification.AuthenticationFailed.Title="Збой аўтэнтыфікацыі WebSocket"
OBSWebSocket.TrayNotification.AuthenticationFailed.Body="Кліент %1 не прайшоў аўтэнтыфікацыю."
OBSWebSocket.TrayNotification.Disconnected.Title="Кліент WebSocket адключаны"
OBSWebSocket.TrayNotification.Disconnected.Body="Кліент %1 адключаны."

View File

@ -1,4 +1,5 @@
OBSWebSocket.Plugin.Description="Fjernstyring af OBS Studio via WebSocket"
OBSWebSocket.Settings.DialogTitle="WebSocket-serverindstillinger"
OBSWebSocket.Settings.PluginSettingsTitle="Plugin-indstillinger"
OBSWebSocket.Settings.ServerEnable="Aktivér WebSocket-server"
OBSWebSocket.Settings.AlertsEnable="Aktivér Systembakke Alarmer"

View File

@ -16,9 +16,9 @@ OBSWebSocket.Settings.ShowConnectInfoWarningMessage="Il semble qu'une sortie (st
OBSWebSocket.Settings.ShowConnectInfoWarningInfoText="Êtes-vous sûr de vouloir afficher vos informations de connexion ?"
OBSWebSocket.Settings.Save.UserPasswordWarningTitle="Avertissement : Problème potentiel de sécurité"
OBSWebSocket.Settings.Save.UserPasswordWarningMessage="obs-websocket enregistre le mot de passe du serveur en texte brut. L'utilisation d'un mot de passe généré par obs-websocket est fortement recommandée."
OBSWebSocket.Settings.Save.UserPasswordWarningInfoText="Êtes-vous sûr de vouloir utiliser votre propre mot de passe ?"
OBSWebSocket.Settings.Save.UserPasswordWarningInfoText="Êtes-vous sûr(e) de vouloir utiliser votre propre mot de passe ?"
OBSWebSocket.Settings.Save.PasswordInvalidErrorTitle="Erreur : Configuration invalide"
OBSWebSocket.Settings.Save.PasswordInvalidErrorMessage="Vous devez utiliser un mot de passe d'au moins 6 caractères"
OBSWebSocket.Settings.Save.PasswordInvalidErrorMessage="Vous devez utiliser un mot de passe de 6 caractères ou plus."
OBSWebSocket.SessionTable.Title="Sessions WebSocket connectées"
OBSWebSocket.SessionTable.RemoteAddressColumnTitle="Adresse distante"
OBSWebSocket.SessionTable.SessionDurationColumnTitle="Durée de session"
@ -30,7 +30,7 @@ OBSWebSocket.ConnectInfo.DialogTitle="Informations de connexion WebSocket"
OBSWebSocket.ConnectInfo.CopyText="Copier"
OBSWebSocket.ConnectInfo.ServerIp="IP du serveur (meilleure estimation)"
OBSWebSocket.ConnectInfo.ServerPort="Port serveur"
OBSWebSocket.ConnectInfo.ServerPassword="Mot de passe serveur"
OBSWebSocket.ConnectInfo.ServerPassword="Mot de passe du serveur"
OBSWebSocket.ConnectInfo.ServerPasswordPlaceholderText="[Authentification désactivée]"
OBSWebSocket.ConnectInfo.QrTitle="QR code de connexion"
OBSWebSocket.TrayNotification.Identified.Title="Nouvelle connexion WebSocket"

View File

@ -8,7 +8,7 @@ OBSWebSocket.Settings.DebugEnableHoverText="Aktifkan pencatatan awakutu untuk pe
OBSWebSocket.Settings.ServerSettingsTitle="Pengaturan Server"
OBSWebSocket.Settings.AuthRequired="Aktifkan Autentikasi"
OBSWebSocket.Settings.Password="Kata Sandi Server"
OBSWebSocket.Settings.GeneratePassword="Ciptakan Kata Sandi"
OBSWebSocket.Settings.GeneratePassword="Buat Kata Sandi"
OBSWebSocket.Settings.ServerPort="Port Server"
OBSWebSocket.Settings.ShowConnectInfo="Tampilkan Informasi Koneksi"
OBSWebSocket.Settings.ShowConnectInfoWarningTitle="Peringatan: Saat Ini Siaran Langsung"
@ -16,7 +16,7 @@ OBSWebSocket.Settings.ShowConnectInfoWarningMessage="Sepertinya sebuah output (s
OBSWebSocket.Settings.ShowConnectInfoWarningInfoText="Anda yakin ingin melihat informasi koneksi Anda?"
OBSWebSocket.Settings.Save.UserPasswordWarningTitle="Peringatan: Potensi Masalah Keamanan"
OBSWebSocket.Settings.Save.UserPasswordWarningMessage="obs-websocket menyimpan kata sandi server sebagai teks biasa. Sangat disarankan untuk menggunakan kata sandi yang diciptakan oleh obs-websocket."
OBSWebSocket.Settings.Save.UserPasswordWarningInfoText="Anda yakin ingin menggunakan kata sandi Anda sendiri?"
OBSWebSocket.Settings.Save.UserPasswordWarningInfoText="Apakah Anda yakin ingin menggunakan kata sandi sendiri?"
OBSWebSocket.Settings.Save.PasswordInvalidErrorTitle="Galat: Konfigurasi Tidak Berlaku"
OBSWebSocket.Settings.Save.PasswordInvalidErrorMessage="Anda harus menggunakan kata sandi yang minimal 6 karakter atau lebih."
OBSWebSocket.SessionTable.Title="Sesi WebSocket yang Terhubung"

View File

@ -1,4 +1,5 @@
OBSWebSocket.Plugin.Description="Kawalan-jauh OBS Studio melalui WebSocket"
OBSWebSocket.Settings.DialogTitle="Tetapan Pelayan WebSocket"
OBSWebSocket.Settings.PluginSettingsTitle="Tetapan Pemalam"
OBSWebSocket.Settings.ServerEnable="Benarkan pelayan WebSocket"
OBSWebSocket.Settings.AlertsEnable="Benarkan Amaran Talam Sistem"

View File

@ -1,2 +1,25 @@
OBSWebSocket.Settings.DialogTitle="WebSocket-tjenerinnstillinger"
OBSWebSocket.Settings.PluginSettingsTitle="Utvidelsesinnstillinger"
OBSWebSocket.Settings.ServerSettingsTitle="Tjenerinnstillinger"
OBSWebSocket.Settings.AuthRequired="Skru på autentisering"
OBSWebSocket.Settings.Password="Server Passord"
OBSWebSocket.Settings.GeneratePassword="Generer Passord"
OBSWebSocket.Settings.ShowConnectInfo="Vis tilkoblingsinfo"
OBSWebSocket.Settings.ShowConnectInfoWarningTitle="Advarsel: For øyeblikket på direktesending"
OBSWebSocket.Settings.Save.UserPasswordWarningTitle="Advarsel: Potensielt sikkerhetsproblem"
OBSWebSocket.Settings.Save.PasswordInvalidErrorTitle="Feil: Ugyldig konfigurasjon"
OBSWebSocket.Settings.Save.PasswordInvalidErrorMessage="Du må bruke et passord på minst 6 tegn."
OBSWebSocket.SessionTable.RemoteAddressColumnTitle="Ekstern adresse"
OBSWebSocket.SessionTable.SessionDurationColumnTitle="Øktens varighet"
OBSWebSocket.SessionTable.MessagesInOutColumnTitle="Innboks/Utboks"
OBSWebSocket.SessionTable.IdentifiedTitle="Identifisert"
OBSWebSocket.ConnectInfo.DialogTitle="WebSocket-tilkoblingsinfo"
OBSWebSocket.ConnectInfo.CopyText="Kopier"
OBSWebSocket.ConnectInfo.ServerIp="Tjenerens IP (beste gjetning)"
OBSWebSocket.ConnectInfo.ServerPort="Tjenerport"
OBSWebSocket.ConnectInfo.ServerPassword="Tjenerpassord"
OBSWebSocket.ConnectInfo.QrTitle="QR-tilkobling"
OBSWebSocket.TrayNotification.Identified.Title="Ny WebSocket-tilkobling"
OBSWebSocket.TrayNotification.Identified.Body="Klient %1 er identifisert."
OBSWebSocket.TrayNotification.Disconnected.Title="WebSocket-klient koblet fra"
OBSWebSocket.TrayNotification.Disconnected.Body="Klient %1 koblet fra."

View File

@ -18,7 +18,7 @@ OBSWebSocket.Settings.Save.UserPasswordWarningTitle="Ostrzeżenie: Potencjalny p
OBSWebSocket.Settings.Save.UserPasswordWarningMessage="obs-websocket przechowuje hasło serwera jako zwykły tekst. Wysoce zalecane jest użycie hasła generowanego przez obs-websocket."
OBSWebSocket.Settings.Save.UserPasswordWarningInfoText="Czy na pewno chcesz użyć własnego hasła?"
OBSWebSocket.Settings.Save.PasswordInvalidErrorTitle="Błąd: Nieprawidłowa konfiguracja"
OBSWebSocket.Settings.Save.PasswordInvalidErrorMessage="Musisz użyć hasła, które ma 6 lub więcej znaków."
OBSWebSocket.Settings.Save.PasswordInvalidErrorMessage="Hasło musi zawierać 6 lub więcej znaków."
OBSWebSocket.SessionTable.Title="Podłączone sesje WebSocket"
OBSWebSocket.SessionTable.RemoteAddressColumnTitle="Adres zdalny"
OBSWebSocket.SessionTable.SessionDurationColumnTitle="Czas trwania sesji"

View File

@ -12,7 +12,7 @@ OBSWebSocket.Settings.GeneratePassword="Generează parola"
OBSWebSocket.Settings.ServerPort="Portul serverului"
OBSWebSocket.Settings.ShowConnectInfo="Afișează informațiile conexiunii"
OBSWebSocket.Settings.ShowConnectInfoWarningTitle="Avertisment: În prezent în direct"
OBSWebSocket.Settings.ShowConnectInfoWarningMessage="Se pare că un output (transmisiune, înregistrare etc.) este activ în prezent."
OBSWebSocket.Settings.ShowConnectInfoWarningMessage="Se pare că un output (stream, înregistrare etc.) este activ în prezent."
OBSWebSocket.Settings.ShowConnectInfoWarningInfoText="Sigur vrei să afișezi informațiile de conectare?"
OBSWebSocket.Settings.Save.UserPasswordWarningTitle="Avertisment: Potențială problemă de securitate"
OBSWebSocket.Settings.Save.UserPasswordWarningMessage="obs-websocket stochează parola serverului ca text simplu. Este foarte recomandat să folosiți o parolă generată de obs-websocket."

View File

@ -28,7 +28,7 @@ OBSWebSocket.SessionTable.KickButtonColumnTitle="Выгнать?"
OBSWebSocket.SessionTable.KickButtonText="Выгнать"
OBSWebSocket.ConnectInfo.DialogTitle="Сведения о подключении WebSocket"
OBSWebSocket.ConnectInfo.CopyText="Копировать"
OBSWebSocket.ConnectInfo.ServerIp="IP сервера (лучшая догадка)"
OBSWebSocket.ConnectInfo.ServerIp="IP сервера (вероятный)"
OBSWebSocket.ConnectInfo.ServerPort="Порт сервера"
OBSWebSocket.ConnectInfo.ServerPassword="Пароль сервера"
OBSWebSocket.ConnectInfo.ServerPasswordPlaceholderText="[Вход отключён]"

View File

@ -1,6 +1,6 @@
OBSWebSocket.Plugin.Description="Fjärrkontroll av OBS Studio via WebSocket"
OBSWebSocket.Settings.DialogTitle="WebSocket-serverinställningar"
OBSWebSocket.Settings.PluginSettingsTitle="Insticksmodulsinställningar"
OBSWebSocket.Settings.PluginSettingsTitle="Insticksprogramsinställningar"
OBSWebSocket.Settings.ServerEnable="Aktivera WebSocket-server"
OBSWebSocket.Settings.AlertsEnable="Aktivera systemfältsmeddelanden"
OBSWebSocket.Settings.DebugEnable="Aktivera felsökningsloggning"
@ -11,7 +11,7 @@ OBSWebSocket.Settings.Password="Serverlösenord"
OBSWebSocket.Settings.GeneratePassword="Generera lösenord"
OBSWebSocket.Settings.ServerPort="Serverport"
OBSWebSocket.Settings.ShowConnectInfo="Visa anslutningsinformation"
OBSWebSocket.Settings.ShowConnectInfoWarningTitle="Varning: Sänder för närvarande"
OBSWebSocket.Settings.ShowConnectInfoWarningTitle="Varning: Direktsändning pågår"
OBSWebSocket.Settings.ShowConnectInfoWarningMessage="Det verkar som om en utmatning (ström, inspelning, etc.) är för närvarande aktiv."
OBSWebSocket.Settings.ShowConnectInfoWarningInfoText="Är du säker på att du vill visa din anslutningsinformation?"
OBSWebSocket.Settings.Save.UserPasswordWarningTitle="Varning: Potentiellt säkerhetsproblem"

View File

@ -29,7 +29,7 @@ OBSWebSocket.SessionTable.KickButtonText="Çıkar"
OBSWebSocket.ConnectInfo.DialogTitle="WebSocket Bağlanma Bilgileri"
OBSWebSocket.ConnectInfo.CopyText="Kopyala"
OBSWebSocket.ConnectInfo.ServerIp="Sunucu IP (En İyi Tahmin)"
OBSWebSocket.ConnectInfo.ServerPort="Sunucu Kapısı"
OBSWebSocket.ConnectInfo.ServerPort="Sunucu Portu"
OBSWebSocket.ConnectInfo.ServerPassword="Sunucu Parolası"
OBSWebSocket.ConnectInfo.ServerPasswordPlaceholderText="[Doğrulama Devre Dışı]"
OBSWebSocket.ConnectInfo.QrTitle="Kare Kod ile Bağlan"

9
data/locale/tt-RU.ini Normal file
View File

@ -0,0 +1,9 @@
OBSWebSocket.Settings.ServerSettingsTitle="Сервер көйләүләре"
OBSWebSocket.Settings.Password="Сервер серсүзе"
OBSWebSocket.Settings.GeneratePassword="Серсүзне ясау"
OBSWebSocket.Settings.ServerPort="Сервер порты"
OBSWebSocket.SessionTable.KickButtonColumnTitle="Чыгарыргамы?"
OBSWebSocket.SessionTable.KickButtonText="Чыгару"
OBSWebSocket.ConnectInfo.CopyText="Күчермә алу"
OBSWebSocket.ConnectInfo.ServerPort="Сервер порты"
OBSWebSocket.ConnectInfo.ServerPassword="Сервер серсүзе"

2
data/locale/ug-CN.ini Normal file
View File

@ -0,0 +1,2 @@
OBSWebSocket.Settings.GeneratePassword="ئىم ھاسىللا"
OBSWebSocket.ConnectInfo.CopyText="كۆچۈر"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <obs.h>
#define OBS_WEBSOCKET_API_VERSION 2
#define OBS_WEBSOCKET_API_VERSION 3
#ifdef __cplusplus
extern "C" {
@ -30,6 +30,7 @@ extern "C" {
typedef void *obs_websocket_vendor;
typedef void (*obs_websocket_request_callback_function)(obs_data_t *, obs_data_t *, void *);
typedef void (*obs_websocket_event_callback_function)(uint64_t, const char *, const char *, void *);
struct obs_websocket_request_response {
unsigned int status_code;
@ -44,7 +45,12 @@ struct obs_websocket_request_callback {
void *priv_data;
};
inline proc_handler_t *_ph;
struct obs_websocket_event_callback {
obs_websocket_event_callback_function callback;
void *priv_data;
};
static proc_handler_t *_ph;
/* ==================== INTERNAL API FUNCTIONS ==================== */
@ -53,7 +59,7 @@ static inline proc_handler_t *obs_websocket_get_ph(void)
proc_handler_t *global_ph = obs_get_proc_handler();
assert(global_ph != NULL);
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
if (!proc_handler_call(global_ph, "obs_websocket_api_get_ph", &cd))
blog(LOG_DEBUG, "Unable to fetch obs-websocket proc handler object. obs-websocket not installed?");
proc_handler_t *ret = (proc_handler_t *)calldata_ptr(&cd, "ph");
@ -91,7 +97,7 @@ static inline unsigned int obs_websocket_get_api_version(void)
if (!obs_websocket_ensure_ph())
return 0;
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
if (!proc_handler_call(_ph, "get_api_version", &cd))
return 1; // API v1 does not include get_api_version
@ -104,7 +110,11 @@ static inline unsigned int obs_websocket_get_api_version(void)
}
// Calls an obs-websocket request. Free response with `obs_websocket_request_response_free()`
static inline obs_websocket_request_response *obs_websocket_call_request(const char *request_type, obs_data_t *request_data = NULL)
static inline struct obs_websocket_request_response *obs_websocket_call_request(const char *request_type, obs_data_t *request_data
#ifdef __cplusplus
= NULL
#endif
)
{
if (!obs_websocket_ensure_ph())
return NULL;
@ -113,14 +123,13 @@ static inline obs_websocket_request_response *obs_websocket_call_request(const c
if (request_data)
request_data_string = obs_data_get_json(request_data);
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "request_type", request_type);
calldata_set_string(&cd, "request_data", request_data_string);
proc_handler_call(_ph, "call_request", &cd);
auto ret = (struct obs_websocket_request_response *)calldata_ptr(&cd, "response");
struct obs_websocket_request_response *ret = (struct obs_websocket_request_response *)calldata_ptr(&cd, "response");
calldata_free(&cd);
@ -140,6 +149,46 @@ static inline void obs_websocket_request_response_free(struct obs_websocket_requ
bfree(response);
}
// Register an event handler to receive obs-websocket events
static inline bool obs_websocket_register_event_callback(obs_websocket_event_callback_function event_callback, void *priv_data)
{
if (!obs_websocket_ensure_ph())
return false;
struct obs_websocket_event_callback cb = {event_callback, priv_data};
calldata_t cd = {0, 0, 0, 0};
calldata_set_ptr(&cd, "callback", &cb);
proc_handler_call(_ph, "register_event_callback", &cd);
bool ret = calldata_bool(&cd, "success");
calldata_free(&cd);
return ret;
}
// Unregister an existing event handler
static inline bool obs_websocket_unregister_event_callback(obs_websocket_event_callback_function event_callback, void *priv_data)
{
if (!obs_websocket_ensure_ph())
return false;
struct obs_websocket_event_callback cb = {event_callback, priv_data};
calldata_t cd = {0, 0, 0, 0};
calldata_set_ptr(&cd, "callback", &cb);
proc_handler_call(_ph, "unregister_event_callback", &cd);
bool ret = calldata_bool(&cd, "success");
calldata_free(&cd);
return ret;
}
/* ==================== VENDOR API FUNCTIONS ==================== */
// ALWAYS CALL ONLY VIA `obs_module_post_load()` CALLBACK!
@ -149,8 +198,7 @@ static inline obs_websocket_vendor obs_websocket_register_vendor(const char *ven
if (!obs_websocket_ensure_ph())
return NULL;
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "name", vendor_name);
proc_handler_call(_ph, "vendor_register", &cd);
@ -164,12 +212,9 @@ static inline obs_websocket_vendor obs_websocket_register_vendor(const char *ven
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};
struct obs_websocket_request_callback cb = {};
cb.callback = request_callback;
cb.priv_data = priv_data;
struct obs_websocket_request_callback cb = {request_callback, priv_data};
calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "type", request_type);
calldata_set_ptr(&cd, "callback", &cb);
@ -182,8 +227,7 @@ static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor ve
// Unregisters an existing vendor request
static inline bool obs_websocket_vendor_unregister_request(obs_websocket_vendor vendor, const char *request_type)
{
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "type", request_type);
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_request_unregister", &cd);
@ -196,8 +240,7 @@ static inline bool obs_websocket_vendor_unregister_request(obs_websocket_vendor
// Emits an event under the vendor's name
static inline bool obs_websocket_vendor_emit_event(obs_websocket_vendor vendor, const char *event_name, obs_data_t *event_data)
{
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "type", event_name);
calldata_set_ptr(&cd, "data", (void *)event_data);

View File

@ -17,60 +17,80 @@ 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 <filesystem>
#include <obs-frontend-api.h>
#include "Config.h"
#include "utils/Crypto.h"
#include "utils/Platform.h"
#include "utils/Obs.h"
#define CONFIG_SECTION_NAME "OBSWebSocket"
#define CONFIG_PARAM_FIRSTLOAD "FirstLoad"
#define CONFIG_PARAM_ENABLED "ServerEnabled"
#define CONFIG_PARAM_PORT "ServerPort"
#define CONFIG_PARAM_ALERTS "AlertsEnabled"
#define CONFIG_PARAM_AUTHREQUIRED "AuthRequired"
#define CONFIG_PARAM_PASSWORD "ServerPassword"
#define PARAM_FIRSTLOAD "FirstLoad"
#define PARAM_ENABLED "ServerEnabled"
#define PARAM_PORT "ServerPort"
#define PARAM_ALERTS "AlertsEnabled"
#define PARAM_AUTHREQUIRED "AuthRequired"
#define PARAM_PASSWORD "ServerPassword"
#define CONFIG_FILE_NAME "config.json"
#define PARAM_FIRSTLOAD "first_load"
#define PARAM_ENABLED "server_enabled"
#define PARAM_PORT "server_port"
#define PARAM_ALERTS "alerts_enabled"
#define PARAM_AUTHREQUIRED "auth_required"
#define PARAM_PASSWORD "server_password"
#define CMDLINE_WEBSOCKET_PORT "websocket_port"
#define CMDLINE_WEBSOCKET_IPV4_ONLY "websocket_ipv4_only"
#define CMDLINE_WEBSOCKET_PASSWORD "websocket_password"
#define CMDLINE_WEBSOCKET_DEBUG "websocket_debug"
Config::Config()
void Config::Load(json config)
{
SetDefaultsToGlobalStore();
}
void Config::Load()
{
config_t *obsConfig = GetConfigStore();
if (!obsConfig) {
blog(LOG_ERROR, "[Config::Load] Unable to fetch OBS config!");
return;
// Only load from plugin config directory if there hasn't been a migration
if (config.is_null()) {
std::string configFilePath = Utils::Obs::StringHelper::GetModuleConfigPath(CONFIG_FILE_NAME);
Utils::Json::GetJsonFileContent(configFilePath, config); // Fetch the existing config, which may not exist
}
FirstLoad = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD);
ServerEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED);
AlertsEnabled = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS);
ServerPort = config_get_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT);
AuthRequired = config_get_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED);
ServerPassword = config_get_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD);
if (!config.is_object()) {
blog(LOG_INFO, "[Config::Load] Existing configuration not found, using defaults.");
config = json::object();
}
if (config.contains(PARAM_FIRSTLOAD) && config[PARAM_FIRSTLOAD].is_boolean())
FirstLoad = config[PARAM_FIRSTLOAD];
if (config.contains(PARAM_ENABLED) && config[PARAM_ENABLED].is_boolean())
ServerEnabled = config[PARAM_ENABLED];
if (config.contains(PARAM_ALERTS) && config[PARAM_ALERTS].is_boolean())
AlertsEnabled = config[PARAM_ALERTS];
if (config.contains(PARAM_PORT) && config[PARAM_PORT].is_number_unsigned())
ServerPort = config[PARAM_PORT];
if (config.contains(PARAM_AUTHREQUIRED) && config[PARAM_AUTHREQUIRED].is_boolean())
AuthRequired = config[PARAM_AUTHREQUIRED];
if (config.contains(PARAM_PASSWORD) && config[PARAM_PASSWORD].is_string())
ServerPassword = config[PARAM_PASSWORD];
// Set server password and save it to the config before processing overrides,
// so that there is always a true configured password regardless of if
// future loads use the override flag.
if (FirstLoad) {
FirstLoad = false;
if (ServerPassword.isEmpty()) {
if (ServerPassword.empty()) {
blog(LOG_INFO, "[Config::Load] (FirstLoad) Generating new server password.");
ServerPassword = QString::fromStdString(Utils::Crypto::GeneratePassword());
ServerPassword = Utils::Crypto::GeneratePassword();
} else {
blog(LOG_INFO, "[Config::Load] (FirstLoad) Not generating new password since one is already configured.");
}
Save();
}
// If there are migrated settings, write them to disk before processing arguments.
if (!config.empty())
Save();
// Process `--websocket_port` override
QString portArgument = Utils::Platform::GetCommandLineArgument(CMDLINE_WEBSOCKET_PORT);
if (portArgument != "") {
@ -97,7 +117,7 @@ void Config::Load()
blog(LOG_INFO, "[Config::Load] --websocket_password passed. Overriding WebSocket password.");
PasswordOverridden = true;
AuthRequired = true;
ServerPassword = passwordArgument;
ServerPassword = passwordArgument.toStdString();
}
// Process `--websocket_debug` override
@ -110,43 +130,98 @@ void Config::Load()
void Config::Save()
{
config_t *obsConfig = GetConfigStore();
if (!obsConfig) {
blog(LOG_ERROR, "[Config::Save] Unable to fetch OBS config!");
return;
}
json config;
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_FIRSTLOAD, FirstLoad);
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ENABLED, ServerEnabled);
if (!PortOverridden) {
config_set_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
}
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_ALERTS, AlertsEnabled);
std::string configFilePath = Utils::Obs::StringHelper::GetModuleConfigPath(CONFIG_FILE_NAME);
Utils::Json::GetJsonFileContent(configFilePath, config); // Fetch the existing config, which may not exist
config[PARAM_FIRSTLOAD] = FirstLoad.load();
config[PARAM_ENABLED] = ServerEnabled.load();
if (!PortOverridden)
config[PARAM_PORT] = ServerPort.load();
config[PARAM_ALERTS] = AlertsEnabled.load();
if (!PasswordOverridden) {
config_set_bool(obsConfig, CONFIG_SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
config[PARAM_AUTHREQUIRED] = AuthRequired.load();
config[PARAM_PASSWORD] = ServerPassword;
}
config_save(obsConfig);
if (Utils::Json::SetJsonFileContent(configFilePath, config))
blog(LOG_DEBUG, "[Config::Save] Saved config.");
else
blog(LOG_ERROR, "[Config::Save] Failed to write config file!");
}
void Config::SetDefaultsToGlobalStore()
// Finds any old values in global.ini and removes them, then returns the values as JSON
json MigrateGlobalConfigData()
{
config_t *obsConfig = GetConfigStore();
if (!obsConfig) {
blog(LOG_ERROR, "[Config::SetDefaultsToGlobalStore] Unable to fetch OBS config!");
return;
// Get existing global config
config_t *config = obs_frontend_get_global_config();
json ret;
// Move values to temporary JSON blob
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_FIRSTLOAD)) {
ret[PARAM_FIRSTLOAD] = config_get_bool(config, CONFIG_SECTION_NAME, CONFIG_PARAM_FIRSTLOAD);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_FIRSTLOAD);
}
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ENABLED)) {
ret[PARAM_ENABLED] = config_get_bool(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ENABLED);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ENABLED);
}
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PORT)) {
ret[PARAM_PORT] = config_get_uint(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PORT);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PORT);
}
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ALERTS)) {
ret[PARAM_ALERTS] = config_get_bool(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ALERTS);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_ALERTS);
}
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_AUTHREQUIRED)) {
ret[PARAM_AUTHREQUIRED] = config_get_bool(config, CONFIG_SECTION_NAME, CONFIG_PARAM_AUTHREQUIRED);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_AUTHREQUIRED);
}
if (config_has_user_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PASSWORD)) {
ret[PARAM_PASSWORD] = config_get_string(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PASSWORD);
config_remove_value(config, CONFIG_SECTION_NAME, CONFIG_PARAM_PASSWORD);
}
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_uint(obsConfig, CONFIG_SECTION_NAME, PARAM_PORT, ServerPort);
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_string(obsConfig, CONFIG_SECTION_NAME, PARAM_PASSWORD, QT_TO_UTF8(ServerPassword));
if (!ret.is_null()) {
blog(LOG_INFO, "[MigrateGlobalConfigData] Some configurations have been migrated from old config");
config_save(config);
}
return ret;
}
config_t *Config::GetConfigStore()
// Migration from storing persistent data in obsWebSocketPersistentData.json to the module config directory
// This will overwrite any persistent data in the destination. People doing manual OBS config modification be warned!
bool MigratePersistentData()
{
return obs_frontend_get_global_config();
std::error_code ec;
// Ensure module config directory exists
auto moduleConfigDirectory = std::filesystem::u8path(Utils::Obs::StringHelper::GetModuleConfigPath(""));
if (!std::filesystem::exists(moduleConfigDirectory, ec))
std::filesystem::create_directories(moduleConfigDirectory, ec);
if (ec) {
blog(LOG_ERROR, "[MigratePersistentData] Failed to create directory `%s`: %s", moduleConfigDirectory.c_str(),
ec.message().c_str());
return false;
}
// Move any existing persistent data to module config directory, then delete old file
auto oldPersistentDataPath = std::filesystem::u8path(Utils::Obs::StringHelper::GetCurrentProfilePath() +
"/../../../obsWebSocketPersistentData.json");
if (std::filesystem::exists(oldPersistentDataPath, ec)) {
auto persistentDataPath =
std::filesystem::u8path(Utils::Obs::StringHelper::GetModuleConfigPath("persistent_data.json"));
std::filesystem::copy_file(oldPersistentDataPath, persistentDataPath, ec);
std::filesystem::remove(oldPersistentDataPath, ec);
blog(LOG_INFO, "[MigratePersistentData] Persistent data migrated to new path");
}
if (ec) {
blog(LOG_ERROR, "[MigratePersistentData] Failed to move persistent data: %s", ec.message().c_str());
return false;
}
return true;
}

View File

@ -23,14 +23,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <QString>
#include <util/config-file.h>
#include "utils/Json.h"
#include "plugin-macros.generated.h"
struct Config {
Config();
void Load();
void Load(json config = nullptr);
void Save();
void SetDefaultsToGlobalStore();
static config_t *GetConfigStore();
std::atomic<bool> PortOverridden = false;
std::atomic<bool> PasswordOverridden = false;
@ -42,5 +40,8 @@ struct Config {
std::atomic<bool> DebugEnabled = false;
std::atomic<bool> AlertsEnabled = false;
std::atomic<bool> AuthRequired = true;
QString ServerPassword;
std::string ServerPassword;
};
json MigrateGlobalConfigData();
bool MigratePersistentData();

View File

@ -18,7 +18,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "WebSocketApi.h"
#include "requesthandler/RequestHandler.h"
#include "obs-websocket.h"
#include "utils/Json.h"
#define RETURN_STATUS(status) \
@ -48,14 +47,19 @@ WebSocketApi::WebSocketApi()
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 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_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,
&call_request, this);
proc_handler_add(_procHandler, "bool register_event_callback(in ptr callback, out bool success)", &register_event_callback,
this);
proc_handler_add(_procHandler, "bool unregister_event_callback(in ptr callback, out bool success)",
&unregister_event_callback, 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, out bool success)",
&vendor_request_register_cb, this);
proc_handler_add(_procHandler, "bool vendor_request_unregister(in ptr vendor, in string type, out bool success)",
&vendor_request_unregister_cb, this);
proc_handler_add(_procHandler, "bool vendor_event_emit(in ptr vendor, in string type, in ptr data, out bool success)",
&vendor_event_emit_cb, this);
proc_handler_t *ph = obs_get_proc_handler();
assert(ph != NULL);
@ -71,6 +75,10 @@ WebSocketApi::~WebSocketApi()
proc_handler_destroy(_procHandler);
size_t numEventCallbacks = _eventCallbacks.size();
_eventCallbacks.clear();
blog_debug("[WebSocketApi::~WebSocketApi] Deleted %ld event callbacks", numEventCallbacks);
for (auto vendor : _vendors) {
blog_debug("[WebSocketApi::~WebSocketApi] Deleting vendor: %s", vendor.first.c_str());
delete vendor.second;
@ -79,9 +87,21 @@ WebSocketApi::~WebSocketApi()
blog_debug("[WebSocketApi::~WebSocketApi] Finished.");
}
void WebSocketApi::SetEventCallback(EventCallback cb)
void WebSocketApi::BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData, uint8_t rpcVersion)
{
_eventCallback = cb;
if (!_obsReady)
return;
// Only broadcast events applicable to the latest RPC version
if (rpcVersion && rpcVersion != CURRENT_RPC_VERSION)
return;
std::string eventDataString = eventData.dump();
std::shared_lock l(_mutex);
for (auto &cb : _eventCallbacks)
cb.callback(requiredIntent, eventType.c_str(), eventDataString.c_str(), cb.priv_data);
}
enum WebSocketApi::RequestReturnCode WebSocketApi::PerformVendorRequest(std::string vendorName, std::string requestType,
@ -126,14 +146,27 @@ void WebSocketApi::get_api_version(void *, calldata_t *cd)
RETURN_SUCCESS();
}
void WebSocketApi::call_request(void *, calldata_t *cd)
void WebSocketApi::call_request(void *priv_data, calldata_t *cd)
{
auto c = static_cast<WebSocketApi *>(priv_data);
#if !defined(PLUGIN_TESTS)
if (!c->_obsReady)
RETURN_FAILURE();
#endif
const char *request_type = calldata_string(cd, "request_type");
const char *request_data = calldata_string(cd, "request_data");
if (!request_type)
RETURN_FAILURE();
#ifdef PLUGIN_TESTS
// Allow plugin tests to complete, even though OBS wouldn't be ready at the time of the test
if (!c->_obsReady && std::string(request_type) != "GetVersion")
RETURN_FAILURE();
#endif
auto response = static_cast<obs_websocket_request_response *>(bzalloc(sizeof(struct obs_websocket_request_response)));
if (!response)
RETURN_FAILURE();
@ -162,6 +195,52 @@ void WebSocketApi::call_request(void *, calldata_t *cd)
RETURN_SUCCESS();
}
void WebSocketApi::register_event_callback(void *priv_data, calldata_t *cd)
{
auto c = static_cast<WebSocketApi *>(priv_data);
void *voidCallback;
if (!calldata_get_ptr(cd, "callback", &voidCallback) || !voidCallback) {
blog(LOG_WARNING, "[WebSocketApi::register_event_callback] Failed due to missing `callback` pointer.");
RETURN_FAILURE();
}
auto cb = static_cast<obs_websocket_event_callback *>(voidCallback);
std::unique_lock l(c->_mutex);
int64_t foundIndex = c->GetEventCallbackIndex(*cb);
if (foundIndex != -1)
RETURN_FAILURE();
c->_eventCallbacks.push_back(*cb);
RETURN_SUCCESS();
}
void WebSocketApi::unregister_event_callback(void *priv_data, calldata_t *cd)
{
auto c = static_cast<WebSocketApi *>(priv_data);
void *voidCallback;
if (!calldata_get_ptr(cd, "callback", &voidCallback) || !voidCallback) {
blog(LOG_WARNING, "[WebSocketApi::register_event_callback] Failed due to missing `callback` pointer.");
RETURN_FAILURE();
}
auto cb = static_cast<obs_websocket_event_callback *>(voidCallback);
std::unique_lock l(c->_mutex);
int64_t foundIndex = c->GetEventCallbackIndex(*cb);
if (foundIndex == -1)
RETURN_FAILURE();
c->_eventCallbacks.erase(c->_eventCallbacks.begin() + foundIndex);
RETURN_SUCCESS();
}
void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
{
auto c = static_cast<WebSocketApi *>(priv_data);
@ -172,7 +251,7 @@ void WebSocketApi::vendor_register_cb(void *priv_data, calldata_t *cd)
RETURN_FAILURE();
}
// Theoretically doesn't need a mutex, but it's good to be safe.
// Theoretically doesn't need a mutex due to module load being single-thread, but it's good to be safe.
std::unique_lock l(c->_mutex);
if (c->_vendors.count(vendorName)) {
@ -289,10 +368,10 @@ void WebSocketApi::vendor_event_emit_cb(void *priv_data, calldata_t *cd)
auto eventData = static_cast<obs_data_t *>(voidEventData);
if (!c->_eventCallback)
if (!c->_vendorEventCallback)
RETURN_FAILURE();
c->_eventCallback(v->_name, eventType, eventData);
c->_vendorEventCallback(v->_name, eventType, eventData);
RETURN_SUCCESS();
}

View File

@ -23,9 +23,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <map>
#include <mutex>
#include <shared_mutex>
#include <atomic>
#include <obs.h>
#include <obs-websocket-api.h>
#include "../lib/obs-websocket-api.h"
#include "utils/Json.h"
#include "plugin-macros.generated.h"
class WebSocketApi {
public:
@ -35,8 +38,6 @@ public:
NoVendorRequest,
};
typedef std::function<void(std::string, std::string, obs_data_t *)> EventCallback;
struct Vendor {
std::shared_mutex _mutex;
std::string _name;
@ -45,23 +46,44 @@ public:
WebSocketApi();
~WebSocketApi();
void SetEventCallback(EventCallback cb);
void BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData = nullptr,
uint8_t rpcVersion = 0);
void SetObsReady(bool ready) { _obsReady = ready; }
enum RequestReturnCode PerformVendorRequest(std::string vendorName, std::string requestName, obs_data_t *requestData,
obs_data_t *responseData);
// Callback for when a vendor emits an event
typedef std::function<void(std::string, std::string, obs_data_t *)> VendorEventCallback;
inline void SetVendorEventCallback(VendorEventCallback cb) { _vendorEventCallback = cb; }
private:
inline int64_t GetEventCallbackIndex(obs_websocket_event_callback &cb)
{
for (int64_t i = 0; i < (int64_t)_eventCallbacks.size(); i++) {
auto currentCb = _eventCallbacks[i];
if (currentCb.callback == cb.callback && currentCb.priv_data == cb.priv_data)
return i;
}
return -1;
}
// Proc handlers
static void get_ph_cb(void *priv_data, calldata_t *cd);
static void get_api_version(void *, calldata_t *cd);
static void call_request(void *, calldata_t *cd);
static void register_event_callback(void *, calldata_t *cd);
static void unregister_event_callback(void *, calldata_t *cd);
static void vendor_register_cb(void *priv_data, calldata_t *cd);
static void vendor_request_register_cb(void *priv_data, calldata_t *cd);
static void vendor_request_unregister_cb(void *priv_data, calldata_t *cd);
static void vendor_event_emit_cb(void *priv_data, calldata_t *cd);
private:
std::shared_mutex _mutex;
EventCallback _eventCallback;
proc_handler_t *_procHandler;
std::map<std::string, Vendor *> _vendors;
std::vector<obs_websocket_event_callback> _eventCallbacks;
std::atomic<bool> _obsReady = false;
VendorEventCallback _vendorEventCallback;
};

View File

@ -27,10 +27,11 @@ EventHandler::EventHandler()
signal_handler_t *coreSignalHandler = obs_get_signal_handler();
if (coreSignalHandler) {
signal_handler_connect(coreSignalHandler, "source_create", SourceCreatedMultiHandler, this);
signal_handler_connect(coreSignalHandler, "source_destroy", SourceDestroyedMultiHandler, this);
signal_handler_connect(coreSignalHandler, "source_remove", SourceRemovedMultiHandler, this);
signal_handler_connect(coreSignalHandler, "source_rename", SourceRenamedMultiHandler, this);
coreSignals.emplace_back(coreSignalHandler, "source_create", SourceCreatedMultiHandler, this);
coreSignals.emplace_back(coreSignalHandler, "source_destroy", SourceDestroyedMultiHandler, this);
coreSignals.emplace_back(coreSignalHandler, "source_remove", SourceRemovedMultiHandler, this);
coreSignals.emplace_back(coreSignalHandler, "source_rename", SourceRenamedMultiHandler, this);
coreSignals.emplace_back(coreSignalHandler, "source_update", SourceUpdatedMultiHandler, this);
} else {
blog(LOG_ERROR, "[EventHandler::EventHandler] Unable to get libobs signal handler!");
}
@ -44,15 +45,7 @@ EventHandler::~EventHandler()
obs_frontend_remove_event_callback(OnFrontendEvent, this);
signal_handler_t *coreSignalHandler = obs_get_signal_handler();
if (coreSignalHandler) {
signal_handler_disconnect(coreSignalHandler, "source_create", SourceCreatedMultiHandler, this);
signal_handler_disconnect(coreSignalHandler, "source_destroy", SourceDestroyedMultiHandler, this);
signal_handler_disconnect(coreSignalHandler, "source_remove", SourceRemovedMultiHandler, this);
signal_handler_disconnect(coreSignalHandler, "source_rename", SourceRenamedMultiHandler, this);
} else {
blog(LOG_ERROR, "[EventHandler::~EventHandler] Unable to get libobs signal handler!");
}
coreSignals.clear();
// Revoke callbacks of all inputs and scenes, in case some still have our callbacks attached
auto enumInputs = [](void *param, obs_source_t *source) {
@ -71,58 +64,47 @@ EventHandler::~EventHandler()
blog_debug("[EventHandler::~EventHandler] Finished.");
}
void EventHandler::SetBroadcastCallback(EventHandler::BroadcastCallback cb)
// Function to increment or decrement refcounts for high volume event subscriptions
void EventHandler::ProcessSubscriptionChange(bool type, uint64_t eventSubscriptions)
{
_broadcastCallback = cb;
}
void EventHandler::SetObsReadyCallback(EventHandler::ObsReadyCallback cb)
{
_obsReadyCallback = cb;
}
// Function to increment refcounts for high volume event subscriptions
void EventHandler::ProcessSubscription(uint64_t eventSubscriptions)
{
if ((eventSubscriptions & EventSubscription::InputVolumeMeters) != 0) {
if (_inputVolumeMetersRef.fetch_add(1) == 0) {
if (_inputVolumeMetersHandler)
blog(LOG_WARNING, "[EventHandler::ProcessSubscription] Input volume meter handler already exists!");
else
_inputVolumeMetersHandler = std::make_unique<Utils::Obs::VolumeMeter::Handler>(
std::bind(&EventHandler::HandleInputVolumeMeters, this, std::placeholders::_1));
if (type) {
if ((eventSubscriptions & EventSubscription::InputVolumeMeters) != 0) {
if (_inputVolumeMetersRef.fetch_add(1) == 0) {
if (_inputVolumeMetersHandler)
blog(LOG_WARNING,
"[EventHandler::ProcessSubscription] Input volume meter handler already exists!");
else
_inputVolumeMetersHandler = std::make_unique<Utils::Obs::VolumeMeter::Handler>(
std::bind(&EventHandler::HandleInputVolumeMeters, this, std::placeholders::_1));
}
}
if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0)
_inputActiveStateChangedRef++;
if ((eventSubscriptions & EventSubscription::InputShowStateChanged) != 0)
_inputShowStateChangedRef++;
if ((eventSubscriptions & EventSubscription::SceneItemTransformChanged) != 0)
_sceneItemTransformChangedRef++;
} else {
if ((eventSubscriptions & EventSubscription::InputVolumeMeters) != 0) {
if (_inputVolumeMetersRef.fetch_sub(1) == 1)
_inputVolumeMetersHandler.reset();
}
if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0)
_inputActiveStateChangedRef--;
if ((eventSubscriptions & EventSubscription::InputShowStateChanged) != 0)
_inputShowStateChangedRef--;
if ((eventSubscriptions & EventSubscription::SceneItemTransformChanged) != 0)
_sceneItemTransformChangedRef--;
}
if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0)
_inputActiveStateChangedRef++;
if ((eventSubscriptions & EventSubscription::InputShowStateChanged) != 0)
_inputShowStateChangedRef++;
if ((eventSubscriptions & EventSubscription::SceneItemTransformChanged) != 0)
_sceneItemTransformChangedRef++;
}
// Function to decrement refcounts for high volume event subscriptions
void EventHandler::ProcessUnsubscription(uint64_t eventSubscriptions)
{
if ((eventSubscriptions & EventSubscription::InputVolumeMeters) != 0) {
if (_inputVolumeMetersRef.fetch_sub(1) == 1)
_inputVolumeMetersHandler.reset();
}
if ((eventSubscriptions & EventSubscription::InputActiveStateChanged) != 0)
_inputActiveStateChangedRef--;
if ((eventSubscriptions & EventSubscription::InputShowStateChanged) != 0)
_inputShowStateChangedRef--;
if ((eventSubscriptions & EventSubscription::SceneItemTransformChanged) != 0)
_sceneItemTransformChangedRef--;
}
// Function required in order to use default arguments
void EventHandler::BroadcastEvent(uint64_t requiredIntent, std::string eventType, json eventData, uint8_t rpcVersion)
{
if (!_broadcastCallback)
if (!_eventCallback)
return;
_broadcastCallback(requiredIntent, eventType, eventData, rpcVersion);
_eventCallback(requiredIntent, eventType, eventData, rpcVersion);
}
// Connect source signals for Inputs, Scenes, and Transitions. Filters are automatically connected.
@ -387,12 +369,21 @@ void EventHandler::OnFrontendEvent(enum obs_frontend_event event, void *private_
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STARTED);
{
OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output();
if (recordOutput) {
signal_handler_t *sh = obs_output_get_signal_handler(recordOutput);
eventHandler->recordFileChangedSignal.Connect(sh, "file_changed", HandleRecordFileChanged,
private_data);
}
}
break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPING:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPING);
break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_STOPPED);
eventHandler->recordFileChangedSignal.Disconnect();
break;
case OBS_FRONTEND_EVENT_RECORDING_PAUSED:
eventHandler->HandleRecordStateChanged(OBS_WEBSOCKET_OUTPUT_PAUSED);
@ -588,6 +579,26 @@ void EventHandler::SourceRenamedMultiHandler(void *param, calldata_t *data)
}
}
void EventHandler::SourceUpdatedMultiHandler(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler *>(param);
obs_source_t *source = GetCalldataPointer<obs_source_t>(data, "source");
if (!source)
return;
switch (obs_source_get_type(source)) {
case OBS_SOURCE_TYPE_INPUT:
eventHandler->HandleInputSettingsChanged(source);
break;
case OBS_SOURCE_TYPE_FILTER:
eventHandler->HandleSourceFilterSettingsChanged(source);
break;
default:
break;
}
}
void EventHandler::StreamOutputReconnectHandler(void *param, calldata_t *)
{
auto eventHandler = static_cast<EventHandler *>(param);

View File

@ -34,21 +34,26 @@ public:
EventHandler();
~EventHandler();
typedef std::function<void(uint64_t, std::string, json, uint8_t)>
BroadcastCallback; // uint64_t requiredIntent, std::string eventType, json eventData, uint8_t rpcVersion
void SetBroadcastCallback(BroadcastCallback cb);
typedef std::function<void(bool)> ObsReadyCallback; // bool ready
void SetObsReadyCallback(ObsReadyCallback cb);
void ProcessSubscriptionChange(bool type, uint64_t eventSubscriptions);
void ProcessSubscription(uint64_t eventSubscriptions);
void ProcessUnsubscription(uint64_t eventSubscriptions);
// Callback when an event fires
typedef std::function<void(uint64_t, std::string, json, uint8_t)>
EventCallback; // uint64_t requiredIntent, std::string eventType, json eventData, uint8_t rpcVersion
inline void SetEventCallback(EventCallback cb) { _eventCallback = cb; }
// Callback when OBS becomes ready or non-ready
typedef std::function<void(bool)> ObsReadyCallback; // bool ready
inline void SetObsReadyCallback(ObsReadyCallback cb) { _obsReadyCallback = cb; }
private:
BroadcastCallback _broadcastCallback;
EventCallback _eventCallback;
ObsReadyCallback _obsReadyCallback;
std::atomic<bool> _obsReady = false;
std::vector<OBSSignal> coreSignals;
OBSSignal recordFileChangedSignal;
std::unique_ptr<Utils::Obs::VolumeMeter::Handler> _inputVolumeMetersHandler;
std::atomic<uint64_t> _inputVolumeMetersRef = 0;
std::atomic<uint64_t> _inputActiveStateChangedRef = 0;
@ -69,9 +74,10 @@ private:
static void SourceCreatedMultiHandler(void *param, calldata_t *data);
static void SourceDestroyedMultiHandler(void *param, calldata_t *data);
static void SourceRemovedMultiHandler(void *param, calldata_t *data);
// Signal handler: source
static void SourceRenamedMultiHandler(void *param, calldata_t *data);
static void SourceUpdatedMultiHandler(void *param, calldata_t *data);
// Signal handler: media sources
static void SourceMediaPauseMultiHandler(void *param, calldata_t *data);
static void SourceMediaPlayMultiHandler(void *param, calldata_t *data);
static void SourceMediaRestartMultiHandler(void *param, calldata_t *data);
@ -106,7 +112,7 @@ private:
void HandleInputCreated(obs_source_t *source);
void HandleInputRemoved(obs_source_t *source);
void HandleInputNameChanged(obs_source_t *source, std::string oldInputName, std::string inputName);
void HandleInputVolumeMeters(std::vector<json> inputs); // AudioMeter::Handler callback
void HandleInputSettingsChanged(obs_source_t *source);
static void HandleInputActiveStateChanged(void *param,
calldata_t *data); // Direct callback
static void HandleInputShowStateChanged(void *param,
@ -123,6 +129,7 @@ private:
calldata_t *data); // Direct callback
static void HandleInputAudioMonitorTypeChanged(void *param,
calldata_t *data); // Direct callback
void HandleInputVolumeMeters(std::vector<json> inputs); // AudioMeter::Handler callback
// Transitions
void HandleCurrentSceneTransitionChanged();
@ -144,12 +151,14 @@ private:
void HandleSourceFilterCreated(obs_source_t *source, obs_source_t *filter);
void HandleSourceFilterRemoved(obs_source_t *source, obs_source_t *filter);
static void HandleSourceFilterNameChanged(void *param,
calldata_t *data); // Direct callback
calldata_t *data); // Direct callback
void HandleSourceFilterSettingsChanged(obs_source_t *source);
static void HandleSourceFilterEnableStateChanged(void *param, calldata_t *data); // Direct callback
// Outputs
void HandleStreamStateChanged(ObsOutputState state);
void HandleRecordStateChanged(ObsOutputState state);
static void HandleRecordFileChanged(void *param, calldata_t *data); // Direct callback
void HandleReplayBufferStateChanged(ObsOutputState state);
void HandleVirtualcamStateChanged(ObsOutputState state);
void HandleReplayBufferSaved();

View File

@ -163,6 +163,32 @@ void EventHandler::HandleSourceFilterNameChanged(void *param, calldata_t *data)
eventHandler->BroadcastEvent(EventSubscription::Filters, "SourceFilterNameChanged", eventData);
}
/**
* An source filter's settings have changed (been updated).
*
* @dataField sourceName | String | Name of the source the filter is on
* @dataField filterName | String | Name of the filter
* @dataField filterSettings | Object | New settings object of the filter
*
* @eventType SourceFilterSettingsChanged
* @eventSubscription Filters
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.4.0
* @api events
* @category filters
*/
void EventHandler::HandleSourceFilterSettingsChanged(obs_source_t *source)
{
OBSDataAutoRelease filterSettings = obs_source_get_settings(source);
json eventData;
eventData["sourceName"] = obs_source_get_name(obs_filter_get_parent(source));
eventData["filterName"] = obs_source_get_name(source);
eventData["filterSettings"] = Utils::Json::ObsDataToJson(filterSettings);
BroadcastEvent(EventSubscription::Filters, "SourceFilterSettingsChanged", eventData);
}
/**
* A source filter's enable state has changed.
*

View File

@ -23,6 +23,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* An input has been created.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
* @dataField inputKind | String | The kind of the input
* @dataField unversionedInputKind | String | The unversioned kind of input (aka no `_v2` stuff)
* @dataField inputSettings | Object | The settings configured to the input when it was created
@ -44,6 +45,7 @@ void EventHandler::HandleInputCreated(obs_source_t *source)
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["inputKind"] = inputKind;
eventData["unversionedInputKind"] = obs_source_get_unversioned_id(source);
eventData["inputSettings"] = Utils::Json::ObsDataToJson(inputSettings);
@ -55,6 +57,7 @@ void EventHandler::HandleInputCreated(obs_source_t *source)
* An input has been removed.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
*
* @eventType InputRemoved
* @eventSubscription Inputs
@ -68,12 +71,14 @@ void EventHandler::HandleInputRemoved(obs_source_t *source)
{
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
BroadcastEvent(EventSubscription::Inputs, "InputRemoved", eventData);
}
/**
* The name of an input has changed.
*
* @dataField inputUuid | String | UUID of the input
* @dataField oldInputName | String | Old name of the input
* @dataField inputName | String | New name of the input
*
@ -85,20 +90,50 @@ void EventHandler::HandleInputRemoved(obs_source_t *source)
* @api events
* @category inputs
*/
void EventHandler::HandleInputNameChanged(obs_source_t *, std::string oldInputName, std::string inputName)
void EventHandler::HandleInputNameChanged(obs_source_t *source, std::string oldInputName, std::string inputName)
{
json eventData;
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["oldInputName"] = oldInputName;
eventData["inputName"] = inputName;
BroadcastEvent(EventSubscription::Inputs, "InputNameChanged", eventData);
}
/**
* An input's settings have changed (been updated).
*
* Note: On some inputs, changing values in the properties dialog will cause an immediate update. Pressing the "Cancel" button will revert the settings, resulting in another event being fired.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
* @dataField inputSettings | Object | New settings object of the input
*
* @eventType InputSettingsChanged
* @eventSubscription Inputs
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.4.0
* @api events
* @category inputs
*/
void EventHandler::HandleInputSettingsChanged(obs_source_t *source)
{
OBSDataAutoRelease inputSettings = obs_source_get_settings(source);
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["inputSettings"] = Utils::Json::ObsDataToJson(inputSettings);
BroadcastEvent(EventSubscription::Inputs, "InputSettingsChanged", eventData);
}
/**
* An input's active state has changed.
*
* When an input is active, it means it's being shown by the program feed.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
* @dataField videoActive | Boolean | Whether the input is active
*
* @eventType InputActiveStateChanged
@ -125,6 +160,7 @@ void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["videoActive"] = obs_source_active(source);
eventHandler->BroadcastEvent(EventSubscription::InputActiveStateChanged, "InputActiveStateChanged", eventData);
}
@ -135,6 +171,7 @@ void EventHandler::HandleInputActiveStateChanged(void *param, calldata_t *data)
* When an input is showing, it means it's being shown by the preview or a dialog.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
* @dataField videoShowing | Boolean | Whether the input is showing
*
* @eventType InputShowStateChanged
@ -161,6 +198,7 @@ void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["videoShowing"] = obs_source_showing(source);
eventHandler->BroadcastEvent(EventSubscription::InputShowStateChanged, "InputShowStateChanged", eventData);
}
@ -169,6 +207,7 @@ void EventHandler::HandleInputShowStateChanged(void *param, calldata_t *data)
* An input's mute state has changed.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
* @dataField inputMuted | Boolean | Whether the input is muted
*
* @eventType InputMuteStateChanged
@ -192,6 +231,7 @@ void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["inputMuted"] = obs_source_muted(source);
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputMuteStateChanged", eventData);
}
@ -200,6 +240,7 @@ void EventHandler::HandleInputMuteStateChanged(void *param, calldata_t *data)
* An input's volume level has changed.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
* @dataField inputVolumeMul | Number | New volume level multiplier
* @dataField inputVolumeDb | Number | New volume level in dB
*
@ -231,6 +272,7 @@ void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["inputVolumeMul"] = inputVolumeMul;
eventData["inputVolumeDb"] = inputVolumeDb;
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputVolumeChanged", eventData);
@ -239,7 +281,8 @@ void EventHandler::HandleInputVolumeChanged(void *param, calldata_t *data)
/**
* The audio balance value of an input has changed.
*
* @dataField inputName | String | Name of the affected input
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
* @dataField inputAudioBalance | Number | New audio balance value of the input
*
* @eventType InputAudioBalanceChanged
@ -265,6 +308,7 @@ void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["inputAudioBalance"] = inputAudioBalance;
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioBalanceChanged", eventData);
}
@ -273,6 +317,7 @@ void EventHandler::HandleInputAudioBalanceChanged(void *param, calldata_t *data)
* The sync offset of an input has changed.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
* @dataField inputAudioSyncOffset | Number | New sync offset in milliseconds
*
* @eventType InputAudioSyncOffsetChanged
@ -298,6 +343,7 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["inputAudioSyncOffset"] = inputAudioSyncOffset / 1000000;
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioSyncOffsetChanged", eventData);
}
@ -306,6 +352,7 @@ void EventHandler::HandleInputAudioSyncOffsetChanged(void *param, calldata_t *da
* The audio tracks of an input have changed.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
* @dataField inputAudioTracks | Object | Object of audio tracks along with their associated enable states
*
* @eventType InputAudioTracksChanged
@ -336,6 +383,7 @@ void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["inputAudioTracks"] = inputAudioTracks;
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioTracksChanged", eventData);
}
@ -350,6 +398,7 @@ void EventHandler::HandleInputAudioTracksChanged(void *param, calldata_t *data)
* - `OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT`
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
* @dataField monitorType | String | New monitor type of the input
*
* @eventType InputAudioMonitorTypeChanged
@ -375,6 +424,7 @@ void EventHandler::HandleInputAudioMonitorTypeChanged(void *param, calldata_t *d
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["monitorType"] = monitorType;
eventHandler->BroadcastEvent(EventSubscription::Inputs, "InputAudioMonitorTypeChanged", eventData);
}

View File

@ -124,6 +124,7 @@ void EventHandler::SourceMediaPreviousMultiHandler(void *param, calldata_t *data
* A media input has started playing.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
*
* @eventType MediaInputPlaybackStarted
* @eventSubscription MediaInputs
@ -146,6 +147,7 @@ void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventHandler->BroadcastEvent(EventSubscription::MediaInputs, "MediaInputPlaybackStarted", eventData);
}
@ -153,6 +155,7 @@ void EventHandler::HandleMediaInputPlaybackStarted(void *param, calldata_t *data
* A media input has finished playing.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
*
* @eventType MediaInputPlaybackEnded
* @eventSubscription MediaInputs
@ -175,6 +178,7 @@ void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventHandler->BroadcastEvent(EventSubscription::MediaInputs, "MediaInputPlaybackEnded", eventData);
}
@ -182,6 +186,7 @@ void EventHandler::HandleMediaInputPlaybackEnded(void *param, calldata_t *data)
* An action has been performed on an input.
*
* @dataField inputName | String | Name of the input
* @dataField inputUuid | String | UUID of the input
* @dataField mediaAction | String | Action performed on the input. See `ObsMediaInputAction` enum
*
* @eventType MediaInputActionTriggered
@ -196,6 +201,7 @@ void EventHandler::HandleMediaInputActionTriggered(obs_source_t *source, ObsMedi
{
json eventData;
eventData["inputName"] = obs_source_get_name(source);
eventData["inputUuid"] = obs_source_get_uuid(source);
eventData["mediaAction"] = GetMediaInputActionString(action);
BroadcastEvent(EventSubscription::MediaInputs, "MediaInputActionTriggered", eventData);
}

View File

@ -87,6 +87,28 @@ void EventHandler::HandleRecordStateChanged(ObsOutputState state)
BroadcastEvent(EventSubscription::Outputs, "RecordStateChanged", eventData);
}
/**
* The record output has started writing to a new file. For example, when a file split happens.
*
* @dataField newOutputPath | String | File name that the output has begun writing to
*
* @eventType RecordFileChanged
* @eventSubscription Outputs
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.5.0
* @api events
* @category outputs
*/
void EventHandler::HandleRecordFileChanged(void *param, calldata_t *data)
{
auto eventHandler = static_cast<EventHandler *>(param);
json eventData;
eventData["newOutputPath"] = calldata_string(data, "next_file");
eventHandler->BroadcastEvent(EventSubscription::Outputs, "RecordFileChanged", eventData);
}
/**
* The state of the replay buffer output has changed.
*

View File

@ -23,7 +23,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* A scene item has been created.
*
* @dataField sceneName | String | Name of the scene the item was added to
* @dataField sceneUuid | String | UUID of the scene the item was added to
* @dataField sourceName | String | Name of the underlying source (input/scene)
* @dataField sourceUuid | String | UUID of the underlying source (input/scene)
* @dataField sceneItemId | Number | Numeric ID of the scene item
* @dataField sceneItemIndex | Number | Index position of the item
*
@ -49,7 +51,9 @@ void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sourceName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
eventData["sourceUuid"] = obs_source_get_uuid(obs_sceneitem_get_source(sceneItem));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemIndex"] = obs_sceneitem_get_order_position(sceneItem);
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemCreated", eventData);
@ -61,7 +65,9 @@ void EventHandler::HandleSceneItemCreated(void *param, calldata_t *data)
* This event is not emitted when the scene the item is in is removed.
*
* @dataField sceneName | String | Name of the scene the item was removed from
* @dataField sceneUuid | String | UUID of the scene the item was removed from
* @dataField sourceName | String | Name of the underlying source (input/scene)
* @dataField sourceUuid | String | UUID of the underlying source (input/scene)
* @dataField sceneItemId | Number | Numeric ID of the scene item
*
* @eventType SceneItemRemoved
@ -86,7 +92,9 @@ void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sourceName"] = obs_source_get_name(obs_sceneitem_get_source(sceneItem));
eventData["sourceUuid"] = obs_source_get_uuid(obs_sceneitem_get_source(sceneItem));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemRemoved", eventData);
}
@ -95,6 +103,7 @@ void EventHandler::HandleSceneItemRemoved(void *param, calldata_t *data)
* A scene's item list has been reindexed.
*
* @dataField sceneName | String | Name of the scene
* @dataField sceneUuid | String | UUID of the scene
* @dataField sceneItems | Array<Object> | Array of scene item objects
*
* @eventType SceneItemListReindexed
@ -115,6 +124,7 @@ void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sceneItems"] = Utils::Obs::ArrayHelper::GetSceneItemList(scene, true);
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemListReindexed", eventData);
}
@ -123,6 +133,7 @@ void EventHandler::HandleSceneItemListReindexed(void *param, calldata_t *data)
* A scene item's enable state has changed.
*
* @dataField sceneName | String | Name of the scene the item is in
* @dataField sceneUuid | String | UUID of the scene the item is in
* @dataField sceneItemId | Number | Numeric ID of the scene item
* @dataField sceneItemEnabled | Boolean | Whether the scene item is enabled (visible)
*
@ -150,6 +161,7 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemEnabled"] = sceneItemEnabled;
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemEnableStateChanged", eventData);
@ -159,6 +171,7 @@ void EventHandler::HandleSceneItemEnableStateChanged(void *param, calldata_t *da
* A scene item's lock state has changed.
*
* @dataField sceneName | String | Name of the scene the item is in
* @dataField sceneUuid | String | UUID of the scene the item is in
* @dataField sceneItemId | Number | Numeric ID of the scene item
* @dataField sceneItemLocked | Boolean | Whether the scene item is locked
*
@ -186,6 +199,7 @@ void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data
json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemLocked"] = sceneItemLocked;
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemLockStateChanged", eventData);
@ -195,6 +209,7 @@ void EventHandler::HandleSceneItemLockStateChanged(void *param, calldata_t *data
* A scene item has been selected in the Ui.
*
* @dataField sceneName | String | Name of the scene the item is in
* @dataField sceneUuid | String | UUID of the scene the item is in
* @dataField sceneItemId | Number | Numeric ID of the scene item
*
* @eventType SceneItemSelected
@ -219,6 +234,7 @@ void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventHandler->BroadcastEvent(EventSubscription::SceneItems, "SceneItemSelected", eventData);
}
@ -227,6 +243,7 @@ void EventHandler::HandleSceneItemSelected(void *param, calldata_t *data)
* The transform/crop of a scene item has changed.
*
* @dataField sceneName | String | The name of the scene the item is in
* @dataField sceneUuid | String | The UUID of the scene the item is in
* @dataField sceneItemId | Number | Numeric ID of the scene item
* @dataField sceneItemTransform | Object | New transform/crop info of the scene item
*
@ -255,6 +272,7 @@ void EventHandler::HandleSceneItemTransformChanged(void *param, calldata_t *data
json eventData;
eventData["sceneName"] = obs_source_get_name(obs_scene_get_source(scene));
eventData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(scene));
eventData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
eventData["sceneItemTransform"] = Utils::Obs::ObjectHelper::GetSceneItemTransform(sceneItem);
eventHandler->BroadcastEvent(EventSubscription::SceneItemTransformChanged, "SceneItemTransformChanged", eventData);

View File

@ -23,6 +23,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* A new scene has been created.
*
* @dataField sceneName | String | Name of the new scene
* @dataField sceneUuid | String | UUID of the new scene
* @dataField isGroup | Boolean | Whether the new scene is a group
*
* @eventType SceneCreated
@ -37,6 +38,7 @@ void EventHandler::HandleSceneCreated(obs_source_t *source)
{
json eventData;
eventData["sceneName"] = obs_source_get_name(source);
eventData["sceneUuid"] = obs_source_get_uuid(source);
eventData["isGroup"] = obs_source_is_group(source);
BroadcastEvent(EventSubscription::Scenes, "SceneCreated", eventData);
}
@ -45,6 +47,7 @@ void EventHandler::HandleSceneCreated(obs_source_t *source)
* A scene has been removed.
*
* @dataField sceneName | String | Name of the removed scene
* @dataField sceneUuid | String | UUID of the removed scene
* @dataField isGroup | Boolean | Whether the scene was a group
*
* @eventType SceneRemoved
@ -59,6 +62,7 @@ void EventHandler::HandleSceneRemoved(obs_source_t *source)
{
json eventData;
eventData["sceneName"] = obs_source_get_name(source);
eventData["sceneUuid"] = obs_source_get_uuid(source);
eventData["isGroup"] = obs_source_is_group(source);
BroadcastEvent(EventSubscription::Scenes, "SceneRemoved", eventData);
}
@ -66,6 +70,7 @@ void EventHandler::HandleSceneRemoved(obs_source_t *source)
/**
* The name of a scene has changed.
*
* @dataField sceneUuid | String | UUID of the scene
* @dataField oldSceneName | String | Old name of the scene
* @dataField sceneName | String | New name of the scene
*
@ -77,9 +82,10 @@ void EventHandler::HandleSceneRemoved(obs_source_t *source)
* @api events
* @category scenes
*/
void EventHandler::HandleSceneNameChanged(obs_source_t *, std::string oldSceneName, std::string sceneName)
void EventHandler::HandleSceneNameChanged(obs_source_t *source, std::string oldSceneName, std::string sceneName)
{
json eventData;
eventData["sceneUuid"] = obs_source_get_uuid(source);
eventData["oldSceneName"] = oldSceneName;
eventData["sceneName"] = sceneName;
BroadcastEvent(EventSubscription::Scenes, "SceneNameChanged", eventData);
@ -89,6 +95,7 @@ void EventHandler::HandleSceneNameChanged(obs_source_t *, std::string oldSceneNa
* The current program scene has changed.
*
* @dataField sceneName | String | Name of the scene that was switched to
* @dataField sceneUuid | String | UUID of the scene that was switched to
*
* @eventType CurrentProgramSceneChanged
* @eventSubscription Scenes
@ -104,6 +111,7 @@ void EventHandler::HandleCurrentProgramSceneChanged()
json eventData;
eventData["sceneName"] = obs_source_get_name(currentScene);
eventData["sceneUuid"] = obs_source_get_uuid(currentScene);
BroadcastEvent(EventSubscription::Scenes, "CurrentProgramSceneChanged", eventData);
}
@ -111,6 +119,7 @@ void EventHandler::HandleCurrentProgramSceneChanged()
* The current preview scene has changed.
*
* @dataField sceneName | String | Name of the scene that was switched to
* @dataField sceneUuid | String | UUID of the scene that was switched to
*
* @eventType CurrentPreviewSceneChanged
* @eventSubscription Scenes
@ -130,6 +139,7 @@ void EventHandler::HandleCurrentPreviewSceneChanged()
json eventData;
eventData["sceneName"] = obs_source_get_name(currentPreviewScene);
eventData["sceneUuid"] = obs_source_get_uuid(currentPreviewScene);
BroadcastEvent(EventSubscription::Scenes, "CurrentPreviewSceneChanged", eventData);
}

View File

@ -23,6 +23,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* The current scene transition has changed.
*
* @dataField transitionName | String | Name of the new transition
* @dataField transitionUuid | String | UUID of the new transition
*
* @eventType CurrentSceneTransitionChanged
* @eventSubscription Transitions
@ -38,6 +39,7 @@ void EventHandler::HandleCurrentSceneTransitionChanged()
json eventData;
eventData["transitionName"] = obs_source_get_name(transition);
eventData["transitionUuid"] = obs_source_get_uuid(transition);
BroadcastEvent(EventSubscription::Transitions, "CurrentSceneTransitionChanged", eventData);
}
@ -65,6 +67,7 @@ void EventHandler::HandleCurrentSceneTransitionDurationChanged()
* A scene transition has started.
*
* @dataField transitionName | String | Scene transition name
* @dataField transitionUuid | String | Scene transition UUID
*
* @eventType SceneTransitionStarted
* @eventSubscription Transitions
@ -84,6 +87,7 @@ void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data)
json eventData;
eventData["transitionName"] = obs_source_get_name(source);
eventData["transitionUuid"] = obs_source_get_uuid(source);
eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionStarted", eventData);
}
@ -93,6 +97,7 @@ void EventHandler::HandleSceneTransitionStarted(void *param, calldata_t *data)
* Note: Does not appear to trigger when the transition is interrupted by the user.
*
* @dataField transitionName | String | Scene transition name
* @dataField transitionUuid | String | Scene transition UUID
*
* @eventType SceneTransitionEnded
* @eventSubscription Transitions
@ -112,6 +117,7 @@ void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data)
json eventData;
eventData["transitionName"] = obs_source_get_name(source);
eventData["transitionUuid"] = obs_source_get_uuid(source);
eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionEnded", eventData);
}
@ -124,6 +130,7 @@ void EventHandler::HandleSceneTransitionEnded(void *param, calldata_t *data)
* Note: Appears to be called by every transition, regardless of relevance.
*
* @dataField transitionName | String | Scene transition name
* @dataField transitionUuid | String | Scene transition UUID
*
* @eventType SceneTransitionVideoEnded
* @eventSubscription Transitions
@ -143,5 +150,6 @@ void EventHandler::HandleSceneTransitionVideoEnded(void *param, calldata_t *data
json eventData;
eventData["transitionName"] = obs_source_get_name(source);
eventData["transitionUuid"] = obs_source_get_uuid(source);
eventHandler->BroadcastEvent(EventSubscription::Transitions, "SceneTransitionVideoEnded", eventData);
}

View File

@ -64,7 +64,7 @@ void ConnectInfo::RefreshData()
QString serverPassword;
if (conf->AuthRequired) {
ui->copyServerPasswordButton->setEnabled(true);
serverPassword = QUrl::toPercentEncoding(conf->ServerPassword);
serverPassword = QUrl::toPercentEncoding(QString::fromStdString(conf->ServerPassword));
} else {
ui->copyServerPasswordButton->setEnabled(false);
serverPassword = obs_module_text("OBSWebSocket.ConnectInfo.ServerPasswordPlaceholderText");

View File

@ -123,7 +123,7 @@ void SettingsDialog::RefreshData()
ui->enableDebugLoggingCheckBox->setChecked(conf->DebugEnabled);
ui->serverPortSpinBox->setValue(conf->ServerPort);
ui->enableAuthenticationCheckBox->setChecked(conf->AuthRequired);
ui->serverPasswordLineEdit->setText(conf->ServerPassword);
ui->serverPasswordLineEdit->setText(QString::fromStdString(conf->ServerPassword));
ui->serverPasswordLineEdit->setEnabled(conf->AuthRequired);
ui->generatePasswordButton->setEnabled(conf->AuthRequired);
@ -158,7 +158,7 @@ void SettingsDialog::SaveFormData()
}
// Show a confirmation box to the user if they attempt to provide their own password
if (passwordManuallyEdited && (conf->ServerPassword != ui->serverPasswordLineEdit->text())) {
if (passwordManuallyEdited && (conf->ServerPassword != ui->serverPasswordLineEdit->text().toStdString())) {
QMessageBox msgBox;
msgBox.setWindowTitle(obs_module_text("OBSWebSocket.Settings.Save.UserPasswordWarningTitle"));
msgBox.setText(obs_module_text("OBSWebSocket.Settings.Save.UserPasswordWarningMessage"));
@ -172,22 +172,22 @@ void SettingsDialog::SaveFormData()
break;
case QMessageBox::No:
default:
ui->serverPasswordLineEdit->setText(conf->ServerPassword);
ui->serverPasswordLineEdit->setText(QString::fromStdString(conf->ServerPassword));
return;
}
}
bool needsRestart =
(conf->ServerEnabled != ui->enableWebSocketServerCheckBox->isChecked()) ||
(conf->ServerPort != ui->serverPortSpinBox->value()) ||
(ui->enableAuthenticationCheckBox->isChecked() && conf->ServerPassword != ui->serverPasswordLineEdit->text());
bool needsRestart = (conf->ServerEnabled != ui->enableWebSocketServerCheckBox->isChecked()) ||
(conf->ServerPort != ui->serverPortSpinBox->value()) ||
(ui->enableAuthenticationCheckBox->isChecked() &&
conf->ServerPassword != ui->serverPasswordLineEdit->text().toStdString());
conf->ServerEnabled = ui->enableWebSocketServerCheckBox->isChecked();
conf->AlertsEnabled = ui->enableSystemTrayAlertsCheckBox->isChecked();
conf->DebugEnabled = ui->enableDebugLoggingCheckBox->isChecked();
conf->ServerPort = ui->serverPortSpinBox->value();
conf->AuthRequired = ui->enableAuthenticationCheckBox->isChecked();
conf->ServerPassword = ui->serverPasswordLineEdit->text();
conf->ServerPassword = ui->serverPasswordLineEdit->text().toStdString();
conf->Save();

View File

@ -48,7 +48,9 @@ WebSocketApiPtr _webSocketApi;
WebSocketServerPtr _webSocketServer;
SettingsDialog *_settingsDialog = nullptr;
void WebSocketApiEventCallback(std::string vendorName, std::string eventType, obs_data_t *obsEventData);
void OnWebSocketApiVendorEvent(std::string vendorName, std::string eventType, obs_data_t *obsEventData);
void OnEvent(uint64_t requiredIntent, std::string eventType, json eventData, uint8_t rpcVersion);
void OnObsReady(bool ready);
bool obs_module_load(void)
{
@ -60,19 +62,30 @@ bool obs_module_load(void)
// Initialize the cpu stats
_cpuUsageInfo = os_cpu_usage_info_start();
// Handle migrations
if (!MigratePersistentData()) {
os_cpu_usage_info_destroy(_cpuUsageInfo);
return false;
}
json migratedConfig = MigrateGlobalConfigData();
// Create the config manager then load the parameters from storage
_config = ConfigPtr(new Config());
_config->Load();
_config = std::make_shared<Config>();
_config->Load(migratedConfig);
// Initialize the event handler
_eventHandler = EventHandlerPtr(new EventHandler());
_eventHandler = std::make_shared<EventHandler>();
_eventHandler->SetEventCallback(OnEvent);
_eventHandler->SetObsReadyCallback(OnObsReady);
// Initialize the plugin/script API
_webSocketApi = WebSocketApiPtr(new WebSocketApi());
_webSocketApi->SetEventCallback(WebSocketApiEventCallback);
_webSocketApi = std::make_shared<WebSocketApi>();
_webSocketApi->SetVendorEventCallback(OnWebSocketApiVendorEvent);
// Initialize the WebSocket server
_webSocketServer = WebSocketServerPtr(new WebSocketServer());
_webSocketServer = std::make_shared<WebSocketServer>();
_webSocketServer->SetClientSubscriptionCallback(std::bind(&EventHandler::ProcessSubscriptionChange, _eventHandler.get(),
std::placeholders::_1, std::placeholders::_2));
// Initialize the settings dialog
obs_frontend_push_ui_translation(obs_module_get_string);
@ -90,12 +103,16 @@ bool obs_module_load(void)
}
#ifdef PLUGIN_TESTS
void test_call_request();
void test_register_event_callback();
void test_register_vendor();
#endif
void obs_module_post_load(void)
{
#ifdef PLUGIN_TESTS
test_call_request();
test_register_event_callback();
test_register_vendor();
#endif
@ -116,17 +133,20 @@ void obs_module_unload(void)
_webSocketServer->Stop();
}
// Destroy the WebSocket server
_webSocketServer.reset();
// Release the WebSocket server
_webSocketServer->SetClientSubscriptionCallback(nullptr);
_webSocketServer = nullptr;
// Destroy the plugin/script api
_webSocketApi.reset();
// Release the plugin/script api
_webSocketApi = nullptr;
// Destroy the event handler
_eventHandler.reset();
// Release the event handler
_eventHandler->SetObsReadyCallback(nullptr);
_eventHandler->SetEventCallback(nullptr);
_eventHandler = nullptr;
// Destroy the config manager
_config.reset();
// Release the config manager
_config = nullptr;
// Destroy the cpu stats
os_cpu_usage_info_destroy(_cpuUsageInfo);
@ -182,7 +202,7 @@ bool IsDebugEnabled()
* @api events
* @category general
*/
void WebSocketApiEventCallback(std::string vendorName, std::string eventType, obs_data_t *obsEventData)
void OnWebSocketApiVendorEvent(std::string vendorName, std::string eventType, obs_data_t *obsEventData)
{
json eventData = Utils::Json::ObsDataToJson(obsEventData);
@ -194,13 +214,62 @@ void WebSocketApiEventCallback(std::string vendorName, std::string eventType, ob
_webSocketServer->BroadcastEvent(EventSubscription::Vendors, "VendorEvent", broadcastEventData);
}
// Sent from: EventHandler
void OnEvent(uint64_t requiredIntent, std::string eventType, json eventData, uint8_t rpcVersion)
{
if (_webSocketServer)
_webSocketServer->BroadcastEvent(requiredIntent, eventType, eventData, rpcVersion);
if (_webSocketApi)
_webSocketApi->BroadcastEvent(requiredIntent, eventType, eventData, rpcVersion);
}
// Sent from: EventHandler
void OnObsReady(bool ready)
{
if (_webSocketServer)
_webSocketServer->SetObsReady(ready);
if (_webSocketApi)
_webSocketApi->SetObsReady(ready);
}
#ifdef PLUGIN_TESTS
void test_call_request()
{
blog(LOG_INFO, "[test_call_request] Testing obs-websocket plugin API request calling...");
struct obs_websocket_request_response *response = obs_websocket_call_request("GetVersion");
if (response) {
blog(LOG_INFO, "[test_call_request] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
response->status_code, response->comment, response->response_data);
obs_websocket_request_response_free(response);
} else {
blog(LOG_ERROR, "[test_call_request] Failed to call GetVersion request via obs-websocket plugin API!");
}
blog(LOG_INFO, "[test_call_request] Test done.");
}
static void test_event_cb(uint64_t eventIntent, const char *eventType, const char *eventData, void *priv_data)
{
blog(LOG_DEBUG, "[test_event_cb] New event! Type: %s | Data: %s", eventType, eventData);
UNUSED_PARAMETER(eventIntent);
UNUSED_PARAMETER(priv_data);
}
void test_register_event_callback()
{
blog(LOG_INFO, "[test_register_event_callback] Registering test event callback...");
if (!obs_websocket_register_event_callback(test_event_cb, nullptr))
blog(LOG_ERROR, "[test_register_event_callback] Failed to register event callback!");
blog(LOG_INFO, "[test_register_event_callback] Test done.");
}
static void test_vendor_request_cb(obs_data_t *requestData, obs_data_t *responseData, void *priv_data)
{
blog(LOG_INFO, "[test_vendor_request_cb] Request called!");
blog(LOG_INFO, "[test_vendor_request_cb] Request data: %s", obs_data_get_json(requestData));
blog(LOG_INFO, "[test_vendor_request_cb] Request called! Request data: %s", obs_data_get_json(requestData));
// Set an item to the response data
obs_data_set_string(responseData, "test", "pp");
@ -211,34 +280,25 @@ static void test_vendor_request_cb(obs_data_t *requestData, obs_data_t *response
void test_register_vendor()
{
blog(LOG_INFO, "[test_register_vendor] Registering test vendor...");
blog(LOG_INFO, "[test_register_vendor] Testing vendor registration...");
// Test plugin API version fetch
uint apiVersion = obs_websocket_get_api_version();
blog(LOG_INFO, "[test_register_vendor] obs-websocket plugin API version: %u", apiVersion);
// Test calling obs-websocket requests
struct obs_websocket_request_response *response = obs_websocket_call_request("GetVersion");
if (response) {
blog(LOG_INFO, "[test_register_vendor] Called GetVersion. Status Code: %u | Comment: %s | Response Data: %s",
response->status_code, response->comment, response->response_data);
obs_websocket_request_response_free(response);
}
// Test vendor creation
auto vendor = obs_websocket_register_vendor("obs-websocket-test");
if (!vendor) {
blog(LOG_WARNING, "[test_register_vendor] Failed to create vendor!");
blog(LOG_ERROR, "[test_register_vendor] Failed to create vendor!");
return;
}
// Test vendor request registration
if (!obs_websocket_vendor_register_request(vendor, "TestRequest", test_vendor_request_cb, vendor)) {
blog(LOG_WARNING, "[test_register_vendor] Failed to register vendor request!");
blog(LOG_ERROR, "[test_register_vendor] Failed to register vendor request!");
return;
}
blog(LOG_INFO, "[test_register_vendor] Post load completed.");
blog(LOG_INFO, "[test_register_vendor] Test done.");
}
#endif

View File

@ -26,6 +26,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "utils/Obs.h"
#include "plugin-macros.generated.h"
#define CURRENT_RPC_VERSION 1
struct Config;
typedef std::shared_ptr<Config> ConfigPtr;

View File

@ -37,7 +37,9 @@ struct SerialFrameBatch {
std::condition_variable condition;
SerialFrameBatch(RequestHandler &requestHandler, json &variables, bool haltOnFailure)
: requestHandler(requestHandler), variables(variables), haltOnFailure(haltOnFailure)
: requestHandler(requestHandler),
variables(variables),
haltOnFailure(haltOnFailure)
{
}
};

View File

@ -111,6 +111,7 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"SetTBarPosition", &RequestHandler::SetTBarPosition},
// Filters
{"GetSourceFilterKindList", &RequestHandler::GetSourceFilterKindList},
{"GetSourceFilterList", &RequestHandler::GetSourceFilterList},
{"GetSourceFilterDefaultSettings", &RequestHandler::GetSourceFilterDefaultSettings},
{"CreateSourceFilter", &RequestHandler::CreateSourceFilter},
@ -125,6 +126,7 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"GetSceneItemList", &RequestHandler::GetSceneItemList},
{"GetGroupSceneItemList", &RequestHandler::GetGroupSceneItemList},
{"GetSceneItemId", &RequestHandler::GetSceneItemId},
{"GetSceneItemSource", &RequestHandler::GetSceneItemSource},
{"CreateSceneItem", &RequestHandler::CreateSceneItem},
{"RemoveSceneItem", &RequestHandler::RemoveSceneItem},
{"DuplicateSceneItem", &RequestHandler::DuplicateSceneItem},
@ -175,6 +177,8 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"ToggleRecordPause", &RequestHandler::ToggleRecordPause},
{"PauseRecord", &RequestHandler::PauseRecord},
{"ResumeRecord", &RequestHandler::ResumeRecord},
{"SplitRecordFile", &RequestHandler::SplitRecordFile},
{"CreateRecordChapter", &RequestHandler::CreateRecordChapter},
// Media Inputs
{"GetMediaInputStatus", &RequestHandler::GetMediaInputStatus},

View File

@ -130,6 +130,7 @@ private:
RequestResult SetTBarPosition(const Request &);
// Filters
RequestResult GetSourceFilterKindList(const Request &);
RequestResult GetSourceFilterList(const Request &);
RequestResult GetSourceFilterDefaultSettings(const Request &);
RequestResult CreateSourceFilter(const Request &);
@ -144,6 +145,7 @@ private:
RequestResult GetSceneItemList(const Request &);
RequestResult GetGroupSceneItemList(const Request &);
RequestResult GetSceneItemId(const Request &);
RequestResult GetSceneItemSource(const Request &);
RequestResult CreateSceneItem(const Request &);
RequestResult RemoveSceneItem(const Request &);
RequestResult DuplicateSceneItem(const Request &);
@ -194,6 +196,8 @@ private:
RequestResult ToggleRecordPause(const Request &);
RequestResult PauseRecord(const Request &);
RequestResult ResumeRecord(const Request &);
RequestResult SplitRecordFile(const Request &);
RequestResult CreateRecordChapter(const Request &);
// Media Inputs
RequestResult GetMediaInputStatus(const Request &);

View File

@ -22,6 +22,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "RequestHandler.h"
#define GLOBAL_PERSISTENT_DATA_FILE_NAME "persistent_data.json"
/**
* Gets the value of a "slot" from the selected persistent data realm.
*
@ -47,11 +49,11 @@ RequestResult RequestHandler::GetPersistentData(const Request &request)
std::string realm = request.RequestData["realm"];
std::string slotName = request.RequestData["slotName"];
std::string persistentDataPath = Utils::Obs::StringHelper::GetCurrentProfilePath();
std::string persistentDataPath;
if (realm == "OBS_WEBSOCKET_DATA_REALM_GLOBAL")
persistentDataPath += "/../../../obsWebSocketPersistentData.json";
persistentDataPath = Utils::Obs::StringHelper::GetModuleConfigPath(GLOBAL_PERSISTENT_DATA_FILE_NAME);
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
persistentDataPath += "/obsWebSocketPersistentData.json";
persistentDataPath = Utils::Obs::StringHelper::GetCurrentProfilePath() + "/obsWebSocketPersistentData.json";
else
return RequestResult::Error(RequestStatus::ResourceNotFound,
"You have specified an invalid persistent data realm.");
@ -92,16 +94,16 @@ RequestResult RequestHandler::SetPersistentData(const Request &request)
std::string slotName = request.RequestData["slotName"];
json slotValue = request.RequestData["slotValue"];
std::string persistentDataPath = Utils::Obs::StringHelper::GetCurrentProfilePath();
std::string persistentDataPath;
if (realm == "OBS_WEBSOCKET_DATA_REALM_GLOBAL")
persistentDataPath += "/../../../obsWebSocketPersistentData.json";
persistentDataPath = Utils::Obs::StringHelper::GetModuleConfigPath(GLOBAL_PERSISTENT_DATA_FILE_NAME);
else if (realm == "OBS_WEBSOCKET_DATA_REALM_PROFILE")
persistentDataPath += "/obsWebSocketPersistentData.json";
persistentDataPath = Utils::Obs::StringHelper::GetCurrentProfilePath() + "/obsWebSocketPersistentData.json";
else
return RequestResult::Error(RequestStatus::ResourceNotFound,
"You have specified an invalid persistent data realm.");
json persistentData = json::object();
json persistentData;
Utils::Json::GetJsonFileContent(persistentDataPath, persistentData);
persistentData[slotName] = slotValue;
if (!Utils::Json::SetJsonFileContent(persistentDataPath, persistentData))
@ -198,10 +200,7 @@ RequestResult RequestHandler::CreateSceneCollection(const Request &request)
if (std::find(sceneCollections.begin(), sceneCollections.end(), sceneCollectionName) != sceneCollections.end())
return RequestResult::Error(RequestStatus::ResourceAlreadyExists);
QMainWindow *mainWindow = static_cast<QMainWindow *>(obs_frontend_get_main_window());
bool success = false;
QMetaObject::invokeMethod(mainWindow, "AddSceneCollection", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success),
Q_ARG(bool, true), Q_ARG(QString, QString::fromStdString(sceneCollectionName)));
bool success = obs_frontend_add_scene_collection(sceneCollectionName.c_str());
if (!success)
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene collection.");
@ -599,9 +598,9 @@ RequestResult RequestHandler::SetStreamServiceSettings(const Request &request)
obs_service_update(currentStreamService, newStreamServiceSettings);
} else {
// TODO: This leaks memory. I have no idea why.
OBSService newStreamService = obs_service_create(requestedStreamServiceType.c_str(), "obs_websocket_custom_service",
requestedStreamServiceSettings, nullptr);
OBSServiceAutoRelease 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.
if (!newStreamService)
return RequestResult::Error(

View File

@ -19,10 +19,32 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "RequestHandler.h"
/**
* Gets an array of all available source filter kinds.
*
* Similar to `GetInputKindList`
*
* @responseField sourceFilterKinds | Array<String> | Array of source filter kinds
*
* @requestType GetSourceFilterKindList
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.4.0
* @api requests
* @category filters
*/
RequestResult RequestHandler::GetSourceFilterKindList(const Request &)
{
json responseData;
responseData["sourceFilterKinds"] = Utils::Obs::ArrayHelper::GetFilterKindList();
return RequestResult::Success(responseData);
}
/**
* Gets an array of all of a source's filters.
*
* @requestField sourceName | String | Name of the source
* @requestField ?sourceName | String | Name of the source
* @requestField ?sourceUuid | String | UUID of the source
*
* @responseField filters | Array<Object> | Array of filters
*
@ -37,7 +59,7 @@ RequestResult RequestHandler::GetSourceFilterList(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
OBSSourceAutoRelease source = request.ValidateSource("sourceName", "sourceUuid", statusCode, comment);
if (!source)
return RequestResult::Error(statusCode, comment);
@ -85,7 +107,8 @@ RequestResult RequestHandler::GetSourceFilterDefaultSettings(const Request &requ
/**
* Creates a new filter, adding it to the specified source.
*
* @requestField sourceName | String | Name of the source to add the filter to
* @requestField ?sourceName | String | Name of the source to add the filter to
* @requestField ?sourceUuid | String | UUID of the source to add the filter to
* @requestField filterName | String | Name of the new filter to be created
* @requestField filterKind | String | The kind of filter to be created
* @requestField ?filterSettings | Object | Settings object to initialize the filter with | Default settings used
@ -102,7 +125,7 @@ RequestResult RequestHandler::CreateSourceFilter(const Request &request)
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
OBSSourceAutoRelease source = request.ValidateSource("sourceName", "sourceUuid", statusCode, comment);
if (!(source && request.ValidateString("filterName", statusCode, comment) &&
request.ValidateString("filterKind", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -138,8 +161,9 @@ RequestResult RequestHandler::CreateSourceFilter(const Request &request)
/**
* Removes a filter from a source.
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField filterName | String | Name of the filter to remove
* @requestField ?sourceName | String | Name of the source the filter is on
* @requestField ?sourceUuid | String | UUID of the source the filter is on
* @requestField filterName | String | Name of the filter to remove
*
* @requestType RemoveSourceFilter
* @complexity 2
@ -152,7 +176,7 @@ RequestResult RequestHandler::RemoveSourceFilter(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
FilterPair pair = request.ValidateFilter(statusCode, comment);
if (!pair.filter)
return RequestResult::Error(statusCode, comment);
@ -164,7 +188,8 @@ RequestResult RequestHandler::RemoveSourceFilter(const Request &request)
/**
* Sets the name of a source filter (rename).
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField ?sourceName | String | Name of the source the filter is on
* @requestField ?sourceUuid | String | UUID of the source the filter is on
* @requestField filterName | String | Current name of the filter
* @requestField newFilterName | String | New name for the filter
*
@ -179,7 +204,7 @@ RequestResult RequestHandler::SetSourceFilterName(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
FilterPair pair = request.ValidateFilter(statusCode, comment);
if (!pair.filter || !request.ValidateString("newFilterName", statusCode, comment))
return RequestResult::Error(statusCode, comment);
@ -197,8 +222,9 @@ RequestResult RequestHandler::SetSourceFilterName(const Request &request)
/**
* Gets the info for a specific source filter.
*
* @requestField sourceName | String | Name of the source
* @requestField filterName | String | Name of the filter
* @requestField ?sourceName | String | Name of the source
* @requestField ?sourceUuid | String | UUID of the source
* @requestField filterName | String | Name of the filter
*
* @responseField filterEnabled | Boolean | Whether the filter is enabled
* @responseField filterIndex | Number | Index of the filter in the list, beginning at 0
@ -216,7 +242,7 @@ RequestResult RequestHandler::GetSourceFilter(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
FilterPair pair = request.ValidateFilter(statusCode, comment);
if (!pair.filter)
return RequestResult::Error(statusCode, comment);
@ -236,7 +262,8 @@ RequestResult RequestHandler::GetSourceFilter(const Request &request)
/**
* Sets the index position of a filter on a source.
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField ?sourceName | String | Name of the source the filter is on
* @requestField ?sourceUuid | String | UUID of the source the filter is on
* @requestField filterName | String | Name of the filter
* @requestField filterIndex | Number | New index position of the filter | >= 0
*
@ -251,7 +278,7 @@ RequestResult RequestHandler::SetSourceFilterIndex(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
FilterPair pair = request.ValidateFilter(statusCode, comment);
if (!(pair.filter && request.ValidateNumber("filterIndex", statusCode, comment, 0, 8192)))
return RequestResult::Error(statusCode, comment);
@ -265,7 +292,8 @@ RequestResult RequestHandler::SetSourceFilterIndex(const Request &request)
/**
* Sets the settings of a source filter.
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField ?sourceName | String | Name of the source the filter is on
* @requestField ?sourceUuid | String | UUID of the source the filter is on
* @requestField filterName | String | Name of the filter to set the settings of
* @requestField filterSettings | Object | Object of settings to apply
* @requestField ?overlay | Boolean | True == apply the settings on top of existing ones, False == reset the input to its defaults, then apply settings. | true
@ -281,7 +309,7 @@ RequestResult RequestHandler::SetSourceFilterSettings(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
FilterPair pair = request.ValidateFilter(statusCode, comment);
if (!(pair.filter && request.ValidateObject("filterSettings", statusCode, comment, true)))
return RequestResult::Error(statusCode, comment);
@ -313,7 +341,8 @@ RequestResult RequestHandler::SetSourceFilterSettings(const Request &request)
/**
* Sets the enable state of a source filter.
*
* @requestField sourceName | String | Name of the source the filter is on
* @requestField ?sourceName | String | Name of the source the filter is on
* @requestField ?sourceUuid | String | UUID of the source the filter is on
* @requestField filterName | String | Name of the filter
* @requestField filterEnabled | Boolean | New enable state of the filter
*
@ -328,7 +357,7 @@ RequestResult RequestHandler::SetSourceFilterEnabled(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
FilterPair pair = request.ValidateFilter("sourceName", "filterName", statusCode, comment);
FilterPair pair = request.ValidateFilter(statusCode, comment);
if (!(pair.filter && request.ValidateBoolean("filterEnabled", statusCode, comment)))
return RequestResult::Error(statusCode, comment);

View File

@ -211,12 +211,14 @@ RequestResult RequestHandler::CallVendorRequest(const Request &request)
}
/**
* Gets an array of all hotkey names in OBS
* Gets an array of all hotkey names in OBS.
*
* Note: Hotkey functionality in obs-websocket comes as-is, and we do not guarantee support if things are broken. In 9/10 usages of hotkey requests, there exists a better, more reliable method via other requests.
*
* @responseField hotkeys | Array<String> | Array of hotkey names
*
* @requestType GetHotkeyList
* @complexity 3
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @category general
@ -230,12 +232,15 @@ RequestResult RequestHandler::GetHotkeyList(const Request &)
}
/**
* Triggers a hotkey using its name. See `GetHotkeyList`
* Triggers a hotkey using its name. See `GetHotkeyList`.
*
* Note: Hotkey functionality in obs-websocket comes as-is, and we do not guarantee support if things are broken. In 9/10 usages of hotkey requests, there exists a better, more reliable method via other requests.
*
* @requestField hotkeyName | String | Name of the hotkey to trigger
* @requestField ?contextName | String | Name of context of the hotkey to trigger
*
* @requestType TriggerHotkeyByName
* @complexity 3
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @category general
@ -248,11 +253,20 @@ RequestResult RequestHandler::TriggerHotkeyByName(const Request &request)
if (!request.ValidateString("hotkeyName", statusCode, comment))
return RequestResult::Error(statusCode, comment);
obs_hotkey_t *hotkey = Utils::Obs::SearchHelper::GetHotkeyByName(request.RequestData["hotkeyName"]);
std::string contextName;
if (request.Contains("contextName")) {
if (!request.ValidateOptionalString("contextName", statusCode, comment))
return RequestResult::Error(statusCode, comment);
contextName = request.RequestData["contextName"];
}
obs_hotkey_t *hotkey = Utils::Obs::SearchHelper::GetHotkeyByName(request.RequestData["hotkeyName"], contextName);
if (!hotkey)
return RequestResult::Error(RequestStatus::ResourceNotFound, "No hotkeys were found by that name.");
obs_hotkey_trigger_routed_callback(obs_hotkey_get_id(hotkey), true);
obs_hotkey_trigger_routed_callback(obs_hotkey_get_id(hotkey), false);
return RequestResult::Success();
}
@ -260,6 +274,8 @@ RequestResult RequestHandler::TriggerHotkeyByName(const Request &request)
/**
* Triggers a hotkey using a sequence of keys.
*
* Note: Hotkey functionality in obs-websocket comes as-is, and we do not guarantee support if things are broken. In 9/10 usages of hotkey requests, there exists a better, more reliable method via other requests.
*
* @requestField ?keyId | String | The OBS key ID to use. See https://github.com/obsproject/obs-studio/blob/master/libobs/obs-hotkeys.h | Not pressed
* @requestField ?keyModifiers | Object | Object containing key modifiers to apply | Ignored
* @requestField ?keyModifiers.shift | Boolean | Press Shift | Not pressed

View File

@ -123,12 +123,14 @@ RequestResult RequestHandler::GetSpecialInputs(const Request &)
/**
* Creates a new input, adding it as a scene item to the specified scene.
*
* @requestField sceneName | String | Name of the scene to add the input to as a scene item
* @requestField ?sceneName | String | Name of the scene to add the input to as a scene item
* @requestField ?sceneUuid | String | UUID of the scene to add the input to as a scene item
* @requestField inputName | String | Name of the new input to created
* @requestField inputKind | String | The kind of input to be created
* @requestField ?inputSettings | Object | Settings object to initialize the input with | Default settings used
* @requestField ?sceneItemEnabled | Boolean | Whether to set the created scene item to enabled or disabled | True
*
* @responseField inputUuid | String | UUID of the newly created input
* @responseField sceneItemId | Number | ID of the newly created scene item
*
* @requestType CreateInput
@ -142,7 +144,7 @@ RequestResult RequestHandler::CreateInput(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment);
OBSSourceAutoRelease sceneSource = request.ValidateScene(statusCode, comment);
if (!(sceneSource && request.ValidateString("inputName", statusCode, comment) &&
request.ValidateString("inputKind", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -185,6 +187,7 @@ RequestResult RequestHandler::CreateInput(const Request &request)
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the input or scene item failed.");
json responseData;
responseData["inputUuid"] = obs_source_get_uuid(obs_sceneitem_get_source(sceneItem));
responseData["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
return RequestResult::Success(responseData);
}
@ -194,7 +197,8 @@ RequestResult RequestHandler::CreateInput(const Request &request)
*
* Note: Will immediately remove all associated scene items.
*
* @requestField inputName | String | Name of the input to remove
* @requestField ?inputName | String | Name of the input to remove
* @requestField ?inputUuid | String | UUID of the input to remove
*
* @requestType RemoveInput
* @complexity 2
@ -207,7 +211,7 @@ RequestResult RequestHandler::RemoveInput(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -222,7 +226,8 @@ RequestResult RequestHandler::RemoveInput(const Request &request)
/**
* Sets the name of an input (rename).
*
* @requestField inputName | String | Current input name
* @requestField ?inputName | String | Current input name
* @requestField ?inputUuid | String | Current input UUID
* @requestField newInputName | String | New name for the input
*
* @requestType SetInputName
@ -236,7 +241,7 @@ RequestResult RequestHandler::SetInputName(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!(input && request.ValidateString("newInputName", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -292,7 +297,8 @@ RequestResult RequestHandler::GetInputDefaultSettings(const Request &request)
*
* Note: Does not include defaults. To create the entire settings object, overlay `inputSettings` over the `defaultInputSettings` provided by `GetInputDefaultSettings`.
*
* @requestField inputName | String | Name of the input to get the settings of
* @requestField ?inputName | String | Name of the input to get the settings of
* @requestField ?inputUuid | String | UUID of the input to get the settings of
*
* @responseField inputSettings | Object | Object of settings for the input
* @responseField inputKind | String | The kind of the input
@ -308,7 +314,7 @@ RequestResult RequestHandler::GetInputSettings(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -323,7 +329,8 @@ RequestResult RequestHandler::GetInputSettings(const Request &request)
/**
* Sets the settings of an input.
*
* @requestField inputName | String | Name of the input to set the settings of
* @requestField ?inputName | String | Name of the input to set the settings of
* @requestField ?inputUuid | String | UUID of the input to set the settings of
* @requestField inputSettings | Object | Object of settings to apply
* @requestField ?overlay | Boolean | True == apply the settings on top of existing ones, False == reset the input to its defaults, then apply settings. | true
*
@ -338,7 +345,7 @@ RequestResult RequestHandler::SetInputSettings(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!(input && request.ValidateObject("inputSettings", statusCode, comment, true)))
return RequestResult::Error(statusCode, comment);
@ -373,7 +380,8 @@ RequestResult RequestHandler::SetInputSettings(const Request &request)
/**
* Gets the audio mute state of an input.
*
* @requestField inputName | String | Name of input to get the mute state of
* @requestField ?inputName | String | Name of input to get the mute state of
* @requestField ?inputUuid | String | UUID of input to get the mute state of
*
* @responseField inputMuted | Boolean | Whether the input is muted
*
@ -388,7 +396,7 @@ RequestResult RequestHandler::GetInputMute(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -403,7 +411,8 @@ RequestResult RequestHandler::GetInputMute(const Request &request)
/**
* Sets the audio mute state of an input.
*
* @requestField inputName | String | Name of the input to set the mute state of
* @requestField ?inputName | String | Name of the input to set the mute state of
* @requestField ?inputUuid | String | UUID of the input to set the mute state of
* @requestField inputMuted | Boolean | Whether to mute the input or not
*
* @requestType SetInputMute
@ -417,7 +426,7 @@ RequestResult RequestHandler::SetInputMute(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!(input && request.ValidateBoolean("inputMuted", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -432,7 +441,8 @@ RequestResult RequestHandler::SetInputMute(const Request &request)
/**
* Toggles the audio mute state of an input.
*
* @requestField inputName | String | Name of the input to toggle the mute state of
* @requestField ?inputName | String | Name of the input to toggle the mute state of
* @requestField ?inputUuid | String | UUID of the input to toggle the mute state of
*
* @responseField inputMuted | Boolean | Whether the input has been muted or unmuted
*
@ -447,7 +457,7 @@ RequestResult RequestHandler::ToggleInputMute(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -465,7 +475,8 @@ RequestResult RequestHandler::ToggleInputMute(const Request &request)
/**
* Gets the current volume setting of an input.
*
* @requestField inputName | String | Name of the input to get the volume of
* @requestField ?inputName | String | Name of the input to get the volume of
* @requestField ?inputUuid | String | UUID of the input to get the volume of
*
* @responseField inputVolumeMul | Number | Volume setting in mul
* @responseField inputVolumeDb | Number | Volume setting in dB
@ -481,7 +492,7 @@ RequestResult RequestHandler::GetInputVolume(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -502,7 +513,8 @@ RequestResult RequestHandler::GetInputVolume(const Request &request)
/**
* Sets the volume setting of an input.
*
* @requestField inputName | String | Name of the input to set the volume of
* @requestField ?inputName | String | Name of the input to set the volume of
* @requestField ?inputUuid | String | UUID of the input to set the volume of
* @requestField ?inputVolumeMul | Number | Volume setting in mul | >= 0, <= 20 | `inputVolumeDb` should be specified
* @requestField ?inputVolumeDb | Number | Volume setting in dB | >= -100, <= 26 | `inputVolumeMul` should be specified
*
@ -517,7 +529,7 @@ RequestResult RequestHandler::SetInputVolume(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -552,7 +564,8 @@ RequestResult RequestHandler::SetInputVolume(const Request &request)
/**
* Gets the audio balance of an input.
*
* @requestField inputName | String | Name of the input to get the audio balance of
* @requestField ?inputName | String | Name of the input to get the audio balance of
* @requestField ?inputUuid | String | UUID of the input to get the audio balance of
*
* @responseField inputAudioBalance | Number | Audio balance value from 0.0-1.0
*
@ -567,7 +580,7 @@ RequestResult RequestHandler::GetInputAudioBalance(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -583,7 +596,8 @@ RequestResult RequestHandler::GetInputAudioBalance(const Request &request)
/**
* Sets the audio balance of an input.
*
* @requestField inputName | String | Name of the input to set the audio balance of
* @requestField ?inputName | String | Name of the input to set the audio balance of
* @requestField ?inputUuid | String | UUID of the input to set the audio balance of
* @requestField inputAudioBalance | Number | New audio balance value | >= 0.0, <= 1.0
*
* @requestType SetInputAudioBalance
@ -597,7 +611,7 @@ RequestResult RequestHandler::SetInputAudioBalance(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!(input && request.ValidateNumber("inputAudioBalance", statusCode, comment, 0.0, 1.0)))
return RequestResult::Error(statusCode, comment);
@ -615,7 +629,8 @@ RequestResult RequestHandler::SetInputAudioBalance(const Request &request)
*
* Note: The audio sync offset can be negative too!
*
* @requestField inputName | String | Name of the input to get the audio sync offset of
* @requestField ?inputName | String | Name of the input to get the audio sync offset of
* @requestField ?inputUuid | String | UUID of the input to get the audio sync offset of
*
* @responseField inputAudioSyncOffset | Number | Audio sync offset in milliseconds
*
@ -630,7 +645,7 @@ RequestResult RequestHandler::GetInputAudioSyncOffset(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -647,7 +662,8 @@ RequestResult RequestHandler::GetInputAudioSyncOffset(const Request &request)
/**
* Sets the audio sync offset of an input.
*
* @requestField inputName | String | Name of the input to set the audio sync offset of
* @requestField ?inputName | String | Name of the input to set the audio sync offset of
* @requestField ?inputUuid | String | UUID of the input to set the audio sync offset of
* @requestField inputAudioSyncOffset | Number | New audio sync offset in milliseconds | >= -950, <= 20000
*
* @requestType SetInputAudioSyncOffset
@ -661,7 +677,7 @@ RequestResult RequestHandler::SetInputAudioSyncOffset(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!(input && request.ValidateNumber("inputAudioSyncOffset", statusCode, comment, -950, 20000)))
return RequestResult::Error(statusCode, comment);
@ -683,7 +699,8 @@ RequestResult RequestHandler::SetInputAudioSyncOffset(const Request &request)
* - `OBS_MONITORING_TYPE_MONITOR_ONLY`
* - `OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT`
*
* @requestField inputName | String | Name of the input to get the audio monitor type of
* @requestField ?inputName | String | Name of the input to get the audio monitor type of
* @requestField ?inputUuid | String | UUID of the input to get the audio monitor type of
*
* @responseField monitorType | String | Audio monitor type
*
@ -698,7 +715,7 @@ RequestResult RequestHandler::GetInputAudioMonitorType(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -714,7 +731,8 @@ RequestResult RequestHandler::GetInputAudioMonitorType(const Request &request)
/**
* Sets the audio monitor type of an input.
*
* @requestField inputName | String | Name of the input to set the audio monitor type of
* @requestField ?inputName | String | Name of the input to set the audio monitor type of
* @requestField ?inputUuid | String | UUID of the input to set the audio monitor type of
* @requestField monitorType | String | Audio monitor type
*
* @requestType SetInputAudioMonitorType
@ -728,7 +746,7 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!(input && request.ValidateString("monitorType", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -759,7 +777,8 @@ RequestResult RequestHandler::SetInputAudioMonitorType(const Request &request)
/**
* Gets the enable state of all audio tracks of an input.
*
* @requestField inputName | String | Name of the input
* @requestField ?inputName | String | Name of the input
* @requestField ?inputUuid | String | UUID of the input
*
* @responseField inputAudioTracks | Object | Object of audio tracks and associated enable states
*
@ -774,7 +793,7 @@ RequestResult RequestHandler::GetInputAudioTracks(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -797,7 +816,8 @@ RequestResult RequestHandler::GetInputAudioTracks(const Request &request)
/**
* Sets the enable state of audio tracks of an input.
*
* @requestField inputName | String | Name of the input
* @requestField ?inputName | String | Name of the input
* @requestField ?inputUuid | String | UUID of the input
* @requestField inputAudioTracks | Object | Track settings to apply
*
* @requestType SetInputAudioTracks
@ -811,7 +831,7 @@ RequestResult RequestHandler::SetInputAudioTracks(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input || !request.ValidateObject("inputAudioTracks", statusCode, comment))
return RequestResult::Error(statusCode, comment);
@ -851,7 +871,8 @@ RequestResult RequestHandler::SetInputAudioTracks(const Request &request)
*
* Note: Use this in cases where an input provides a dynamic, selectable list of items. For example, display capture, where it provides a list of available displays.
*
* @requestField inputName | String | Name of the input
* @requestField ?inputName | String | Name of the input
* @requestField ?inputUuid | String | UUID of the input
* @requestField propertyName | String | Name of the list property to get the items of
*
* @responseField propertyItems | Array<Object> | Array of items in the list property
@ -867,7 +888,7 @@ RequestResult RequestHandler::GetInputPropertiesListPropertyItems(const Request
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!(input && request.ValidateString("propertyName", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -895,7 +916,8 @@ RequestResult RequestHandler::GetInputPropertiesListPropertyItems(const Request
*
* Note: Use this in cases where there is a button in the properties of an input that cannot be accessed in any other way. For example, browser sources, where there is a refresh button.
*
* @requestField inputName | String | Name of the input
* @requestField ?inputName | String | Name of the input
* @requestField ?inputUuid | String | UUID of the input
* @requestField propertyName | String | Name of the button property to press
*
* @requestType PressInputPropertiesButton
@ -909,7 +931,7 @@ RequestResult RequestHandler::PressInputPropertiesButton(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!(input && request.ValidateString("propertyName", statusCode, comment)))
return RequestResult::Error(statusCode, comment);

View File

@ -39,7 +39,8 @@ bool IsMediaTimeValid(obs_source_t *input)
* - `OBS_MEDIA_STATE_ENDED`
* - `OBS_MEDIA_STATE_ERROR`
*
* @requestField inputName | String | Name of the media input
* @requestField ?inputName | String | Name of the media input
* @requestField ?inputUuid | String | UUID of the media input
*
* @responseField mediaState | String | State of the media input
* @responseField mediaDuration | Number | Total duration of the playing media in milliseconds. `null` if not playing
@ -56,7 +57,7 @@ RequestResult RequestHandler::GetMediaInputStatus(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -80,7 +81,8 @@ RequestResult RequestHandler::GetMediaInputStatus(const Request &request)
*
* This request does not perform bounds checking of the cursor position.
*
* @requestField inputName | String | Name of the media input
* @requestField ?inputName | String | Name of the media input
* @requestField ?inputUuid | String | UUID of the media input
* @requestField mediaCursor | Number | New cursor position to set | >= 0
*
* @requestType SetMediaInputCursor
@ -94,7 +96,7 @@ RequestResult RequestHandler::SetMediaInputCursor(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!(input && request.ValidateNumber("mediaCursor", statusCode, comment, 0)))
return RequestResult::Error(statusCode, comment);
@ -115,7 +117,8 @@ RequestResult RequestHandler::SetMediaInputCursor(const Request &request)
*
* This request does not perform bounds checking of the cursor position.
*
* @requestField inputName | String | Name of the media input
* @requestField ?inputName | String | Name of the media input
* @requestField ?inputUuid | String | UUID of the media input
* @requestField mediaCursorOffset | Number | Value to offset the current cursor position by | None
*
* @requestType OffsetMediaInputCursor
@ -129,7 +132,7 @@ RequestResult RequestHandler::OffsetMediaInputCursor(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!(input && request.ValidateNumber("mediaCursorOffset", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -151,7 +154,8 @@ RequestResult RequestHandler::OffsetMediaInputCursor(const Request &request)
/**
* Triggers an action on a media input.
*
* @requestField inputName | String | Name of the media input
* @requestField ?inputName | String | Name of the media input
* @requestField ?inputUuid | String | UUID of the media input
* @requestField mediaAction | String | Identifier of the `ObsMediaInputAction` enum
*
* @requestType TriggerMediaInputAction
@ -165,7 +169,7 @@ RequestResult RequestHandler::TriggerMediaInputAction(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!(input && request.ValidateString("mediaAction", statusCode, comment)))
return RequestResult::Error(statusCode, comment);

View File

@ -21,11 +21,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
static bool VirtualCamAvailable()
{
OBSDataAutoRelease privateData = obs_get_private_data();
if (!privateData)
return false;
return obs_data_get_bool(privateData, "vcamEnabled");
OBSOutputAutoRelease output = obs_frontend_get_virtualcam_output();
return output != nullptr;
}
static bool ReplayBufferAvailable()

View File

@ -54,6 +54,8 @@ RequestResult RequestHandler::GetRecordStatus(const Request &)
/**
* Toggles the status of the record output.
*
* @responseField outputActive | Boolean | The new active state of the output
*
* @requestType ToggleRecord
* @complexity 1
* @rpcVersion -1
@ -187,3 +189,60 @@ RequestResult RequestHandler::ResumeRecord(const Request &)
return RequestResult::Success();
}
/**
* Splits the current file being recorded into a new file.
*
* @requestType SplitRecordFile
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.5.0
* @api requests
* @category record
*/
RequestResult RequestHandler::SplitRecordFile(const Request &)
{
if (!obs_frontend_recording_active())
return RequestResult::Error(RequestStatus::OutputNotRunning);
if (!obs_frontend_recording_split_file())
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
"Verify that file splitting is enabled in the output settings.");
return RequestResult::Success();
}
/**
* Adds a new chapter marker to the file currently being recorded.
*
* Note: As of OBS 30.2.0, the only file format supporting this feature is Hybrid MP4.
*
* @requestField ?chapterName | String | Name of the new chapter
*
* @requestType CreateRecordChapter
* @complexity 2
* @rpcVersion -1
* @initialVersion 5.5.0
* @api requests
* @category record
*/
RequestResult RequestHandler::CreateRecordChapter(const Request &request)
{
std::string chapterName;
if (request.Contains("chapterName")) {
RequestStatus::RequestStatus statusCode;
std::string comment;
if (!request.ValidateOptionalString("chapterName", statusCode, comment))
return RequestResult::Error(statusCode, comment);
chapterName = request.RequestData["chapterName"];
}
if (!obs_frontend_recording_active())
return RequestResult::Error(RequestStatus::OutputNotRunning);
if (!obs_frontend_recording_add_chapter(chapterName.empty() ? nullptr : chapterName.c_str()))
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
"Verify that the output being used supports chapter markers.");
return RequestResult::Success();
}

View File

@ -24,7 +24,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
*
* Scenes only
*
* @requestField sceneName | String | Name of the scene to get the items of
* @requestField ?sceneName | String | Name of the scene to get the items of
* @requestField ?sceneUuid | String | UUID of the scene to get the items of
*
* @responseField sceneItems | Array<Object> | Array of scene items in the scene
*
@ -39,7 +40,7 @@ RequestResult RequestHandler::GetSceneItemList(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
OBSSourceAutoRelease scene = request.ValidateScene(statusCode, comment);
if (!scene)
return RequestResult::Error(statusCode, comment);
@ -56,7 +57,8 @@ RequestResult RequestHandler::GetSceneItemList(const Request &request)
*
* Groups only
*
* @requestField sceneName | String | Name of the group to get the items of
* @requestField ?sceneName | String | Name of the group to get the items of
* @requestField ?sceneUuid | String | UUID of the group to get the items of
*
* @responseField sceneItems | Array<Object> | Array of scene items in the group
*
@ -71,7 +73,7 @@ RequestResult RequestHandler::GetGroupSceneItemList(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY);
OBSSourceAutoRelease scene = request.ValidateScene(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_GROUP_ONLY);
if (!scene)
return RequestResult::Error(statusCode, comment);
@ -86,7 +88,8 @@ RequestResult RequestHandler::GetGroupSceneItemList(const Request &request)
*
* Scenes and Groups
*
* @requestField sceneName | String | Name of the scene or group to search in
* @requestField ?sceneName | String | Name of the scene or group to search in
* @requestField ?sceneUuid | String | UUID of the scene or group to search in
* @requestField sourceName | String | Name of the source to find
* @requestField ?searchOffset | Number | Number of matches to skip during search. >= 0 means first forward. -1 means last (top) item | >= -1 | 0
*
@ -103,9 +106,8 @@ RequestResult RequestHandler::GetSceneItemId(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneAutoRelease scene =
request.ValidateScene2("sceneName", statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(scene && request.ValidateString("sourceName", statusCode, comment)))
OBSSceneAutoRelease scene = request.ValidateScene2(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(scene && request.ValidateString("sourceName", statusCode, comment))) // TODO: Source UUID support
return RequestResult::Error(statusCode, comment);
std::string sourceName = request.RequestData["sourceName"];
@ -128,13 +130,49 @@ RequestResult RequestHandler::GetSceneItemId(const Request &request)
return RequestResult::Success(responseData);
}
/**
* Gets the source associated with a scene item.
*
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
*
* @responseField sourceName | String | Name of the source associated with the scene item
* @responseField sourceUuid | String | UUID of the source associated with the scene item
*
* @requestType GetSceneItemSource
* @complexity 3
* @rpcVersion -1
* @initialVersion 5.4.0
* @api requests
* @category scene items
*/
RequestResult RequestHandler::GetSceneItemSource(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(statusCode, comment);
if (!sceneItem)
return RequestResult::Error(statusCode, comment);
OBSSource source = obs_sceneitem_get_source(sceneItem);
json responseData;
responseData["sourceName"] = obs_source_get_name(source);
responseData["sourceUuid"] = obs_source_get_uuid(source);
return RequestResult::Success(responseData);
}
/**
* Creates a new scene item using a source.
*
* Scenes only
*
* @requestField sceneName | String | Name of the scene to create the new item in
* @requestField sourceName | String | Name of the source to add to the scene
* @requestField ?sceneName | String | Name of the scene to create the new item in
* @requestField ?sceneUuid | String | UUID of the scene to create the new item in
* @requestField ?sourceName | String | Name of the source to add to the scene
* @requestField ?sourceUuid | String | UUID of the source to add to the scene
* @requestField ?sceneItemEnabled | Boolean | Enable state to apply to the scene item on creation | True
*
* @responseField sceneItemId | Number | Numeric ID of the scene item
@ -150,17 +188,17 @@ RequestResult RequestHandler::CreateSceneItem(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment);
OBSSourceAutoRelease sceneSource = request.ValidateScene(statusCode, comment);
if (!sceneSource)
return RequestResult::Error(statusCode, comment);
OBSScene scene = obs_scene_from_source(sceneSource);
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
OBSSourceAutoRelease source = request.ValidateSource("sourceName", "sourceUuid", statusCode, comment);
if (!source)
return RequestResult::Error(statusCode, comment);
if (request.RequestData["sceneName"] == request.RequestData["sourceName"])
if (sceneSource == source)
return RequestResult::Error(RequestStatus::CannotAct, "You cannot create scene item of a scene within itself.");
bool sceneItemEnabled = true;
@ -185,7 +223,8 @@ RequestResult RequestHandler::CreateSceneItem(const Request &request)
*
* Scenes only
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
*
* @requestType RemoveSceneItem
@ -199,7 +238,7 @@ RequestResult RequestHandler::RemoveSceneItem(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment);
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(statusCode, comment);
if (!sceneItem)
return RequestResult::Error(statusCode, comment);
@ -214,9 +253,11 @@ RequestResult RequestHandler::RemoveSceneItem(const Request &request)
*
* Scenes only
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
* @requestField ?destinationSceneName | String | Name of the scene to create the duplicated item in | `sceneName` is assumed
* @requestField ?destinationSceneName | String | Name of the scene to create the duplicated item in | From scene is assumed
* @requestField ?destinationSceneUuid | String | UUID of the scene to create the duplicated item in | From scene is assumed
*
* @responseField sceneItemId | Number | Numeric ID of the duplicated scene item
*
@ -231,16 +272,26 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment);
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem(statusCode, comment);
if (!sceneItem)
return RequestResult::Error(statusCode, comment);
// Get destination scene
obs_scene_t *destinationScene;
if (request.Contains("destinationSceneName")) {
destinationScene = request.ValidateScene2("destinationSceneName", statusCode, comment);
if (!destinationScene)
OBSSourceAutoRelease destinationSceneSource =
request.ValidateSource("destinationSceneName", "destinationSceneUuid", statusCode, comment);
if (!destinationSceneSource)
return RequestResult::Error(statusCode, comment);
// Reimplementation of ValidateScene2
if (obs_source_get_type(destinationSceneSource) != OBS_SOURCE_TYPE_SCENE)
return RequestResult::Error(RequestStatus::InvalidResourceType, "The specified source is not a scene.");
if (obs_source_is_group(destinationSceneSource))
return RequestResult::Error(RequestStatus::InvalidResourceType,
"The specified source is not a scene. (Is group)");
destinationScene = obs_scene_get_ref(obs_scene_from_source(destinationSceneSource));
} else {
destinationScene = obs_scene_get_ref(obs_sceneitem_get_scene(sceneItem));
if (!destinationScene)
@ -258,7 +309,7 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request &request)
bool sceneItemEnabled = obs_sceneitem_visible(sceneItem);
obs_transform_info sceneItemTransform;
obs_sceneitem_crop sceneItemCrop;
obs_sceneitem_get_info(sceneItem, &sceneItemTransform);
obs_sceneitem_get_info2(sceneItem, &sceneItemTransform);
obs_sceneitem_get_crop(sceneItem, &sceneItemCrop);
// Create the new item
@ -279,7 +330,8 @@ RequestResult RequestHandler::DuplicateSceneItem(const Request &request)
*
* Scenes and Groups
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
*
* @responseField sceneItemTransform | Object | Object containing scene item transform info
@ -295,8 +347,8 @@ RequestResult RequestHandler::GetSceneItemTransform(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem)
return RequestResult::Error(statusCode, comment);
@ -309,7 +361,8 @@ RequestResult RequestHandler::GetSceneItemTransform(const Request &request)
/**
* Sets the transform and crop info of a scene item.
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
* @requestField sceneItemTransform | Object | Object containing scene item transform info to update
*
@ -324,8 +377,8 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(sceneItem && request.ValidateObject("sceneItemTransform", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -336,7 +389,7 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
bool cropChanged = false;
obs_transform_info sceneItemTransform;
obs_sceneitem_crop sceneItemCrop;
obs_sceneitem_get_info(sceneItem, &sceneItemTransform);
obs_sceneitem_get_info2(sceneItem, &sceneItemTransform);
obs_sceneitem_get_crop(sceneItem, &sceneItemCrop);
OBSSource source = obs_sceneitem_get_source(sceneItem);
@ -449,11 +502,18 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
cropChanged = true;
}
if (r.Contains("cropToBounds")) {
if (!r.ValidateOptionalBoolean("cropToBounds", statusCode, comment))
return RequestResult::Error(statusCode, comment);
sceneItemTransform.crop_to_bounds = r.RequestData["cropToBounds"];
transformChanged = true;
}
if (!transformChanged && !cropChanged)
return RequestResult::Error(RequestStatus::CannotAct, "You have not provided any valid transform changes.");
if (transformChanged)
obs_sceneitem_set_info(sceneItem, &sceneItemTransform);
obs_sceneitem_set_info2(sceneItem, &sceneItemTransform);
if (cropChanged)
obs_sceneitem_set_crop(sceneItem, &sceneItemCrop);
@ -466,7 +526,8 @@ RequestResult RequestHandler::SetSceneItemTransform(const Request &request)
*
* Scenes and Groups
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
*
* @responseField sceneItemEnabled | Boolean | Whether the scene item is enabled. `true` for enabled, `false` for disabled
@ -482,8 +543,8 @@ RequestResult RequestHandler::GetSceneItemEnabled(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem)
return RequestResult::Error(statusCode, comment);
@ -498,7 +559,8 @@ RequestResult RequestHandler::GetSceneItemEnabled(const Request &request)
*
* Scenes and Groups
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
* @requestField sceneItemEnabled | Boolean | New enable state of the scene item
*
@ -513,8 +575,8 @@ RequestResult RequestHandler::SetSceneItemEnabled(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(sceneItem && request.ValidateBoolean("sceneItemEnabled", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -530,7 +592,8 @@ RequestResult RequestHandler::SetSceneItemEnabled(const Request &request)
*
* Scenes and Groups
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
*
* @responseField sceneItemLocked | Boolean | Whether the scene item is locked. `true` for locked, `false` for unlocked
@ -546,8 +609,8 @@ RequestResult RequestHandler::GetSceneItemLocked(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem)
return RequestResult::Error(statusCode, comment);
@ -562,8 +625,9 @@ RequestResult RequestHandler::GetSceneItemLocked(const Request &request)
*
* Scenes and Group
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
* @requestField sceneItemLocked | Boolean | New lock state of the scene item
*
* @requestType SetSceneItemLocked
@ -577,8 +641,8 @@ RequestResult RequestHandler::SetSceneItemLocked(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(sceneItem && request.ValidateBoolean("sceneItemLocked", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -596,7 +660,8 @@ RequestResult RequestHandler::SetSceneItemLocked(const Request &request)
*
* Scenes and Groups
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
*
* @responseField sceneItemIndex | Number | Index position of the scene item
@ -612,8 +677,8 @@ RequestResult RequestHandler::GetSceneItemIndex(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem)
return RequestResult::Error(statusCode, comment);
@ -628,7 +693,8 @@ RequestResult RequestHandler::GetSceneItemIndex(const Request &request)
*
* Scenes and Groups
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
* @requestField sceneItemIndex | Number | New index position of the scene item | >= 0
*
@ -643,8 +709,8 @@ RequestResult RequestHandler::SetSceneItemIndex(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(sceneItem && request.ValidateNumber("sceneItemIndex", statusCode, comment, 0, 8192)))
return RequestResult::Error(statusCode, comment);
@ -670,7 +736,8 @@ RequestResult RequestHandler::SetSceneItemIndex(const Request &request)
*
* Scenes and Groups
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
*
* @responseField sceneItemBlendMode | String | Current blend mode
@ -686,8 +753,8 @@ RequestResult RequestHandler::GetSceneItemBlendMode(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem)
return RequestResult::Error(statusCode, comment);
@ -704,7 +771,8 @@ RequestResult RequestHandler::GetSceneItemBlendMode(const Request &request)
*
* Scenes and Groups
*
* @requestField sceneName | String | Name of the scene the item is in
* @requestField ?sceneName | String | Name of the scene the item is in
* @requestField ?sceneUuid | String | UUID of the scene the item is in
* @requestField sceneItemId | Number | Numeric ID of the scene item | >= 0
* @requestField sceneItemBlendMode | String | New blend mode
*
@ -719,8 +787,8 @@ RequestResult RequestHandler::SetSceneItemBlendMode(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!(sceneItem && request.ValidateString("sceneItemBlendMode", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -739,8 +807,8 @@ RequestResult RequestHandler::GetSceneItemPrivateSettings(const Request &request
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem)
return RequestResult::Error(statusCode, comment);
@ -757,8 +825,8 @@ RequestResult RequestHandler::SetSceneItemPrivateSettings(const Request &request
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSceneItemAutoRelease sceneItem = request.ValidateSceneItem("sceneName", "sceneItemId", statusCode, comment,
OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
OBSSceneItemAutoRelease sceneItem =
request.ValidateSceneItem(statusCode, comment, OBS_WEBSOCKET_SCENE_FILTER_SCENE_OR_GROUP);
if (!sceneItem || !request.ValidateObject("sceneItemSettings", statusCode, comment, true))
return RequestResult::Error(statusCode, comment);

View File

@ -22,8 +22,10 @@ with this program. If not, see <https://www.gnu.org/licenses/>
/**
* Gets an array of all scenes in OBS.
*
* @responseField currentProgramSceneName | String | Current program scene
* @responseField currentPreviewSceneName | String | Current preview scene. `null` if not in studio mode
* @responseField currentProgramSceneName | String | Current program scene name. Can be `null` if internal state desync
* @responseField currentProgramSceneUuid | String | Current program scene UUID. Can be `null` if internal state desync
* @responseField currentPreviewSceneName | String | Current preview scene name. `null` if not in studio mode
* @responseField currentPreviewSceneUuid | String | Current preview scene UUID. `null` if not in studio mode
* @responseField scenes | Array<Object> | Array of scenes
*
* @requestType GetSceneList
@ -38,16 +40,22 @@ RequestResult RequestHandler::GetSceneList(const Request &)
json responseData;
OBSSourceAutoRelease currentProgramScene = obs_frontend_get_current_scene();
if (currentProgramScene)
if (currentProgramScene) {
responseData["currentProgramSceneName"] = obs_source_get_name(currentProgramScene);
else
responseData["currentProgramSceneUuid"] = obs_source_get_uuid(currentProgramScene);
} else {
responseData["currentProgramSceneName"] = nullptr;
responseData["currentProgramSceneUuid"] = nullptr;
}
OBSSourceAutoRelease currentPreviewScene = obs_frontend_get_current_preview_scene();
if (currentPreviewScene)
if (currentPreviewScene) {
responseData["currentPreviewSceneName"] = obs_source_get_name(currentPreviewScene);
else
responseData["currentPreviewSceneUuid"] = obs_source_get_uuid(currentPreviewScene);
} else {
responseData["currentPreviewSceneName"] = nullptr;
responseData["currentPreviewSceneUuid"] = nullptr;
}
responseData["scenes"] = Utils::Obs::ArrayHelper::GetSceneList();
@ -80,7 +88,12 @@ RequestResult RequestHandler::GetGroupList(const Request &)
/**
* Gets the current program scene.
*
* @responseField currentProgramSceneName | String | Current program scene
* Note: This request is slated to have the `currentProgram`-prefixed fields removed from in an upcoming RPC version.
*
* @responseField sceneName | String | Current program scene name
* @responseField sceneUuid | String | Current program scene UUID
* @responseField currentProgramSceneName | String | Current program scene name (Deprecated)
* @responseField currentProgramSceneUuid | String | Current program scene UUID (Deprecated)
*
* @requestType GetCurrentProgramScene
* @complexity 1
@ -93,7 +106,8 @@ RequestResult RequestHandler::GetCurrentProgramScene(const Request &)
{
json responseData;
OBSSourceAutoRelease currentProgramScene = obs_frontend_get_current_scene();
responseData["currentProgramSceneName"] = obs_source_get_name(currentProgramScene);
responseData["sceneName"] = responseData["currentProgramSceneName"] = obs_source_get_name(currentProgramScene);
responseData["sceneUuid"] = responseData["currentProgramSceneUuid"] = obs_source_get_uuid(currentProgramScene);
return RequestResult::Success(responseData);
}
@ -101,7 +115,8 @@ RequestResult RequestHandler::GetCurrentProgramScene(const Request &)
/**
* Sets the current program scene.
*
* @requestField sceneName | String | Scene to set as the current program scene
* @requestField ?sceneName | String | Scene name to set as the current program scene
* @requestField ?sceneUuid | String | Scene UUID to set as the current program scene
*
* @requestType SetCurrentProgramScene
* @complexity 1
@ -114,7 +129,7 @@ RequestResult RequestHandler::SetCurrentProgramScene(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
OBSSourceAutoRelease scene = request.ValidateScene(statusCode, comment);
if (!scene)
return RequestResult::Error(statusCode, comment);
@ -128,7 +143,12 @@ RequestResult RequestHandler::SetCurrentProgramScene(const Request &request)
*
* Only available when studio mode is enabled.
*
* @responseField currentPreviewSceneName | String | Current preview scene
* Note: This request is slated to have the `currentPreview`-prefixed fields removed from in an upcoming RPC version.
*
* @responseField sceneName | String | Current preview scene name
* @responseField sceneUuid | String | Current preview scene UUID
* @responseField currentPreviewSceneName | String | Current preview scene name
* @responseField currentPreviewSceneUuid | String | Current preview scene UUID
*
* @requestType GetCurrentPreviewScene
* @complexity 1
@ -145,7 +165,8 @@ RequestResult RequestHandler::GetCurrentPreviewScene(const Request &)
OBSSourceAutoRelease currentPreviewScene = obs_frontend_get_current_preview_scene();
json responseData;
responseData["currentPreviewSceneName"] = obs_source_get_name(currentPreviewScene);
responseData["sceneName"] = responseData["currentPreviewSceneName"] = obs_source_get_name(currentPreviewScene);
responseData["sceneUuid"] = responseData["currentPreviewSceneUuid"] = obs_source_get_uuid(currentPreviewScene);
return RequestResult::Success(responseData);
}
@ -155,7 +176,8 @@ RequestResult RequestHandler::GetCurrentPreviewScene(const Request &)
*
* Only available when studio mode is enabled.
*
* @requestField sceneName | String | Scene to set as the current preview scene
* @requestField ?sceneName | String | Scene name to set as the current preview scene
* @requestField ?sceneUuid | String | Scene UUID to set as the current preview scene
*
* @requestType SetCurrentPreviewScene
* @complexity 1
@ -171,7 +193,7 @@ RequestResult RequestHandler::SetCurrentPreviewScene(const Request &request)
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
OBSSourceAutoRelease scene = request.ValidateScene(statusCode, comment);
if (!scene)
return RequestResult::Error(statusCode, comment);
@ -185,6 +207,8 @@ RequestResult RequestHandler::SetCurrentPreviewScene(const Request &request)
*
* @requestField sceneName | String | Name for the new scene
*
* @responseField sceneUuid | String | UUID of the created scene
*
* @requestType CreateScene
* @complexity 2
* @rpcVersion -1
@ -205,19 +229,21 @@ RequestResult RequestHandler::CreateScene(const Request &request)
if (scene)
return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that scene name.");
obs_scene_t *createdScene = obs_scene_create(sceneName.c_str());
OBSSceneAutoRelease createdScene = obs_scene_create(sceneName.c_str());
if (!createdScene)
return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Failed to create the scene.");
obs_scene_release(createdScene);
json responseData;
responseData["sceneUuid"] = obs_source_get_uuid(obs_scene_get_source(createdScene));
return RequestResult::Success();
return RequestResult::Success(responseData);
}
/**
* Removes a scene from OBS.
*
* @requestField sceneName | String | Name of the scene to remove
* @requestField ?sceneName | String | Name of the scene to remove
* @requestField ?sceneUuid | String | UUID of the scene to remove
*
* @requestType RemoveScene
* @complexity 2
@ -230,7 +256,7 @@ RequestResult RequestHandler::RemoveScene(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
OBSSourceAutoRelease scene = request.ValidateScene(statusCode, comment);
if (!scene)
return RequestResult::Error(statusCode, comment);
@ -246,7 +272,8 @@ RequestResult RequestHandler::RemoveScene(const Request &request)
/**
* Sets the name of a scene (rename).
*
* @requestField sceneName | String | Name of the scene to be renamed
* @requestField ?sceneName | String | Name of the scene to be renamed
* @requestField ?sceneUuid | String | UUID of the scene to be renamed
* @requestField newSceneName | String | New name for the scene
*
* @requestType SetSceneName
@ -260,7 +287,7 @@ RequestResult RequestHandler::SetSceneName(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
OBSSourceAutoRelease scene = request.ValidateScene(statusCode, comment);
if (!(scene && request.ValidateString("newSceneName", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -279,7 +306,10 @@ RequestResult RequestHandler::SetSceneName(const Request &request)
/**
* Gets the scene transition overridden for a scene.
*
* @requestField sceneName | String | Name of the scene
* Note: A transition UUID response field is not currently able to be implemented as of 2024-1-18.
*
* @requestField ?sceneName | String | Name of the scene
* @requestField ?sceneUuid | String | UUID of the scene
*
* @responseField transitionName | String | Name of the overridden scene transition, else `null`
* @responseField transitionDuration | Number | Duration of the overridden scene transition, else `null`
@ -295,7 +325,7 @@ RequestResult RequestHandler::GetSceneSceneTransitionOverride(const Request &req
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
OBSSourceAutoRelease scene = request.ValidateScene(statusCode, comment);
if (!scene)
return RequestResult::Error(statusCode, comment);
@ -319,7 +349,8 @@ RequestResult RequestHandler::GetSceneSceneTransitionOverride(const Request &req
/**
* Sets the scene transition overridden for a scene.
*
* @requestField sceneName | String | Name of the scene
* @requestField ?sceneName | String | Name of the scene
* @requestField ?sceneUuid | String | UUID of the scene
* @requestField ?transitionName | String | Name of the scene transition to use as override. Specify `null` to remove | Unchanged
* @requestField ?transitionDuration | Number | Duration to use for any overridden transition. Specify `null` to remove | >= 50, <= 20000 | Unchanged
*
@ -334,7 +365,7 @@ RequestResult RequestHandler::SetSceneSceneTransitionOverride(const Request &req
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease scene = request.ValidateScene("sceneName", statusCode, comment);
OBSSourceAutoRelease scene = request.ValidateScene(statusCode, comment);
if (!scene)
return RequestResult::Error(statusCode, comment);

View File

@ -28,8 +28,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
QImage TakeSourceScreenshot(obs_source_t *source, bool &success, uint32_t requestedWidth = 0, uint32_t requestedHeight = 0)
{
// Get info about the requested source
const uint32_t sourceWidth = obs_source_get_base_width(source);
const uint32_t sourceHeight = obs_source_get_base_height(source);
const uint32_t sourceWidth = obs_source_get_width(source);
const uint32_t sourceHeight = obs_source_get_height(source);
const double sourceAspectRatio = ((double)sourceWidth / (double)sourceHeight);
uint32_t imgWidth = sourceWidth;
@ -114,7 +114,8 @@ bool IsImageFormatValid(std::string format)
*
* **Compatible with inputs and scenes.**
*
* @requestField sourceName | String | Name of the source to get the active state of
* @requestField ?sourceName | String | Name of the source to get the active state of
* @requestField ?sourceUuid | String | UUID of the source to get the active state of
*
* @responseField videoActive | Boolean | Whether the source is showing in Program
* @responseField videoShowing | Boolean | Whether the source is showing in the UI (Preview, Projector, Properties)
@ -130,7 +131,7 @@ RequestResult RequestHandler::GetSourceActive(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
OBSSourceAutoRelease source = request.ValidateSource("sourceName", "sourceUuid", statusCode, comment);
if (!source)
return RequestResult::Error(statusCode, comment);
@ -151,7 +152,8 @@ RequestResult RequestHandler::GetSourceActive(const Request &request)
*
* **Compatible with inputs and scenes.**
*
* @requestField sourceName | String | Name of the source to take a screenshot of
* @requestField ?sourceName | String | Name of the source to take a screenshot of
* @requestField ?sourceUuid | String | UUID of the source to take a screenshot of
* @requestField imageFormat | String | Image compression format to use. Use `GetVersion` to get compatible image formats
* @requestField ?imageWidth | Number | Width to scale the screenshot to | >= 8, <= 4096 | Source value is used
* @requestField ?imageHeight | Number | Height to scale the screenshot to | >= 8, <= 4096 | Source value is used
@ -170,7 +172,7 @@ RequestResult RequestHandler::GetSourceScreenshot(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
OBSSourceAutoRelease source = request.ValidateSource("sourceName", "sourceUuid", statusCode, comment);
if (!(source && request.ValidateString("imageFormat", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -238,15 +240,14 @@ RequestResult RequestHandler::GetSourceScreenshot(const Request &request)
*
* **Compatible with inputs and scenes.**
*
* @requestField sourceName | String | Name of the source to take a screenshot of
* @requestField ?sourceName | String | Name of the source to take a screenshot of
* @requestField ?sourceUuid | String | UUID of the source to take a screenshot of
* @requestField imageFormat | String | Image compression format to use. Use `GetVersion` to get compatible image formats
* @requestField imageFilePath | String | Path to save the screenshot file to. Eg. `C:\Users\user\Desktop\screenshot.png`
* @requestField ?imageWidth | Number | Width to scale the screenshot to | >= 8, <= 4096 | Source value is used
* @requestField ?imageHeight | Number | Height to scale the screenshot to | >= 8, <= 4096 | Source value is used
* @requestField ?imageCompressionQuality | Number | Compression quality to use. 0 for high compression, 100 for uncompressed. -1 to use "default" (whatever that means, idk) | >= -1, <= 100 | -1
*
* @responseField imageData | String | Base64-encoded screenshot
*
* @requestType SaveSourceScreenshot
* @complexity 3
* @rpcVersion -1
@ -258,7 +259,7 @@ RequestResult RequestHandler::SaveSourceScreenshot(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
OBSSourceAutoRelease source = request.ValidateSource("sourceName", "sourceUuid", statusCode, comment);
if (!(source && request.ValidateString("imageFormat", statusCode, comment) &&
request.ValidateString("imageFilePath", statusCode, comment)))
return RequestResult::Error(statusCode, comment);
@ -321,7 +322,7 @@ RequestResult RequestHandler::GetSourcePrivateSettings(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
OBSSourceAutoRelease source = request.ValidateSource("sourceName", "sourceUuid", statusCode, comment);
if (!source)
return RequestResult::Error(statusCode, comment);
@ -338,7 +339,7 @@ RequestResult RequestHandler::SetSourcePrivateSettings(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
OBSSourceAutoRelease source = request.ValidateSource("sourceName", "sourceUuid", statusCode, comment);
if (!source || !request.ValidateObject("sourceSettings", statusCode, comment, true))
return RequestResult::Error(statusCode, comment);

View File

@ -44,12 +44,16 @@ RequestResult RequestHandler::GetStreamStatus(const Request &)
uint64_t outputDuration = Utils::Obs::NumberHelper::GetOutputDuration(streamOutput);
float outputCongestion = obs_output_get_congestion(streamOutput);
if (std::isnan(outputCongestion)) // libobs does not handle NaN, so we're handling it here
outputCongestion = 0.0f;
json responseData;
responseData["outputActive"] = obs_output_active(streamOutput);
responseData["outputReconnecting"] = obs_output_reconnecting(streamOutput);
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
responseData["outputDuration"] = outputDuration;
responseData["outputCongestion"] = obs_output_get_congestion(streamOutput);
responseData["outputCongestion"] = outputCongestion;
responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(streamOutput);
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(streamOutput);
responseData["outputTotalFrames"] = obs_output_get_total_frames(streamOutput);

View File

@ -46,6 +46,7 @@ RequestResult RequestHandler::GetTransitionKindList(const Request &)
* Gets an array of all scene transitions in OBS.
*
* @responseField currentSceneTransitionName | String | Name of the current scene transition. Can be null
* @responseField currentSceneTransitionUuid | String | UUID of the current scene transition. Can be null
* @responseField currentSceneTransitionKind | String | Kind of the current scene transition. Can be null
* @responseField transitions | Array<Object> | Array of transitions
*
@ -63,9 +64,11 @@ RequestResult RequestHandler::GetSceneTransitionList(const Request &)
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
if (transition) {
responseData["currentSceneTransitionName"] = obs_source_get_name(transition);
responseData["currentSceneTransitionUuid"] = obs_source_get_uuid(transition);
responseData["currentSceneTransitionKind"] = obs_source_get_id(transition);
} else {
responseData["currentSceneTransitionName"] = nullptr;
responseData["currentSceneTransitionUuid"] = nullptr;
responseData["currentSceneTransitionKind"] = nullptr;
}
@ -78,6 +81,7 @@ RequestResult RequestHandler::GetSceneTransitionList(const Request &)
* Gets information about the current scene transition.
*
* @responseField transitionName | String | Name of the transition
* @responseField transitionUuid | String | UUID of the transition
* @responseField transitionKind | String | Kind of the transition
* @responseField transitionFixed | Boolean | Whether the transition uses a fixed (unconfigurable) duration
* @responseField transitionDuration | Number | Configured transition duration in milliseconds. `null` if transition is fixed
@ -100,6 +104,7 @@ RequestResult RequestHandler::GetCurrentSceneTransition(const Request &)
json responseData;
responseData["transitionName"] = obs_source_get_name(transition);
responseData["transitionUuid"] = obs_source_get_uuid(transition);
responseData["transitionKind"] = obs_source_get_id(transition);
if (obs_transition_fixed(transition)) {

View File

@ -82,7 +82,8 @@ RequestResult RequestHandler::SetStudioModeEnabled(const Request &request)
/**
* Opens the properties dialog of an input.
*
* @requestField inputName | String | Name of the input to open the dialog of
* @requestField ?inputName | String | Name of the input to open the dialog of
* @requestField ?inputUuid | String | UUID of the input to open the dialog of
*
* @requestType OpenInputPropertiesDialog
* @complexity 1
@ -95,7 +96,7 @@ RequestResult RequestHandler::OpenInputPropertiesDialog(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -107,7 +108,8 @@ RequestResult RequestHandler::OpenInputPropertiesDialog(const Request &request)
/**
* Opens the filters dialog of an input.
*
* @requestField inputName | String | Name of the input to open the dialog of
* @requestField ?inputName | String | Name of the input to open the dialog of
* @requestField ?inputUuid | String | UUID of the input to open the dialog of
*
* @requestType OpenInputFiltersDialog
* @complexity 1
@ -120,7 +122,7 @@ RequestResult RequestHandler::OpenInputFiltersDialog(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -132,7 +134,8 @@ RequestResult RequestHandler::OpenInputFiltersDialog(const Request &request)
/**
* Opens the interact dialog of an input.
*
* @requestField inputName | String | Name of the input to open the dialog of
* @requestField ?inputName | String | Name of the input to open the dialog of
* @requestField ?inputUuid | String | UUID of the input to open the dialog of
*
* @requestType OpenInputInteractDialog
* @complexity 1
@ -145,7 +148,7 @@ RequestResult RequestHandler::OpenInputInteractDialog(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment);
OBSSourceAutoRelease input = request.ValidateInput(statusCode, comment);
if (!input)
return RequestResult::Error(statusCode, comment);
@ -262,7 +265,8 @@ RequestResult RequestHandler::OpenVideoMixProjector(const Request &request)
*
* 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 ?sourceName | String | Name of the source to open a projector for
* @requestField ?sourceUuid | String | UUID 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
*
@ -277,7 +281,7 @@ RequestResult RequestHandler::OpenSourceProjector(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSSourceAutoRelease source = request.ValidateSource("sourceName", statusCode, comment);
OBSSourceAutoRelease source = request.ValidateSource("sourceName", "sourceUuid", statusCode, comment);
if (!source)
return RequestResult::Error(statusCode, comment);

View File

@ -211,28 +211,41 @@ bool Request::ValidateArray(const std::string &keyName, RequestStatus::RequestSt
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 &nameKeyName, const std::string &uuidKeyName,
RequestStatus::RequestStatus &statusCode, std::string &comment) const
{
if (!ValidateString(keyName, statusCode, comment))
return nullptr;
std::string sourceName = RequestData[keyName];
obs_source_t *ret = obs_get_source_by_name(sourceName.c_str());
if (!ret) {
statusCode = RequestStatus::ResourceNotFound;
comment = std::string("No source was found by the name of `") + sourceName + "`.";
return nullptr;
if (ValidateString(nameKeyName, statusCode, comment)) {
std::string sourceName = RequestData[nameKeyName];
obs_source_t *ret = obs_get_source_by_name(sourceName.c_str());
if (!ret) {
statusCode = RequestStatus::ResourceNotFound;
comment = std::string("No source was found by the name of `") + sourceName + "`.";
return nullptr;
}
return ret;
}
return ret;
if (ValidateString(uuidKeyName, statusCode, comment)) {
std::string sourceUuid = RequestData[uuidKeyName];
obs_source_t *ret = obs_get_source_by_uuid(sourceUuid.c_str());
if (!ret) {
statusCode = RequestStatus::ResourceNotFound;
comment = std::string("No source was found by the UUID of `") + sourceUuid + "`.";
return nullptr;
}
return ret;
}
statusCode = RequestStatus::MissingRequestField;
comment = std::string("Your request must contain at least one of the following fields: `") + nameKeyName + "` or `" +
uuidKeyName + "`.";
return nullptr;
}
obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
obs_source_t *Request::ValidateScene(RequestStatus::RequestStatus &statusCode, std::string &comment,
const ObsWebSocketSceneFilter filter) const
{
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
obs_source_t *ret = ValidateSource("sceneName", "sceneUuid", statusCode, comment);
if (!ret)
return nullptr;
@ -259,10 +272,10 @@ obs_source_t *Request::ValidateScene(const std::string &keyName, RequestStatus::
return ret;
}
obs_scene_t *Request::ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
obs_scene_t *Request::ValidateScene2(RequestStatus::RequestStatus &statusCode, std::string &comment,
const ObsWebSocketSceneFilter filter) const
{
OBSSourceAutoRelease sceneSource = ValidateSource(keyName, statusCode, comment);
OBSSourceAutoRelease sceneSource = ValidateSource("sceneName", "sceneUuid", statusCode, comment);
if (!sceneSource)
return nullptr;
@ -290,10 +303,9 @@ 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(RequestStatus::RequestStatus &statusCode, std::string &comment) const
{
obs_source_t *ret = ValidateSource(keyName, statusCode, comment);
obs_source_t *ret = ValidateSource("inputName", "inputUuid", statusCode, comment);
if (!ret)
return nullptr;
@ -307,47 +319,46 @@ obs_source_t *Request::ValidateInput(const std::string &keyName, RequestStatus::
return ret;
}
FilterPair Request::ValidateFilter(const std::string &sourceKeyName, const std::string &filterKeyName,
RequestStatus::RequestStatus &statusCode, std::string &comment) const
FilterPair Request::ValidateFilter(RequestStatus::RequestStatus &statusCode, std::string &comment) const
{
obs_source_t *source = ValidateSource(sourceKeyName, statusCode, comment);
obs_source_t *source = ValidateSource("sourceName", "sourceUuid", statusCode, comment);
if (!source)
return FilterPair{source, nullptr};
if (!ValidateString(filterKeyName, statusCode, comment))
if (!ValidateString("filterName", statusCode, comment))
return FilterPair{source, nullptr};
std::string filterName = RequestData[filterKeyName];
std::string filterName = RequestData["filterName"];
obs_source_t *filter = obs_source_get_filter_by_name(source, filterName.c_str());
if (!filter) {
std::string sourceName = obs_source_get_name(source);
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 `") + sourceName + "` with the name `" + filterName + "`.";
return FilterPair{source, nullptr};
}
return FilterPair{source, filter};
}
obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName,
RequestStatus::RequestStatus &statusCode, std::string &comment,
obs_sceneitem_t *Request::ValidateSceneItem(RequestStatus::RequestStatus &statusCode, std::string &comment,
const ObsWebSocketSceneFilter filter) const
{
OBSSceneAutoRelease scene = ValidateScene2(sceneKeyName, statusCode, comment, filter);
OBSSceneAutoRelease scene = ValidateScene2(statusCode, comment, filter);
if (!scene)
return nullptr;
if (!ValidateNumber(sceneItemIdKeyName, statusCode, comment, 0))
if (!ValidateNumber("sceneItemId", statusCode, comment, 0))
return nullptr;
int64_t sceneItemId = RequestData[sceneItemIdKeyName];
int64_t sceneItemId = RequestData["sceneItemId"];
OBSSceneItem sceneItem = obs_scene_find_sceneitem_by_id(scene, sceneItemId);
if (!sceneItem) {
std::string sceneName = obs_source_get_name(obs_scene_get_source(scene));
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 `") + sceneName + "` with the ID `" +
std::to_string(sceneItemId) + "`.";
return nullptr;
}

View File

@ -64,18 +64,15 @@ struct Request {
const bool allowEmpty = false) const;
// All return values have incremented refcounts
obs_source_t *ValidateSource(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
std::string &comment) const;
obs_source_t *ValidateScene(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
obs_source_t *ValidateSource(const std::string &nameKeyName, const std::string &uuidKeyName,
RequestStatus::RequestStatus &statusCode, std::string &comment) const;
obs_source_t *ValidateScene(RequestStatus::RequestStatus &statusCode, std::string &comment,
const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
obs_scene_t *ValidateScene2(const std::string &keyName, RequestStatus::RequestStatus &statusCode, std::string &comment,
obs_scene_t *ValidateScene2(RequestStatus::RequestStatus &statusCode, std::string &comment,
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,
obs_source_t *ValidateInput(RequestStatus::RequestStatus &statusCode, std::string &comment) const;
FilterPair ValidateFilter(RequestStatus::RequestStatus &statusCode, std::string &comment) const;
obs_sceneitem_t *ValidateSceneItem(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;

View File

@ -21,6 +21,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
RequestBatchRequest::RequestBatchRequest(const std::string &requestType, const json &requestData,
RequestBatchExecutionType::RequestBatchExecutionType executionType,
const json &inputVariables, const json &outputVariables)
: Request(requestType, requestData, executionType), InputVariables(inputVariables), OutputVariables(outputVariables)
: Request(requestType, requestData, executionType),
InputVariables(inputVariables),
OutputVariables(outputVariables)
{
}

View File

@ -20,7 +20,10 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "RequestResult.h"
RequestResult::RequestResult(RequestStatus::RequestStatus statusCode, json responseData, std::string comment)
: StatusCode(statusCode), ResponseData(responseData), Comment(comment), SleepFrames(0)
: StatusCode(statusCode),
ResponseData(responseData),
Comment(comment),
SleepFrames(0)
{
}

View File

@ -77,5 +77,8 @@ namespace RequestBatchExecutionType {
Parallel = 2,
};
inline bool IsValid(int8_t executionType) { return executionType >= None && executionType <= Parallel; }
inline bool IsValid(int8_t executionType)
{
return executionType >= None && executionType <= Parallel;
}
}

View File

@ -17,8 +17,9 @@ 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 <fstream>
#include "Json.h"
#include "Platform.h"
#include "plugin-macros.generated.h"
bool Utils::Json::JsonArrayIsValidObsArray(const json &j)
@ -177,21 +178,43 @@ json Utils::Json::ObsDataToJson(obs_data_t *d, bool includeDefault)
bool Utils::Json::GetJsonFileContent(std::string fileName, json &content)
{
std::string textContent;
if (!Utils::Platform::GetTextFileContent(fileName, textContent))
std::ifstream f(fileName);
if (!f.is_open())
return false;
try {
content = json::parse(textContent);
content = json::parse(f);
} catch (json::parse_error &e) {
blog(LOG_WARNING, "Failed to decode content of JSON file `%s`. Error: %s", fileName.c_str(), e.what());
blog(LOG_WARNING, "[Utils::Json::GetJsonFileContent] Failed to decode content of JSON file `%s`. Error: %s",
fileName.c_str(), e.what());
return false;
}
return true;
}
bool Utils::Json::SetJsonFileContent(std::string fileName, const json &content, bool createNew)
bool Utils::Json::SetJsonFileContent(std::string fileName, const json &content, bool makeDirs)
{
std::string textContent = content.dump(2);
return Utils::Platform::SetTextFileContent(fileName, textContent, createNew);
if (makeDirs) {
std::error_code ec;
auto p = std::filesystem::path(fileName).parent_path();
if (!ec && !std::filesystem::exists(p, ec))
std::filesystem::create_directories(p, ec);
if (ec) {
blog(LOG_ERROR, "[Utils::Json::SetJsonFileContent] Failed to create path directories: %s",
ec.message().c_str());
return false;
}
}
std::ofstream f(fileName);
if (!f.is_open()) {
blog(LOG_ERROR, "[Utils::Json::SetJsonFileContent] Failed to open file `%s` for writing", fileName.c_str());
return false;
}
// Set indent to 2 spaces, then dump content
f << std::setw(2) << content;
return true;
}

View File

@ -76,7 +76,10 @@ namespace Utils {
obs_data_t *JsonToObsData(json j);
json ObsDataToJson(obs_data_t *d, bool includeDefault = false);
bool GetJsonFileContent(std::string fileName, json &content);
bool SetJsonFileContent(std::string fileName, const json &content, bool createNew = true);
static inline bool Contains(const json &j, std::string key) { return j.contains(key) && !j[key].is_null(); }
bool SetJsonFileContent(std::string fileName, const json &content, bool makeDirs = true);
static inline bool Contains(const json &j, std::string key)
{
return j.contains(key) && !j[key].is_null();
}
}
}

View File

@ -29,36 +29,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
inline void ___properties_dummy_addref(obs_properties_t *) {}
using OBSPropertiesAutoDestroy = OBSRef<obs_properties_t *, ___properties_dummy_addref, obs_properties_destroy>;
#if !defined(OBS_AUTORELEASE)
inline void ___source_dummy_addref(obs_source_t *) {}
inline void ___scene_dummy_addref(obs_scene_t *) {}
inline void ___sceneitem_dummy_addref(obs_sceneitem_t *) {}
inline void ___data_dummy_addref(obs_data_t *) {}
inline void ___data_array_dummy_addref(obs_data_array_t *) {}
inline void ___output_dummy_addref(obs_output_t *) {}
inline void ___encoder_dummy_addref(obs_encoder_t *) {}
inline void ___service_dummy_addref(obs_service_t *) {}
inline void ___weak_source_dummy_addref(obs_weak_source_t *) {}
inline void ___weak_output_dummy_addref(obs_weak_output_t *) {}
inline void ___weak_encoder_dummy_addref(obs_weak_encoder_t *) {}
inline void ___weak_service_dummy_addref(obs_weak_service_t *) {}
using OBSSourceAutoRelease = OBSRef<obs_source_t *, ___source_dummy_addref, obs_source_release>;
using OBSSceneAutoRelease = OBSRef<obs_scene_t *, ___scene_dummy_addref, obs_scene_release>;
using OBSSceneItemAutoRelease = OBSRef<obs_sceneitem_t *, ___sceneitem_dummy_addref, obs_sceneitem_release>;
using OBSDataAutoRelease = OBSRef<obs_data_t *, ___data_dummy_addref, obs_data_release>;
using OBSDataArrayAutoRelease = OBSRef<obs_data_array_t *, ___data_array_dummy_addref, obs_data_array_release>;
using OBSOutputAutoRelease = OBSRef<obs_output_t *, ___output_dummy_addref, obs_output_release>;
using OBSEncoderAutoRelease = OBSRef<obs_encoder_t *, ___encoder_dummy_addref, obs_encoder_release>;
using OBSServiceAutoRelease = OBSRef<obs_service_t *, ___service_dummy_addref, obs_service_release>;
using OBSWeakSourceAutoRelease = OBSRef<obs_weak_source_t *, ___weak_source_dummy_addref, obs_weak_source_release>;
using OBSWeakOutputAutoRelease = OBSRef<obs_weak_output_t *, ___weak_output_dummy_addref, obs_weak_output_release>;
using OBSWeakEncoderAutoRelease = OBSRef<obs_weak_encoder_t *, ___weak_encoder_dummy_addref, obs_weak_encoder_release>;
using OBSWeakServiceAutoRelease = OBSRef<obs_weak_service_t *, ___weak_service_dummy_addref, obs_weak_service_release>;
#endif
template<typename T> T *GetCalldataPointer(const calldata_t *data, const char *name)
{
void *ptr = nullptr;
@ -72,7 +42,7 @@ enum ObsOutputState {
*
* @enumIdentifier OBS_WEBSOCKET_OUTPUT_UNKNOWN
* @enumType ObsOutputState
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -82,7 +52,7 @@ enum ObsOutputState {
*
* @enumIdentifier OBS_WEBSOCKET_OUTPUT_STARTING
* @enumType ObsOutputState
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -92,7 +62,7 @@ enum ObsOutputState {
*
* @enumIdentifier OBS_WEBSOCKET_OUTPUT_STARTED
* @enumType ObsOutputState
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -102,7 +72,7 @@ enum ObsOutputState {
*
* @enumIdentifier OBS_WEBSOCKET_OUTPUT_STOPPING
* @enumType ObsOutputState
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -112,7 +82,7 @@ enum ObsOutputState {
*
* @enumIdentifier OBS_WEBSOCKET_OUTPUT_STOPPED
* @enumType ObsOutputState
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -122,7 +92,7 @@ enum ObsOutputState {
*
* @enumIdentifier OBS_WEBSOCKET_OUTPUT_RECONNECTING
* @enumType ObsOutputState
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -132,7 +102,7 @@ enum ObsOutputState {
*
* @enumIdentifier OBS_WEBSOCKET_OUTPUT_RECONNECTED
* @enumType ObsOutputState
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.1.0
* @api enums
*/
@ -142,7 +112,7 @@ enum ObsOutputState {
*
* @enumIdentifier OBS_WEBSOCKET_OUTPUT_PAUSED
* @enumType ObsOutputState
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.1.0
* @api enums
*/
@ -152,7 +122,7 @@ enum ObsOutputState {
*
* @enumIdentifier OBS_WEBSOCKET_OUTPUT_RESUMED
* @enumType ObsOutputState
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -176,7 +146,7 @@ enum ObsMediaInputAction {
*
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE
* @enumType ObsMediaInputAction
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -186,7 +156,7 @@ enum ObsMediaInputAction {
*
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY
* @enumType ObsMediaInputAction
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -196,7 +166,7 @@ enum ObsMediaInputAction {
*
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE
* @enumType ObsMediaInputAction
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -206,7 +176,7 @@ enum ObsMediaInputAction {
*
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP
* @enumType ObsMediaInputAction
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -216,7 +186,7 @@ enum ObsMediaInputAction {
*
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART
* @enumType ObsMediaInputAction
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -226,7 +196,7 @@ enum ObsMediaInputAction {
*
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT
* @enumType ObsMediaInputAction
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -236,7 +206,7 @@ enum ObsMediaInputAction {
*
* @enumIdentifier OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS
* @enumType ObsMediaInputAction
* @rpcVersion 1
* @rpcVersion -1
* @initialVersion 5.0.0
* @api enums
*/
@ -257,6 +227,7 @@ namespace Utils {
namespace Obs {
namespace StringHelper {
std::string GetObsVersion();
std::string GetModuleConfigPath(std::string fileName);
std::string GetCurrentSceneCollection();
std::string GetCurrentProfile();
std::string GetCurrentProfilePath();
@ -297,7 +268,7 @@ namespace Utils {
}
namespace SearchHelper {
obs_hotkey_t *GetHotkeyByName(std::string name);
obs_hotkey_t *GetHotkeyByName(std::string name, std::string context);
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

View File

@ -33,7 +33,7 @@ static void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
data->sceneItem = obs_scene_add(scene, data->source);
if (data->sceneItemTransform)
obs_sceneitem_set_info(data->sceneItem, data->sceneItemTransform);
obs_sceneitem_set_info2(data->sceneItem, data->sceneItemTransform);
if (data->sceneItemCrop)
obs_sceneitem_set_crop(data->sceneItem, data->sceneItemCrop);

View File

@ -96,6 +96,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneList()
json sceneJson;
sceneJson["sceneName"] = obs_source_get_name(scene);
sceneJson["sceneUuid"] = obs_source_get_uuid(scene);
sceneJson["sceneIndex"] = sceneList.sources.num - i - 1;
ret.push_back(sceneJson);
@ -150,6 +151,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneItemList(obs_scene_t *scene,
item["sceneItemBlendMode"] = obs_sceneitem_get_blending_mode(sceneItem);
OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
item["sourceName"] = obs_source_get_name(itemSource);
item["sourceUuid"] = obs_source_get_uuid(itemSource);
item["sourceType"] = obs_source_get_type(itemSource);
if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
item["inputKind"] = obs_source_get_id(itemSource);
@ -195,6 +197,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetInputList(std::string inputKind)
json inputJson;
inputJson["inputName"] = obs_source_get_name(input);
inputJson["inputUuid"] = obs_source_get_uuid(input);
inputJson["inputKind"] = inputKind;
inputJson["unversionedInputKind"] = obs_source_get_unversioned_id(input);
@ -281,6 +284,7 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSceneTransitionList()
obs_source_t *transition = transitionList.sources.array[i];
json transitionJson;
transitionJson["transitionName"] = obs_source_get_name(transition);
transitionJson["transitionUuid"] = obs_source_get_uuid(transition);
transitionJson["transitionKind"] = obs_source_get_id(transition);
transitionJson["transitionFixed"] = obs_transition_fixed(transition);
transitionJson["transitionConfigurable"] = obs_source_configurable(transition);

View File

@ -50,7 +50,7 @@ json Utils::Obs::ObjectHelper::GetSceneItemTransform(obs_sceneitem_t *item)
obs_transform_info osi;
obs_sceneitem_crop crop;
obs_sceneitem_get_info(item, &osi);
obs_sceneitem_get_info2(item, &osi);
obs_sceneitem_get_crop(item, &crop);
OBSSource source = obs_sceneitem_get_source(item);
@ -83,5 +83,7 @@ json Utils::Obs::ObjectHelper::GetSceneItemTransform(obs_sceneitem_t *item)
ret["cropTop"] = (int)crop.top;
ret["cropBottom"] = (int)crop.bottom;
ret["cropToBounds"] = osi.crop_to_bounds;
return ret;
}

View File

@ -19,7 +19,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "Obs.h"
#include "plugin-macros.generated.h"
obs_hotkey_t *Utils::Obs::SearchHelper::GetHotkeyByName(std::string name)
obs_hotkey_t *Utils::Obs::SearchHelper::GetHotkeyByName(std::string name, std::string context)
{
if (name.empty())
return nullptr;
@ -27,8 +27,50 @@ obs_hotkey_t *Utils::Obs::SearchHelper::GetHotkeyByName(std::string name)
auto hotkeys = ArrayHelper::GetHotkeyList();
for (auto hotkey : hotkeys) {
if (obs_hotkey_get_name(hotkey) == name)
if (obs_hotkey_get_name(hotkey) != name)
continue;
if (context.empty())
return hotkey;
auto type = obs_hotkey_get_registerer_type(hotkey);
if (type == OBS_HOTKEY_REGISTERER_SOURCE) {
OBSSourceAutoRelease source =
obs_weak_source_get_source((obs_weak_source_t *)obs_hotkey_get_registerer(hotkey));
if (!source)
continue;
if (context != obs_source_get_name(source))
continue;
} else if (type == OBS_HOTKEY_REGISTERER_OUTPUT) {
OBSOutputAutoRelease output =
obs_weak_output_get_output((obs_weak_output_t *)obs_hotkey_get_registerer(hotkey));
if (!output)
continue;
if (context != obs_output_get_name(output))
continue;
} else if (type == OBS_HOTKEY_REGISTERER_ENCODER) {
OBSEncoderAutoRelease encoder =
obs_weak_encoder_get_encoder((obs_weak_encoder_t *)obs_hotkey_get_registerer(hotkey));
if (!encoder)
continue;
if (context != obs_encoder_get_name(encoder))
continue;
} else if (type == OBS_HOTKEY_REGISTERER_SERVICE) {
OBSServiceAutoRelease service =
obs_weak_service_get_service((obs_weak_service_t *)obs_hotkey_get_registerer(hotkey));
if (!service)
continue;
if (context != obs_service_get_name(service))
continue;
}
return hotkey;
}
return nullptr;

View File

@ -20,13 +20,13 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <inttypes.h>
#include <QString>
#include <obs-module.h>
#include <util/util.hpp>
#include "Obs.h"
#include "plugin-macros.generated.h"
#define CASE(x) \
case x: \
return #x;
std::string Utils::Obs::StringHelper::GetObsVersion()
{
uint32_t version = obs_get_version();
@ -40,36 +40,34 @@ std::string Utils::Obs::StringHelper::GetObsVersion()
return combined.toStdString();
}
std::string Utils::Obs::StringHelper::GetModuleConfigPath(std::string fileName)
{
BPtr<char> configPath = obs_module_config_path(fileName.c_str());
return std::string(configPath.Get());
}
std::string Utils::Obs::StringHelper::GetCurrentSceneCollection()
{
char *sceneCollectionName = obs_frontend_get_current_scene_collection();
std::string ret = sceneCollectionName;
bfree(sceneCollectionName);
return ret;
BPtr<char> sceneCollectionName = obs_frontend_get_current_scene_collection();
return std::string(sceneCollectionName.Get());
}
std::string Utils::Obs::StringHelper::GetCurrentProfile()
{
char *profileName = obs_frontend_get_current_profile();
std::string ret = profileName;
bfree(profileName);
return ret;
BPtr<char> profileName = obs_frontend_get_current_profile();
return std::string(profileName.Get());
}
std::string Utils::Obs::StringHelper::GetCurrentProfilePath()
{
char *profilePath = obs_frontend_get_current_profile_path();
std::string ret = profilePath;
bfree(profilePath);
return ret;
BPtr<char> profilePath = obs_frontend_get_current_profile_path();
return std::string(profilePath.Get());
}
std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath()
{
char *recordOutputPath = obs_frontend_get_current_record_output_path();
std::string ret = recordOutputPath;
bfree(recordOutputPath);
return ret;
BPtr<char> recordOutputPath = obs_frontend_get_current_record_output_path();
return std::string(recordOutputPath.Get());
}
std::string Utils::Obs::StringHelper::GetLastRecordFileName()
@ -94,18 +92,14 @@ std::string Utils::Obs::StringHelper::GetLastRecordFileName()
std::string Utils::Obs::StringHelper::GetLastReplayBufferFileName()
{
char *replayBufferPath = obs_frontend_get_last_replay();
std::string ret = replayBufferPath;
bfree(replayBufferPath);
return ret;
BPtr<char> replayBufferPath = obs_frontend_get_last_replay();
return std::string(replayBufferPath.Get());
}
std::string Utils::Obs::StringHelper::GetLastScreenshotFileName()
{
char *screenshotPath = obs_frontend_get_last_screenshot();
std::string ret = screenshotPath;
bfree(screenshotPath);
return ret;
BPtr<char> screenshotPath = obs_frontend_get_last_screenshot();
return std::string(screenshotPath.Get());
}
std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)

View File

@ -93,6 +93,7 @@ json Utils::Obs::VolumeMeter::Meter::GetMeterData()
l.unlock();
ret["inputName"] = obs_source_get_name(input);
ret["inputUuid"] = obs_source_get_uuid(input);
ret["inputLevelsMul"] = levels;
return ret;
@ -237,7 +238,9 @@ void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, callda
}
Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod)
: _updateCallback(cb), _updatePeriod(updatePeriod), _running(false)
: _updateCallback(cb),
_updatePeriod(updatePeriod),
_running(false)
{
signal_handler_t *sh = obs_get_signal_handler();
if (!sh)

View File

@ -21,7 +21,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <QCommandLineParser>
#include <QNetworkInterface>
#include <QHostAddress>
#include <QFile>
#include <obs-frontend-api.h>
#include "Platform.h"
@ -116,36 +115,13 @@ void Utils::Platform::SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QS
obs_queue_task(
OBS_TASK_UI,
[](void *param) {
void *systemTrayPtr = obs_frontend_get_system_tray();
auto systemTray = static_cast<QSystemTrayIcon *>(systemTrayPtr);
auto notification = static_cast<SystemTrayNotification *>(param);
systemTray->showMessage(notification->title, notification->body, notification->icon);
void *systemTrayPtr = obs_frontend_get_system_tray();
if (systemTrayPtr) {
auto systemTray = static_cast<QSystemTrayIcon *>(systemTrayPtr);
systemTray->showMessage(notification->title, notification->body, notification->icon);
}
delete notification;
},
(void *)notification, false);
}
bool Utils::Platform::GetTextFileContent(std::string fileName, std::string &content)
{
QFile f(QString::fromStdString(fileName));
if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
content = f.readAll().toStdString();
return true;
}
bool Utils::Platform::SetTextFileContent(std::string fileName, std::string content, bool createNew)
{
if (!createNew && !QFile::exists(QString::fromStdString(fileName)))
return false;
QFile f(QString::fromStdString(fileName));
if (!f.open(QIODevice::WriteOnly | QIODevice::Text))
return false;
QTextStream out(&f);
out << content.c_str();
return true;
}

View File

@ -29,7 +29,5 @@ namespace Utils {
QString GetCommandLineArgument(QString arg);
bool GetCommandLineFlagSet(QString arg);
void SendTrayNotification(QSystemTrayIcon::MessageIcon icon, QString title, QString body);
bool GetTextFileContent(std::string fileName, std::string &content);
bool SetTextFileContent(std::string filePath, std::string content, bool createNew = true);
}
}

View File

@ -24,7 +24,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <obs-frontend-api.h>
#include "WebSocketServer.h"
#include "../eventhandler/EventHandler.h"
#include "../obs-websocket.h"
#include "../Config.h"
#include "../utils/Crypto.h"
@ -47,23 +46,10 @@ WebSocketServer::WebSocketServer() : QObject(nullptr)
_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();
if (eventHandler) {
eventHandler->SetBroadcastCallback(std::bind(&WebSocketServer::BroadcastEvent, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
eventHandler->SetObsReadyCallback(std::bind(&WebSocketServer::onObsReady, this, std::placeholders::_1));
}
}
WebSocketServer::~WebSocketServer()
{
auto eventHandler = GetEventHandler();
if (eventHandler) {
eventHandler->SetObsReadyCallback(nullptr);
eventHandler->SetBroadcastCallback(nullptr);
}
if (_server.is_listening())
Stop();
}
@ -97,7 +83,7 @@ void WebSocketServer::Start()
}
_authenticationSalt = Utils::Crypto::GenerateSalt();
_authenticationSecret = Utils::Crypto::GenerateSecret(conf->ServerPassword.toStdString(), _authenticationSalt);
_authenticationSecret = Utils::Crypto::GenerateSecret(conf->ServerPassword, _authenticationSalt);
// Set log levels if debug is enabled
if (IsDebugEnabled()) {
@ -215,11 +201,6 @@ std::vector<WebSocketServer::WebSocketSessionState> WebSocketServer::GetWebSocke
return webSocketSessions;
}
void WebSocketServer::onObsReady(bool ready)
{
_obsReady = ready;
}
bool WebSocketServer::onValidate(websocketpp::connection_hdl hdl)
{
auto conn = _server.get_con_from_hdl(hdl);
@ -327,11 +308,9 @@ void WebSocketServer::onClose(websocketpp::connection_hdl hdl)
_sessions.erase(hdl);
lock.unlock();
// If client was identified, decrement appropriate refs in eventhandler.
if (isIdentified) {
auto eventHandler = GetEventHandler();
eventHandler->ProcessUnsubscription(eventSubscriptions);
}
// If client was identified, announce unsubscription
if (isIdentified && _clientSubscriptionCallback)
_clientSubscriptionCallback(false, eventSubscriptions);
// Build SessionState object for signal
WebSocketSessionState state;

View File

@ -30,8 +30,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "rpc/WebSocketSession.h"
#include "types/WebSocketCloseCode.h"
#include "types/WebSocketOpCode.h"
#include "../utils/Json.h"
#include "../requesthandler/rpc/Request.h"
#include "../utils/Json.h"
#include "plugin-macros.generated.h"
class WebSocketServer : QObject {
@ -57,12 +57,14 @@ public:
void InvalidateSession(websocketpp::connection_hdl hdl);
void BroadcastEvent(uint64_t requiredIntent, const std::string &eventType, const json &eventData = nullptr,
uint8_t rpcVersion = 0);
bool IsListening() { return _server.is_listening(); }
inline void SetObsReady(bool ready) { _obsReady = ready; }
inline bool IsListening() { return _server.is_listening(); }
std::vector<WebSocketSessionState> GetWebSocketSessions();
inline QThreadPool *GetThreadPool() { return &_threadPool; }
QThreadPool *GetThreadPool() { return &_threadPool; }
// Callback for when a client subscribes or unsubscribes. `true` for sub, `false` for unsub
typedef std::function<void(bool, uint64_t)> ClientSubscriptionCallback; // bool type, uint64_t eventSubscriptions
inline void SetClientSubscriptionCallback(ClientSubscriptionCallback cb) { _clientSubscriptionCallback = cb; }
signals:
void ClientConnected(WebSocketSessionState state);
@ -77,7 +79,6 @@ private:
void ServerRunner();
void onObsReady(bool loaded);
bool onValidate(websocketpp::connection_hdl hdl);
void onOpen(websocketpp::connection_hdl hdl);
void onClose(websocketpp::connection_hdl hdl);
@ -98,4 +99,6 @@ private:
std::map<websocketpp::connection_hdl, SessionPtr, std::owner_less<websocketpp::connection_hdl>> _sessions;
std::atomic<bool> _obsReady = false;
ClientSubscriptionCallback _clientSubscriptionCallback;
};

View File

@ -23,7 +23,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "WebSocketServer.h"
#include "../requesthandler/RequestHandler.h"
#include "../requesthandler/RequestBatchHandler.h"
#include "../eventhandler/EventHandler.h"
#include "../obs-websocket.h"
#include "../Config.h"
#include "../utils/Crypto.h"
@ -32,7 +31,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
static bool IsSupportedRpcVersion(uint8_t requestedVersion)
{
return (requestedVersion == 1);
return (requestedVersion == CURRENT_RPC_VERSION);
}
static json ConstructRequestResult(RequestResult requestResult, const json &requestJson)
@ -149,9 +148,9 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
if (ret.closeCode != WebSocketCloseCode::DontClose)
return;
// Increment refs for event subscriptions
auto eventHandler = GetEventHandler();
eventHandler->ProcessSubscription(session->EventSubscriptions());
// Announce subscribe
if (_clientSubscriptionCallback)
_clientSubscriptionCallback(true, session->EventSubscriptions());
// Mark session as identified
session->SetIsIdentified(true);
@ -172,16 +171,17 @@ void WebSocketServer::ProcessMessage(SessionPtr session, WebSocketServer::Proces
case WebSocketOpCode::Reidentify: { // Reidentify
std::unique_lock<std::mutex> sessionLock(session->OperationMutex);
// Decrement refs for current subscriptions
auto eventHandler = GetEventHandler();
eventHandler->ProcessUnsubscription(session->EventSubscriptions());
// Announce unsubscribe
if (_clientSubscriptionCallback)
_clientSubscriptionCallback(false, session->EventSubscriptions());
SetSessionParameters(session, ret, payloadData);
if (ret.closeCode != WebSocketCloseCode::DontClose)
return;
// Increment refs for new subscriptions
eventHandler->ProcessSubscription(session->EventSubscriptions());
// Announce subscribe
if (_clientSubscriptionCallback)
_clientSubscriptionCallback(true, session->EventSubscriptions());
ret.result["op"] = WebSocketOpCode::Identified;
ret.result["d"]["negotiatedRpcVersion"] = session->RpcVersion();

View File

@ -122,5 +122,8 @@ namespace WebSocketOpCode {
RequestBatchResponse = 9,
};
inline bool IsValid(uint8_t opCode) { return opCode >= Hello && opCode <= RequestBatchResponse; }
inline bool IsValid(uint8_t opCode)
{
return opCode >= Hello && opCode <= RequestBatchResponse;
}
}