Compare commits

...

237 Commits
4.8.0 ... 4.9.0

Author SHA1 Message Date
2f252f67ba CI: Fix tag detection 2021-02-10 16:26:38 -08:00
9e554ce527 ci(macos): update InstallAppleCertificate action to version 2 2021-02-10 13:32:13 +01:00
6217f009fd docs(ci): Update protocol.md - 4647312 [skip ci] 2021-02-04 21:53:47 +00:00
46473126d2 Merge pull request #668 from Palakis/cleanup-for-v4.9.0
Cleanup for v4.9.0
2021-02-04 22:53:01 +01:00
b6ee6e9e22 docs(ci): Update protocol.md - 1891f62 [skip ci] 2021-02-04 15:53:51 +00:00
1891f62c22 Merge pull request #657 from Palakis/feature/authentication-enabled-by-default
Config: authentication enabled by default
2021-02-04 07:53:07 -08:00
212d1cbfc2 Merge https://github.com/Palakis/obs-websocket into cleanup-for-v4.9.0 2021-02-04 07:51:47 -08:00
73d93e476f docs(ci): Update protocol.md - 80d8286 [skip ci] 2021-02-04 15:51:04 +00:00
80d82861ae Merge pull request #665 from Palakis/feature/batch-requests
Batch Requests
2021-02-04 07:50:22 -08:00
8d034f53a4 Chore: Bump version to 4.9.0 2021-02-04 07:47:36 -08:00
6d3aa3a828 Requests: Add abortOnFail to ExecuteBatch request
We do not currently have atomicy in this request, as it would be
incredibly difficult to add, but this is at least useful for avoiding
further data corruption in the case that there is a malformed request
and multiple requests depend on the success of the previous one.
2021-02-04 07:37:27 -08:00
ed23aba0ac Requests: Improve documentation of ExecuteBatch 2021-02-04 07:15:37 -08:00
3847e5d6af Revert "Remove deprecated requests"
This reverts commit e95d8d36aa.
2021-02-04 05:40:01 -08:00
f4465e2e9b requests(ExecuteBatch): documentation 2021-02-03 05:47:53 +01:00
fe2e87074a requests(ExecuteBatch): handle message-id in sub-requests 2021-02-03 05:33:08 +01:00
5b100d15d7 OBSRemoteProtocol: fix missing message id 2021-02-03 05:31:53 +01:00
87cd36673e OBSRemoteProtocol: fix memory leak 2021-02-03 04:16:04 +01:00
47b84f7316 Merge https://github.com/Palakis/obs-websocket into cleanup-for-v4.9.0 2021-02-02 08:28:41 -08:00
a18da3e6cd WSRequestHandler: Organize requests
Organize requests into their categories aphabetically, and how they appear in their appriate source files.
2021-02-02 08:26:47 -08:00
6bb33fa18b docs(ci): Update protocol.md - 59e6695 [skip ci] 2021-02-02 16:10:24 +00:00
59e6695a5d Merge pull request #666 from Palakis/feature/RefreshBrowserSource
Requests: Add RefreshBrowserSource
2021-02-02 17:09:30 +01:00
861cde7e61 Requests/Events: Use Note: for all notes
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah
my ocdddddddddddddddddddddddddddddddddddddd
2021-02-02 07:39:52 -08:00
e95d8d36aa Remove deprecated requests 2021-02-02 07:29:11 -08:00
04304ecf94 Requests/Events: Change all unreleased tags to 4.9.0 2021-02-02 07:23:45 -08:00
3989ea0780 Requests: Add RefreshBrowserSource 2021-02-02 06:16:49 -08:00
e39585befc Password Dialog: Only change form options and not underlying config 2021-02-02 03:04:03 -08:00
20fa14563c Settings Dialog: Don't need ShowSettingsDialog() 2021-02-02 03:03:37 -08:00
10910aa06d requests(general): ExecuteBatch WIP 2021-02-02 11:26:22 +01:00
e6c2c90677 Password Prompt: Improve english strings 2021-02-01 05:33:01 -08:00
98712c7b71 Password Prompt: Enable authRequired toggle and highlight password 2021-02-01 05:16:47 -08:00
2015d40186 docs(introduction): update authentication section 2021-01-31 01:58:22 +01:00
a8aa34529e config: simplify initial password setup 2021-01-31 01:51:19 +01:00
38cf0270b1 settings: add auth disabled warning 2021-01-31 01:26:38 +01:00
c8ca79f00b config: add locale texts for initial password setup 2021-01-31 00:58:56 +01:00
c02382b6e5 config: initial password setup prompt wip 2021-01-29 19:16:53 +01:00
cb7c77d29e docs(ci): Update protocol.md - 83e23c9 [skip ci] 2021-01-28 23:26:40 +00:00
83e23c9c41 Merge pull request #638 from Palakis/feature/getsourcedefaultsettings
Requests: Add GetSourceDefaultSettings
2021-01-29 00:25:35 +01:00
c6db90ae07 request(GetSourceDefaultSettings): add sourceKind return value + fix param spec 2021-01-29 00:08:20 +01:00
c2aa7263bd docs(ci): Update protocol.md - 25be7ee [skip ci] 2021-01-28 22:57:37 +00:00
25be7ee14e Merge pull request #628 from gdhgdhgdh/sceneitems-by-id
Request: add ID support for SetSceneItemRender
2021-01-28 23:56:53 +01:00
332876495d Utils: remove unused struct 2021-01-28 23:04:02 +01:00
6cbe50e3e7 request(SetSceneItemRender): refactor conditional 2021-01-28 22:56:01 +01:00
599eaf85ce requests(SceneItems): remove unused variables 2021-01-28 22:55:40 +01:00
c03ca47e37 Merge branch 'opencollective' into 4.x-current 2021-01-28 22:48:52 +01:00
2246ce5142 Merge branch '4.x-current' into opencollective 2021-01-28 22:48:34 +01:00
79774921e4 Merge pull request #659 from harm27/harm27-obs-java-client
readme: Added obs-java-client to the available API's
2021-01-21 13:30:43 -08:00
ab453a0322 readme: Added obs-java-client to the available API's 2021-01-21 11:06:59 +01:00
3725e400ce Merge branch 'feature/getsourcedefaultsettings' of https://github.com/Palakis/obs-websocket into feature/getsourcedefaultsettings 2021-01-20 04:22:38 -08:00
9859c7b25f Utils: Use obs_data_item_get_name in OBSDataGetDefaults 2021-01-20 04:21:43 -08:00
056c1ef1fc Merge pull request #641 from VMinute/bugfix/fix-list-scenes-collections
General: Add ScenesCollection object
2021-01-20 10:37:31 +01:00
41fbfb159a Merge branch '4.x-current' into feature/getsourcedefaultsettings 2021-01-20 10:26:34 +01:00
740a8a8d06 General: Add ScenesCollection object
Adds a ScenesCollection object in the protocol definition,
replacing the current Array<String> return  with
Array<ScenesCollection>, keeping it more coherent with
other requests that return objects in the same format.
This will help automated code generation from comment.json
that otherwise would require ad-hoc handling for that specific
request.

Signed-off-by: Valter Minute <valter.minute@gmail.com>
2021-01-20 10:22:33 +01:00
59ab9548c2 docs(ci): Update protocol.md - a1cf7f8 [skip ci] 2021-01-20 09:22:17 +00:00
a1cf7f8d32 Merge pull request #658 from Palakis/fix/tab-formatting
Misc: Fix indentation of src files
2021-01-20 10:21:24 +01:00
0d59983d1a Misc: Fix indentation of src files
Replace 4-space code with tabs to conform with the way its supposed to be.
2021-01-19 18:30:18 -08:00
488a57e2de Requests: Fix GetSourceDefaultSettings indentation and docs 2021-01-19 18:23:54 -08:00
dd9ad67e6b Config: authentication enabled by default 2021-01-20 00:44:34 +01:00
492e9d8df7 editorconfig: set indent_size for C/C++ files 2021-01-20 00:28:50 +01:00
bc3b09dce4 WSServer(stop): wait for all connections to close 2021-01-20 00:08:24 +01:00
5fd95ce374 Merge pull request #649 from dnaka91/homebrew-instructions
readme: Add Homebrew installation instructions
2021-01-19 19:08:50 +01:00
77380a098e docs(ci): Update protocol.md - afc9c54 [skip ci] 2021-01-19 18:07:43 +00:00
afc9c549d6 Merge pull request #643 from Palakis/fix/createscene-sinceversion
Requests: Fix wrong docs version for CreateScene
2021-01-19 19:06:40 +01:00
77f38bbf63 docs(ci): Update protocol.md - 853eeb2 [skip ci] 2021-01-18 10:52:53 +00:00
853eeb2e6b Merge pull request #636 from Palakis/enhancement/latest-translations
Translations: Update translations with latest from crowdin
2021-01-18 11:51:50 +01:00
6ed6b4a679 Merge pull request #637 from Palakis/fix/getstreamingstatus-docs
Docs: Add recording-paused to documentation
2021-01-18 11:51:17 +01:00
99987f9373 Merge pull request #644 from eric/stop-ws-server-on-exit
Stop WSServer on OBS exit
2021-01-18 11:49:00 +01:00
b02239f3e3 Merge pull request #650 from dnaka91/rust-api
readme: Add Rust client library
2021-01-08 04:59:14 -08:00
bc436e9ec4 Reorder library list 2021-01-08 04:58:07 -08:00
7e0874abb4 Translations: Remove dutch StartFailed message and update other translations
For some reason crowdin included it even though it is not approved.
The current unapproved translation uses the old string.
2021-01-08 04:57:03 -08:00
3d704702ba readme: Add Rust client library
I created a client library in Rust to remote control OBS with
obs-websocket. Adding my library to the list of available
language APIs.
2021-01-07 21:58:29 +09:00
2735f80637 readme: Add Homebrew installation instructions
Give a short hint that the project can be installed with Homebrew on
MacOS as alternative to installing manually.
2021-01-07 21:49:41 +09:00
9275f7c2a9 Server: Stop WSServer on OBS exit
This should fix issues with race conditions and crashes on OBS exit.
2020-12-29 11:41:47 -08:00
229641aba0 Requests: Fix wrong docs version for CreateScene 2020-12-24 13:38:58 -08:00
ba143f2636 Translations: Update translations with latest from crowdin 2020-12-24 13:28:01 -08:00
ffb34c3fd4 Requests: Add GetSourceDefaultSettings
Adds a request, mainly for development purposes, which returns an
object of default settings for a given sourceKind (eg. `vlc_source`)
2020-12-21 00:06:50 -08:00
31c717eb40 Docs: Add recording-paused to documentation [skip ci]
It wasn't included for some reason.
2020-12-20 00:29:24 -08:00
7ae016bd3b Merge pull request #627 from Palakis/bugfix/deb-file-permissions
ci(linux): fix deb package file permissions
2020-12-20 00:02:44 -08:00
501e0f63f5 ci(linux): fix deb package file permissions 2020-12-19 22:54:46 -08:00
a42398c457 Merge pull request #635 from Palakis/enhancement/codefactor-badge
README: Add codefactor badge
2020-12-20 07:20:59 +01:00
d77e4ab10d docs(ci): Update protocol.md - 8a8ea92 [skip ci] 2020-12-20 06:20:47 +00:00
8a8ea92140 Merge pull request #629 from marcan/volumes
Requests: SetVolume: allow volumes > 1.0
2020-12-19 22:20:12 -08:00
adc46a80f9 Requests: SetVolume: allow volumes > 1.0
Allow volumes > 1.0 / > 0dB, which is legal in OBS (you can do this in
Advanced Audio Properties). OBS allows up to +26dB gain, so we do the
same. This corresponds to approximately 20x linear gain (actually
19.9526231497, but 20 is easier to explain and OBS doesn't care).
2020-12-19 22:16:47 -08:00
3203f50a43 Merge pull request #609 from Palakis/remove-captions-flag
CI: Remove BUILD_CAPTIONS flag
2020-12-20 06:55:19 +01:00
3dea5fd4f4 README: Add codefactor badge
They give us an A and I think that's pretty cool so might as well display it
2020-12-19 21:51:25 -08:00
1024a2198a Merge pull request #631 from leafac/patch-1
Add link to obs-cli
2020-12-16 14:11:02 -08:00
c8f0d5a3e4 Add link to obs-cli 2020-12-16 21:57:23 +00:00
3e55b3d7bc Request: add ID support for SetSceneItemRender 2020-12-08 17:23:53 +00:00
a93c4dfbbd docs(ci): Update protocol.md - aceb437 [skip ci] 2020-11-29 11:40:39 +00:00
aceb437c5f Merge pull request #621 from Palakis/feature/current-recording-filename
Feature/current recording filename
2020-11-29 12:39:49 +01:00
a137ccd8ba requests(GetRecordingStatus): add recordingFilename property 2020-11-29 12:24:49 +01:00
96bd4141fd events: add recordingFilename property to RecordingStarted, RecordingStopping and RecordingStopped 2020-11-29 12:22:26 +01:00
e794762f72 docs(ci): Update protocol.md - 9999b30 [skip ci] 2020-11-28 04:50:08 +00:00
9999b30d1a docs(scene items): fix documentation for GetSceneItemProperties
Fixes #607
2020-11-28 05:49:09 +01:00
569e9681e5 docs(ci): Update protocol.md - 9edc3ea [skip ci] 2020-11-27 23:39:48 +00:00
9edc3eafa3 Merge pull request #589 from Palakis/feature/createsource
Requests: Add CreateSource and clean up code
2020-11-28 00:39:09 +01:00
a1fcc35fd6 requests(CreateSource): pass settings on source creation 2020-11-28 00:19:27 +01:00
80d21ce80d WSRequestHandler: fix indent 2020-11-27 23:53:39 +01:00
08178b9354 requests(CreateSource): use the OBSSourceAutoRelease container instead of manipulating refs manually 2020-11-27 19:40:02 +01:00
60ce25c689 requests(CreateSource): fix docs 2020-11-27 19:38:55 +01:00
eaf34f3c3a requests(CreateSource): simplified settings apply
obs_source_update already does that internally
2020-11-27 19:38:01 +01:00
ef0e907014 docs(ci): Update protocol.md - 9fb0f56 [skip ci] 2020-11-27 16:24:32 +00:00
9fb0f56aa0 Merge pull request #619 from Palakis/feature/events-add-target-new-state
Events: provide new state of entity (in events that don't do it already)
2020-11-27 17:23:51 +01:00
ae9ea8510c events: fix docs 2020-11-27 17:10:27 +01:00
14409dec4f events(TransitionListChanged): provide transitions list property 2020-11-27 17:09:07 +01:00
0cdea68567 events(ScenesChanged): provide scenes list property 2020-11-27 17:07:28 +01:00
542761e411 events(ProfileListChanged): add profile list property 2020-11-27 17:00:10 +01:00
0dc8e070ff events(SceneCollectionListChanged): add scene collections list property 2020-11-27 17:00:05 +01:00
fe52cd8db1 events(ProfileChanged): add new profile name 2020-11-27 16:59:57 +01:00
d87a7e896b events(SceneCollectionChanged): add new scene collection name 2020-11-27 16:59:52 +01:00
f7616ade1f docs(ci): Update protocol.md - f14379a [skip ci] 2020-11-27 15:41:27 +00:00
f14379af68 Merge pull request #439 from Palakis/feature/t-bar
T-Bar control
2020-11-27 16:39:56 +01:00
a53df39e46 requests(transitions): yet another doc fix 2020-11-27 16:15:03 +01:00
98187e2bd7 requests(transitions): documentation fixes cont. 2020-11-27 16:10:38 +01:00
3ab7de99f3 requests(transitions): documentation fixes 2020-11-27 16:03:21 +01:00
1fef0691ed Merge pull request #618 from Palakis/bugfix/output-commands-crash
requests(outputs): fix corrupted parameters object on MSVC when using findOutputOrFail
2020-11-27 15:49:43 +01:00
6b03efed42 requests(outputs): fix corrupted parameters object on MSVC when using findOutputOrFail 2020-11-27 15:33:40 +01:00
eddd4abe76 ci(linux): update frontend api headers to v26 2020-11-27 12:45:57 +01:00
e30dce8b21 Merge branch '4.x-current' into feature/createsource 2020-11-27 12:24:13 +01:00
6d12af3858 Merge branch '4.x-current' into feature/t-bar 2020-11-27 12:23:47 +01:00
b9cbd0ecd1 docs(ci): Update protocol.md - 3d7511c [skip ci] 2020-11-27 11:19:36 +00:00
3d7511ca75 Merge pull request #605 from Palakis/feature/get-set-transition-settings
Get/Set Transition Settings
2020-11-27 12:18:50 +01:00
fabf68b635 CI: Remove BUILD_CAPTIONS flag
OBS has removed the `BUILD_CAPTIONS` flag from their repo. No reason to keep it
2020-11-02 23:20:40 -08:00
11326274e0 docs(ci): Update protocol.md - acd3940 [skip ci] 2020-10-25 18:34:53 +00:00
acd3940a7b Merge pull request #593 from Palakis/feature/getsceneitemlist-default-scene
requests(GetSceneItemList): fallback to current scene if sceneName is not specified
2020-10-25 19:34:11 +01:00
8ba441da7f requests(transitions): remove TODO 2020-10-25 19:33:42 +01:00
5d9d5e0746 request(SetSourceSettings): remove redundant obs_data_apply step 2020-10-25 19:25:48 +01:00
7a31e88ed6 requests: add GetTransitionSettings and SetTransitionSettings 2020-10-25 19:25:26 +01:00
d9f35a855d docs(ci): Update protocol.md - 9678d59 [skip ci] 2020-10-01 07:56:26 +00:00
9678d59bbe Merge pull request #598 from Palakis/fix/hotkey-docs-typo
Docs: Fix typo
2020-10-01 09:55:34 +02:00
0be5564ce7 Docs: Fix typo 2020-09-30 15:57:13 -07:00
8a90051ab5 docs(ci): Update protocol.md - 02c7a7f [skip ci] 2020-09-30 22:22:57 +00:00
02c7a7f7c7 Merge pull request #595 from LorenaGdL/feature/hotkey-requests
Requests: Add hotkeys press events
2020-09-30 15:22:22 -07:00
e7c8c1d6b6 Requests: Update hotkey docs and rename some variables 2020-09-30 15:02:59 -07:00
449ad6d13c PR review changes: Modify requests names
Changes requests names to "TriggerHotkeyByName" and "TriggerHotkeyBySequence"
2020-09-30 19:15:23 +00:00
ae4ee0332c Fix macOS CI (#597)
* ci(macos): fix attempt #1

* Revert "ci(macos): fix attempt #1"

This reverts commit b846490136.

* ci(macos): provide QTDIR when building OBS

* Revert "ci(macos): provide QTDIR when building OBS"

This reverts commit 74a882fcf4.

* ci(macos): use local qt formula

* ci(macos): update qt.rb
2020-09-30 12:19:37 +02:00
4a4c97aac4 request(GetSceneItemList): provide scene name in response 2020-09-29 23:48:35 +02:00
4491da0350 requests(GetSceneItemList): fix empty/non-present parameter detection 2020-09-29 23:48:17 +02:00
32960afc1b requests(GetSceneItemList): update docs 2020-09-29 19:45:07 +02:00
46f624e3b9 requests(GetSceneItemList): fallback to current scene if sceneName is not specified 2020-09-29 19:37:28 +02:00
9daae857de Requests: Add ProcessHotkeyByCombination
Adds a new request called `ProcessHotkeyByCombination` which calls the
routine associated with a given hotkey. The hotkey is identified by the
combination of keys set by the user to trigger it. Multiple routines
are called if the same combination is bound to different hotkeys.
2020-09-29 13:43:00 +00:00
bac5b1551b Requests: Add ProcessHotkeyByName
Adds a new request called `ProcessHotkeyByName` which calls the routine
associated with a given hotkey. The hotkey is identified by the unique
name set while registering it. Works with both OBS default hotkeys and
hotkeys added by plugins/scripts.
2020-09-29 13:38:32 +00:00
f61f45fa23 Merge pull request #592 from rodrigograca31/improve-docs
docs: 📝 list multiple available web clients
2020-09-24 12:54:52 -07:00
fbc6e02ff6 docs: 📝 list multiple available web clients 2020-09-24 13:16:57 +01:00
56a17a9131 Merge pull request #560 from Palakis/enhancement/various-todos
Requests: Scene collection and profile name checking
2020-09-16 21:51:08 +02:00
a148f7fd7c requests(Profiles + SceneCollections): fix crash on change 2020-09-16 21:45:31 +02:00
0dd0d01e8f requests(Profiles + SceneCollections): don't use QString where it is not needed 2020-09-15 18:17:45 +02:00
bcd16d791c Merge pull request #559 from Palakis/enhancement/ipv4-option
Settings: Add option to lock binding to IPv4
2020-09-15 17:19:27 +02:00
539e636939 requests(SceneCollections): fix typo (profiles instead of scene collections) 2020-09-15 17:14:53 +02:00
7f1c4a1c4c Utils: simplified string list lookup 2020-09-15 17:12:34 +02:00
6b254e0ad5 Requests: Add CreateSource and clean up code 2020-09-15 02:51:03 -07:00
f36185a964 Merge pull request #583 from Palakis/fix/no
Installer: Don't show warning when obs directory is not empty
2020-09-15 11:36:54 +02:00
588487f860 docs(ci): Update protocol.md - f3ab210 [skip ci] 2020-09-15 09:36:27 +00:00
f3ab2100f0 Merge pull request #578 from Palakis/fix/request-versions
Docs: Fix requests marked as "unreleased"
2020-09-15 11:35:46 +02:00
f96b40bbf8 Merge pull request #573 from Palakis/fix/sfr-enabled
Docs: Add `enabled` field to `SourceFiltersReordered`
2020-09-15 11:35:23 +02:00
9679de097f Installer: Don't show warning when obs directory is not empty
More info here: https://jrsoftware.org/ishelp/index.php?topic=setup_direxistswarning
2020-08-21 22:43:42 -07:00
a51ad1383b Docs: Fix requests marked as "unreleased" 2020-08-12 16:54:03 -07:00
1dd96fa714 Docs: Add enabled field to SourceFiltersReordered
Was noticed in Discord that this was missing from the docs
2020-07-31 22:08:07 -07:00
8285805108 Merge pull request #571 from Palakis/bugfix/569-tray-icon-crash
Utils(GetTrayIcon): refactor with frontend API function
2020-07-29 17:06:01 +02:00
d91b3f8dd9 Utils(GetTrayIcon): refactor with frontend API function 2020-07-28 02:23:31 +02:00
6734c928a2 Merge pull request #563 from venepe/raspberry-pi
CMakeLists: Compile for arm targets
2020-07-19 12:22:15 +02:00
58448f363b CMakeLists: Compile for arm targets 2020-07-18 13:48:52 -05:00
2e32b7e299 Merge branch '4.x-current' into enhancement/various-todos 2020-07-15 15:02:00 +02:00
a651a1a69f docs(ci): Update protocol.md - c368696 [skip ci] 2020-07-15 13:01:23 +00:00
c368696ddc docs(ci): Update protocol.md - 94269c0 [skip ci] 2020-07-15 13:00:59 +00:00
94269c0640 Merge pull request #557 from julijane/feature-screenshot-default
TakeSourceScreenshot: default to current scene
2020-07-15 15:00:43 +02:00
5da6588ee6 Merge pull request #555 from Palakis/fix/docs-updates
Docs: Various docs improvements
2020-07-15 14:59:52 +02:00
ac9c6b4a1f Docs: Fix GDIPlus requests documentation
`bk-color` and `bk-opacity` are actually `bk_color` and `bk_opacity`
respectively. Issue reported in #561
2020-07-12 02:22:26 -07:00
aa8fa00811 Docs: More updates
Changes the media stuff's `@since 4.9.0` to `@since unreleased`
to reduce potential confusion on the docs. As a part of the final
4.9.0 commits, I will be changing it back to 4.9.0. We also fix the
contributing document since it was all messed up.
2020-07-11 05:26:49 -07:00
9c4bd7a487 Requests: Scene collection and profile name checking
For `SetCurrentProfile` and `SetCurrentSceneCollection`, check if it
exists before attempting to switch to it, and return an error if it
does not exist.
2020-07-11 05:00:10 -07:00
eb00294bbb Settings: Add option to lock binding to IPv4
WebsocketPP defaults to binding to an IPv6 interface. Since IPv6
is broken on so many networks, this adds an option to force WebsocketPP
to use the default IPv4 interface to bind to.
2020-07-09 22:21:48 -07:00
04c60fd1ac docs(ci): Update protocol.md - 80de8ad [skip ci] 2020-07-09 10:40:21 +00:00
80de8ada57 ci: fix Azure Pipelines not running on 4.x-current 2020-07-09 12:39:40 +02:00
9cdb32d56a Docs: Various docs improvements
I noticed that some parts of the docs were inconsistent/lacking.
Decided to go through all of them and update them.
2020-07-09 03:27:44 -07:00
31a505f4a4 Do it when sourceName is omitted 2020-07-09 00:50:29 +02:00
bfa6cd0e19 TakeSourceScreenshot: default to current scene 2020-07-09 00:43:54 +02:00
c51ecda6b4 Merge pull request #527 from Palakis/feature/component-management
Requests: Sceneitem manipulation requests
2020-07-03 18:21:01 +02:00
471d44ae11 Merge pull request #551 from Palakis/fix/mediafixes
Requests/Events: Use sourceKind instead of sourceTypeId
2020-07-03 16:22:20 +02:00
513bd1372b Docs, Requests: Fix GetSceneItemList code revisions
Replace the `sourceId` term with `sourceKind` to match everything else.
2020-07-03 07:12:28 -07:00
355cee0db9 Merge branch '4.x-current' into feature/component-management 2020-07-03 07:07:52 -07:00
38936173d1 Requests/Events: Use sourceKind instead of sourceTypeId
Left in some old terms accidentally. This fixes it.
2020-07-03 07:03:25 -07:00
eea56b4c71 Merge pull request #497 from Palakis/category-mediacontrol
Requests/Events: Media Source/VLC
2020-07-03 15:56:21 +02:00
802cc3a48f Apply suggestions from code review 2020-07-03 15:48:56 +02:00
ba4720012d Merge pull request #550 from Palakis/enhancement/readme-fix
Docs: Move ssl tunnelling guide to its own page
2020-07-03 15:30:34 +02:00
4f83b73732 Docs: Move ssl tunnelling guide to its own page [skip ci]
Having the SSL tunneling instructions on the README caused clutter and was
unnecessary.
2020-07-03 06:23:48 -07:00
68db13fc24 Merge pull request #549 from Palakis/feature/more-output-statuses
Requests: Add Replay Buffer and Recording Statuses
2020-07-03 14:29:25 +02:00
2d4dd580b4 Merge pull request #547 from WizardCM/audio-active
Requests, Events: Add support for Audio Active
2020-07-03 03:42:36 -07:00
d03e94ada8 events: Add support for audio_active, fix docs version 2020-07-03 03:30:58 -07:00
91126a59c2 Merge pull request #505 from Palakis/docs-updates
Docs: Update and split up main documentation
2020-07-03 11:47:54 +02:00
bd18aee43c Docs: Update issue template to be **better** 2020-07-03 02:42:29 -07:00
4ead1c3de5 Requests: Add Replay Buffer and Recording Statuses
Adds `GetReplayBufferStatus` and `GetRecordingStatus`.

v5.0 Will remove the recording status functionality from
`GetStreamingStatus`
2020-07-03 02:26:38 -07:00
f3b2a6eba2 Merge pull request #471 from PatTheMav/github-actions-tag
CI: Add Github Actions workflow for release tags
2020-07-03 10:22:39 +02:00
c4d9a27d2f Merge pull request #470 from PatTheMav/github-actions-pr
CI: Add Github Actions workflow for PRs and pushes to main branch
2020-07-03 10:22:09 +02:00
fdcba2734d Events, Docs: Refactor media events and improve docs
Move duplicated functionality to a helper function and added some docs
clarifying the behavior of the events, and fixed a few typos in
the request handlers.
2020-07-02 13:13:19 -07:00
981ba7b28f Merge pull request #529 from Palakis/fix/getvolumedecibel
Requests: Fix useDecibel response when volume is -infinity
2020-07-02 21:08:45 +02:00
822a1751a2 requests: Add support for audio_active 2020-07-01 22:23:22 +10:00
a4279d09a6 Merge pull request #546 from curtgrimes/bugfix/reenable-macos-captions
[Bug Fix] Reenable building with captions on MacOS
2020-07-01 04:24:40 -07:00
14d43ac05b [Bug Fix] Reenable building with captions on MacOS
OBS on MacOS supports closed caption encoding. The Windows-only dependencies [here](https://github.com/obsproject/obs-studio/blob/master/UI/frontend-plugins/frontend-tools/CMakeLists.txt#L73) are only related to audio capture, not closed caption encoding.
2020-06-29 20:24:11 -05:00
5e6c92ab16 Merge branch '4.x-current' into category-mediacontrol 2020-06-08 01:04:16 -07:00
9ced745258 Merge pull request #533 from Twasi/bugfix/Correct-position-data-type
Bugfix/correct position data type
2020-06-04 14:30:58 -07:00
948f6ccd95 Also change all other occurrences and documentation where int was used for transform position 2020-06-04 15:42:27 +02:00
1c58727ca9 Change position datatype from int to double (as it is in OBS) 2020-06-04 15:28:47 +02:00
612bd9960c readme: add TLS tunneling section 2020-06-04 10:14:58 +02:00
1474499886 Requests: Add GetSceneItemList
Adds a new `GetSceneItemList` request to get all scene items in a
specified scene.
2020-05-29 00:00:16 -07:00
cf99c68843 Requests: Fix useDecibel response when volume is -infinity
When the volume in OBS is -infinity, `GetVolume` returns a decibel value of
either 0, or in some cases no `volume` property at all. This makes `GetVolume`
return a decibel value of -100.0 if the real volume is -infinity.
2020-05-28 22:45:08 -07:00
d2865b9f4f Requests: Add AddSceneItem
New request `AddSceneItem` which allows you to add a
source to a scene. Returns an itemId which can be used
to manipulate the item further if necessary.
2020-05-28 22:20:25 -07:00
865c0c79db Docs: Rework some more of the readme 2020-05-28 16:45:05 -07:00
3462e7f50e Docs: Add new logo and update header 2020-05-28 16:35:21 -07:00
37e412e710 Docs: Add discord shield to readme 2020-05-28 16:16:31 -07:00
5822992b44 Merge pull request #524 from ProstoSanja/create-scene
Add request `CreateScene` to `scenes`
category
2020-05-27 21:21:14 -07:00
eb7787fb7d Docs: Fix param name 2020-05-27 11:18:39 +03:00
f1371034f7 Request: Add CreateScene 2020-05-27 01:03:01 +03:00
b00a93dd6b Requests: Add GetMediaSourcesList and minor refactor
Adds `GetMediaSourcesList` which returns a list of all media sources,
along with their individual media playback states. In the process I
refactored a switch which coverts the enum to a QString into its own
helper function.
2020-05-19 20:20:35 -07:00
7918438747 Events: Add media events
Adds the following events:

- `MediaPlaying`
- `MediaPaused`
- `MediaRestarted`
- `MediaStopped`
- `MediaNext`
- `MediaPrevious`
- `MediaStarted`
- `MediaEnded`
2020-05-16 12:56:42 -07:00
31a085db73 Docs: Update @since items 2020-05-16 12:11:26 -07:00
72997eaa85 Merge branch '4.x-current' into category-mediacontrol 2020-05-16 12:08:28 -07:00
a1ea84b286 Docs: Its finally complaining less 2020-05-14 04:46:12 -07:00
baba8790bc testing 2020-05-14 04:28:57 -07:00
1da7f47af9 Docs: Add pr template 2020-05-14 03:43:18 -07:00
60d9b72fea Docs: Maybe it will complain less now 2020-05-14 03:25:34 -07:00
7a7a8b7ed1 Docs: Even more changes 2020-05-14 03:19:14 -07:00
100565081c Docs: More changes 2020-05-14 03:11:17 -07:00
27f82434b6 Docs: More work on various things 2020-05-14 03:01:28 -07:00
353a9aa671 Update docs - initial pr draft commit 2020-05-14 02:13:50 -07:00
121d9f4920 Remove GetTransitionPosition
Moving it to its own PR since this PR will not make it into 4.8
2020-05-14 01:14:46 -07:00
64062cc879 Merge branch '4.x-current' into feature/t-bar 2020-05-13 20:51:23 -07:00
7ca902e39a A few formatting fixes 2020-05-13 20:48:54 -07:00
10ed2738f5 Add ReleaseTBar request and update SetTBarPosition to depend on OBS pr 2927 2020-05-13 00:10:57 -07:00
4eb7bed2ff Lots of changes
- Move `SetTBarPosition` to transitions category
- Add another check to `SetTBarPosition` so that we dont break the fabric of time with the wrong values
- Create `GetTransitionPosition` request in transitions category
2020-05-12 15:44:57 -07:00
acfdb26135 A lot more requests. 2020-05-10 23:21:24 -07:00
37cf8e9d29 Rename MediaPlayPause to PlayPauseMedia
Works better with the other upcoming media control request names
2020-05-10 21:13:48 -07:00
1c6670c9b0 Create new category and add MediaPlayPause request 2020-05-10 21:03:38 -07:00
a479f529af CI: Add Github Actions workflow for release tags 2020-04-21 17:42:07 +02:00
76635ff31b CI: Add Github Actions workflow for PRs and pushes to main branch 2020-04-21 17:29:59 +02:00
71391914ae Added financial contributors to the README 2020-04-14 11:21:57 -07:00
93c2dab634 Merge branch '4.x-current' into feature/t-bar 2020-03-28 19:23:52 +01:00
297d920fdc fix 2020-03-18 01:19:02 +01:00
c741a73893 request(SetTBarPosition): wip 2020-03-18 01:18:20 +01:00
74 changed files with 9310 additions and 3578 deletions

View File

@ -3,6 +3,7 @@ insert_final_newline = true
[*.{c,cpp,h,hpp}]
indent_style = tab
indent_size = 4
[*.{yml,yaml}]
indent_style = space

View File

@ -1,22 +0,0 @@
## Contributing to obs-websocket
### Translating obs-websocket to your language
Localization happens on Crowdin: https://crowdin.com/project/obs-websocket
### Writing code for obs-websocket
#### Coding Guidelines
- Function and variable names: snake_case for C names, CamelCase for C++ names
- Tabs are 8 columns wide
- 80 columns max.
#### Commit Guidelines
- Commits follow the 50/72 standard:
- 50 characters max for the title
- One empty line after the title
- Description wrapped to 72 columns max per line.
- Commit titles:
- Use present tense
- Prefix the title with a "scope" name
- e.g: "CI: fix wrong behaviour when packaging for OS X"
- Typical scopes: CI, General, Request, Event, Server
- Look at existing commits for more examples

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
open_collective: obs-websocket
github: Palakis
custom: https://www.paypal.me/stephanelepin

View File

@ -1,16 +1,20 @@
##### Issue type
Bug report? Feature request? Other?
<!--- Uncomment one of the two options below. -->
<!--- - Bug report -->
<!--- - Feature request -->
##### Description
*Replace this with a description of the bug encountered or feature requested.*
<!--- Describe the bug encountered or feature requested. -->
##### Steps to reproduce and other useful info
*If it's a bug, please describe the steps to reproduce it and PLEASE include an OBS log file. Otherwise, remove this section.*
<!--- If it's a bug, please describe the steps to reproduce it and PLEASE include an OBS log file. Otherwise, remove this section. -->
##### Technical information
- **Operating System** :
- **OBS Studio version** :
- **obs-websocket version** :
##### Development Environment
*If you're trying to compile obs-websocket, please describe your compiler type and version (e.g: GCC 4.7, VC2013, ...), and the CMake settings used.
Remove this section if it doesn't apply to your case.*
<!--- If you're trying to compile obs-websocket, please describe your compiler type and version (e.g: GCC 4.7, VC2013, ...), and the CMake settings used. -->
<!--- Remove this section if it does not apply. -->

BIN
.github/images/obsws_logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

35
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,35 @@
<!--- Please fill out the following template, which will help other contributors review your Pull Request. -->
<!--- Make sure youve read the contribution guidelines here: https://github.com/Palakis/obs-websocket/blob/4.x-current/CONTRIBUTING.md -->
### Description
<!--- Describe your changes. -->
### Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes/closes an open issue or implements feature request, -->
<!--- please link to the issue here. -->
### How Has This Been Tested?
<!--- Please describe in detail how you tested your changes, along with the OS(s) you tested with. -->
Tested OS(s):
### Types of changes
<!--- What types of changes does your PR introduce? Uncomment all that apply -->
<!--- - Bug fix (non-breaking change which fixes an issue) -->
<!--- - New request/event (non-breaking) -->
<!--- - Documentation change (a change to documentation pages) -->
<!--- - Enhancement (modification to a current event/request which adds functionality) -->
<!--- - Performance enhancement (non-breaking change which improves efficiency) -->
<!--- - Code cleanup (non-breaking change which makes code smaller or more readable) -->
### Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] I have read the [**contributing** document](https://github.com/Palakis/obs-websocket/blob/4.x-current/CONTRIBUTING.md).
- [ ] My code is not on the master branch.
- [ ] The code has been tested.
- [ ] All commit messages are properly formatted and commits squashed where appropriate.
- [ ] I have included updates to all appropriate documentation.

412
.github/workflows/pr_push.yml vendored Normal file
View File

@ -0,0 +1,412 @@
name: 'CI Multiplatform Build'
on:
push:
paths-ignore:
- 'docs/**'
branches:
- 4.x-current
pull_request:
paths-ignore:
- '**.md'
branches:
- 4.x-current
jobs:
windows:
name: 'Windows 32+64bit'
runs-on: [windows-latest]
if: contains(github.event.head_commit.message, '[skip ci]') != true
env:
QT_VERSION: '5.10.1'
WINDOWS_DEPS_VERSION: '2017'
CMAKE_GENERATOR: "Visual Studio 16 2019"
CMAKE_SYSTEM_VERSION: "10.0"
steps:
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.0
- name: 'Checkout'
uses: actions/checkout@v2
with:
path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive'
- name: 'Checkout OBS'
uses: actions/checkout@v2
with:
repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio
submodules: 'recursive'
- name: 'Get OBS-Studio git info'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git fetch --prune --unshallow
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git checkout ${{ env.OBS_GIT_TAG }}
git submodule update
- name: 'Get obs-websocket git info'
shell: bash
working-directory: ${{ github.workspace }}/obs-websocket
run: |
git fetch --prune --unshallow
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Install prerequisite: QT'
run: |
curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C -
7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT"
- name: 'Install prerequisite: Pre-built OBS dependencies'
run: |
curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C -
7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps"
- name: 'Restore OBS 32-bit build v${{ env.OBS_GIT_TAG }} from cache'
id: build-cache-obs-32
uses: actions/cache@v1
env:
CACHE_NAME: 'build-cache-obs-32'
with:
path: ${{ github.workspace }}/obs-studio/build32
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
restore-keys: |
${{ runner.os }}-${{ env.CACHE_NAME }}-
- name: 'Configure OBS 32-bit'
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio
run: |
mkdir .\build32
cd .\build32
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES ..
- name: 'Build OBS-Studio 32-bit'
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio
run: |
msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj
msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj
- name: 'Restore OBS 64-bit build v${{ env.OBS_GIT_TAG }} from cache'
id: build-cache-obs-64
uses: actions/cache@v1
env:
CACHE_NAME: 'build-cache-obs-64'
with:
path: ${{ github.workspace }}/obs-studio/build64
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
restore-keys: |
${{ runner.os }}-${{ env.CACHE_NAME }}-
- name: 'Configure OBS 64-bit'
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio
run: |
mkdir .\build64
cd .\build64
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES ..
- name: 'Build OBS-Studio 64-bit'
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio
run: |
msbuild /m /p:Configuration=RelWithDebInfo .\build64\libobs\libobs.vcxproj
msbuild /m /p:Configuration=RelWithDebInfo .\build64\UI\obs-frontend-api\obs-frontend-api.vcxproj
- name: 'Configure obs-websocket 64-bit'
working-directory: ${{ github.workspace }}/obs-websocket
run: |
mkdir .\build64
cd .\build64
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build64\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build64\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" ..
- name: 'Configure obs-websocket 32-bit'
working-directory: ${{ github.workspace }}/obs-websocket
run: |
mkdir .\build32
cd .\build32
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build32\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build32\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" ..
- name: 'Build obs-websocket 64-bit'
working-directory: ${{ github.workspace }}/obs-websocket
run: msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-websocket.sln
- name: 'Build obs-websocket 32-bit'
working-directory: ${{ github.workspace }}/obs-websocket
run: msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-websocket.sln
- name: 'Set PR artifact filename'
shell: bash
run: |
FILENAME="obs-websocket-${{ env.GIT_HASH }}-Windows"
echo "::set-env name=FILENAME::$FILENAME"
- name: 'Package obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket
run: |
mkdir package
cd package
7z a "${{ env.WIN_FILENAME }}.zip" "..\release\*"
iscc ..\installer\installer.iss /O. /F"${{ env.WIN_FILENAME }}-Installer"
- name: 'Publish ${{ env.WIN_FILENAME }}.zip'
if: success()
uses: actions/upload-artifact@v2-preview
with:
name: '${{ env.GIT_HASH }}-Windows'
path: ${{ github.workspace }}/obs-websocket/package/*.zip
- name: 'Publish ${{ env.WIN_FILENAME }}-Installer.exe'
if: success()
uses: actions/upload-artifact@v2-preview
with:
name: '${{ env.GIT_HASH }}-Windows-Installer'
path: ${{ github.workspace }}/obs-websocket/package/*.exe
ubuntu64:
name: "Linux/Ubuntu 64-bit"
runs-on: [ubuntu-latest]
if: contains(github.event.head_commit.message, '[skip ci]') != true
steps:
- name: 'Checkout'
uses: actions/checkout@v2
with:
path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive'
- name: 'Checkout OBS'
uses: actions/checkout@v2
with:
repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio
submodules: 'recursive'
- name: 'Get OBS-Studio git info'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git fetch --prune --unshallow
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git checkout ${{ env.OBS_GIT_TAG }}
git submodule update
- name: 'Get obs-websocket git info'
working-directory: ${{ github.workspace }}/obs-websocket
run: |
git fetch --prune --unshallow
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Install prerequisites (Apt)'
shell: bash
run: |
sudo dpkg --add-architecture amd64
sudo apt-get -qq update
sudo apt-get install -y \
build-essential \
checkinstall \
cmake \
libasound2-dev \
libavcodec-dev \
libavdevice-dev \
libavfilter-dev \
libavformat-dev \
libavutil-dev \
libcurl4-openssl-dev \
libfdk-aac-dev \
libfontconfig-dev \
libfreetype6-dev \
libgl1-mesa-dev \
libjack-jackd2-dev \
libjansson-dev \
libluajit-5.1-dev \
libpulse-dev \
libqt5x11extras5-dev \
libspeexdsp-dev \
libswresample-dev \
libswscale-dev \
libudev-dev \
libv4l-dev \
libva-dev \
libvlc-dev \
libx11-dev \
libx264-dev \
libxcb-randr0-dev \
libxcb-shm0-dev \
libxcb-xinerama0-dev \
libxcomposite-dev \
libxinerama-dev \
libmbedtls-dev \
pkg-config \
python3-dev \
qtbase5-dev \
libqt5svg5-dev \
swig
- name: 'Configure OBS-Studio'
working-directory: ${{ github.workspace }}/obs-studio
shell: bash
run: |
mkdir ./build
cd ./build
cmake -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr ..
- name: 'Build OBS-Studio'
working-directory: ${{ github.workspace }}/obs-studio
shell: bash
run: |
set -e
cd ./build
make -j4 libobs obs-frontend-api
- name: 'Install OBS-Studio'
working-directory: ${{ github.workspace }}/obs-studio
shell: bash
run: |
cd ./build
sudo cp ./libobs/libobs.so /usr/lib
sudo cp ./UI/obs-frontend-api/libobs-frontend-api.so /usr/lib
sudo mkdir -p /usr/include/obs
sudo cp ../UI/obs-frontend-api/obs-frontend-api.h /usr/include/obs/obs-frontend-api.h
- name: 'Configure obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
mkdir ./build
cd ./build
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr ..
- name: 'Build obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
set -e
cd ./build
make -j4
- name: 'Set PR artifact filename'
shell: bash
run: |
FILENAME="obs-websocket-1-${{ env.GIT_HASH }}-1_amd64.deb"
echo "::set-env name=FILENAME::$FILENAME"
- name: 'Package ${{ env.FILENAME }}'
if: success()
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
VERSION="1-${{ env.GIT_HASH }}-git"
cd ./build
sudo checkinstall -y --type=debian --fstrans=no -nodoc \
--backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion=$VERSION \
--pkglicense="GPLv2.0" --maintainer="${{ github.event.pusher.email }}" --pkggroup="video" \
--pkgsource="${{ github.event.repository.html_url }}" \
--requires="obs-studio,libqt5core5a,libqt5widgets5,qt5-image-formats-plugins" \
--pakdir="../package"
sudo chmod ao+r ../package/*
cd -
- name: 'Publish ${{ env.FILENAME }}'
if: success()
uses: actions/upload-artifact@v2-preview
with:
name: '${{ env.GIT_HASH }}-linux'
path: '${{ github.workspace }}/obs-websocket/package/*.deb'
macos64:
name: "macOS 64-bit"
runs-on: [macos-latest]
if: contains(github.event.head_commit.message, '[skip ci]') != true
env:
MACOS_DEPS_VERSION: '2020-04-18'
QT_VERSION: '5.14.1'
steps:
- name: 'Checkout'
uses: actions/checkout@v2
with:
path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive'
- name: 'Checkout OBS'
uses: actions/checkout@v2
with:
repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio
submodules: 'recursive'
- name: 'Get OBS-Studio git info'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git fetch --prune --unshallow
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git checkout ${{ env.OBS_GIT_TAG }}
git submodule update
- name: 'Get obs-websocket git info'
working-directory: ${{ github.workspace }}/obs-websocket
run: |
git fetch --prune --unshallow
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Install prerequisites (Homebrew)'
shell: bash
run: |
brew bundle --file ${{ github.workspace }}/obs-websocket/CI/macos/Brewfile
- name: 'Install prerequisite: Pre-built OBS dependencies'
shell: bash
run: |
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz
tar -xf ${{ github.workspace }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp"
- name: 'Configure OBS Studio'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
mkdir build
cd build
cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DENABLE_SCRIPTING=NO -DDepsPath=/tmp/obsdeps -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/${{ env.QT_VERSION }}/lib/cmake ..
- name: 'Build OBS Studio libraries'
working-directory: ${{ github.workspace }}/obs-studio
shell: bash
run: |
set -e
cd ./build
make -j4 libobs obs-frontend-api
- name: 'Configure obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
mkdir build
cd build
cmake -DQTDIR=/usr/local/Cellar/qt/${{ env.QT_VERSION }} -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DLIBOBS_LIB=${{ github.workspace }}/obs-studio/libobs -DOBS_FRONTEND_LIB="${{ github.workspace }}/obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr ..
- name: 'Build obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
set -e
cd ./build
make -j4
- name: 'Install prerequisite: Packages app'
if: success()
shell: bash
run: |
curl -L -O https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg
sudo installer -pkg ${{ github.workspace }}/Packages.pkg -target /
- name: 'Set PR artifact filename'
shell: bash
run: |
FILENAME_UNSIGNED="obs-websocket-${{ env.GIT_HASH }}-macOS-Unsigned.pkg"
echo "::set-env name=FILENAME_UNSIGNED::$FILENAME_UNSIGNED"
- name: 'Fix linked dynamic library paths'
if: success()
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./build/obs-websocket.so
install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./build/obs-websocket.so
install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./build/obs-websocket.so
echo "Dependencies for obs-websocket"
otool -L ./build/obs-websocket.so
- name: 'Package ${{ env.FILENAME }}'
if: success()
working-directory: ./obs-websocket
shell: bash
run: |
packagesbuild ./CI/macos/obs-websocket.pkgproj
mv ./release/obs-websocket.pkg ./release/${{ env.FILENAME_UNSIGNED }}
- name: 'Publish ${{ env.FILENAME_UNSIGNED }} artifact'
if: success()
uses: actions/upload-artifact@v2-preview
with:
name: '${{ env.GIT_HASH }}-macOS'
path: ${{ github.workspace }}/obs-websocket/release/*.pkg

484
.github/workflows/tag_release.yml vendored Normal file
View File

@ -0,0 +1,484 @@
name: 'CI Multiplatform Release'
on:
push:
paths-ignore:
- 'docs/**'
tags:
- '[45].[0-9]+.[0-9]+'
jobs:
windows:
name: 'Windows 32+64bit'
runs-on: [windows-latest]
env:
QT_VERSION: '5.10.1'
WINDOWS_DEPS_VERSION: '2017'
CMAKE_GENERATOR: "Visual Studio 16 2019"
CMAKE_SYSTEM_VERSION: "10.0"
steps:
- name: 'Add msbuild to PATH'
uses: microsoft/setup-msbuild@v1.0.0
- name: 'Checkout'
uses: actions/checkout@v2
with:
path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive'
- name: 'Checkout OBS'
uses: actions/checkout@v2
with:
repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio
submodules: 'recursive'
- name: 'Get OBS-Studio git info'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git fetch --prune --unshallow
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git checkout ${{ env.OBS_GIT_TAG }}
git submodule update
- name: 'Get obs-websocket git info'
shell: bash
working-directory: ${{ github.workspace }}/obs-websocket
run: |
git fetch --prune --unshallow
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Install prerequisite: QT'
run: |
curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C -
7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}\cmbuild\QT"
- name: 'Install prerequisite: Pre-built OBS dependencies'
run: |
curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C -
7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}\cmbuild\deps"
- name: 'Restore OBS 32-bit build v${{ env.OBS_GIT_TAG }} from cache'
id: build-cache-obs-32
uses: actions/cache@v1
env:
CACHE_NAME: 'build-cache-obs-32'
with:
path: ${{ github.workspace }}/obs-studio/build32
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
restore-keys: |
${{ runner.os }}-${{ env.CACHE_NAME }}-
- name: 'Configure OBS 32-bit'
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio
run: |
mkdir .\build32
cd .\build32
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win32" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES ..
- name: 'Build OBS-Studio 32-bit'
if: steps.build-cache-obs-32.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio
run: |
msbuild /m /p:Configuration=RelWithDebInfo .\build32\libobs\libobs.vcxproj
msbuild /m /p:Configuration=RelWithDebInfo .\build32\UI\obs-frontend-api\obs-frontend-api.vcxproj
- name: 'Restore OBS 64-bit build v${{ env.OBS_GIT_TAG }} from cache'
id: build-cache-obs-64
uses: actions/cache@v1
env:
CACHE_NAME: 'build-cache-obs-64'
with:
path: ${{ github.workspace }}/obs-studio/build64
key: ${{ runner.os }}-${{ env.CACHE_NAME }}-${{ env.OBS_GIT_TAG }}
restore-keys: |
${{ runner.os }}-${{ env.CACHE_NAME }}-
- name: 'Configure OBS 64-bit'
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio
run: |
mkdir .\build64
cd .\build64
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DDepsPath="${{ github.workspace }}\cmbuild\deps\win64" -DCOPIED_DEPENDENCIES=NO -DCOPY_DEPENDENCIES=YES ..
- name: 'Build OBS-Studio 64-bit'
if: steps.build-cache-obs-64.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}/obs-studio
run: |
msbuild /m /p:Configuration=RelWithDebInfo .\build64\libobs\libobs.vcxproj
msbuild /m /p:Configuration=RelWithDebInfo .\build64\UI\obs-frontend-api\obs-frontend-api.vcxproj
- name: 'Configure obs-websocket 64-bit'
working-directory: ${{ github.workspace }}/obs-websocket
run: |
mkdir .\build64
cd .\build64
cmake -G "${{ env.CMAKE_GENERATOR }}" -A x64 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017_64" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build64\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build64\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" ..
- name: 'Configure obs-websocket 32-bit'
working-directory: ${{ github.workspace }}/obs-websocket
run: |
mkdir .\build32
cd .\build32
cmake -G "${{ env.CMAKE_GENERATOR }}" -A Win32 -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DQTDIR="${{ github.workspace }}\cmbuild\QT\${{ env.QT_VERSION }}\msvc2017" -DLibObs_DIR="${{ github.workspace }}\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="${{ github.workspace }}\obs-studio\libobs" -DLIBOBS_LIB="${{ github.workspace }}\obs-studio\build32\libobs\RelWithDebInfo\obs.lib" -DOBS_FRONTEND_LIB="${{ github.workspace }}\obs-studio\build32\UI\obs-frontend-api\RelWithDebInfo\obs-frontend-api.lib" ..
- name: 'Build obs-websocket 64-bit'
working-directory: ${{ github.workspace }}/obs-websocket
run: msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-websocket.sln
- name: 'Build obs-websocket 32-bit'
working-directory: ${{ github.workspace }}/obs-websocket
run: msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-websocket.sln
- name: 'Set release filename'
shell: bash
run: |
FILENAME="obs-websocket-${{ env.GIT_TAG }}-Windows"
echo "::set-env name=WIN_FILENAME::$FILENAME"
- name: 'Package obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket
run: |
mkdir package
cd package
7z a "${{ env.WIN_FILENAME }}.zip" "..\release\*"
iscc ..\installer\installer.iss /O. /F"${{ env.WIN_FILENAME }}-Installer"
- name: 'Publish ${{ env.WIN_FILENAME }}.zip'
if: success()
uses: actions/upload-artifact@v2-preview
with:
name: '${{ env.GIT_TAG }}-Windows'
path: ${{ github.workspace }}/obs-websocket/package/*.zip
- name: 'Publish ${{ env.WIN_FILENAME }}-Installer.exe'
if: success()
uses: actions/upload-artifact@v2-preview
with:
name: '${{ env.GIT_TAG }}-Windows-Installer'
path: ${{ github.workspace }}/obs-websocket/package/*.exe
ubuntu64:
name: "Linux/Ubuntu 64-bit"
runs-on: [ubuntu-latest]
steps:
- name: 'Checkout'
uses: actions/checkout@v2
with:
path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive'
- name: 'Checkout OBS'
uses: actions/checkout@v2
with:
repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio
submodules: 'recursive'
- name: 'Get OBS-Studio git info'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git fetch --prune --unshallow
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git checkout ${{ env.OBS_GIT_TAG }}
git submodule update
- name: 'Get obs-websocket git info'
working-directory: ${{ github.workspace }}/obs-websocket
run: |
git fetch --prune --unshallow
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Install prerequisites (Apt)'
shell: bash
run: |
sudo dpkg --add-architecture amd64
sudo apt-get -qq update
sudo apt-get install -y \
build-essential \
checkinstall \
cmake \
libasound2-dev \
libavcodec-dev \
libavdevice-dev \
libavfilter-dev \
libavformat-dev \
libavutil-dev \
libcurl4-openssl-dev \
libfdk-aac-dev \
libfontconfig-dev \
libfreetype6-dev \
libgl1-mesa-dev \
libjack-jackd2-dev \
libjansson-dev \
libluajit-5.1-dev \
libpulse-dev \
libqt5x11extras5-dev \
libspeexdsp-dev \
libswresample-dev \
libswscale-dev \
libudev-dev \
libv4l-dev \
libva-dev \
libvlc-dev \
libx11-dev \
libx264-dev \
libxcb-randr0-dev \
libxcb-shm0-dev \
libxcb-xinerama0-dev \
libxcomposite-dev \
libxinerama-dev \
libmbedtls-dev \
pkg-config \
python3-dev \
qtbase5-dev \
libqt5svg5-dev \
swig
- name: 'Configure OBS-Studio'
working-directory: ${{ github.workspace }}/obs-studio
shell: bash
run: |
mkdir ./build
cd ./build
cmake -DDISABLE_PLUGINS=YES -DENABLE_SCRIPTING=NO -DUNIX_STRUCTURE=YES -DCMAKE_INSTALL_PREFIX=/usr ..
- name: 'Build OBS-Studio'
working-directory: ${{ github.workspace }}/obs-studio
shell: bash
run: |
set -e
cd ./build
make -j4 libobs obs-frontend-api
- name: 'Install OBS-Studio'
working-directory: ${{ github.workspace }}/obs-studio
shell: bash
run: |
cd ./build
sudo cp ./libobs/libobs.so /usr/lib
sudo cp ./UI/obs-frontend-api/libobs-frontend-api.so /usr/lib
sudo mkdir -p /usr/include/obs
sudo cp ../UI/obs-frontend-api/obs-frontend-api.h /usr/include/obs/obs-frontend-api.h
- name: 'Configure obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
mkdir ./build
cd ./build
cmake -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DCMAKE_INSTALL_PREFIX=/usr ..
- name: 'Build obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
set -e
cd ./build
make -j4
- name: 'Set release filename'
shell: bash
run: |
FILENAME="obs-websocket-${{ env.GIT_TAG }}-1_amd64.deb"
echo "::set-env name=LINUX_FILENAME::$FILENAME"
- name: 'Package ${{ env.LINUX_FILENAME }}'
if: success()
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
VERSION="${{ env.GIT_TAG }}"
cd ./build
sudo checkinstall -y --type=debian --fstrans=no -nodoc \
--backup=no --deldoc=yes --install=no --pkgname=obs-websocket --pkgversion=$VERSION \
--pkglicense="GPLv2.0" --maintainer="${{ github.event.pusher.email }}" --pkggroup="video" \
--pkgsource="${{ github.event.repository.html_url }}" \
--requires="obs-studio,libqt5core5a,libqt5widgets5,qt5-image-formats-plugins" \
--pakdir="../package"
sudo chmod ao+r ../package/*
cd -
- name: 'Publish ${{ env.LINUX_FILENAME }}'
if: success()
uses: actions/upload-artifact@v2-preview
with:
name: '${{ env.GIT_TAG }}-linux'
path: '${{ github.workspace }}/obs-websocket/package/*.deb'
macos64:
name: "macOS 64-bit"
runs-on: [macos-latest]
env:
MACOS_DEPS_VERSION: '2020-04-18'
QT_VERSION: '5.14.1'
steps:
- name: 'Checkout'
uses: actions/checkout@v2
with:
path: ${{ github.workspace }}/obs-websocket
submodules: 'recursive'
- name: 'Checkout OBS'
uses: actions/checkout@v2
with:
repository: obsproject/obs-studio
path: ${{ github.workspace }}/obs-studio
submodules: 'recursive'
- name: 'Get OBS-Studio git info'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git fetch --prune --unshallow
echo ::set-env name=OBS_GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
echo ::set-env name=OBS_GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=OBS_GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Checkout last OBS-Studio release (${{ env.OBS_GIT_TAG }})'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
git checkout ${{ env.OBS_GIT_TAG }}
git submodule update
- name: 'Get obs-websocket git info'
working-directory: ${{ github.workspace }}/obs-websocket
run: |
git fetch --prune --unshallow
echo ::set-env name=GIT_BRANCH::${{ github.event.pull_request.head.ref }}
echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
- name: 'Install prerequisites (Homebrew)'
shell: bash
run: |
brew bundle --file ${{ github.workspace }}/obs-websocket/CI/macos/Brewfile
- name: 'Install prerequisite: Pre-built OBS dependencies'
shell: bash
run: |
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz
tar -xf ${{ github.workspace }}/osx-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp"
- name: 'Configure OBS Studio'
shell: bash
working-directory: ${{ github.workspace }}/obs-studio
run: |
mkdir build
cd build
cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DENABLE_SCRIPTING=NO -DDepsPath=/tmp/obsdeps -DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt/${{ env.QT_VERSION }}/lib/cmake ..
- name: 'Build OBS Studio libraries'
working-directory: ${{ github.workspace }}/obs-studio
shell: bash
run: |
set -e
cd ./build
make -j4 libobs obs-frontend-api
- name: 'Configure obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
mkdir build
cd build
cmake -DQTDIR=/usr/local/Cellar/qt/${{ env.QT_VERSION }} -DLIBOBS_INCLUDE_DIR=${{ github.workspace }}/obs-studio/libobs -DLIBOBS_LIB=${{ github.workspace }}/obs-studio/libobs -DOBS_FRONTEND_LIB="${{ github.workspace }}/obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr ..
- name: 'Build obs-websocket'
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
set -e
cd ./build
make -j4
- name: 'Install prerequisite: Packages app'
if: success()
shell: bash
run: |
curl -L -O https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg
sudo installer -pkg ${{ github.workspace }}/Packages.pkg -target /
- name: 'Set release filename'
if: success() && startsWith(github.ref, 'refs/tags')
shell: bash
run: |
FILENAME_UNSIGNED="obs-websocket-${{ env.GIT_TAG }}-macOS-Unsigned.pkg"
FILENAME="obs-websocket-${{ env.GIT_TAG }}-macOS.pkg"
echo "::set-env name=MAC_FILENAME_UNSIGNED::$FILENAME_UNSIGNED"
echo "::set-env name=MAC_FILENAME::$FILENAME"
- name: 'Fix linked dynamic library paths'
if: success()
working-directory: ${{ github.workspace }}/obs-websocket
shell: bash
run: |
install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./build/obs-websocket.so
install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./build/obs-websocket.so
install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./build/obs-websocket.so
echo "Dependencies for obs-websocket"
otool -L ./build/obs-websocket.so
- name: 'Install Apple Developer Certificate'
if: success()
uses: apple-actions/import-codesign-certs@253ddeeac23f2bdad1646faac5c8c2832e800071
with:
p12-file-base64: ${{ secrets.MACOS_CERT_CODESIGN }}
p12-password: ${{ secrets.MACOS_CERT_PASS }}
- name: 'Code signing'
if: success()
working-directory: ./obs-websocket
shell: bash
run: |
set -e
codesign --sign "${{ secrets.MACOS_IDENT_CODESIGN }}" ./build/obs-websocket.so
packagesbuild ./CI/macos/obs-websocket.pkgproj
mv ./release/obs-websocket.pkg ./release/${{ env.MAC_FILENAME_UNSIGNED }}
productsign --sign "${{ secrets.MACOS_IDENT_INSTALLER }}" ./release/${{ env.MAC_FILENAME_UNSIGNED }} ./release/${{ env.MAC_FILENAME }}
rm ./release/${{ env.MAC_FILENAME_UNSIGNED }}
- name: 'Notarization'
if: success()
working-directory: ./obs-websocket
shell: bash
run: |
set -e
xcrun altool --store-password-in-keychain-item "AC_PASSWORD" -u "${{ secrets.MACOS_IDENT_USER }}" -p "${{ secrets.MACOS_IDENT_PASS }}"
xcnotary precheck ./release/${{ env.MAC_FILENAME }}
if [ "$?" -eq 0 ]; then xcnotary notarize ./release/${{ env.MAC_FILENAME }} --developer-account "${{ secrets.MACOS_IDENT_USER }}" --developer-password-keychain-item "AC_PASSWORD" --provider "${{ secrets.MACOS_IDENT_PROVIDER }}"; fi
- name: 'Publish ${{ env.MAC_FILENAME }} artifact'
if: success() && startsWith(github.ref, 'refs/tags')
uses: actions/upload-artifact@v2-preview
with:
name: '${{ env.GIT_TAG }}-macOS'
path: ${{ github.workspace }}/obs-websocket/release/*.pkg
make-release:
name: 'Create and upload release'
runs-on: [ubuntu-latest]
needs: [windows, ubuntu64, macos64]
steps:
- name: 'Get the version'
shell: bash
id: get_version
run: |
echo ::set-env name=TAG_VERSION::${GITHUB_REF/refs\/tags\//}
- name: 'Create Release'
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ env.TAG_VERSION }}
release_name: obs-websocket ${{ env.TAG_VERSION }}
draft: false
prerelease: false
- name: 'Download release artifacts'
uses: actions/download-artifact@v2-preview
- name: 'Upload Windows .zip artifact to release'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-Windows/obs-websocket-${{ env.TAG_VERSION }}-Windows.zip
asset_name: obs-websocket-${{ env.TAG_VERSION }}-Windows.zip
asset_content_type: application/zip
- name: 'Upload Windows .exe artifact to release'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-Windows-Installer/obs-websocket-${{ env.TAG_VERSION }}-Windows-Installer.exe
asset_name: obs-websocket-${{ env.TAG_VERSION }}-Windows-Installer.exe
asset_content_type: application/zip
- name: 'Upload Linux artifact to release'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-linux/obs-websocket_${{ env.TAG_VERSION }}-1_amd64.deb
asset_name: obs-websocket-${{ env.TAG_VERSION }}-1_amd64.deb
asset_content_type: application/octet-stream
- name: 'Upload macOS artifact to release'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ github.workspace }}/${{ env.TAG_VERSION }}-macOS/obs-websocket-${{ env.TAG_VERSION }}-macOS.pkg
asset_name: obs-websocket-${{ env.TAG_VERSION }}-macOS.pkg
asset_content_type: application/octet-stream

View File

@ -29,12 +29,18 @@ make -j4
sudo make install
```
On other linux OS's, use this cmake command instead:
```shell
cmake -DLIBOBS_INCLUDE_DIR="<path to the libobs sub-folder in obs-studio's source code>" -DCMAKE_INSTALL_PREFIX=/usr ..
```
## OS X
As a prerequisite, you will need Xcode for your current OSX version, the Xcode command line tools, and [Homebrew](https://brew.sh/).
Homebrew's setup will guide you in getting your system set up, you should be good to go once Homebrew is successfully up and running.
Use of the Travis macOS CI scripts is recommended. Please note that these
Use of the macOS CI scripts is recommended. Please note that these
scripts install new software and can change several settings on your system. An
existing obs-studio development environment is not required, as
`install-build-obs-macos.sh` will install it for you. If you already have a
@ -57,6 +63,4 @@ This will result in a ready-to-use `obs-websocket.pkg` installer in the `release
## Automated Builds
- Windows: [![Automated Build status for Windows](https://ci.appveyor.com/api/projects/status/github/Palakis/obs-websocket)](https://ci.appveyor.com/project/Palakis/obs-websocket/history)
- Linux: [![Automated Build status for Linux](https://travis-ci.org/Palakis/obs-websocket.svg?branch=master)](https://travis-ci.org/Palakis/obs-websocket)
- macOS: [![Automated Build status for macOS](https://img.shields.io/azure-devops/build/Palakis/obs-websocket/Palakis.obs-websocket.svg)](https://dev.azure.com/Palakis/obs-websocket/_build)
[![Build Status](https://dev.azure.com/Palakis/obs-websocket/_apis/build/status/Palakis.obs-websocket?branchName=4.x-current)](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current)

View File

@ -35,13 +35,8 @@ done
# qtwebsockets deps
echo "[obs-websocket] Installing obs-websocket dependency 'QT 5.10.1'.."
# =!= NOTICE =!=
# When building QT5 from sources on macOS 10.13+, use local qt5 formula:
# brew install ./CI/macos/qt.rb
# Pouring from the bottle is much quicker though, so use bottle for now.
# =!= NOTICE =!=
brew install https://gist.githubusercontent.com/DDRBoxman/b3956fab6073335a4bf151db0dcbd4ad/raw/ed1342a8a86793ea8c10d8b4d712a654da121ace/qt.rb
brew install ./CI/macos/qt.rb
# Pin this version of QT5 to avoid `brew upgrade`
# upgrading it to incompatible version

View File

@ -14,6 +14,6 @@ sudo apt-get install -y \
qtbase5-dev
# Dirty hack
sudo wget -O /usr/include/obs/obs-frontend-api.h https://raw.githubusercontent.com/obsproject/obs-studio/25.0.0/UI/obs-frontend-api/obs-frontend-api.h
sudo wget -O /usr/include/obs/obs-frontend-api.h https://raw.githubusercontent.com/obsproject/obs-studio/26.0.0/UI/obs-frontend-api/obs-frontend-api.h
sudo ldconfig

10
CI/macos/Brewfile Normal file
View File

@ -0,0 +1,10 @@
tap "akeru-inc/tap"
brew "jack"
brew "speexdsp"
brew "cmake"
brew "freetype"
brew "fdk-aac"
brew "https://gist.githubusercontent.com/DDRBoxman/9c7a2b08933166f4b61ed9a44b242609/raw/ef4de6c587c6bd7f50210eccd5bd51ff08e6de13/qt.rb"
brew "swig", link: false
brew "https://gist.githubusercontent.com/DDRBoxman/4cada55c51803a2f963fa40ce55c9d3e/raw/572c67e908bfbc1bcb8c476ea77ea3935133f5b5/swig.rb"
brew "akeru-inc/tap/xcnotary"

View File

@ -518,7 +518,7 @@
<key>OVERWRITE_PERMISSIONS</key>
<false/>
<key>VERSION</key>
<string>4.8.0</string>
<string>4.9.0</string>
</dict>
<key>PROJECT_COMMENTS</key>
<dict>

View File

@ -3,10 +3,10 @@
class Qt < Formula
desc "Cross-platform application and UI framework"
homepage "https://www.qt.io/"
url "https://download.qt.io/archive/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz"
mirror "https://mirrorservice.org/sites/download.qt-project.org/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz"
url "https://download.qt.io/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz"
mirror "https://www.mirrorservice.org/sites/download.qt-project.org/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz"
sha256 "05ffba7b811b854ed558abf2be2ddbd3bb6ddd0b60ea4b5da75d277ac15e740a"
head "https://code.qt.io/qt/qt5.git", :branch => "5.10.1", :shallow => false
head "https://code.qt.io/qt/qt5.git", :branch => "5.10", :shallow => false
bottle do
sha256 "8b4bad005596a5f8790150fe455db998ac2406f4e0f04140d6656205d844d266" => :high_sierra
@ -18,17 +18,16 @@ class Qt < Formula
option "with-docs", "Build documentation"
option "with-examples", "Build examples"
deprecated_option "with-mysql" => "with-mysql-client"
option "without-proprietary-codecs", "Don't build with proprietary codecs (e.g. mp3)"
# OS X 10.7 Lion is still supported in Qt 5.5, but is no longer a reference
# configuration and thus untested in practice. Builds on OS X 10.7 have been
# reported to fail: <https://github.com/Homebrew/homebrew/issues/45284>.
depends_on :macos => :mountain_lion
# depends_on :macos => :mountain_lion
depends_on "pkg-config" => :build
depends_on :xcode => :build
depends_on "mysql-client" => :optional
depends_on "mysql" => :optional
depends_on "postgresql" => :optional
# Restore `.pc` files for framework-based build of Qt 5 on OS X. This
@ -44,23 +43,6 @@ class Qt < Formula
sha256 "48ff18be2f4050de7288bddbae7f47e949512ac4bcd126c2f504be2ac701158b"
end
# Fix compile error on macOS 10.13 around QFixed:
# https://github.com/Homebrew/homebrew-core/issues/27095
# https://bugreports.qt.io/browse/QTBUG-67545
patch do
url "https://raw.githubusercontent.com/z00m1n/formula-patches/0de0e229/qt/QTBUG-67545.patch"
sha256 "4a115097c7582c7dce4207f5500d13feb8c990eb8a05a43f41953985976ebe6c"
end
# Fix compile error on macOS 10.13 caused by qtlocation dependency
# mapbox-gl-native using Boost 1.62.0 does not build with C++ 17:
# https://github.com/Homebrew/homebrew-core/issues/27095
# https://bugreports.qt.io/browse/QTBUG-67810
patch do
url "https://raw.githubusercontent.com/z00m1n/formula-patches/a1a1f0dd/qt/QTBUG-67810.patch"
sha256 "8ee0bf71df1043f08ebae3aa35036be29c4d9ebff8a27e3b0411a6bd635e9382"
end
def install
args = %W[
-verbose
@ -76,12 +58,11 @@ class Qt < Formula
-no-rpath
-pkg-config
-dbus-runtime
-no-assimp
]
args << "-nomake" << "examples" if build.without? "examples"
if build.with? "mysql-client"
if build.with? "mysql"
args << "-plugin-sql-mysql"
(buildpath/"brew_shim/mysql_config").write <<~EOS
#!/bin/sh
@ -96,6 +77,7 @@ class Qt < Formula
end
args << "-plugin-sql-psql" if build.with? "postgresql"
args << "-proprietary-codecs" if build.with? "proprietary-codecs"
system "./configure", *args
system "make"

View File

@ -5,8 +5,9 @@ set -e
export GIT_HASH=$(git rev-parse --short HEAD)
export PKG_VERSION="1-$GIT_HASH-$BRANCH_SHORT_NAME-git"
if [[ "$BRANCH_FULL_NAME" =~ "^refs/tags/" ]]; then
if [[ $BRANCH_FULL_NAME =~ ^refs/tags/ ]]; then
export PKG_VERSION="$BRANCH_SHORT_NAME"
echo "[obs-websocket] Branch is a tag. Setting version to `$PKG_VERSION`."
fi
cd ./build

View File

@ -24,13 +24,13 @@ if not exist build64 mkdir build64
echo Running cmake for obs-studio %OBSLatestTag% 32-bit...
cd build32
cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DepsPath32%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DepsPath32%" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
echo:
echo:
echo Running cmake for obs-studio %OBSLatestTag% 64-bit...
cd ..\build64
cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DepsPath64%" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DepsPath64%" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
echo:
echo:

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.5)
project(obs-websocket VERSION 4.8.0)
project(obs-websocket VERSION 4.9.0)
set(CMAKE_PREFIX_PATH "${QTDIR}")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
@ -11,6 +11,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_definitions(-DASIO_STANDALONE)
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
set(CMAKE_CXX_FLAGS "-mfpu=neon")
endif()
if (WIN32 OR APPLE)
include(external/FindLibObs.cmake)
endif()
@ -35,6 +39,7 @@ set(obs-websocket_SOURCES
src/WSRequestHandler_StudioMode.cpp
src/WSRequestHandler_Transitions.cpp
src/WSRequestHandler_Outputs.cpp
src/WSRequestHandler_MediaControl.cpp
src/WSEvents.cpp
src/Config.cpp
src/Utils.cpp
@ -187,12 +192,17 @@ if(UNIX AND NOT APPLE)
file(GLOB locale_files data/locale/*.ini)
set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
if(${USE_UBUNTU_FIX})
install(TARGETS obs-websocket
LIBRARY DESTINATION "/usr/lib/obs-plugins")
install(TARGETS obs-websocket LIBRARY
DESTINATION "/usr/lib/obs-plugins"
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
endif()
install(TARGETS obs-websocket
LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins")
install(TARGETS obs-websocket LIBRARY
DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins"
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
install(FILES ${locale_files}
DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/obs/obs-plugins/obs-websocket/locale")

82
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,82 @@
# Contributing to obs-websocket
## Translating obs-websocket to your language
Localization happens on [Crowdin](https://crowdin.com/project/obs-websocket)
## Branches
**Development happens on `4.x-current`**
## Writing code for obs-websocket
### Code Formatting Guidelines
* Function and variable names: snake_case for C names, camelCase for C++ method names
* Request and Event names should use MixedCaps names
* Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml)
* Code is indented with Tabs. Assume they are 8 columns wide
* 80 columns max code width. (Docs can be larger)
* New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples).
These are required to automatically generate the [protocol specification document](docs/generated/protocol.md).
### Code Best-Practices
* Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this:
```cpp
if (success) {
return req->SendOKResponse();
} else {
return req->SendErrorResponse("something went wrong");
}
```
is better like this:
```cpp
if (!success) {
return req->SendErrorResponse("something went wrong");
}
return req->SendOKResponse();
```
* Some example common response/request property names are:
* `sceneName` - The name of a scene
* `sourceName` - The name of a source
* `fromScene` - From a scene - scene name
### Commit Guidelines
* Commits follow the 50/72 standard:
* 50 characters max for the commit title (excluding scope name)
* One empty line after the title
* Description wrapped to 72 columns max width per line.
* Commit titles:
* Use present tense
* Prefix the title with a "scope" name
* e.g: "CI: fix wrong behaviour when packaging for OS X"
* Typical scopes: CI, General, Requests, Events, Server
**Example commit:**
```
Requests: Add GetTransitionPosition
Adds a new request called `GetTransitionPosition` which gets the current
transition's state from 0.0f to 1.0f. Works with both auto and manual
transitions.
```
### Pull Requests
* Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`).
* Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...)
* Only open a pull request if you are ready to show off your work.
* If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers),
create it as a Draft Pull Request (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request").

136
README.md
View File

@ -1,19 +1,35 @@
obs-websocket
==============
# obs-websocket
<p align="center">
<img src="/.github/images/obsws_logo.png" width=150 align="center">
</p>
WebSockets API for OBS Studio.
Follow the main author on Twitter for news & updates : [@LePalakis](https://twitter.com/LePalakis)
[![Build Status](https://dev.azure.com/Palakis/obs-websocket/_apis/build/status/Palakis.obs-websocket?branchName=4.x-current)](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current)
[![CodeFactor](https://www.codefactor.io/repository/github/palakis/obs-websocket/badge)](https://www.codefactor.io/repository/github/palakis/obs-websocket)
[![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fold_left.svg?style=social&label=Follow%20%40LePalakis)](https://twitter.com/LePalakis)
[![Discord](https://img.shields.io/discord/715691013825364120.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/WBaSQ3A)
[![Financial Contributors on Open Collective](https://opencollective.com/obs-websocket/all/badge.svg?label=financial+contributors)](https://opencollective.com/obs-websocket)
## Downloads
Binaries for Windows, MacOS, and Linux are available in the [Releases](https://github.com/Palakis/obs-websocket/releases) section.
### Homebrew
If you're using MacOS you can use Homebrew for installation as well:
```sh
brew install obs-websocket
```
## Using obs-websocket
A web client and frontend made by [t2t2](https://github.com/t2t2/obs-tablet-remote) (compatible with tablets and other touch interfaces) is available here : http://t2t2.github.io/obs-tablet-remote/
Here is a list of available web clients: (compatible with tablets and other touch interfaces)
- [Niek/obs-web](https://github.com/Niek/obs-web)
- [t2t2/obs-tablet-remote](https://github.com/t2t2/obs-tablet-remote)
It is **highly recommended** to protect obs-websocket with a password against unauthorized control. To do this, open the "Websocket server settings" dialog under OBS' "Tools" menu. In the settings dialogs, you can enable or disable authentication and set a password for it.
@ -35,10 +51,19 @@ Here's a list of available language APIs for obs-websocket :
- Python 3.5+ with asyncio: [obs-ws-rc](https://github.com/KirillMysnik/obs-ws-rc) by Kirill Mysnik
- Python 3.6+ with asyncio: [simpleobsws](https://github.com/IRLToolkit/simpleobsws) by tt2468
- Java 8+: [obs-websocket-java](https://github.com/Twasi/websocket-obs-java) by TwasiNET
- Java 11+: [obs-java-client](https://github.com/harm27/obs-java-client) by harm27
- Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf
- Rust: [obws](https://github.com/dnaka91/obws) by dnaka91
- HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468
- CLI: [obs-cli](https://github.com/leafac/obs-cli) by leafac
I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop me an email at `stephane /dot/ lepin /at/ gmail /dot/ com` !
I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop a message in `#project-showoff` in the [discord server!](https://discord.gg/WBaSQ3A)
### Securing obs-websocket (via TLS/SSL)
If you are intending to use obs-websocket outside of a LAN environment, it is highly recommended to secure the connection using a tunneling service.
See the SSL [tunnelling guide](SSL-TUNNELLING.md) for easy instructions on how to encrypt your websocket connection.
## Compiling obs-websocket
@ -46,80 +71,23 @@ See the [build instructions](BUILDING.md).
## Contributing
### Branches
Development happens on `4.x-current`
### Pull Requests
Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). Start your work in a new branch
based on the main one (e.g.: `cool-new-feature`, `fix-palakis-mistakes`, ...) and open a Pull Request once you feel ready to show your work.
**If your Pull Request is not ready to merge yet, create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request").
### Code style & formatting
Source code is indented with tabs, with spaces allowed for alignment.
Regarding protocol changes: new and updated request types / events must always come with accompanying documentation comments (see existing protocol elements for examples).
These are required to automatically generate the [protocol specification document](docs/generated/protocol.md).
Among other recommendations: favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this:
```cpp
if (success) {
return req->SendOKResponse();
} else {
return req->SendErrorResponse("something went wrong");
}
```
is better like this:
```cpp
if (!success) {
return req->SendErrorResponse("something went wrong");
}
return req->SendOKResponse();
```
See [the contributing document](/CONTRIBUTING.md)
## Translations
**Your help is welcome on translations**. Please join the localization project on Crowdin: https://crowdin.com/project/obs-websocket
**Your help is welcome on translations.**
Please join the localization project on [Crowdin](https://crowdin.com/project/obs-websocket)
## Special thanks
In (almost) order of appearance:
- [Brendan H.](https://github.com/haganbmj): Code contributions and gooder English in the Protocol specification
- [Mikhail Swift](https://github.com/mikhailswift): Code contributions
- [Tobias Frahmer](https://github.com/Frahmer): Initial German localization
- [Genture](https://github.com/Genteure): Initial Simplified Chinese and Traditional Chinese localizations
- [Larissa Gabilan](https://github.com/laris151): Initial Portuguese localization
- [Andy Asquelt](https://github.com/asquelt): Initial Polish localization
- [Marcel Haazen](https://github.com/nekocentral): Initial Dutch localization
- [Peter Antonvich](https://github.com/pantonvich): Code contributions
- [yinzara](https://github.com/yinzara): Code contributions
- [Chris Angelico](https://github.com/Rosuav): Code contributions
- [Guillaume "Elektordi" Genty](https://github.com/Elektordi): Code contributions
- [Marwin M](https://github.com/dragonbane0): Code contributions
- [Logan S.](https://github.com/lsdaniel): Code contributions
- [RainbowEK](https://github.com/RainbowEK): Code contributions
- [RytoEX](https://github.com/RytoEX): CI script and code contributions
- [Theodore Stoddard](https://github.com/TStod): Code contributions
- [Philip Loche](https://github.com/PicoCentauri): Code contributions
- [Patrick Heyer](https://github.com/PatTheMav): Code contributions and CI fixes
- [Alex Van Camp](https://github.com/Lange): Code contributions
- [Freddie Meyer](https://github.com/DungFu): Code contributions
- [Casey Muller](https://github.com/caseymrm): CI fixes
- [Chris Angelico](https://github.com/Rosuav): Documentation fixes
Thank you so much to all of the contibutors [(here)](https://github.com/Palakis/obs-websocket/graphs/contributors) for your amazing help.
And also: special thanks to supporters of the project!
## Supporters
They have contributed financially to the project and made possible the addition of several features into obs-websocket. Many thanks to them!
These supporters have contributed financially to the project and made possible the addition of several features into obs-websocket. Many thanks to them!
---
@ -132,3 +100,33 @@ They have contributed financially to the project and made possible the addition
[MediaUnit](http://www.mediaunit.no) is a Norwegian media company developing products and services for the media industry, primarly focused on web and events.
[![MediaUnit](.github/images/mediaunit_logo_black.png)](http://www.mediaunit.no/)
## Contributors
### Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
<a href="https://github.com/Palakis/obs-websocket/graphs/contributors"><img src="https://opencollective.com/obs-websocket/contributors.svg?width=890&button=false" /></a>
### Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/obs-websocket/contribute)]
#### Individuals
<a href="https://opencollective.com/obs-websocket"><img src="https://opencollective.com/obs-websocket/individuals.svg?width=890"></a>
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/obs-websocket/contribute)]
<a href="https://opencollective.com/obs-websocket/organization/0/website"><img src="https://opencollective.com/obs-websocket/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/obs-websocket/organization/1/website"><img src="https://opencollective.com/obs-websocket/organization/1/avatar.svg"></a>
<a href="https://opencollective.com/obs-websocket/organization/2/website"><img src="https://opencollective.com/obs-websocket/organization/2/avatar.svg"></a>
<a href="https://opencollective.com/obs-websocket/organization/3/website"><img src="https://opencollective.com/obs-websocket/organization/3/avatar.svg"></a>
<a href="https://opencollective.com/obs-websocket/organization/4/website"><img src="https://opencollective.com/obs-websocket/organization/4/avatar.svg"></a>
<a href="https://opencollective.com/obs-websocket/organization/5/website"><img src="https://opencollective.com/obs-websocket/organization/5/avatar.svg"></a>
<a href="https://opencollective.com/obs-websocket/organization/6/website"><img src="https://opencollective.com/obs-websocket/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/obs-websocket/organization/7/website"><img src="https://opencollective.com/obs-websocket/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/obs-websocket/organization/8/website"><img src="https://opencollective.com/obs-websocket/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/obs-websocket/organization/9/website"><img src="https://opencollective.com/obs-websocket/organization/9/avatar.svg"></a>

45
SSL-TUNNELLING.md Normal file
View File

@ -0,0 +1,45 @@
# Connecting over a TLS/secure connection (or remotely)
If you want to expose the WebSocket server of obs-websocket over a secure TLS connection (or to connect remotely), the easiest approach is to use a localhost tunneling service like [ngrok](https://ngrok.com/) or [pagekite](https://pagekite.net/).
**Before doing this, secure the WebSocket server first by enabling authentication with a strong password!**
**Please bear in mind that doing this will expose your OBS instance to the open Internet and the security risks it implies. *You've been warned!***
## ngrok
[Install the ngrok CLI tool](https://ngrok.com/download) on a linux OS, then start ngrok bound to port 4444 like this:
```bash
ngrok http 4444
```
The ngrok command will output something like this:
```text
ngrok by @inconshreveable
Tunnel Status online
Version 2.0/2.0
Web Interface http://127.0.0.1:4040
Forwarding http://TUNNEL_ID.ngrok.io -> localhost:4444
Forwarding https://TUNNEL_ID.ngrok.io -> localhost:4444
```
Where `TUNNEL_ID` is, as the name implies, the unique name of your ngrok tunnel. You'll get a new one every time you start ngrok.
Then, use `wss://TUNNEL_ID.ngrok.io` to connect to obs-websocket over TLS.
See the [ngrok documentation](https://ngrok.com/docs) for more tunneling options and settings.
## PageKite
[Install the PageKite CLI tool](http://pagekite.net/downloads), then start PageKite bound to port 4444 like this (replace NAME with one of your choosing):
```bash
python pagekite.py 4444 NAME.pagekite.me
```
Then, use `wss://NAME.pagekite.me` to connect to obs-websocket over TLS.

View File

@ -4,7 +4,7 @@ variables:
trigger:
branches:
include:
- master
- '4.x-current'
tags:
include:
- '*'
@ -160,7 +160,7 @@ jobs:
- script: ./CI/build-macos.sh
displayName: 'Build obs-websocket'
- task: InstallAppleCertificate@1
- task: InstallAppleCertificate@2
displayName: 'Install release signing certificates'
condition: eq(variables['isReleaseMode'], true)
inputs:

View File

@ -0,0 +1 @@

View File

@ -3,6 +3,7 @@ OBSWebsocket.Settings.ServerEnable="WebSockets-Server aktivieren"
OBSWebsocket.Settings.ServerPort="Server-Port"
OBSWebsocket.Settings.AuthRequired="Authentifizierung aktivieren"
OBSWebsocket.Settings.Password="Passwort"
OBSWebsocket.Settings.LockToIPv4="Nur IPv4 verwenden (deaktiviert IPv6)"
OBSWebsocket.Settings.DebugEnable="Debug-Protokollierung aktivieren"
OBSWebsocket.Settings.AlertsEnable="Infobereichbenachrichtigungen aktivieren"
OBSWebsocket.NotifyConnect.Title="Neue Websocket-Verbindung"
@ -10,7 +11,8 @@ OBSWebsocket.NotifyConnect.Message="Client %1 verbunden"
OBSWebsocket.NotifyDisconnect.Title="Websocket-Client getrennt"
OBSWebsocket.NotifyDisconnect.Message="Client %1 getrennt"
OBSWebsocket.Server.StartFailed.Title="Websocket-Serverfehler"
OBSWebsocket.Server.StartFailed.Message="Der WebSockets-Server konnte nicht gestartet werden, mögliche Gründe:\n - Der TCP-Port %1 wird möglicherweise gerade von einem anderen Programm verwendet. Versuchen Sie einen anderen Port in den Websocket-Servereinstellungen zu setzen oder alle Programme zu beenden, die den Port möglicherweise verwenden.\n - Ein unbekannter Netzwerkfehler ist aufgetreten. Versuchen Sie es mit anderen Einstellungen, einem OBS-Neustart oder einem Systemneustart erneut."
OBSWebsocket.Server.StartFailed.Message="Der WebSocket-Server konnte nicht gestartet werden, mögliche Gründe:\n - Der TCP-Port %1 wird möglicherweise gerade von einem anderen Programm verwendet. Versuchen Sie einen anderen Port in den Websocket-Servereinstellungen zu setzen oder alle Programme zu beenden, die den Port möglicherweise verwenden.\n - Fehler: %2"
OBSWebsocket.ProfileChanged.Started="WebSockets-Server in diesem Profil aktiviert. Server gestartet."
OBSWebsocket.ProfileChanged.Stopped="WebSockets-Server in diesem Profil deaktiviert. Server gestoppt."
OBSWebsocket.ProfileChanged.Restarted="WebSockets-Server in diesem Profil geändert. Server startet neu."

View File

@ -3,8 +3,10 @@ OBSWebsocket.Settings.ServerEnable="Enable WebSockets server"
OBSWebsocket.Settings.ServerPort="Server Port"
OBSWebsocket.Settings.AuthRequired="Enable authentication"
OBSWebsocket.Settings.Password="Password"
OBSWebsocket.Settings.LockToIPv4="Lock server to only using IPv4"
OBSWebsocket.Settings.DebugEnable="Enable debug logging"
OBSWebsocket.Settings.AlertsEnable="Enable System Tray Alerts"
OBSWebsocket.Settings.AuthDisabledWarning="Running obs-websocket with authentication disabled is not recommended, as it allows attackers to easily collect sensetive data. Are you sure you want to proceed?"
OBSWebsocket.NotifyConnect.Title="New WebSocket connection"
OBSWebsocket.NotifyConnect.Message="Client %1 connected"
OBSWebsocket.NotifyDisconnect.Title="WebSocket client disconnected"
@ -14,3 +16,6 @@ OBSWebsocket.Server.StartFailed.Message="The WebSockets server failed to start,
OBSWebsocket.ProfileChanged.Started="WebSockets server enabled in this profile. Server started."
OBSWebsocket.ProfileChanged.Stopped="WebSockets server disabled in this profile. Server stopped."
OBSWebsocket.ProfileChanged.Restarted="WebSockets server port changed in this profile. Server restarted."
OBSWebsocket.InitialPasswordSetup.Title="obs-websocket - Server Password Configuration"
OBSWebsocket.InitialPasswordSetup.Text="It looks like you are running obs-websocket for the first time. Do you want to configure a password now for the WebSockets server? Setting a password is highly recommended."
OBSWebsocket.InitialPasswordSetup.DismissedText="You can configure a server password later in the WebSockets Server Settings. (Under the Tools menu of OBS Studio)"

View File

@ -1,3 +1,4 @@
OBSWebsocket.Settings.DialogTitle="Configuración del servidor WebSocket"
OBSWebsocket.Settings.ServerEnable="Habilitar el servidor WebSockets"
OBSWebsocket.Settings.ServerPort="Puerto del Servidor"
OBSWebsocket.Settings.AuthRequired="Habilitar autenticación"
@ -9,4 +10,7 @@ OBSWebsocket.NotifyConnect.Message="Cliente %1 conectado"
OBSWebsocket.NotifyDisconnect.Title="Cliente WebSocket desconectado"
OBSWebsocket.NotifyDisconnect.Message="Cliente %1 desconectado"
OBSWebsocket.Server.StartFailed.Title="Falla en el servidor WebSockets"
OBSWebsocket.Server.StartFailed.Message="El servidor obs-websocket no se pudo iniciar, tal vez porque: \n - el puerto TCP %1 podría estar actualmente siendo usado este sistema, posiblemente por otra aplicación. Intente configurar un puerto TCP diferente en la configuración del servidor WebSocket, o detenga cualquier aplicación que pudiese estar utilizando este puerto \n - Un error de red desconocido ha afectado su sistema. Inténtelo de nuevo cambiando la configuración, reiniciando OBS o reiniciando su sistema."
OBSWebsocket.ProfileChanged.Started="El servidor WebSocket esta habilitado en este perfil. El servidor ha iniciado."
OBSWebsocket.ProfileChanged.Stopped="Servidor WebSockets deshabilitado en este perfil. Servidor detenido."
OBSWebsocket.ProfileChanged.Restarted="Puerto del servidor WebSockets cambiado en este perfil. Servidor reiniciado."

View File

@ -10,7 +10,7 @@ OBSWebsocket.NotifyConnect.Message="Le client %1 s'est connecté"
OBSWebsocket.NotifyDisconnect.Title="Déconnexion WebSocket"
OBSWebsocket.NotifyDisconnect.Message="Le client %1 s'est déconnecté"
OBSWebsocket.Server.StartFailed.Title="Impossible de démarrer le serveur WebSockets"
OBSWebsocket.Server.StartFailed.Message="Le serveur WebSockets n'a pas pu démarrer, peut-être parce que :\n - Le port TCP %1 est en cours d'utilisation sur ce système, certainement par un autre programme. Essayez un port différent dans les réglages du serveur WebSocket, ou arrêtez tout programme susceptible d'utiliser ce port.\n - Une erreur réseau inconnue est survenue. Essayez à nouveau en modifiant vos réglages, en redémarrant OBS ou en redémarrant votre ordinateur."
OBSWebsocket.ProfileChanged.Started="Serveur WebSockets actif dans ce profil."
OBSWebsocket.ProfileChanged.Stopped="Serveur WebSockets désactivé dans ce profil."
OBSWebsocket.ProfileChanged.Restarted="Le port actuel diffère du port configuré dans ce profil. Serveur WebSockets redémarré."

View File

@ -0,0 +1 @@

View File

@ -1,7 +1,9 @@
OBSWebsocket.Settings.DialogTitle="Impostazioni del server di WebSocket"
OBSWebsocket.Settings.ServerEnable="Abilitare il server WebSockets"
OBSWebsocket.Settings.ServerPort="Porta del server"
OBSWebsocket.Settings.AuthRequired="Abilitare l'autenticazione"
OBSWebsocket.Settings.Password="Password"
OBSWebsocket.Settings.LockToIPv4="Blocca il server per usare solo IPv4"
OBSWebsocket.Settings.DebugEnable="Attivare la registrazione di debug"
OBSWebsocket.Settings.AlertsEnable="Attivare gli avvisi di vassoio di sistema"
OBSWebsocket.NotifyConnect.Title="Nuova connessione WebSocket"
@ -9,4 +11,8 @@ OBSWebsocket.NotifyConnect.Message="%1 cliente collegato"
OBSWebsocket.NotifyDisconnect.Title="WebSocket cliente disconnesso"
OBSWebsocket.NotifyDisconnect.Message="%1 cliente disconnesso"
OBSWebsocket.Server.StartFailed.Title="Errore del WebSocket Server"
OBSWebsocket.Server.StartFailed.Message="Impossibile avviare, forse perché il server di WebSockets: \n - %1 porta TCP potrebbe essere attualmente in uso altrove su questo sistema, possibilmente da un'altra applicazione. Provare a impostare una porta TCP diversa nelle impostazioni del server di WebSockets, o arrestare tutte le applicazioni che potrebbero utilizzare questa porta. \n - è verificato un errore di rete sconosciuto sul sistema. Riprova modificando le impostazioni, riavviare OBS o riavvio del sistema."
OBSWebsocket.Server.StartFailed.Message="L'avvio del server WebSockets è fallito, forse perché:\n - La porta TCP %1 può attualmente essere in uso altrove su questo sistema, forse da un'altra applicazione. Provare a impostare una porta TCP diversa nelle impostazioni del server WebSocket o interrompere qualsiasi applicazione che potrebbe utilizzare questa porta.\n - Messaggio di errore: %2"
OBSWebsocket.ProfileChanged.Started="Server WebSockets abilitato in questo profilo. Il server è avviato."
OBSWebsocket.ProfileChanged.Stopped="Server WebSocket disabilitato in questo profilo. Server interrotto."
OBSWebsocket.ProfileChanged.Restarted="La porta del server WebSocket è stata modificata in questo profilo. Il server è stato riavviato."

View File

@ -1,3 +1,4 @@
OBSWebsocket.Settings.DialogTitle="Websocket サーバー設定"
OBSWebsocket.Settings.ServerEnable="WebSockets サーバーを有効にする"
OBSWebsocket.Settings.ServerPort="サーバーポート"
OBSWebsocket.Settings.AuthRequired="認証を有効にする"
@ -9,3 +10,7 @@ OBSWebsocket.NotifyConnect.Message="接続されているクライアント %1"
OBSWebsocket.NotifyDisconnect.Title="WebSocket クライアントが切断されました"
OBSWebsocket.NotifyDisconnect.Message="切断されたクライアント %1"
OBSWebsocket.Server.StartFailed.Title="WebSockets サーバー障害"
OBSWebsocket.ProfileChanged.Started="このプロファイルでWebSocketサーバが有効になりました。サーバを起動しました。"
OBSWebsocket.ProfileChanged.Stopped="このプロファイルでWebSocketサーバが無効になりました。サーバを停止しました。"
OBSWebsocket.ProfileChanged.Restarted="このプロファイルでWebSocketサーバの通信ポートが変更されました。サーバを再起動しました。"

View File

@ -0,0 +1,18 @@
OBSWebsocket.Settings.DialogTitle="웹소켓 서버 설정"
OBSWebsocket.Settings.ServerEnable="웹소켓 서버 활성화"
OBSWebsocket.Settings.ServerPort="서버 포트"
OBSWebsocket.Settings.AuthRequired="인증 활성화"
OBSWebsocket.Settings.Password="비밀번호"
OBSWebsocket.Settings.LockToIPv4="IPv4만 이용하여 서버를 연결"
OBSWebsocket.Settings.DebugEnable="디버그 로깅 활성화"
OBSWebsocket.Settings.AlertsEnable="시스템 트레이 알림 활성화"
OBSWebsocket.NotifyConnect.Title="새로운 웹소켓 연결"
OBSWebsocket.NotifyConnect.Message="클라이언트 %1 가 연결되었습니다"
OBSWebsocket.NotifyDisconnect.Title="웹소켓 클라이언트가 연결 해제되었습니다"
OBSWebsocket.NotifyDisconnect.Message="클라이언트 %1 가 연결이 해제되었습니다"
OBSWebsocket.Server.StartFailed.Title="웹소켓 서버 시작이 실패하였습니다"
OBSWebsocket.Server.StartFailed.Message="웹소켓 서버가 시작되지 못했습니다. \n 시스템 상의 다른 프로그램이 TCP 포트 %1을 사용 중인 것으로 보입니다. 다른 TCP 포트를 사용하거나, 해당 포트를 사용 중인 프로그램을 종료하고 다시 시도해주세요. \n 에러 메시지: %2"
OBSWebsocket.ProfileChanged.Started="프로파일 설정에 따라 웹소켓 서버가 활성화되었습니다. 서버가 시작되었습니다."
OBSWebsocket.ProfileChanged.Stopped="프로파일 설정에 따라 웹소켓 서버가 비활성화되었습니다. 서버가 중지되었습니다."
OBSWebsocket.ProfileChanged.Restarted="프로파일 설정에 따라 웹소켓 포트가 변경되었습니다. 서버가 재시작되었습니다."

View File

@ -1,4 +1,9 @@
OBSWebsocket.Settings.DialogTitle="WebSockets Server Instellingen"
OBSWebsocket.Settings.ServerEnable="WebSockets server inschakelen"
OBSWebsocket.Settings.ServerPort="Serverpoort"
OBSWebsocket.Settings.AuthRequired="Verificatie inschakelen"
OBSWebsocket.Settings.Password="Wachtwoord"
OBSWebsocket.Settings.LockToIPv4="Server vergrendelen om alleen IPv4 te gebruiken"
OBSWebsocket.Settings.DebugEnable="Activeer debug logs"
OBSWebsocket.Settings.AlertsEnable="Systemvak waarschuwingen inschakelen"
OBSWebsocket.NotifyConnect.Title="Nieuwe WebSocket verbinding"
@ -7,3 +12,7 @@ OBSWebsocket.NotifyDisconnect.Title="WebSocket client connectie verbroken"
OBSWebsocket.NotifyDisconnect.Message="Client %1 losgekoppeld"
OBSWebsocket.Server.StartFailed.Title="Fout in WebSocket server"
OBSWebsocket.Server.StartFailed.Message="De obs-websocket server kan niet worden gestart, misschien omdat: \n - TCP-poort %1 momenteel elders wordt gebruikt op dit systeem, eventueel door een andere toepassing. Probeer een andere TCP-poort in te stellen in de WebSocket Server-instellingen of stop elke toepassing die deze poort zou kunnen gebruiken.\n Een onbekende Netwerkfout op uw systeem. Probeer het opnieuw door de instellingen te wijzigen, OBS te herstarten of uw systeem te herstarten."
OBSWebsocket.ProfileChanged.Started="WebSockets server ingeschakeld in dit profiel. Server gestart."
OBSWebsocket.ProfileChanged.Stopped="WebSockets server uitgeschakeld in dit profiel. Server is gestopt."
OBSWebsocket.ProfileChanged.Restarted="WebSockets server poort is veranderd in dit profiel. Server is herstart."

View File

@ -1,5 +1,9 @@
OBSWebsocket.Settings.DialogTitle="Ustawienia serwera WebSockets"
OBSWebsocket.Settings.ServerEnable="Włącz serwer WebSockets"
OBSWebsocket.Settings.ServerPort="Port serwera"
OBSWebsocket.Settings.AuthRequired="Wymagaj uwierzytelniania"
OBSWebsocket.Settings.Password="Hasło"
OBSWebsocket.Settings.LockToIPv4="Zablokuj serwer tylko za pomocą IPv4"
OBSWebsocket.Settings.DebugEnable="Włącz rejestrowanie debugowania"
OBSWebsocket.Settings.AlertsEnable="Włącz powiadomienia w zasobniku systemowym"
OBSWebsocket.NotifyConnect.Title="Nowe połączenie WebSocket"
@ -7,4 +11,5 @@ OBSWebsocket.NotifyConnect.Message="Klient %1 połączony"
OBSWebsocket.NotifyDisconnect.Title="Klient WebSocket odłączony"
OBSWebsocket.NotifyDisconnect.Message="Klient %1 rozłączony"
OBSWebsocket.Server.StartFailed.Title="Awaria serwera WebSockets"
OBSWebsocket.Server.StartFailed.Message="Nie udało się uruchomić serwera WebSockets, może a powodu: \n - TCP port %1 może być obecnie używany gdzie indziej w tym systemie, możliwie przez inną aplikację. Spróbuj ustawić inny port TCP w ustawieniach serwera WebSockets, lub zatrzymać dowolną aplikację, która może używać tego portu \n -nieznany błąd sieci wydarzył się w systemie. Spróbuj ponownie, zmieniając ustawienia, ponownie uruchamiając OBS lub ponownie uruchamiając system."
OBSWebsocket.ProfileChanged.Started="Serwer WebSockets włączony w tym profilu. Serwer uruchomiony."

View File

@ -1,7 +1,9 @@
OBSWebsocket.Settings.DialogTitle="Configurações do servidor de WebSockets"
OBSWebsocket.Settings.ServerEnable="Habilitar servidor de WebSockets"
OBSWebsocket.Settings.ServerPort="Porta do Servidor"
OBSWebsocket.Settings.AuthRequired="Activar autenticação"
OBSWebsocket.Settings.Password="Palavra passe"
OBSWebsocket.Settings.LockToIPv4="Forçar apenas o uso de IPv4 (desabilitar IPv6)"
OBSWebsocket.Settings.DebugEnable="Habilitar registro de debug"
OBSWebsocket.Settings.AlertsEnable="Ativar Alertas da bandeja do sistema"
OBSWebsocket.NotifyConnect.Title="Nova conexão WebSocket"
@ -9,4 +11,8 @@ OBSWebsocket.NotifyConnect.Message="Cliente %1 conectado"
OBSWebsocket.NotifyDisconnect.Title="WebSocket cliente desconectado"
OBSWebsocket.NotifyDisconnect.Message="Cliente %1 desconectado"
OBSWebsocket.Server.StartFailed.Title="Falha do servidor de WebSocket"
OBSWebsocket.Server.StartFailed.Message="O servidor de WebSockets falhou ao iniciar, talvez porque: \n - TCP port %1 pode estar atualmente em uso em outro lugar sobre este sistema, possivelmente por outro aplicativo. Tente definir uma porta TCP diferente nas configurações do servidor de WebSockets, ou parar qualquer aplicativo que poderia estar usando este porto \n - um erro de rede desconhecido aconteceu no seu sistema. Tente novamente alterar configurações, reiniciando OBS ou reiniciando o sistema."
OBSWebsocket.Server.StartFailed.Message="O servidor de WebSockets falhou ao iniciar, possivelmente porque:\n - A porta TCP %1 pode estar em uso em algum outro lugar neste sistema, possivelmente por outro aplicativo. Tente definir uma porta TCP diferente nas configurações do servidor de WebSockets, ou pare qualquer aplicativo que possa estar usando essa porta.\n - Mensagem de erro: %2"
OBSWebsocket.ProfileChanged.Started="Servidor de WebSockets habilitado nesse perfil. Servidor iniciado."
OBSWebsocket.ProfileChanged.Stopped="Servidor de WebSockets desabilitado nesse perfil. Servidor parado."
OBSWebsocket.ProfileChanged.Restarted="Porta do servidor de WebSockets foi alterada neste perfil. Servidor reiniciado."

View File

@ -3,6 +3,7 @@ OBSWebsocket.Settings.ServerEnable="Включить сервер WebSockets"
OBSWebsocket.Settings.ServerPort="Порт сервера"
OBSWebsocket.Settings.AuthRequired="Включить авторизацию"
OBSWebsocket.Settings.Password="Пароль"
OBSWebsocket.Settings.LockToIPv4="Блокировка сервера только с использованием IPv4"
OBSWebsocket.Settings.DebugEnable="Включить ведение журнала отладки"
OBSWebsocket.Settings.AlertsEnable="Включить оповещения в системном трее"
OBSWebsocket.NotifyConnect.Title="Новое соединение WebSocket"
@ -10,4 +11,8 @@ OBSWebsocket.NotifyConnect.Message="Клиент %1 подключен"
OBSWebsocket.NotifyDisconnect.Title="Клиент WebSocket отключён"
OBSWebsocket.NotifyDisconnect.Message="Клиент %1 отключен"
OBSWebsocket.Server.StartFailed.Title="Сбой сервера WebSockets"
OBSWebsocket.Server.StartFailed.Message="Ошибка запуска сервера WebSockets. Вероятные причины:\n - Возможно, TCP-порт %1 занят другим приложением в системе. Попробуйте задать другой TCP-порт в настройках сервера WebSockets или закройте приложение, которое может использовать данный порт.\n - Произошла неизвестная сетевая ошибка в системе. Попробуйте снова после изменения настроек, перезапуска OBS или системы."
OBSWebsocket.Server.StartFailed.Message="Сервер WebSockets не запустился, возможно, потому, что:\n-TCP-порт %1 в настоящее время может использоваться в другом месте этой системы, возможно, другим приложением. Попробуйте установить другой TCP-порт в настройках сервера WebSocket или остановить любое приложение, которое может использовать этот порт.\n-сообщение об ошибке: %2"
OBSWebsocket.ProfileChanged.Started="Сервер WebSockets включен в этом профиле. Сервер запущен."
OBSWebsocket.ProfileChanged.Stopped="Сервер WebSockets отключен в этом профиле. Сервер остановлен."
OBSWebsocket.ProfileChanged.Restarted="Порт сервера WebSockets изменен в этом профиле. Сервер перезапущен."

View File

@ -1,3 +1,4 @@
OBSWebsocket.Settings.DialogTitle="WebSockets 服务器设置"
OBSWebsocket.Settings.ServerEnable="启用 WebSockets 服务器"
OBSWebsocket.Settings.ServerPort="服务器端口"
OBSWebsocket.Settings.AuthRequired="启用身份验证"
@ -9,4 +10,7 @@ OBSWebsocket.NotifyConnect.Message="客户端 %1 已连接"
OBSWebsocket.NotifyDisconnect.Title="WebSocket 客户端已断开"
OBSWebsocket.NotifyDisconnect.Message="客户端 %1 已断开连接"
OBSWebsocket.Server.StartFailed.Title="WebSockets 服务器错误"
OBSWebsocket.Server.StartFailed.Message="WebSockets 服务器启动失败,可能是因为:\n - TCP 端口 %1 可能被本机的另一个应用程序占用。尝试在 WebSockets 服务器设置中设置不同的 TCP 端口,或关闭任何可能使用此端口的应用程序。\n - 在您的系统上发生了未知的网络错误。请尝试更改设置、重新启动 OBS 或重新启动系统。"
OBSWebsocket.ProfileChanged.Started="此配置文件中启用了 WebSockets 服务器。服务器已启动。"
OBSWebsocket.ProfileChanged.Stopped="此配置文件中禁用了 WebSockets 服务器。服务器已停止。"
OBSWebsocket.ProfileChanged.Restarted="此配置文件中的 WebSockets 服务器端口已更改。服务器已重新启动。"

View File

@ -6,4 +6,4 @@ OBSWebsocket.NotifyConnect.Message="客戶端 %1 已連線"
OBSWebsocket.NotifyDisconnect.Title="WebSocket 客戶端已離線"
OBSWebsocket.NotifyDisconnect.Message="客戶端 %1 已離線"
OBSWebsocket.Server.StartFailed.Title="WebSocket 伺服器錯誤"
OBSWebsocket.Server.StartFailed.Message="WebSockets 伺服器啟動失敗,可能的原因有:\n - TCP 連接埠 %1 被系統的其他程式所使用,試著為 WebSockets 伺服器指定不同的 TCP 連接埠,或是關閉任何可能使用此連接埠的程式。\n - 發生的未知的網路錯誤,試著更改設定、重新啟動 OBS 或是重新啟動您的系統。"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,12 @@
# obs-websocket 4.8.0 protocol reference
# obs-websocket 4.9.0 protocol reference
# General Introduction
Messages are exchanged between the client and the server as JSON objects.
This protocol is based on the original OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio.
This protocol is based on the original OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio. As of v5.0.0, backwards compatability with the protocol will not be kept.
# Authentication
**Starting with obs-websocket 4.9, authentication is enabled by default and users are encouraged to configure a password on first run.**
`obs-websocket` uses SHA256 to transmit credentials.
A request for [`GetAuthRequired`](#getauthrequired) returns two elements:
@ -32,3 +34,5 @@ auth_response_string = secret + challenge
auth_response_hash = binary_sha256(auth_response_string)
auth_response = base64_encode(auth_response_hash)
```
You can also refer to any of the client libraries listed on the [README](README.md) for examples of how to authenticate.

View File

@ -6,6 +6,6 @@ Requests are sent by the client and require at least the following two fields:
Once a request is sent, the server will return a JSON response with at least the following fields:
- `message-id` _String_: The client defined identifier specified in the request.
- `status` _String_: Response status, will be one of the following: `ok`, `error`
- `error` _String_: An error message accompanying an `error` status.
- `error` _String (Optional)_: An error message accompanying an `error` status.
Additional information may be required/returned depending on the request type. See below for more information.

View File

@ -2,7 +2,7 @@
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "obs-websocket"
#define MyAppVersion "4.8.0"
#define MyAppVersion "4.9.0"
#define MyAppPublisher "Stephane Lepin"
#define MyAppURL "http://github.com/Palakis/obs-websocket"
@ -23,6 +23,7 @@ DefaultGroupName={#MyAppName}
OutputBaseFilename=obs-websocket-Windows-Installer
Compression=lzma
SolidCompression=yes
DirExistsWarning=no
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

View File

@ -18,19 +18,26 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <obs-frontend-api.h>
#include <QtCore/QObject>
#include <QtCore/QCryptographicHash>
#include <QtCore/QTime>
#include <QtWidgets/QSystemTrayIcon>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
#define SECTION_NAME "WebsocketAPI"
#define PARAM_ENABLE "ServerEnabled"
#define PARAM_PORT "ServerPort"
#define PARAM_LOCKTOIPV4 "LockToIPv4"
#define PARAM_DEBUG "DebugEnabled"
#define PARAM_ALERT "AlertsEnabled"
#define PARAM_AUTHREQUIRED "AuthRequired"
#define PARAM_SECRET "AuthSecret"
#define PARAM_SALT "AuthSalt"
#define GLOBAL_AUTH_SETUP_PROMPTED "AuthSetupPrompted"
#include "Utils.h"
#include "WSServer.h"
@ -41,9 +48,10 @@ with this program. If not, see <https://www.gnu.org/licenses/>
Config::Config() :
ServerEnabled(true),
ServerPort(4444),
LockToIPv4(false),
DebugEnabled(false),
AlertsEnabled(true),
AuthRequired(false),
AuthRequired(true),
Secret(""),
Salt(""),
SettingsLoaded(false)
@ -67,6 +75,7 @@ void Config::Load()
ServerEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ENABLE);
ServerPort = config_get_uint(obsConfig, SECTION_NAME, PARAM_PORT);
LockToIPv4 = config_get_bool(obsConfig, SECTION_NAME, PARAM_LOCKTOIPV4);
DebugEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_DEBUG);
AlertsEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ALERT);
@ -82,6 +91,7 @@ void Config::Save()
config_set_bool(obsConfig, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
config_set_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort);
config_set_bool(obsConfig, SECTION_NAME, PARAM_LOCKTOIPV4, LockToIPv4);
config_set_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
config_set_bool(obsConfig, SECTION_NAME, PARAM_ALERT, AlertsEnabled);
@ -104,6 +114,8 @@ void Config::SetDefaults()
SECTION_NAME, PARAM_ENABLE, ServerEnabled);
config_set_default_uint(obsConfig,
SECTION_NAME, PARAM_PORT, ServerPort);
config_set_default_bool(obsConfig,
SECTION_NAME, PARAM_LOCKTOIPV4, LockToIPv4);
config_set_default_bool(obsConfig,
SECTION_NAME, PARAM_DEBUG, DebugEnabled);
@ -124,6 +136,70 @@ config_t* Config::GetConfigStore()
return obs_frontend_get_profile_config();
}
void Config::MigrateFromGlobalSettings()
{
config_t* source = obs_frontend_get_global_config();
config_t* destination = obs_frontend_get_profile_config();
if(config_has_user_value(source, SECTION_NAME, PARAM_ENABLE)) {
bool value = config_get_bool(source, SECTION_NAME, PARAM_ENABLE);
config_set_bool(destination, SECTION_NAME, PARAM_ENABLE, value);
config_remove_value(source, SECTION_NAME, PARAM_ENABLE);
}
if(config_has_user_value(source, SECTION_NAME, PARAM_PORT)) {
uint64_t value = config_get_uint(source, SECTION_NAME, PARAM_PORT);
config_set_uint(destination, SECTION_NAME, PARAM_PORT, value);
config_remove_value(source, SECTION_NAME, PARAM_PORT);
}
if(config_has_user_value(source, SECTION_NAME, PARAM_LOCKTOIPV4)) {
bool value = config_get_bool(source, SECTION_NAME, PARAM_LOCKTOIPV4);
config_set_bool(destination, SECTION_NAME, PARAM_LOCKTOIPV4, value);
config_remove_value(source, SECTION_NAME, PARAM_LOCKTOIPV4);
}
if(config_has_user_value(source, SECTION_NAME, PARAM_DEBUG)) {
bool value = config_get_bool(source, SECTION_NAME, PARAM_DEBUG);
config_set_bool(destination, SECTION_NAME, PARAM_DEBUG, value);
config_remove_value(source, SECTION_NAME, PARAM_DEBUG);
}
if(config_has_user_value(source, SECTION_NAME, PARAM_ALERT)) {
bool value = config_get_bool(source, SECTION_NAME, PARAM_ALERT);
config_set_bool(destination, SECTION_NAME, PARAM_ALERT, value);
config_remove_value(source, SECTION_NAME, PARAM_ALERT);
}
if(config_has_user_value(source, SECTION_NAME, PARAM_AUTHREQUIRED)) {
bool value = config_get_bool(source, SECTION_NAME, PARAM_AUTHREQUIRED);
config_set_bool(destination, SECTION_NAME, PARAM_AUTHREQUIRED, value);
config_remove_value(source, SECTION_NAME, PARAM_AUTHREQUIRED);
}
if(config_has_user_value(source, SECTION_NAME, PARAM_SECRET)) {
const char* value = config_get_string(source, SECTION_NAME, PARAM_SECRET);
config_set_string(destination, SECTION_NAME, PARAM_SECRET, value);
config_remove_value(source, SECTION_NAME, PARAM_SECRET);
}
if(config_has_user_value(source, SECTION_NAME, PARAM_SALT)) {
const char* value = config_get_string(source, SECTION_NAME, PARAM_SALT);
config_set_string(destination, SECTION_NAME, PARAM_SALT, value);
config_remove_value(source, SECTION_NAME, PARAM_SALT);
}
config_save(destination);
}
QString Config::GenerateSalt()
{
// Generate 32 random chars
@ -205,16 +281,17 @@ void Config::OnFrontendEvent(enum obs_frontend_event event, void* param)
bool previousEnabled = config->ServerEnabled;
uint64_t previousPort = config->ServerPort;
bool previousLock = config->LockToIPv4;
config->SetDefaults();
config->Load();
if (config->ServerEnabled != previousEnabled || config->ServerPort != previousPort) {
if (config->ServerEnabled != previousEnabled || config->ServerPort != previousPort || config->LockToIPv4 != previousLock) {
auto server = GetServer();
server->stop();
if (config->ServerEnabled) {
server->start(config->ServerPort);
server->start(config->ServerPort, config->LockToIPv4);
if (previousEnabled != config->ServerEnabled) {
Utils::SysTrayNotify(startMessage, QSystemTrayIcon::MessageIcon::Information);
@ -226,61 +303,46 @@ void Config::OnFrontendEvent(enum obs_frontend_event event, void* param)
}
}
}
else if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
FirstRunPasswordSetup();
}
}
void Config::MigrateFromGlobalSettings()
void Config::FirstRunPasswordSetup()
{
config_t* source = obs_frontend_get_global_config();
config_t* destination = obs_frontend_get_profile_config();
if(config_has_user_value(source, SECTION_NAME, PARAM_ENABLE)) {
bool value = config_get_bool(source, SECTION_NAME, PARAM_ENABLE);
config_set_bool(destination, SECTION_NAME, PARAM_ENABLE, value);
config_remove_value(source, SECTION_NAME, PARAM_ENABLE);
// check if we already showed the auth setup prompt to the user, independently of the current settings (tied to the current profile)
config_t* globalConfig = obs_frontend_get_global_config();
bool alreadyPrompted = config_get_bool(globalConfig, SECTION_NAME, GLOBAL_AUTH_SETUP_PROMPTED);
if (alreadyPrompted) {
return;
}
if(config_has_user_value(source, SECTION_NAME, PARAM_PORT)) {
uint64_t value = config_get_uint(source, SECTION_NAME, PARAM_PORT);
config_set_uint(destination, SECTION_NAME, PARAM_PORT, value);
// lift the flag up and save it
config_set_bool(globalConfig, SECTION_NAME, GLOBAL_AUTH_SETUP_PROMPTED, true);
config_save(globalConfig);
config_remove_value(source, SECTION_NAME, PARAM_PORT);
// check if the password is already set
auto config = GetConfig();
if (!(config->Secret.isEmpty()) && !(config->Salt.isEmpty())) {
return;
}
if(config_has_user_value(source, SECTION_NAME, PARAM_DEBUG)) {
bool value = config_get_bool(source, SECTION_NAME, PARAM_DEBUG);
config_set_bool(destination, SECTION_NAME, PARAM_DEBUG, value);
obs_frontend_push_ui_translation(obs_module_get_string);
QString dialogTitle = QObject::tr("OBSWebsocket.InitialPasswordSetup.Title");
QString dialogText = QObject::tr("OBSWebsocket.InitialPasswordSetup.Text");
QString dismissedText = QObject::tr("OBSWebsocket.InitialPasswordSetup.DismissedText");
obs_frontend_pop_ui_translation();
config_remove_value(source, SECTION_NAME, PARAM_DEBUG);
auto mainWindow = reinterpret_cast<QMainWindow*>(
obs_frontend_get_main_window()
);
QMessageBox::StandardButton response = QMessageBox::question(mainWindow, dialogTitle, dialogText);
if (response == QMessageBox::Yes) {
ShowPasswordSetting();
}
if(config_has_user_value(source, SECTION_NAME, PARAM_ALERT)) {
bool value = config_get_bool(source, SECTION_NAME, PARAM_ALERT);
config_set_bool(destination, SECTION_NAME, PARAM_ALERT, value);
config_remove_value(source, SECTION_NAME, PARAM_ALERT);
else {
// tell the user they still can set the password later in our settings dialog
QMessageBox::information(mainWindow, dialogTitle, dismissedText);
}
if(config_has_user_value(source, SECTION_NAME, PARAM_AUTHREQUIRED)) {
bool value = config_get_bool(source, SECTION_NAME, PARAM_AUTHREQUIRED);
config_set_bool(destination, SECTION_NAME, PARAM_AUTHREQUIRED, value);
config_remove_value(source, SECTION_NAME, PARAM_AUTHREQUIRED);
}
if(config_has_user_value(source, SECTION_NAME, PARAM_SECRET)) {
const char* value = config_get_string(source, SECTION_NAME, PARAM_SECRET);
config_set_string(destination, SECTION_NAME, PARAM_SECRET, value);
config_remove_value(source, SECTION_NAME, PARAM_SECRET);
}
if(config_has_user_value(source, SECTION_NAME, PARAM_SALT)) {
const char* value = config_get_string(source, SECTION_NAME, PARAM_SALT);
config_set_string(destination, SECTION_NAME, PARAM_SALT, value);
config_remove_value(source, SECTION_NAME, PARAM_SALT);
}
config_save(destination);
}

View File

@ -42,6 +42,7 @@ class Config {
bool ServerEnabled;
uint64_t ServerPort;
bool LockToIPv4;
bool DebugEnabled;
bool AlertsEnabled;
@ -54,4 +55,5 @@ class Config {
private:
static void OnFrontendEvent(enum obs_frontend_event event, void* param);
static void FirstRunPasswordSetup();
};

View File

@ -24,6 +24,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <obs-frontend-api.h>
#include <obs.hpp>
#include <util/platform.h>
#include <obs-data.h>
#include "obs-websocket.h"
@ -51,6 +52,25 @@ obs_bounds_type getBoundsTypeFromName(QString name) {
return boundTypeNames.key(name);
}
bool Utils::StringInStringList(char** strings, const char* string) {
if (!strings) {
return false;
}
size_t index = 0;
while (strings[index] != NULL) {
char* value = strings[index];
if (strcmp(value, string) == 0) {
return true;
}
index++;
}
return false;
}
obs_data_array_t* Utils::StringListToArray(char** strings, const char* key) {
obs_data_array_t* list = obs_data_array_create();
@ -485,11 +505,8 @@ QString Utils::OBSVersionString() {
}
QSystemTrayIcon* Utils::GetTrayIcon() {
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
if (!main) return nullptr;
QList<QSystemTrayIcon*> trays = main->findChildren<QSystemTrayIcon*>();
return trays.isEmpty() ? nullptr : trays.first();
void* systemTray = obs_frontend_get_system_tray();
return reinterpret_cast<QSystemTrayIcon*>(systemTray);
}
void Utils::SysTrayNotify(QString text,
@ -680,12 +697,35 @@ bool Utils::SetFilenameFormatting(const char* filenameFormatting) {
return true;
}
const char* Utils::GetCurrentRecordingFilename()
{
OBSOutputAutoRelease recordingOutput = obs_frontend_get_recording_output();
if (!recordingOutput) {
return nullptr;
}
OBSDataAutoRelease settings = obs_output_get_settings(recordingOutput);
// mimicks the behavior of BasicOutputHandler::GetRecordingFilename :
// try to fetch the path from the "url" property, then try "path" if the first one
// didn't yield any result
OBSDataItemAutoRelease item = obs_data_item_byname(settings, "url");
if (!item) {
item = obs_data_item_byname(settings, "path");
if (!item) {
return nullptr;
}
}
return obs_data_item_get_string(item);
}
// Transform properties copy-pasted from WSRequestHandler_SceneItems.cpp because typedefs can't be extended yet
/**
* @typedef {Object} `SceneItemTransform`
* @property {int} `position.x` The x position of the scene item from the left.
* @property {int} `position.y` The y position of the scene item from the top.
* @property {double} `position.x` The x position of the scene item from the left.
* @property {double} `position.y` The y position of the scene item from the top.
* @property {int} `position.alignment` The point on the scene item that the item is manipulated from.
* @property {double} `rotation` The clockwise rotation of the scene item in degrees around the point of alignment.
* @property {double} `scale.x` The x-scale factor of the scene item.
@ -859,3 +899,45 @@ QString Utils::nsToTimestamp(uint64_t ns)
return QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
}
void Utils::AddSourceHelper(void *_data, obs_scene_t *scene)
{
auto *data = reinterpret_cast<AddSourceData*>(_data);
data->sceneItem = obs_scene_add(scene, data->source);
obs_sceneitem_set_visible(data->sceneItem, data->setVisible);
}
obs_data_t *Utils::OBSDataGetDefaults(obs_data_t *data)
{
obs_data_t *returnData = obs_data_create();
obs_data_item_t *item = NULL;
for (item = obs_data_first(data); item; obs_data_item_next(&item)) {
enum obs_data_type type = obs_data_item_gettype(item);
const char *name = obs_data_item_get_name(item);
if (type == OBS_DATA_STRING) {
const char *val = obs_data_item_get_string(item);
obs_data_set_string(returnData, name, val);
} else if (type == OBS_DATA_NUMBER) {
enum obs_data_number_type type = obs_data_item_numtype(item);
if (type == OBS_DATA_NUM_INT) {
long long val = obs_data_item_get_int(item);
obs_data_set_int(returnData, name, val);
} else {
double val = obs_data_item_get_double(item);
obs_data_set_double(returnData, name, val);
}
} else if (type == OBS_DATA_BOOLEAN) {
bool val = obs_data_item_get_bool(item);
obs_data_set_bool(returnData, name, val);
} else if (type == OBS_DATA_OBJECT) {
OBSDataAutoRelease obj = obs_data_item_get_obj(item);
obs_data_set_obj(returnData, name, obj);
} else if (type == OBS_DATA_ARRAY) {
OBSDataArrayAutoRelease array = obs_data_item_get_array(item);
obs_data_set_array(returnData, name, array);
}
}
return returnData;
}

View File

@ -35,6 +35,7 @@ typedef void(*PauseRecordingFunction)(bool);
typedef bool(*RecordingPausedFunction)();
namespace Utils {
bool StringInStringList(char** strings, const char* string);
obs_data_array_t* StringListToArray(char** strings, const char* key);
obs_data_array_t* GetSceneItems(obs_source_t* source);
obs_data_t* GetSceneItemData(obs_sceneitem_t* item);
@ -84,5 +85,15 @@ namespace Utils {
const char* GetFilenameFormatting();
bool SetFilenameFormatting(const char* filenameFormatting);
const char* GetCurrentRecordingFilename();
QString nsToTimestamp(uint64_t ns);
struct AddSourceData {
obs_source_t *source;
obs_sceneitem_t *sceneItem;
bool setVisible;
};
void AddSourceHelper(void *_data, obs_scene_t *scene);
obs_data_t *OBSDataGetDefaults(obs_data_t *data);
};

View File

@ -233,6 +233,7 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private
case OBS_FRONTEND_EVENT_EXIT:
owner->unhookTransitionPlaybackEvents();
owner->OnExit();
owner->_srv->stop();
break;
}
}
@ -271,11 +272,22 @@ void WSEvents::connectSourceSignals(obs_source_t* source) {
signal_handler_connect(sh, "volume", OnSourceVolumeChange, this);
signal_handler_connect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this);
signal_handler_connect(sh, "audio_mixers", OnSourceAudioMixersChanged, this);
signal_handler_connect(sh, "audio_activate", OnSourceAudioActivated, this);
signal_handler_connect(sh, "audio_deactivate", OnSourceAudioDeactivated, this);
signal_handler_connect(sh, "filter_add", OnSourceFilterAdded, this);
signal_handler_connect(sh, "filter_remove", OnSourceFilterRemoved, this);
signal_handler_connect(sh, "reorder_filters", OnSourceFilterOrderChanged, this);
signal_handler_connect(sh, "media_play", OnMediaPlaying, this);
signal_handler_connect(sh, "media_pause", OnMediaPaused, this);
signal_handler_connect(sh, "media_restart", OnMediaRestarted, this);
signal_handler_connect(sh, "media_stopped", OnMediaStopped, this);
signal_handler_connect(sh, "media_next", OnMediaNext, this);
signal_handler_connect(sh, "media_previous", OnMediaPrevious, this);
signal_handler_connect(sh, "media_started", OnMediaStarted, this);
signal_handler_connect(sh, "media_ended", OnMediaEnded, this);
if (sourceType == OBS_SOURCE_TYPE_SCENE) {
signal_handler_connect(sh, "reorder", OnSceneReordered, this);
signal_handler_connect(sh, "item_add", OnSceneItemAdd, this);
@ -303,6 +315,8 @@ void WSEvents::disconnectSourceSignals(obs_source_t* source) {
signal_handler_disconnect(sh, "volume", OnSourceVolumeChange, this);
signal_handler_disconnect(sh, "audio_sync", OnSourceAudioSyncOffsetChanged, this);
signal_handler_disconnect(sh, "audio_mixers", OnSourceAudioMixersChanged, this);
signal_handler_disconnect(sh, "audio_activate", OnSourceAudioActivated, this);
signal_handler_disconnect(sh, "audio_deactivate", OnSourceAudioDeactivated, this);
signal_handler_disconnect(sh, "filter_add", OnSourceFilterAdded, this);
signal_handler_disconnect(sh, "filter_remove", OnSourceFilterRemoved, this);
@ -322,6 +336,15 @@ void WSEvents::disconnectSourceSignals(obs_source_t* source) {
signal_handler_disconnect(sh, "transition_start", OnTransitionBegin, this);
signal_handler_disconnect(sh, "transition_stop", OnTransitionEnd, this);
signal_handler_disconnect(sh, "transition_video_stop", OnTransitionVideoEnd, this);
signal_handler_disconnect(sh, "media_play", OnMediaPlaying, this);
signal_handler_disconnect(sh, "media_pause", OnMediaPaused, this);
signal_handler_disconnect(sh, "media_restart", OnMediaRestarted, this);
signal_handler_disconnect(sh, "media_stopped", OnMediaStopped, this);
signal_handler_disconnect(sh, "media_next", OnMediaNext, this);
signal_handler_disconnect(sh, "media_previous", OnMediaPrevious, this);
signal_handler_disconnect(sh, "media_started", OnMediaStarted, this);
signal_handler_disconnect(sh, "media_ended", OnMediaEnded, this);
}
void WSEvents::connectFilterSignals(obs_source_t* filter) {
@ -407,6 +430,16 @@ QString WSEvents::getRecordingTimecode() {
return Utils::nsToTimestamp(getRecordingTime());
}
OBSDataAutoRelease getMediaSourceData(calldata_t* data) {
OBSDataAutoRelease fields = obs_data_create();
OBSSource source = calldata_get_pointer<obs_source_t>(data, "source");
obs_data_set_string(fields, "sourceName", obs_source_get_name(source));
obs_data_set_string(fields, "sourceKind", obs_source_get_id(source));
return fields;
}
/**
* Indicates a scene change.
*
@ -433,25 +466,37 @@ void WSEvents::OnSceneChange() {
* The scene list has been modified.
* Scenes have been added, removed, or renamed.
*
* Note: This event is not fired when the scenes are reordered.
*
* @return {Array<Scene>} `scenes` Scenes list.
*
* @api events
* @name ScenesChanged
* @category scenes
* @since 0.3
*/
void WSEvents::OnSceneListChange() {
broadcastUpdate("ScenesChanged");
OBSDataArrayAutoRelease scenes = Utils::GetScenes();
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_array(fields, "scenes", scenes);
broadcastUpdate("ScenesChanged", fields);
}
/**
* Triggered when switching to another scene collection or when renaming the current scene collection.
*
* @return {String} `sceneCollection` Name of the new current scene collection.
*
* @api events
* @name SceneCollectionChanged
* @category scenes
* @since 4.0.0
*/
void WSEvents::OnSceneCollectionChange() {
broadcastUpdate("SceneCollectionChanged");
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_string(fields, "sceneCollection", obs_frontend_get_current_scene_collection());
broadcastUpdate("SceneCollectionChanged", fields);
OnTransitionListChange();
OnTransitionChange();
@ -463,13 +508,23 @@ void WSEvents::OnSceneCollectionChange() {
/**
* Triggered when a scene collection is created, added, renamed, or removed.
*
* @return {Array<Object>} `sceneCollections` Scene collections list.
* @return {String} `sceneCollections.*.name` Scene collection name.
*
* @api events
* @name SceneCollectionListChanged
* @category scenes
* @since 4.0.0
*/
void WSEvents::OnSceneCollectionListChange() {
broadcastUpdate("SceneCollectionListChanged");
char** sceneCollections = obs_frontend_get_scene_collections();
OBSDataArrayAutoRelease sceneCollectionsList =
Utils::StringListToArray(sceneCollections, "name");
bfree(sceneCollections);
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_array(fields, "sceneCollections", sceneCollectionsList);
broadcastUpdate("SceneCollectionListChanged", fields);
}
/**
@ -496,37 +551,68 @@ void WSEvents::OnTransitionChange() {
* The list of available transitions has been modified.
* Transitions have been added, removed, or renamed.
*
* @return {Array<Object>} `transitions` Transitions list.
* @return {String} `transitions.*.name` Transition name.
*
* @api events
* @name TransitionListChanged
* @category transitions
* @since 4.0.0
*/
void WSEvents::OnTransitionListChange() {
broadcastUpdate("TransitionListChanged");
obs_frontend_source_list transitionList = {};
obs_frontend_get_transitions(&transitionList);
OBSDataArrayAutoRelease transitions = obs_data_array_create();
for (size_t i = 0; i < transitionList.sources.num; i++) {
OBSSource transition = transitionList.sources.array[i];
OBSDataAutoRelease obj = obs_data_create();
obs_data_set_string(obj, "name", obs_source_get_name(transition));
obs_data_array_push_back(transitions, obj);
}
obs_frontend_source_list_free(&transitionList);
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_array(fields, "transitions", transitions);
broadcastUpdate("TransitionListChanged", fields);
}
/**
* Triggered when switching to another profile or when renaming the current profile.
*
* @return {String} `profile` Name of the new current profile.
*
* @api events
* @name ProfileChanged
* @category profiles
* @since 4.0.0
*/
void WSEvents::OnProfileChange() {
broadcastUpdate("ProfileChanged");
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_string(fields, "profile", obs_frontend_get_current_profile());
broadcastUpdate("ProfileChanged", fields);
}
/**
* Triggered when a profile is created, added, renamed, or removed.
*
* @return {Array<Object>} `profiles` Profiles list.
* @return {String} `profiles.*.name` Profile name.
*
* @api events
* @name ProfileListChanged
* @category profiles
* @since 4.0.0
*/
void WSEvents::OnProfileListChange() {
broadcastUpdate("ProfileListChanged");
char** profiles = obs_frontend_get_profiles();
OBSDataArrayAutoRelease profilesList = Utils::StringListToArray(profiles, "name");
bfree(profiles);
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_array(fields, "profiles", profilesList);
broadcastUpdate("ProfileListChanged", fields);
}
/**
@ -594,6 +680,9 @@ void WSEvents::OnStreamStopped() {
/**
* A request to start recording has been issued.
*
* Note: `recordingFilename` is not provided in this event because this information
* is not available at the time this event is emitted.
*
* @api events
* @name RecordingStarting
* @category recording
@ -606,37 +695,49 @@ void WSEvents::OnRecordingStarting() {
/**
* Recording started successfully.
*
* @return {String} `recordingFilename` Absolute path to the file of the current recording.
*
* @api events
* @name RecordingStarted
* @category recording
* @since 0.3
*/
void WSEvents::OnRecordingStarted() {
broadcastUpdate("RecordingStarted");
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "recordingFilename", Utils::GetCurrentRecordingFilename());
broadcastUpdate("RecordingStarted", data);
}
/**
* A request to stop recording has been issued.
*
* @return {String} `recordingFilename` Absolute path to the file of the current recording.
*
* @api events
* @name RecordingStopping
* @category recording
* @since 0.3
*/
void WSEvents::OnRecordingStopping() {
broadcastUpdate("RecordingStopping");
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "recordingFilename", Utils::GetCurrentRecordingFilename());
broadcastUpdate("RecordingStopping", data);
}
/**
* Recording stopped successfully.
*
* @return {String} `recordingFilename` Absolute path to the file of the current recording.
*
* @api events
* @name RecordingStopped
* @category recording
* @since 0.3
*/
void WSEvents::OnRecordingStopped() {
broadcastUpdate("RecordingStopped");
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "recordingFilename", Utils::GetCurrentRecordingFilename());
broadcastUpdate("RecordingStopped", data);
}
/**
@ -724,7 +825,7 @@ void WSEvents::OnExit() {
}
/**
* Emit every 2 seconds.
* Emitted every 2 seconds when stream is active.
*
* @return {boolean} `streaming` Current streaming state.
* @return {boolean} `recording` Current recording state.
@ -831,6 +932,7 @@ void WSEvents::StreamStatus() {
* @api events
* @name Heartbeat
* @category general
* @since v0.3
*/
void WSEvents::Heartbeat() {
@ -922,7 +1024,7 @@ void WSEvents::OnTransitionBegin(void* param, calldata_t* data) {
/**
* A transition (other than "cut") has ended.
* Please note that the `from-scene` field is not available in TransitionEnd.
* Note: The `from-scene` field is not available in TransitionEnd.
*
* @return {String} `name` Transition name.
* @return {String} `type` Transition type.
@ -1098,6 +1200,52 @@ void WSEvents::OnSourceMuteStateChange(void* param, calldata_t* data) {
self->broadcastUpdate("SourceMuteStateChanged", fields);
}
/**
* A source has removed audio.
*
* @return {String} `sourceName` Source name
*
* @api events
* @name SourceAudioDeactivated
* @category sources
* @since 4.9.0
*/
void WSEvents::OnSourceAudioDeactivated(void* param, calldata_t* data) {
auto self = reinterpret_cast<WSEvents*>(param);
OBSSource source = calldata_get_pointer<obs_source_t>(data, "source");
if (!source) {
return;
}
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_string(fields, "sourceName", obs_source_get_name(source));
self->broadcastUpdate("SourceAudioDeactivated", fields);
}
/**
* A source has added audio.
*
* @return {String} `sourceName` Source name
*
* @api events
* @name SourceAudioActivated
* @category sources
* @since 4.9.0
*/
void WSEvents::OnSourceAudioActivated(void* param, calldata_t* data) {
auto self = reinterpret_cast<WSEvents*>(param);
OBSSource source = calldata_get_pointer<obs_source_t>(data, "source");
if (!source) {
return;
}
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_string(fields, "sourceName", obs_source_get_name(source));
self->broadcastUpdate("SourceAudioActivated", fields);
}
/**
* The audio sync offset of a source has changed.
*
@ -1316,6 +1464,7 @@ void WSEvents::OnSourceFilterVisibilityChanged(void* param, calldata_t* data) {
* @return {Array<Object>} `filters` Ordered Filters list
* @return {String} `filters.*.name` Filter name
* @return {String} `filters.*.type` Filter type
* @return {boolean} `filters.*.enabled` Filter visibility status
*
* @api events
* @name SourceFiltersReordered
@ -1339,7 +1488,175 @@ void WSEvents::OnSourceFilterOrderChanged(void* param, calldata_t* data) {
}
/**
* Scene items have been reordered.
* A media source has started playing.
*
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
*
* @return {String} `sourceName` Source name
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaPlaying
* @category media
* @since 4.9.0
*/
void WSEvents::OnMediaPlaying(void* param, calldata_t* data) {
auto self = reinterpret_cast<WSEvents*>(param);
OBSDataAutoRelease fields = getMediaSourceData(data);
self->broadcastUpdate("MediaPlaying", fields);
}
/**
* A media source has been paused.
*
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
*
* @return {String} `sourceName` Source name
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaPaused
* @category media
* @since 4.9.0
*/
void WSEvents::OnMediaPaused(void* param, calldata_t* data) {
auto self = reinterpret_cast<WSEvents*>(param);
OBSDataAutoRelease fields = getMediaSourceData(data);
self->broadcastUpdate("MediaPaused", fields);
}
/**
* A media source has been restarted.
*
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
*
* @return {String} `sourceName` Source name
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaRestarted
* @category media
* @since 4.9.0
*/
void WSEvents::OnMediaRestarted(void* param, calldata_t* data) {
auto self = reinterpret_cast<WSEvents*>(param);
OBSDataAutoRelease fields = getMediaSourceData(data);
self->broadcastUpdate("MediaRestarted", fields);
}
/**
* A media source has been stopped.
*
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
*
* @return {String} `sourceName` Source name
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaStopped
* @category media
* @since 4.9.0
*/
void WSEvents::OnMediaStopped(void* param, calldata_t* data) {
auto self = reinterpret_cast<WSEvents*>(param);
OBSDataAutoRelease fields = getMediaSourceData(data);
self->broadcastUpdate("MediaStopped", fields);
}
/**
* A media source has gone to the next item in the playlist.
*
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
*
* @return {String} `sourceName` Source name
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaNext
* @category media
* @since 4.9.0
*/
void WSEvents::OnMediaNext(void* param, calldata_t* data) {
auto self = reinterpret_cast<WSEvents*>(param);
OBSDataAutoRelease fields = getMediaSourceData(data);
self->broadcastUpdate("MediaNext", fields);
}
/**
* A media source has gone to the previous item in the playlist.
*
* Note: This event is only emitted when something actively controls the media/VLC source. In other words, the source will never emit this on its own naturally.
*
* @return {String} `sourceName` Source name
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaPrevious
* @category media
* @since 4.9.0
*/
void WSEvents::OnMediaPrevious(void* param, calldata_t* data) {
auto self = reinterpret_cast<WSEvents*>(param);
OBSDataAutoRelease fields = getMediaSourceData(data);
self->broadcastUpdate("MediaPrevious", fields);
}
/**
* A media source has been started.
*
* Note: These events are emitted by the OBS sources themselves. For example when the media file starts playing. The behavior depends on the type of media source being used.
*
* @return {String} `sourceName` Source name
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaStarted
* @category media
* @since 4.9.0
*/
void WSEvents::OnMediaStarted(void* param, calldata_t* data) {
auto self = reinterpret_cast<WSEvents*>(param);
OBSDataAutoRelease fields = getMediaSourceData(data);
self->broadcastUpdate("MediaStarted", fields);
}
/**
* A media source has ended.
*
* Note: These events are emitted by the OBS sources themselves. For example when the media file ends. The behavior depends on the type of media source being used.
*
* @return {String} `sourceName` Source name
* @return {String} `sourceKind` The ID type of the source (Eg. `vlc_source` or `ffmpeg_source`)
*
* @api events
* @name MediaEnded
* @category media
* @since 4.9.0
*/
void WSEvents::OnMediaEnded(void* param, calldata_t* data) {
auto self = reinterpret_cast<WSEvents*>(param);
OBSDataAutoRelease fields = getMediaSourceData(data);
self->broadcastUpdate("MediaEnded", fields);
}
/**
* Scene items within a scene have been reordered.
*
* @return {String} `scene-name` Name of the scene where items have been reordered.
* @return {Array<Object>} `scene-items` Ordered list of scene items
@ -1382,7 +1699,7 @@ void WSEvents::OnSceneReordered(void* param, calldata_t* data) {
}
/**
* An item has been added to the current scene.
* A scene item has been added to a scene.
*
* @return {String} `scene-name` Name of the scene.
* @return {String} `item-name` Name of the item added to the scene.
@ -1415,7 +1732,7 @@ void WSEvents::OnSceneItemAdd(void* param, calldata_t* data) {
}
/**
* An item has been removed from the current scene.
* A scene item has been removed from a scene.
*
* @return {String} `scene-name` Name of the scene.
* @return {String} `item-name` Name of the item removed from the scene.
@ -1448,7 +1765,7 @@ void WSEvents::OnSceneItemDelete(void* param, calldata_t* data) {
}
/**
* An item's visibility has been toggled.
* A scene item's visibility has been toggled.
*
* @return {String} `scene-name` Name of the scene.
* @return {String} `item-name` Name of the item in the scene.
@ -1486,7 +1803,7 @@ void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) {
}
/**
* An item's locked status has been toggled.
* A scene item's locked status has been toggled.
*
* @return {String} `scene-name` Name of the scene.
* @return {String} `item-name` Name of the item in the scene.
@ -1524,7 +1841,7 @@ void WSEvents::OnSceneItemLockChanged(void* param, calldata_t* data) {
}
/**
* An item's transform has been changed.
* A scene item's transform has been changed.
*
* @return {String} `scene-name` Name of the scene.
* @return {String} `item-name` Name of the item in the scene.
@ -1675,7 +1992,7 @@ void WSEvents::OnStudioModeSwitched(bool checked) {
}
/**
* A custom broadcast message was received
* A custom broadcast message, sent by the server, requested by one of the websocket clients.
*
* @return {String} `realm` Identifier provided by the sender
* @return {Object} `data` User-defined data

View File

@ -126,6 +126,8 @@ private:
static void OnSourceMuteStateChange(void* param, calldata_t* data);
static void OnSourceAudioSyncOffsetChanged(void* param, calldata_t* data);
static void OnSourceAudioMixersChanged(void* param, calldata_t* data);
static void OnSourceAudioActivated(void* param, calldata_t* data);
static void OnSourceAudioDeactivated(void* param, calldata_t* data);
static void OnSourceRename(void* param, calldata_t* data);
@ -134,6 +136,15 @@ private:
static void OnSourceFilterVisibilityChanged(void* param, calldata_t* data);
static void OnSourceFilterOrderChanged(void* param, calldata_t* data);
static void OnMediaPlaying(void* param, calldata_t* data);
static void OnMediaPaused(void* param, calldata_t* data);
static void OnMediaRestarted(void* param, calldata_t* data);
static void OnMediaStopped(void* param, calldata_t* data);
static void OnMediaNext(void* param, calldata_t* data);
static void OnMediaPrevious(void* param, calldata_t* data);
static void OnMediaStarted(void* param, calldata_t* data);
static void OnMediaEnded(void* param, calldata_t* data);
static void OnSceneReordered(void* param, calldata_t* data);
static void OnSceneItemAdd(void* param, calldata_t* data);
static void OnSceneItemDelete(void* param, calldata_t* data);

View File

@ -29,83 +29,113 @@
using namespace std::placeholders;
const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap{
// Category: General
{ "GetVersion", &WSRequestHandler::GetVersion },
{ "GetAuthRequired", &WSRequestHandler::GetAuthRequired },
{ "Authenticate", &WSRequestHandler::Authenticate },
{ "GetStats", &WSRequestHandler::GetStats },
{ "SetHeartbeat", &WSRequestHandler::SetHeartbeat },
{ "GetVideoInfo", &WSRequestHandler::GetVideoInfo },
{ "OpenProjector", &WSRequestHandler::OpenProjector },
{ "SetFilenameFormatting", &WSRequestHandler::SetFilenameFormatting },
{ "GetFilenameFormatting", &WSRequestHandler::GetFilenameFormatting },
{ "GetStats", &WSRequestHandler::GetStats },
{ "BroadcastCustomMessage", &WSRequestHandler::BroadcastCustomMessage },
{ "GetVideoInfo", &WSRequestHandler::GetVideoInfo },
{ "OpenProjector", &WSRequestHandler::OpenProjector },
{ "TriggerHotkeyByName", &WSRequestHandler::TriggerHotkeyByName },
{ "TriggerHotkeyBySequence", &WSRequestHandler::TriggerHotkeyBySequence },
{ "ExecuteBatch", &WSRequestHandler::ExecuteBatch },
{ "SetCurrentScene", &WSRequestHandler::SetCurrentScene },
{ "GetCurrentScene", &WSRequestHandler::GetCurrentScene },
{ "GetSceneList", &WSRequestHandler::GetSceneList },
{ "SetSceneTransitionOverride", &WSRequestHandler::SetSceneTransitionOverride },
{ "RemoveSceneTransitionOverride", &WSRequestHandler::RemoveSceneTransitionOverride },
{ "GetSceneTransitionOverride", &WSRequestHandler::GetSceneTransitionOverride },
// Category: Media Control
{ "PlayPauseMedia", &WSRequestHandler::PlayPauseMedia },
{ "RestartMedia", &WSRequestHandler::RestartMedia },
{ "StopMedia", &WSRequestHandler::StopMedia },
{ "NextMedia", &WSRequestHandler::NextMedia },
{ "PreviousMedia", &WSRequestHandler::PreviousMedia },
{ "GetMediaDuration", &WSRequestHandler::GetMediaDuration },
{ "GetMediaTime", &WSRequestHandler::GetMediaTime },
{ "SetMediaTime", &WSRequestHandler::SetMediaTime },
{ "ScrubMedia", &WSRequestHandler::ScrubMedia },
{ "GetMediaState", &WSRequestHandler::GetMediaState },
{ "GetMediaSourcesList", &WSRequestHandler::GetMediaSourcesList },
{ "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat
{ "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender },
{ "SetSceneItemPosition", &WSRequestHandler::SetSceneItemPosition },
{ "SetSceneItemTransform", &WSRequestHandler::SetSceneItemTransform },
{ "SetSceneItemCrop", &WSRequestHandler::SetSceneItemCrop },
{ "GetSceneItemProperties", &WSRequestHandler::GetSceneItemProperties },
{ "SetSceneItemProperties", &WSRequestHandler::SetSceneItemProperties },
{ "ResetSceneItem", &WSRequestHandler::ResetSceneItem },
{ "DeleteSceneItem", &WSRequestHandler::DeleteSceneItem },
{ "DuplicateSceneItem", &WSRequestHandler::DuplicateSceneItem },
{ "ReorderSceneItems", &WSRequestHandler::ReorderSceneItems },
// Category: Outputs
{ "ListOutputs", &WSRequestHandler::ListOutputs },
{ "GetOutputInfo", &WSRequestHandler::GetOutputInfo },
{ "StartOutput", &WSRequestHandler::StartOutput },
{ "StopOutput", &WSRequestHandler::StopOutput },
{ "GetStreamingStatus", &WSRequestHandler::GetStreamingStatus },
{ "StartStopStreaming", &WSRequestHandler::StartStopStreaming },
// Category: Profiles
{ "SetCurrentProfile", &WSRequestHandler::SetCurrentProfile },
{ "GetCurrentProfile", &WSRequestHandler::GetCurrentProfile },
{ "ListProfiles", &WSRequestHandler::ListProfiles },
// Category: Recording
{ "GetRecordingStatus", &WSRequestHandler::GetRecordingStatus },
{ "StartStopRecording", &WSRequestHandler::StartStopRecording },
{ "StartStreaming", &WSRequestHandler::StartStreaming },
{ "StopStreaming", &WSRequestHandler::StopStreaming },
{ "StartRecording", &WSRequestHandler::StartRecording },
{ "StopRecording", &WSRequestHandler::StopRecording },
{ "PauseRecording", &WSRequestHandler::PauseRecording },
{ "ResumeRecording", &WSRequestHandler::ResumeRecording },
{ "SetRecordingFolder", &WSRequestHandler::SetRecordingFolder },
{ "GetRecordingFolder", &WSRequestHandler::GetRecordingFolder },
// Category: Replay Buffer
{ "GetReplayBufferStatus", &WSRequestHandler::GetReplayBufferStatus },
{ "StartStopReplayBuffer", &WSRequestHandler::StartStopReplayBuffer },
{ "StartReplayBuffer", &WSRequestHandler::StartReplayBuffer },
{ "StopReplayBuffer", &WSRequestHandler::StopReplayBuffer },
{ "SaveReplayBuffer", &WSRequestHandler::SaveReplayBuffer },
{ "SetRecordingFolder", &WSRequestHandler::SetRecordingFolder },
{ "GetRecordingFolder", &WSRequestHandler::GetRecordingFolder },
// Category: Scene Collections
{ "SetCurrentSceneCollection", &WSRequestHandler::SetCurrentSceneCollection },
{ "GetCurrentSceneCollection", &WSRequestHandler::GetCurrentSceneCollection },
{ "ListSceneCollections", &WSRequestHandler::ListSceneCollections },
{ "GetTransitionList", &WSRequestHandler::GetTransitionList },
{ "GetCurrentTransition", &WSRequestHandler::GetCurrentTransition },
{ "SetCurrentTransition", &WSRequestHandler::SetCurrentTransition },
{ "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration },
{ "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration },
{ "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition },
// Category: Scene Items
{ "GetSceneItemList", &WSRequestHandler::GetSceneItemList },
{ "GetSceneItemProperties", &WSRequestHandler::GetSceneItemProperties },
{ "SetSceneItemProperties", &WSRequestHandler::SetSceneItemProperties },
{ "ResetSceneItem", &WSRequestHandler::ResetSceneItem },
{ "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender },
{ "SetSceneItemPosition", &WSRequestHandler::SetSceneItemPosition },
{ "SetSceneItemTransform", &WSRequestHandler::SetSceneItemTransform },
{ "SetSceneItemCrop", &WSRequestHandler::SetSceneItemCrop },
{ "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat TODO: Remove in 5.0.0
{ "DeleteSceneItem", &WSRequestHandler::DeleteSceneItem },
{ "AddSceneItem", &WSRequestHandler::AddSceneItem },
{ "DuplicateSceneItem", &WSRequestHandler::DuplicateSceneItem },
{ "SetVolume", &WSRequestHandler::SetVolume },
// Category: Scenes
{ "SetCurrentScene", &WSRequestHandler::SetCurrentScene },
{ "GetCurrentScene", &WSRequestHandler::GetCurrentScene },
{ "GetSceneList", &WSRequestHandler::GetSceneList },
{ "CreateScene", &WSRequestHandler::CreateScene },
{ "ReorderSceneItems", &WSRequestHandler::ReorderSceneItems },
{ "SetSceneTransitionOverride", &WSRequestHandler::SetSceneTransitionOverride },
{ "RemoveSceneTransitionOverride", &WSRequestHandler::RemoveSceneTransitionOverride },
{ "GetSceneTransitionOverride", &WSRequestHandler::GetSceneTransitionOverride },
// Category: Sources
{ "CreateSource", &WSRequestHandler::CreateSource },
{ "GetSourcesList", &WSRequestHandler::GetSourcesList },
{ "GetSourceTypesList", &WSRequestHandler::GetSourceTypesList },
{ "GetVolume", &WSRequestHandler::GetVolume },
{ "ToggleMute", &WSRequestHandler::ToggleMute },
{ "SetMute", &WSRequestHandler::SetMute },
{ "SetVolume", &WSRequestHandler::SetVolume },
{ "GetMute", &WSRequestHandler::GetMute },
{ "SetMute", &WSRequestHandler::SetMute },
{ "ToggleMute", &WSRequestHandler::ToggleMute },
{ "GetAudioActive", &WSRequestHandler::GetAudioActive },
{ "SetSourceName", &WSRequestHandler::SetSourceName },
{ "SetSyncOffset", &WSRequestHandler::SetSyncOffset },
{ "GetSyncOffset", &WSRequestHandler::GetSyncOffset },
{ "GetSpecialSources", &WSRequestHandler::GetSpecialSources },
{ "GetSourcesList", &WSRequestHandler::GetSourcesList },
{ "GetSourceTypesList", &WSRequestHandler::GetSourceTypesList },
{ "GetSourceSettings", &WSRequestHandler::GetSourceSettings },
{ "SetSourceSettings", &WSRequestHandler::SetSourceSettings },
{ "GetAudioMonitorType", &WSRequestHandler::GetAudioMonitorType },
{ "SetAudioMonitorType", &WSRequestHandler::SetAudioMonitorType },
{ "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot },
{ "GetTextGDIPlusProperties", &WSRequestHandler::GetTextGDIPlusProperties },
{ "SetTextGDIPlusProperties", &WSRequestHandler::SetTextGDIPlusProperties },
{ "GetTextFreetype2Properties", &WSRequestHandler::GetTextFreetype2Properties },
{ "SetTextFreetype2Properties", &WSRequestHandler::SetTextFreetype2Properties },
{ "GetBrowserSourceProperties", &WSRequestHandler::GetBrowserSourceProperties },
{ "SetBrowserSourceProperties", &WSRequestHandler::SetBrowserSourceProperties },
{ "GetSpecialSources", &WSRequestHandler::GetSpecialSources },
{ "GetSourceFilters", &WSRequestHandler::GetSourceFilters },
{ "GetSourceFilterInfo", &WSRequestHandler::GetSourceFilterInfo },
{ "AddFilterToSource", &WSRequestHandler::AddFilterToSource },
@ -114,22 +144,23 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
{ "MoveSourceFilter", &WSRequestHandler::MoveSourceFilter },
{ "SetSourceFilterSettings", &WSRequestHandler::SetSourceFilterSettings },
{ "SetSourceFilterVisibility", &WSRequestHandler::SetSourceFilterVisibility },
{ "GetAudioMonitorType", &WSRequestHandler::GetAudioMonitorType },
{ "SetAudioMonitorType", &WSRequestHandler::SetAudioMonitorType },
{ "GetSourceDefaultSettings", &WSRequestHandler::GetSourceDefaultSettings },
{ "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot },
{ "RefreshBrowserSource", &WSRequestHandler::RefreshBrowserSource },
{ "SetCurrentSceneCollection", &WSRequestHandler::SetCurrentSceneCollection },
{ "GetCurrentSceneCollection", &WSRequestHandler::GetCurrentSceneCollection },
{ "ListSceneCollections", &WSRequestHandler::ListSceneCollections },
{ "SetCurrentProfile", &WSRequestHandler::SetCurrentProfile },
{ "GetCurrentProfile", &WSRequestHandler::GetCurrentProfile },
{ "ListProfiles", &WSRequestHandler::ListProfiles },
// Category: Streaming
{ "GetStreamingStatus", &WSRequestHandler::GetStreamingStatus },
{ "StartStopStreaming", &WSRequestHandler::StartStopStreaming },
{ "StartStreaming", &WSRequestHandler::StartStreaming },
{ "StopStreaming", &WSRequestHandler::StopStreaming },
{ "SetStreamSettings", &WSRequestHandler::SetStreamSettings },
{ "GetStreamSettings", &WSRequestHandler::GetStreamSettings },
{ "SaveStreamSettings", &WSRequestHandler::SaveStreamSettings },
#if BUILD_CAPTIONS
{ "SendCaptions", &WSRequestHandler::SendCaptions },
#endif
// Category: Studio Mode
{ "GetStudioModeStatus", &WSRequestHandler::GetStudioModeStatus },
{ "GetPreviewScene", &WSRequestHandler::GetPreviewScene },
{ "SetPreviewScene", &WSRequestHandler::SetPreviewScene },
@ -138,19 +169,17 @@ const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap {
{ "DisableStudioMode", &WSRequestHandler::DisableStudioMode },
{ "ToggleStudioMode", &WSRequestHandler::ToggleStudioMode },
{ "SetTextGDIPlusProperties", &WSRequestHandler::SetTextGDIPlusProperties },
{ "GetTextGDIPlusProperties", &WSRequestHandler::GetTextGDIPlusProperties },
{ "SetTextFreetype2Properties", &WSRequestHandler::SetTextFreetype2Properties },
{ "GetTextFreetype2Properties", &WSRequestHandler::GetTextFreetype2Properties },
{ "GetBrowserSourceProperties", &WSRequestHandler::GetBrowserSourceProperties },
{ "SetBrowserSourceProperties", &WSRequestHandler::SetBrowserSourceProperties },
{ "ListOutputs", &WSRequestHandler::ListOutputs },
{ "GetOutputInfo", &WSRequestHandler::GetOutputInfo },
{ "StartOutput", &WSRequestHandler::StartOutput },
{ "StopOutput", &WSRequestHandler::StopOutput }
// Category: Transitions
{ "GetTransitionList", &WSRequestHandler::GetTransitionList },
{ "GetCurrentTransition", &WSRequestHandler::GetCurrentTransition },
{ "SetCurrentTransition", &WSRequestHandler::SetCurrentTransition },
{ "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration },
{ "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration },
{ "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition },
{ "GetTransitionSettings", &WSRequestHandler::GetTransitionSettings },
{ "SetTransitionSettings", &WSRequestHandler::SetTransitionSettings },
{ "ReleaseTBar", &WSRequestHandler::ReleaseTBar },
{ "SetTBarPosition", &WSRequestHandler::SetTBarPosition }
};
const QSet<QString> WSRequestHandler::authNotRequired {

View File

@ -47,82 +47,112 @@ class WSRequestHandler {
static const QHash<QString, RpcMethodHandler> messageMap;
static const QSet<QString> authNotRequired;
// Category: General
RpcResponse GetVersion(const RpcRequest&);
RpcResponse GetAuthRequired(const RpcRequest&);
RpcResponse Authenticate(const RpcRequest&);
RpcResponse GetStats(const RpcRequest&);
RpcResponse SetHeartbeat(const RpcRequest&);
RpcResponse GetVideoInfo(const RpcRequest&);
RpcResponse OpenProjector(const RpcRequest&);
RpcResponse SetFilenameFormatting(const RpcRequest&);
RpcResponse GetFilenameFormatting(const RpcRequest&);
RpcResponse GetStats(const RpcRequest&);
RpcResponse BroadcastCustomMessage(const RpcRequest&);
RpcResponse GetVideoInfo(const RpcRequest&);
RpcResponse OpenProjector(const RpcRequest&);
RpcResponse TriggerHotkeyByName(const RpcRequest&);
RpcResponse TriggerHotkeyBySequence(const RpcRequest&);
RpcResponse ExecuteBatch(const RpcRequest&);
RpcResponse SetCurrentScene(const RpcRequest&);
RpcResponse GetCurrentScene(const RpcRequest&);
RpcResponse GetSceneList(const RpcRequest&);
RpcResponse SetSceneTransitionOverride(const RpcRequest&);
RpcResponse RemoveSceneTransitionOverride(const RpcRequest&);
RpcResponse GetSceneTransitionOverride(const RpcRequest&);
// Category: Media Control
RpcResponse PlayPauseMedia(const RpcRequest&);
RpcResponse RestartMedia(const RpcRequest&);
RpcResponse StopMedia(const RpcRequest&);
RpcResponse NextMedia(const RpcRequest&);
RpcResponse PreviousMedia(const RpcRequest&);
RpcResponse GetMediaDuration(const RpcRequest&);
RpcResponse GetMediaTime(const RpcRequest&);
RpcResponse SetMediaTime(const RpcRequest&);
RpcResponse ScrubMedia(const RpcRequest&);
RpcResponse GetMediaState(const RpcRequest&);
RpcResponse GetMediaSourcesList(const RpcRequest&);
RpcResponse SetSceneItemRender(const RpcRequest&);
RpcResponse SetSceneItemPosition(const RpcRequest&);
RpcResponse SetSceneItemTransform(const RpcRequest&);
RpcResponse SetSceneItemCrop(const RpcRequest&);
RpcResponse GetSceneItemProperties(const RpcRequest&);
RpcResponse SetSceneItemProperties(const RpcRequest&);
RpcResponse ResetSceneItem(const RpcRequest&);
RpcResponse DuplicateSceneItem(const RpcRequest&);
RpcResponse DeleteSceneItem(const RpcRequest&);
RpcResponse ReorderSceneItems(const RpcRequest&);
// Category: Outputs
RpcResponse ListOutputs(const RpcRequest&);
RpcResponse GetOutputInfo(const RpcRequest&);
RpcResponse StartOutput(const RpcRequest&);
RpcResponse StopOutput(const RpcRequest&);
RpcResponse GetStreamingStatus(const RpcRequest&);
RpcResponse StartStopStreaming(const RpcRequest&);
// Category: Profiles
RpcResponse SetCurrentProfile(const RpcRequest&);
RpcResponse GetCurrentProfile(const RpcRequest&);
RpcResponse ListProfiles(const RpcRequest&);
// Category: Recording
RpcResponse GetRecordingStatus(const RpcRequest&);
RpcResponse StartStopRecording(const RpcRequest&);
RpcResponse StartStreaming(const RpcRequest&);
RpcResponse StopStreaming(const RpcRequest&);
RpcResponse StartRecording(const RpcRequest&);
RpcResponse StopRecording(const RpcRequest&);
RpcResponse PauseRecording(const RpcRequest&);
RpcResponse ResumeRecording(const RpcRequest&);
RpcResponse SetRecordingFolder(const RpcRequest&);
RpcResponse GetRecordingFolder(const RpcRequest&);
// Category: Replay Buffer
RpcResponse GetReplayBufferStatus(const RpcRequest&);
RpcResponse StartStopReplayBuffer(const RpcRequest&);
RpcResponse StartReplayBuffer(const RpcRequest&);
RpcResponse StopReplayBuffer(const RpcRequest&);
RpcResponse SaveReplayBuffer(const RpcRequest&);
RpcResponse SetRecordingFolder(const RpcRequest&);
RpcResponse GetRecordingFolder(const RpcRequest&);
// Category: Scene Collections
RpcResponse SetCurrentSceneCollection(const RpcRequest&);
RpcResponse GetCurrentSceneCollection(const RpcRequest&);
RpcResponse ListSceneCollections(const RpcRequest&);
RpcResponse GetTransitionList(const RpcRequest&);
RpcResponse GetCurrentTransition(const RpcRequest&);
RpcResponse SetCurrentTransition(const RpcRequest&);
RpcResponse SetTransitionDuration(const RpcRequest&);
RpcResponse GetTransitionDuration(const RpcRequest&);
RpcResponse GetTransitionPosition(const RpcRequest&);
// Category: Scene Items
RpcResponse GetSceneItemList(const RpcRequest&);
RpcResponse GetSceneItemProperties(const RpcRequest&);
RpcResponse SetSceneItemProperties(const RpcRequest&);
RpcResponse ResetSceneItem(const RpcRequest&);
RpcResponse SetSceneItemRender(const RpcRequest&);
RpcResponse SetSceneItemPosition(const RpcRequest&);
RpcResponse SetSceneItemTransform(const RpcRequest&);
RpcResponse SetSceneItemCrop(const RpcRequest&);
RpcResponse DeleteSceneItem(const RpcRequest&);
RpcResponse AddSceneItem(const RpcRequest&);
RpcResponse DuplicateSceneItem(const RpcRequest&);
RpcResponse SetVolume(const RpcRequest&);
// Category: Scenes
RpcResponse SetCurrentScene(const RpcRequest&);
RpcResponse GetCurrentScene(const RpcRequest&);
RpcResponse GetSceneList(const RpcRequest&);
RpcResponse CreateScene(const RpcRequest&);
RpcResponse ReorderSceneItems(const RpcRequest&);
RpcResponse SetSceneTransitionOverride(const RpcRequest&);
RpcResponse RemoveSceneTransitionOverride(const RpcRequest&);
RpcResponse GetSceneTransitionOverride(const RpcRequest&);
// Category: Sources
RpcResponse CreateSource(const RpcRequest&);
RpcResponse GetSourcesList(const RpcRequest&);
RpcResponse GetSourceTypesList(const RpcRequest&);
RpcResponse GetVolume(const RpcRequest&);
RpcResponse ToggleMute(const RpcRequest&);
RpcResponse SetMute(const RpcRequest&);
RpcResponse SetVolume(const RpcRequest&);
RpcResponse GetMute(const RpcRequest&);
RpcResponse SetMute(const RpcRequest&);
RpcResponse ToggleMute(const RpcRequest&);
RpcResponse GetAudioActive(const RpcRequest&);
RpcResponse SetSourceName(const RpcRequest&);
RpcResponse SetSyncOffset(const RpcRequest&);
RpcResponse GetSyncOffset(const RpcRequest&);
RpcResponse GetSpecialSources(const RpcRequest&);
RpcResponse GetSourcesList(const RpcRequest&);
RpcResponse GetSourceTypesList(const RpcRequest&);
RpcResponse GetSourceSettings(const RpcRequest&);
RpcResponse SetSourceSettings(const RpcRequest&);
RpcResponse GetAudioMonitorType(const RpcRequest&);
RpcResponse SetAudioMonitorType(const RpcRequest&);
RpcResponse TakeSourceScreenshot(const RpcRequest&);
RpcResponse GetTextGDIPlusProperties(const RpcRequest&);
RpcResponse SetTextGDIPlusProperties(const RpcRequest&);
RpcResponse GetTextFreetype2Properties(const RpcRequest&);
RpcResponse SetTextFreetype2Properties(const RpcRequest&);
RpcResponse GetBrowserSourceProperties(const RpcRequest&);
RpcResponse SetBrowserSourceProperties(const RpcRequest&);
RpcResponse GetSpecialSources(const RpcRequest&);
RpcResponse GetSourceFilters(const RpcRequest&);
RpcResponse GetSourceFilterInfo(const RpcRequest&);
RpcResponse AddFilterToSource(const RpcRequest&);
@ -131,22 +161,23 @@ class WSRequestHandler {
RpcResponse MoveSourceFilter(const RpcRequest&);
RpcResponse SetSourceFilterSettings(const RpcRequest&);
RpcResponse SetSourceFilterVisibility(const RpcRequest&);
RpcResponse GetAudioMonitorType(const RpcRequest&);
RpcResponse SetAudioMonitorType(const RpcRequest&);
RpcResponse GetSourceDefaultSettings(const RpcRequest&);
RpcResponse TakeSourceScreenshot(const RpcRequest&);
RpcResponse RefreshBrowserSource(const RpcRequest&);
RpcResponse SetCurrentSceneCollection(const RpcRequest&);
RpcResponse GetCurrentSceneCollection(const RpcRequest&);
RpcResponse ListSceneCollections(const RpcRequest&);
RpcResponse SetCurrentProfile(const RpcRequest&);
RpcResponse GetCurrentProfile(const RpcRequest&);
RpcResponse ListProfiles(const RpcRequest&);
// Category: Streaming
RpcResponse GetStreamingStatus(const RpcRequest&);
RpcResponse StartStopStreaming(const RpcRequest&);
RpcResponse StartStreaming(const RpcRequest&);
RpcResponse StopStreaming(const RpcRequest&);
RpcResponse SetStreamSettings(const RpcRequest&);
RpcResponse GetStreamSettings(const RpcRequest&);
RpcResponse SaveStreamSettings(const RpcRequest&);
#if BUILD_CAPTIONS
RpcResponse SendCaptions(const RpcRequest&);
#endif
// Category: Studio Mode
RpcResponse GetStudioModeStatus(const RpcRequest&);
RpcResponse GetPreviewScene(const RpcRequest&);
RpcResponse SetPreviewScene(const RpcRequest&);
@ -155,17 +186,15 @@ class WSRequestHandler {
RpcResponse DisableStudioMode(const RpcRequest&);
RpcResponse ToggleStudioMode(const RpcRequest&);
RpcResponse SetTextGDIPlusProperties(const RpcRequest&);
RpcResponse GetTextGDIPlusProperties(const RpcRequest&);
RpcResponse SetTextFreetype2Properties(const RpcRequest&);
RpcResponse GetTextFreetype2Properties(const RpcRequest&);
RpcResponse SetBrowserSourceProperties(const RpcRequest&);
RpcResponse GetBrowserSourceProperties(const RpcRequest&);
RpcResponse ListOutputs(const RpcRequest&);
RpcResponse GetOutputInfo(const RpcRequest&);
RpcResponse StartOutput(const RpcRequest&);
RpcResponse StopOutput(const RpcRequest&);
// Category: Transitions
RpcResponse GetTransitionList(const RpcRequest&);
RpcResponse GetCurrentTransition(const RpcRequest&);
RpcResponse SetCurrentTransition(const RpcRequest&);
RpcResponse SetTransitionDuration(const RpcRequest&);
RpcResponse GetTransitionDuration(const RpcRequest&);
RpcResponse GetTransitionPosition(const RpcRequest&);
RpcResponse GetTransitionSettings(const RpcRequest&);
RpcResponse SetTransitionSettings(const RpcRequest&);
RpcResponse ReleaseTBar(const RpcRequest&);
RpcResponse SetTBarPosition(const RpcRequest&);
};

View File

@ -7,6 +7,7 @@
#include "Config.h"
#include "Utils.h"
#include "WSEvents.h"
#include "protocol/OBSRemoteProtocol.h"
#define CASE(x) case x: return #x;
const char *describe_output_format(int format) {
@ -171,6 +172,7 @@ RpcResponse WSRequestHandler::Authenticate(const RpcRequest& request) {
* @name SetHeartbeat
* @category general
* @since 4.3.0
* @deprecated Since 4.9.0. Please poll the appropriate data using requests. Will be removed in v5.0.0.
*/
RpcResponse WSRequestHandler::SetHeartbeat(const RpcRequest& request) {
if (!request.hasField("enable")) {
@ -231,7 +233,7 @@ RpcResponse WSRequestHandler::GetFilenameFormatting(const RpcRequest& request) {
/**
* Get OBS stats (almost the same info as provided in OBS' stats window)
*
* @return {OBSStats} `stats` OBS stats
* @return {OBSStats} `stats` [OBS stats](#obsstats)
*
* @api requests
* @name GetStats
@ -320,9 +322,9 @@ RpcResponse WSRequestHandler::GetVideoInfo(const RpcRequest& request) {
/**
* Open a projector window or create a projector on a monitor. Requires OBS v24.0.4 or newer.
*
* @param {String (Optional)} `type` Type of projector: Preview (default), Source, Scene, StudioProgram, or Multiview (case insensitive).
* @param {String (Optional)} `type` Type of projector: `Preview` (default), `Source`, `Scene`, `StudioProgram`, or `Multiview` (case insensitive).
* @param {int (Optional)} `monitor` Monitor to open the projector on. If -1 or omitted, opens a window.
* @param {String (Optional)} `geometry` Size and position of the projector window (only if monitor is -1). Encoded in Base64 using Qt's geometry encoding (https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors.
* @param {String (Optional)} `geometry` Size and position of the projector window (only if monitor is -1). Encoded in Base64 using [Qt's geometry encoding](https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors.
* @param {String (Optional)} `name` Name of the source or scene to be displayed (ignored for other projector types).
*
* @api requests
@ -344,3 +346,130 @@ RpcResponse WSRequestHandler::OpenProjector(const RpcRequest& request) {
obs_frontend_open_projector(type, monitor, geometry, name);
return request.success();
}
/**
* Executes hotkey routine, identified by hotkey unique name
*
* @param {String} `hotkeyName` Unique name of the hotkey, as defined when registering the hotkey (e.g. "ReplayBuffer.Save")
*
* @api requests
* @name TriggerHotkeyByName
* @category general
* @since 4.9.0
*/
RpcResponse WSRequestHandler::TriggerHotkeyByName(const RpcRequest& request) {
const char* name = obs_data_get_string(request.parameters(), "hotkeyName");
obs_hotkey_t* hk = Utils::FindHotkeyByName(name);
if (!hk) {
return request.failed("hotkey not found");
}
obs_hotkey_trigger_routed_callback(obs_hotkey_get_id(hk), true);
return request.success();
}
/**
* Executes hotkey routine, identified by bound combination of keys. A single key combination might trigger multiple hotkey routines depending on user settings
*
* @param {String} `keyId` Main key identifier (e.g. `OBS_KEY_A` for key "A"). Available identifiers [here](https://github.com/obsproject/obs-studio/blob/master/libobs/obs-hotkeys.h)
* @param {Object (Optional)} `keyModifiers` Optional key modifiers object. False entries can be ommitted
* @param {boolean} `keyModifiers.shift` Trigger Shift Key
* @param {boolean} `keyModifiers.alt` Trigger Alt Key
* @param {boolean} `keyModifiers.control` Trigger Control (Ctrl) Key
* @param {boolean} `keyModifiers.command` Trigger Command Key (Mac)
*
* @api requests
* @name TriggerHotkeyBySequence
* @category general
* @since 4.9.0
*/
RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request) {
if (!request.hasField("keyId")) {
return request.failed("missing request keyId parameter");
}
OBSDataAutoRelease data = obs_data_get_obj(request.parameters(), "keyModifiers");
obs_key_combination_t combo = {0};
uint32_t modifiers = 0;
if (obs_data_get_bool(data, "shift"))
modifiers |= INTERACT_SHIFT_KEY;
if (obs_data_get_bool(data, "control"))
modifiers |= INTERACT_CONTROL_KEY;
if (obs_data_get_bool(data, "alt"))
modifiers |= INTERACT_ALT_KEY;
if (obs_data_get_bool(data, "command"))
modifiers |= INTERACT_COMMAND_KEY;
combo.modifiers = modifiers;
combo.key = obs_key_from_name(obs_data_get_string(request.parameters(), "keyId"));
if (!modifiers
&& (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) {
return request.failed("invalid key-modifier combination");
}
// Inject hotkey press-release sequence
obs_hotkey_inject_event(combo, false);
obs_hotkey_inject_event(combo, true);
obs_hotkey_inject_event(combo, false);
return request.success();
}
/**
* Executes a list of requests sequentially (one-by-one on the same thread).
*
* @param {Array<Object>} `requests` Array of requests to perform. Executed in order.
* @param {String} `requests.*.request-type` Request type. Eg. `GetVersion`.
* @param {String (Optional)} `requests.*.message-id` ID of the individual request. Can be any string and not required to be unique. Defaults to empty string if not specified.
* @param {boolean (Optional)} `abortOnFail` Stop processing batch requests if one returns a failure.
*
* @return {Array<Object>} `results` Batch requests results, ordered sequentially.
* @return {String} `results.*.message-id` ID of the individual request which was originally provided by the client.
* @return {String} `results.*.status` Status response as string. Either `ok` or `error`.
* @return {String (Optional)} `results.*.error` Error message accompanying an `error` status.
*
* @api requests
* @name ExecuteBatch
* @category general
* @since 4.9.0
*/
RpcResponse WSRequestHandler::ExecuteBatch(const RpcRequest& request) {
if (!request.hasField("requests")) {
return request.failed("missing request parameters");
}
bool abortOnFail = obs_data_get_bool(request.parameters(), "abortOnFail");
OBSDataArrayAutoRelease results = obs_data_array_create();
OBSDataArrayAutoRelease requests = obs_data_get_array(request.parameters(), "requests");
size_t requestsCount = obs_data_array_count(requests);
for (size_t i = 0; i < requestsCount; i++) {
OBSDataAutoRelease requestData = obs_data_array_item(requests, i);
QString messageId = obs_data_get_string(requestData, "message-id");
QString methodName = obs_data_get_string(requestData, "request-type");
obs_data_unset_user_value(requestData, "request-type");
obs_data_unset_user_value(requestData, "message-id");
// build RpcRequest from json data object
RpcRequest subRequest(messageId, methodName, requestData);
// execute the request
RpcResponse subResponse = processRequest(subRequest);
// transform response into json data
OBSDataAutoRelease subResponseData = OBSRemoteProtocol::rpcResponseToJsonData(subResponse);
obs_data_array_push_back(results, subResponseData);
// if told to abort on fail and a failure occurs, stop request processing and return the progress
if (abortOnFail && (subResponse.status() == RpcResponse::Status::Error))
break;
}
OBSDataAutoRelease response = obs_data_create();
obs_data_set_array(response, "results", results);
return request.success(response);
}

View File

@ -0,0 +1,396 @@
#include "Utils.h"
#include "WSRequestHandler.h"
bool isMediaSource(const QString& sourceKind)
{
return (sourceKind == "vlc_source" || sourceKind == "ffmpeg_source");
}
QString getSourceMediaState(obs_source_t *source)
{
QString mediaState;
enum obs_media_state mstate = obs_source_media_get_state(source);
switch (mstate) {
case OBS_MEDIA_STATE_NONE:
mediaState = "none";
break;
case OBS_MEDIA_STATE_PLAYING:
mediaState = "playing";
break;
case OBS_MEDIA_STATE_OPENING:
mediaState = "opening";
break;
case OBS_MEDIA_STATE_BUFFERING:
mediaState = "buffering";
break;
case OBS_MEDIA_STATE_PAUSED:
mediaState = "paused";
break;
case OBS_MEDIA_STATE_STOPPED:
mediaState = "stopped";
break;
case OBS_MEDIA_STATE_ENDED:
mediaState = "ended";
break;
case OBS_MEDIA_STATE_ERROR:
mediaState = "error";
break;
default:
mediaState = "unknown";
}
return mediaState;
}
/**
* Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
*
* @param {String} `sourceName` Source name.
* @param {boolean} `playPause` Whether to pause or play the source. `false` for play, `true` for pause.
*
* @api requests
* @name PlayPauseMedia
* @category media control
* @since 4.9.0
*/
RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) {
if ((!request.hasField("sourceName")) || (!request.hasField("playPause"))) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
bool playPause = obs_data_get_bool(request.parameters(), "playPause");
if (sourceName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
obs_source_media_play_pause(source, playPause);
return request.success();
}
/**
* Restart a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
*
* @param {String} `sourceName` Source name.
*
* @api requests
* @name RestartMedia
* @category media control
* @since 4.9.0
*/
RpcResponse WSRequestHandler::RestartMedia(const RpcRequest& request) {
if (!request.hasField("sourceName")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
if (sourceName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
obs_source_media_restart(source);
return request.success();
}
/**
* Stop a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
*
* @param {String} `sourceName` Source name.
*
* @api requests
* @name StopMedia
* @category media control
* @since 4.9.0
*/
RpcResponse WSRequestHandler::StopMedia(const RpcRequest& request) {
if (!request.hasField("sourceName")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
if (sourceName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
obs_source_media_stop(source);
return request.success();
}
/**
* Skip to the next media item in the playlist. Supports only vlc media source (as of OBS v25.0.8)
*
* @param {String} `sourceName` Source name.
*
* @api requests
* @name NextMedia
* @category media control
* @since 4.9.0
*/
RpcResponse WSRequestHandler::NextMedia(const RpcRequest& request) {
if (!request.hasField("sourceName")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
if (sourceName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
obs_source_media_next(source);
return request.success();
}
/**
* Go to the previous media item in the playlist. Supports only vlc media source (as of OBS v25.0.8)
*
* @param {String} `sourceName` Source name.
*
* @api requests
* @name PreviousMedia
* @category media control
* @since 4.9.0
*/
RpcResponse WSRequestHandler::PreviousMedia(const RpcRequest& request) {
if (!request.hasField("sourceName")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
if (sourceName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
obs_source_media_previous(source);
return request.success();
}
/**
* Get the length of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
* Note: For some reason, for the first 5 or so seconds that the media is playing, the total duration can be off by upwards of 50ms.
*
* @param {String} `sourceName` Source name.
*
* @return {int} `mediaDuration` The total length of media in milliseconds..
*
* @api requests
* @name GetMediaDuration
* @category media control
* @since 4.9.0
*/
RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) {
if (!request.hasField("sourceName")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
if (sourceName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
OBSDataAutoRelease response = obs_data_create();
obs_data_set_int(response, "mediaDuration", obs_source_media_get_duration(source));
return request.success(response);
}
/**
* Get the current timestamp of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
*
* @param {String} `sourceName` Source name.
*
* @return {int} `timestamp` The time in milliseconds since the start of the media.
*
* @api requests
* @name GetMediaTime
* @category media control
* @since 4.9.0
*/
RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) {
if (!request.hasField("sourceName")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
if (sourceName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
OBSDataAutoRelease response = obs_data_create();
obs_data_set_int(response, "timestamp", obs_source_media_get_time(source));
return request.success(response);
}
/**
* Set the timestamp of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
*
* @param {String} `sourceName` Source name.
* @param {int} `timestamp` Milliseconds to set the timestamp to.
*
* @api requests
* @name SetMediaTime
* @category media control
* @since 4.9.0
*/
RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) {
if (!request.hasField("sourceName") || !request.hasField("timestamp")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
int64_t timestamp = (int64_t)obs_data_get_int(request.parameters(), "timestamp");
if (sourceName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
obs_source_media_set_time(source, timestamp);
return request.success();
}
/**
* Scrub media using a supplied offset. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
* Note: Due to processing/network delays, this request is not perfect. The processing rate of this request has also not been tested.
*
* @param {String} `sourceName` Source name.
* @param {int} `timeOffset` Millisecond offset (positive or negative) to offset the current media position.
*
* @api requests
* @name ScrubMedia
* @category media control
* @since 4.9.0
*/
RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) {
if (!request.hasField("sourceName") || !request.hasField("timeOffset")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
int64_t timeOffset = (int64_t)obs_data_get_int(request.parameters(), "timeOffset");
if (sourceName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
int64_t newTime = obs_source_media_get_time(source) + timeOffset;
if (newTime < 0) {
newTime = 0;
}
obs_source_media_set_time(source, newTime);
return request.success();
}
/**
* Get the current playing state of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
*
* @param {String} `sourceName` Source name.
*
* @return {String} `mediaState` The media state of the provided source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown`
*
* @api requests
* @name GetMediaState
* @category media control
* @since 4.9.0
*/
RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) {
if (!request.hasField("sourceName")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
if (sourceName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "mediaState", getSourceMediaState(source).toUtf8());
return request.success(response);
}
/**
* List the media state of all media sources (vlc and media source)
*
* @return {Array<Object>} `mediaSources` Array of sources
* @return {String} `mediaSources.*.sourceName` Unique source name
* @return {String} `mediaSources.*.sourceKind` Unique source internal type (a.k.a `ffmpeg_source` or `vlc_source`)
* @return {String} `mediaSources.*.mediaState` The current state of media for that source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown`
*
* @api requests
* @name GetMediaSourcesList
* @category sources
* @since 4.9.0
*/
RpcResponse WSRequestHandler::GetMediaSourcesList(const RpcRequest& request)
{
OBSDataArrayAutoRelease sourcesArray = obs_data_array_create();
auto sourceEnumProc = [](void* privateData, obs_source_t* source) -> bool {
obs_data_array_t* sourcesArray = (obs_data_array_t*)privateData;
QString sourceKind = obs_source_get_id(source);
if (isMediaSource(sourceKind)) {
OBSDataAutoRelease sourceData = obs_data_create();
obs_data_set_string(sourceData, "sourceName", obs_source_get_name(source));
obs_data_set_string(sourceData, "sourceKind", sourceKind.toUtf8());
QString mediaState = getSourceMediaState(source);
obs_data_set_string(sourceData, "mediaState", mediaState.toUtf8());
obs_data_array_push_back(sourcesArray, sourceData);
}
return true;
};
obs_enum_sources(sourceEnumProc, sourcesArray);
OBSDataAutoRelease response = obs_data_create();
obs_data_set_array(response, "mediaSources", sourcesArray);
return request.success(response);
}

View File

@ -59,7 +59,7 @@ obs_data_t* getOutputInfo(obs_output_t* output)
return data;
}
RpcResponse findOutputOrFail(const RpcRequest& request, std::function<RpcResponse (obs_output_t*)> callback)
RpcResponse findOutputOrFail(const RpcRequest& request, std::function<RpcResponse (obs_output_t*, const RpcRequest&)> callback)
{
if (!request.hasField("outputName")) {
return request.failed("missing request parameters");
@ -71,7 +71,7 @@ RpcResponse findOutputOrFail(const RpcRequest& request, std::function<RpcRespons
return request.failed("specified output doesn't exist");
}
return callback(output);
return callback(output, request);
}
/**
@ -117,7 +117,7 @@ RpcResponse WSRequestHandler::ListOutputs(const RpcRequest& request)
*/
RpcResponse WSRequestHandler::GetOutputInfo(const RpcRequest& request)
{
return findOutputOrFail(request, [request](obs_output_t* output) {
return findOutputOrFail(request, [](obs_output_t* output, const RpcRequest& request) {
OBSDataAutoRelease outputInfo = getOutputInfo(output);
OBSDataAutoRelease fields = obs_data_create();
@ -129,6 +129,8 @@ RpcResponse WSRequestHandler::GetOutputInfo(const RpcRequest& request)
/**
* Start an output
*
* Note: Controlling outputs is an experimental feature of obs-websocket. Some plugins which add outputs to OBS may not function properly when they are controlled in this way.
*
* @param {String} `outputName` Output name
*
* @api requests
@ -138,7 +140,7 @@ RpcResponse WSRequestHandler::GetOutputInfo(const RpcRequest& request)
*/
RpcResponse WSRequestHandler::StartOutput(const RpcRequest& request)
{
return findOutputOrFail(request, [request](obs_output_t* output) {
return findOutputOrFail(request, [](obs_output_t* output, const RpcRequest& request) {
if (obs_output_active(output)) {
return request.failed("output already active");
}
@ -157,6 +159,8 @@ RpcResponse WSRequestHandler::StartOutput(const RpcRequest& request)
/**
* Stop an output
*
* Note: Controlling outputs is an experimental feature of obs-websocket. Some plugins which add outputs to OBS may not function properly when they are controlled in this way.
*
* @param {String} `outputName` Output name
* @param {boolean (optional)} `force` Force stop (default: false)
*
@ -167,7 +171,7 @@ RpcResponse WSRequestHandler::StartOutput(const RpcRequest& request)
*/
RpcResponse WSRequestHandler::StopOutput(const RpcRequest& request)
{
return findOutputOrFail(request, [request](obs_output_t* output) {
return findOutputOrFail(request, [](obs_output_t* output, const RpcRequest& request) {
if (!obs_output_active(output)) {
return request.failed("output not active");
}

View File

@ -17,13 +17,22 @@ RpcResponse WSRequestHandler::SetCurrentProfile(const RpcRequest& request) {
return request.failed("missing request parameters");
}
QString profileName = obs_data_get_string(request.parameters(), "profile-name");
if (profileName.isEmpty()) {
const char* profileName = obs_data_get_string(request.parameters(), "profile-name");
if (!profileName) {
return request.failed("invalid request parameters");
}
// TODO : check if profile exists
obs_frontend_set_current_profile(profileName.toUtf8());
char** profiles = obs_frontend_get_profiles();
bool profileExists = Utils::StringInStringList(profiles, profileName);
bfree(profiles);
if (!profileExists) {
return request.failed("profile does not exist");
}
obs_queue_task(OBS_TASK_UI, [](void* param) {
obs_frontend_set_current_profile(reinterpret_cast<const char*>(param));
}, (void*)profileName, true);
return request.success();
}
@ -49,6 +58,7 @@ RpcResponse WSRequestHandler::GetCurrentProfile(const RpcRequest& request) {
* Get a list of available profiles.
*
* @return {Array<Object>} `profiles` List of available profiles.
* @return {String} `profiles.*.profile-name` Filter name
*
* @api requests
* @name ListProfiles

View File

@ -1,8 +1,10 @@
#include "obs-websocket.h"
#include "WSRequestHandler.h"
#include <functional>
#include <util/platform.h>
#include "Utils.h"
#include "WSEvents.h"
RpcResponse ifCanPause(const RpcRequest& request, std::function<RpcResponse()> callback)
{
@ -14,7 +16,36 @@ RpcResponse ifCanPause(const RpcRequest& request, std::function<RpcResponse()> c
}
/**
* Toggle recording on or off.
* Get current recording status.
*
* @return {boolean} `isRecording` Current recording status.
* @return {boolean} `isRecordingPaused` Whether the recording is paused or not.
* @return {String (optional)} `recordTimecode` Time elapsed since recording started (only present if currently recording).
* @return {String (optional)} `recordingFilename` Absolute path to the recording file (only present if currently recording).
*
* @api requests
* @name GetRecordingStatus
* @category recording
* @since 4.9.0
*/
RpcResponse WSRequestHandler::GetRecordingStatus(const RpcRequest& request) {
auto events = GetEventsSystem();
OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "isRecording", obs_frontend_recording_active());
obs_data_set_bool(data, "isRecordingPaused", obs_frontend_recording_paused());
if (obs_frontend_recording_active()) {
QString recordingTimecode = events->getRecordingTimecode();
obs_data_set_string(data, "recordTimecode", recordingTimecode.toUtf8().constData());
obs_data_set_string(data, "recordingFilename", Utils::GetCurrentRecordingFilename());
}
return request.success(data);
}
/**
* Toggle recording on or off (depending on the current recording state).
*
* @api requests
* @name StartStopRecording
@ -106,7 +137,7 @@ RpcResponse WSRequestHandler::ResumeRecording(const RpcRequest& request) {
* In the current profile, sets the recording folder of the Simple and Advanced
* output modes to the specified value.
*
* Please note: if `SetRecordingFolder` is called while a recording is
* Note: If `SetRecordingFolder` is called while a recording is
* in progress, the change won't be applied immediately and will be
* effective on the next recording.
*

View File

@ -1,9 +1,29 @@
#include "obs-websocket.h"
#include "WSEvents.h"
#include "Utils.h"
#include "WSRequestHandler.h"
/**
* Toggle the Replay Buffer on/off.
* Get the status of the OBS replay buffer.
*
* @return {boolean} `isReplayBufferActive` Current recording status.
*
* @api requests
* @name GetReplayBufferStatus
* @category replay buffer
* @since 4.9.0
*/
RpcResponse WSRequestHandler::GetReplayBufferStatus(const RpcRequest& request) {
OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "isReplayBufferActive", obs_frontend_replay_buffer_active());
return request.success(data);
}
/**
* Toggle the Replay Buffer on/off (depending on the current state of the replay buffer).
*
* @api requests
* @name StartStopReplayBuffer

View File

@ -2,6 +2,11 @@
#include "WSRequestHandler.h"
/**
* @typedef {Object} `ScenesCollection`
* @property {String} `sc-name` Name of the scene collection
*/
/**
* Change the active scene collection.
*
@ -17,13 +22,22 @@ RpcResponse WSRequestHandler::SetCurrentSceneCollection(const RpcRequest& reques
return request.failed("missing request parameters");
}
QString sceneCollection = obs_data_get_string(request.parameters(), "sc-name");
if (sceneCollection.isEmpty()) {
const char* sceneCollection = obs_data_get_string(request.parameters(), "sc-name");
if (!sceneCollection) {
return request.failed("invalid request parameters");
}
// TODO : Check if specified profile exists and if changing is allowed
obs_frontend_set_current_scene_collection(sceneCollection.toUtf8());
char** collections = obs_frontend_get_scene_collections();
bool collectionExists = Utils::StringInStringList(collections, sceneCollection);
bfree(collections);
if (!collectionExists) {
return request.failed("scene collection does not exist");
}
obs_queue_task(OBS_TASK_UI, [](void* param) {
obs_frontend_set_current_scene_collection(reinterpret_cast<const char*>(param));
}, (void*)sceneCollection, true);
return request.success();
}
@ -50,7 +64,7 @@ RpcResponse WSRequestHandler::GetCurrentSceneCollection(const RpcRequest& reques
/**
* List available scene collections
*
* @return {Array<String>} `scene-collections` Scene collections list
* @return {Array<ScenesCollection>} `scene-collections` Scene collections list
*
* @api requests
* @name ListSceneCollections

View File

@ -2,6 +2,78 @@
#include "WSRequestHandler.h"
/**
* Get a list of all scene items in a scene.
*
* @param {String (optional)} `sceneName` Name of the scene to get the list of scene items from. Defaults to the current scene if not specified.
*
* @return {String} `sceneName` Name of the requested (or current) scene
* @return {Array<Object>} `sceneItems` Array of scene items
* @return {int} `sceneItems.*.itemId` Unique item id of the source item
* @return {String} `sceneItems.*.sourceKind` ID if the scene item's source. For example `vlc_source` or `image_source`
* @return {String} `sceneItems.*.sourceName` Name of the scene item's source
* @return {String} `sceneItems.*.sourceType` Type of the scene item's source. Either `input`, `group`, or `scene`
*
* @api requests
* @name GetSceneItemList
* @category scene items
* @since 4.9.0
*/
RpcResponse WSRequestHandler::GetSceneItemList(const RpcRequest& request) {
const char* sceneName = obs_data_get_string(request.parameters(), "sceneName");
OBSSourceAutoRelease sceneSource;
if (sceneName && strcmp(sceneName, "") != 0) {
sceneSource = obs_get_source_by_name(sceneName);
} else {
sceneSource = obs_frontend_get_current_scene();
}
OBSScene scene = obs_scene_from_source(sceneSource);
if (!scene) {
return request.failed("requested scene is invalid or doesnt exist");
}
OBSDataArrayAutoRelease sceneItemArray = obs_data_array_create();
auto sceneItemEnumProc = [](obs_scene_t *, obs_sceneitem_t* item, void* privateData) -> bool {
obs_data_array_t* sceneItemArray = (obs_data_array_t*)privateData;
OBSDataAutoRelease sceneItemData = obs_data_create();
obs_data_set_int(sceneItemData, "itemId", obs_sceneitem_get_id(item));
OBSSource source = obs_sceneitem_get_source(item);
obs_data_set_string(sceneItemData, "sourceKind", obs_source_get_id(source));
obs_data_set_string(sceneItemData, "sourceName", obs_source_get_name(source));
QString typeString = "";
enum obs_source_type sourceType = obs_source_get_type(source);
switch (sourceType) {
case OBS_SOURCE_TYPE_INPUT:
typeString = "input";
break;
case OBS_SOURCE_TYPE_SCENE:
typeString = "scene";
break;
default:
typeString = "unknown";
break;
}
obs_data_set_string(sceneItemData, "sourceType", typeString.toUtf8());
obs_data_array_push_back(sceneItemArray, sceneItemData);
return true;
};
obs_scene_enum_items(scene, sceneItemEnumProc, sceneItemArray);
OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "sceneName", obs_source_get_name(sceneSource));
obs_data_set_array(response, "sceneItems", sceneItemArray);
return request.success(response);
}
/**
* Gets the scene specific properties of the specified source item.
* Coordinates are relative to the item's parent (the scene or group it belongs to).
@ -13,9 +85,9 @@
*
* @return {String} `name` Scene Item name.
* @return {int} `itemId` Scene Item ID.
* @return {int} `position.x` The x position of the source from the left.
* @return {int} `position.y` The y position of the source from the top.
* @return {int} `position.alignment` The point on the source that the item is manipulated from.
* @return {double} `position.x` The x position of the source from the left.
* @return {double} `position.y` The y position of the source from the top.
* @return {int} `position.alignment` The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis.
* @return {double} `rotation` The clockwise rotation of the item in degrees around the point of alignment.
* @return {double} `scale.x` The x-scale factor of the source.
* @return {double} `scale.y` The y-scale factor of the source.
@ -34,7 +106,6 @@
* @return {int} `sourceHeight` Base source (without scaling) of the source
* @return {double} `width` Scene item width (base source width multiplied by the horizontal scaling factor)
* @return {double} `height` Scene item height (base source height multiplied by the vertical scaling factor)
* @return {int} `alignment` The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis.
* @return {String (optional)} `parentGroupName` Name of the item's parent (if this item belongs to a group)
* @return {Array<SceneItemTransform> (optional)} `groupChildren` List of children (if this item is a group)
*
@ -79,8 +150,8 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request)
* @param {String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).
* @param {String (optional)} `item.name` Scene Item name (if the `item` field is an object)
* @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object)
* @param {int (optional)} `position.x` The new x position of the source.
* @param {int (optional)} `position.y` The new y position of the source.
* @param {double (optional)} `position.x` The new x position of the source.
* @param {double (optional)} `position.y` The new y position of the source.
* @param {int (optional)} `position.alignment` The new alignment of the source.
* @param {double (optional)} `rotation` The new clockwise rotation of the item in degrees.
* @param {double (optional)} `scale.x` The new x scale of the item.
@ -134,10 +205,10 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
vec2 newPosition = oldPosition;
if (obs_data_has_user_value(reqPosition, "x")) {
newPosition.x = obs_data_get_int(reqPosition, "x");
newPosition.x = obs_data_get_double(reqPosition, "x");
}
if (obs_data_has_user_value(reqPosition, "y")) {
newPosition.y = obs_data_get_int(reqPosition, "y");
newPosition.y = obs_data_get_double(reqPosition, "y");
}
if (obs_data_has_user_value(reqPosition, "alignment")) {
@ -322,26 +393,27 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) {
* Show or hide a specified source item in a specified scene.
*
* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the currently active scene.
* @param {String} `source` Scene Item name.
* @param {String (optional)} `source` Scene Item name.
* @param {int (optional)} `item` Scene Item id
* @param {boolean} `render` true = shown ; false = hidden
*
* @api requests
* @name SetSceneItemRender
* @category scene items
* @since 0.3
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
*/
RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) {
if (!request.hasField("source") ||
!request.hasField("render"))
bool doesntHaveSourceOrItemParameter = !(request.hasField("source") || request.hasField("item"));
if (!request.hasField("render") || doesntHaveSourceOrItemParameter)
{
return request.failed("missing request parameters");
}
const char* itemName = obs_data_get_string(request.parameters(), "source");
int64_t itemId = obs_data_get_int(request.parameters(), "item");
bool isVisible = obs_data_get_bool(request.parameters(), "render");
if (!itemName) {
if (!itemName && !itemId) {
return request.failed("invalid request parameters");
}
@ -351,12 +423,19 @@ RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) {
return request.failed("requested scene doesn't exist");
}
OBSSceneItemAutoRelease sceneItem =
Utils::GetSceneItemFromName(scene, itemName);
if (!sceneItem) {
return request.failed("specified scene item doesn't exist");
}
OBSSceneItemAutoRelease sceneItem;
if (strlen(itemName)) {
sceneItem = Utils::GetSceneItemFromName(scene, itemName);
if (!sceneItem) {
return request.failed("specified scene item name doesn't exist");
}
} else {
sceneItem = Utils::GetSceneItemFromId(scene, itemId);
if (!sceneItem) {
return request.failed("specified scene item ID doesn't exist");
}
}
obs_sceneitem_set_visible(sceneItem, isVisible);
return request.success();
}
@ -544,6 +623,59 @@ RpcResponse WSRequestHandler::DeleteSceneItem(const RpcRequest& request) {
return request.success();
}
/**
* Creates a scene item in a scene. In other words, this is how you add a source into a scene.
*
* @param {String} `sceneName` Name of the scene to create the scene item in
* @param {String} `sourceName` Name of the source to be added
* @param {boolean} `setVisible` Whether to make the sceneitem visible on creation or not. Default `true`
*
* @return {int} `itemId` Numerical ID of the created scene item
*
* @api requests
* @name AddSceneItem
* @category scene items
* @since 4.9.0
*/
RpcResponse WSRequestHandler::AddSceneItem(const RpcRequest& request) {
if (!request.hasField("sceneName") || !request.hasField("sourceName")) {
return request.failed("missing request parameters");
}
const char* sceneName = obs_data_get_string(request.parameters(), "sceneName");
OBSSourceAutoRelease sceneSource = obs_get_source_by_name(sceneName);
OBSScene scene = obs_scene_from_source(sceneSource);
if (!scene) {
return request.failed("requested scene is invalid or doesnt exist");
}
const char* sourceName = obs_data_get_string(request.parameters(), "sourceName");
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName);
if (!source) {
return request.failed("requested source does not exist");
}
if (source == sceneSource) {
return request.failed("you cannot add a scene as a sceneitem to itself");
}
Utils::AddSourceData data;
data.source = source;
data.setVisible = true;
if (request.hasField("setVisible")) {
data.setVisible = obs_data_get_bool(request.parameters(), "setVisible");
}
obs_enter_graphics();
obs_scene_atomic_update(scene, Utils::AddSourceHelper, &data);
obs_leave_graphics();
OBSDataAutoRelease responseData = obs_data_create();
obs_data_set_int(responseData, "itemId", obs_sceneitem_get_id(data.sceneItem));
return request.success(responseData);
}
/**
* Duplicates a scene item.
*

View File

@ -79,13 +79,38 @@ RpcResponse WSRequestHandler::GetSceneList(const RpcRequest& request) {
return request.success(data);
}
/**
* Create a new scene scene.
*
* @param {String} `sceneName` Name of the scene to create.
*
* @api requests
* @name CreateScene
* @category scenes
* @since 4.9.0
*/
RpcResponse WSRequestHandler::CreateScene(const RpcRequest& request) {
if (!request.hasField("sceneName")) {
return request.failed("missing request parameters");
}
const char* sceneName = obs_data_get_string(request.parameters(), "sceneName");
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName);
if (source) {
return request.failed("scene with this name already exists");
}
obs_scene_create(sceneName);
return request.success();
}
/**
* Changes the order of scene items in the requested scene.
*
* @param {String (optional)} `scene` Name of the scene to reorder (defaults to current).
* @param {Array<Scene>} `items` Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene
* @param {int (optional)} `items[].id` Id of a specific scene item. Unique on a scene by scene basis.
* @param {String (optional)} `items[].name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene.
* @param {int (optional)} `items.*.id` Id of a specific scene item. Unique on a scene by scene basis.
* @param {String (optional)} `items.*.name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene.
*
* @api requests
* @name ReorderSceneItems
@ -159,7 +184,7 @@ RpcResponse WSRequestHandler::ReorderSceneItems(const RpcRequest& request) {
* @api requests
* @name SetSceneTransitionOverride
* @category scenes
* @since 4.9.0
* @since 4.8.0
*/
RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& request) {
if (!request.hasField("sceneName") || !request.hasField("transitionName")) {
@ -205,7 +230,7 @@ RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& reque
* @api requests
* @name RemoveSceneTransitionOverride
* @category scenes
* @since 4.9.0
* @since 4.8.0
*/
RpcResponse WSRequestHandler::RemoveSceneTransitionOverride(const RpcRequest& request) {
if (!request.hasField("sceneName")) {
@ -241,7 +266,7 @@ RpcResponse WSRequestHandler::RemoveSceneTransitionOverride(const RpcRequest& re
* @api requests
* @name GetSceneTransitionOverride
* @category scenes
* @since 4.9.0
* @since 4.8.0
*/
RpcResponse WSRequestHandler::GetSceneTransitionOverride(const RpcRequest& request) {
if (!request.hasField("sceneName")) {

View File

@ -18,12 +18,81 @@ bool isTextFreeType2Source(const QString& sourceKind)
return (sourceKind == "text_ft2_source" || sourceKind == "text_ft2_source_v2");
}
/**
* Create a source and add it as a sceneitem to a scene.
*
* @param {String} `sourceName` Source name.
* @param {String} `sourceKind` Source kind, Eg. `vlc_source`.
* @param {String} `sceneName` Scene to add the new source to.
* @param {Object (optional)} `sourceSettings` Source settings data.
* @param {boolean (optional)} `setVisible` Set the created SceneItem as visible or not. Defaults to true
*
* @return {int} `itemId` ID of the SceneItem in the scene.
*
* @api requests
* @name CreateSource
* @category sources
* @since 4.9.0
*/
RpcResponse WSRequestHandler::CreateSource(const RpcRequest& request)
{
if (!request.hasField("sourceName") || !request.hasField("sourceKind") || !request.hasField("sceneName")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
QString sourceKind = obs_data_get_string(request.parameters(), "sourceKind");
if (sourceName.isEmpty() || sourceKind.isEmpty()) {
return request.failed("empty sourceKind or sourceName parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (source) {
return request.failed("a source with that name already exists");
}
const char* sceneName = obs_data_get_string(request.parameters(), "sceneName");
OBSSourceAutoRelease sceneSource = obs_get_source_by_name(sceneName);
OBSScene scene = obs_scene_from_source(sceneSource);
if (!scene) {
return request.failed("requested scene is invalid or doesnt exist");
}
OBSDataAutoRelease sourceSettings = nullptr;
if (request.hasField("sourceSettings")) {
sourceSettings = obs_data_get_obj(request.parameters(), "sourceSettings");
}
OBSSourceAutoRelease newSource = obs_source_create(sourceKind.toUtf8(), sourceName.toUtf8(), sourceSettings, nullptr);
if (!newSource) {
return request.failed("failed to create the source");
}
obs_source_set_enabled(newSource, true);
Utils::AddSourceData data;
data.source = newSource;
data.setVisible = true;
if (request.hasField("setVisible")) {
data.setVisible = obs_data_get_bool(request.parameters(), "setVisible");
}
obs_enter_graphics();
obs_scene_atomic_update(scene, Utils::AddSourceHelper, &data);
obs_leave_graphics();
OBSDataAutoRelease responseData = obs_data_create();
obs_data_set_int(responseData, "itemId", obs_sceneitem_get_id(data.sceneItem));
return request.success(responseData);
}
/**
* List all sources available in the running OBS instance
*
* @return {Array<Object>} `sources` Array of sources
* @return {String} `sources.*.name` Unique source name
* @return {String} `sources.*.typeId` Non-unique source internal type (a.k.a type id)
* @return {String} `sources.*.typeId` Non-unique source internal type (a.k.a kind)
* @return {String} `sources.*.type` Source type. Value is one of the following: "input", "filter", "transition", "scene" or "unknown"
*
* @api requests
@ -162,7 +231,7 @@ RpcResponse WSRequestHandler::GetSourceTypesList(const RpcRequest& request)
* @param {boolean (optional)} `useDecibel` Output volume in decibels of attenuation instead of amplitude/mul.
*
* @return {String} `name` Source name.
* @return {double} `volume` Volume of the source. Between `0.0` and `1.0` if using mul, under `0.0` if using dB (since it is attenuating).
* @return {double} `volume` Volume of the source. Between `0.0` and `20.0` if using mul, under `26.0` if using dB.
* @return {boolean} `muted` Indicates whether the source is muted.
*
* @api requests
@ -193,6 +262,10 @@ RpcResponse WSRequestHandler::GetVolume(const RpcRequest& request)
volume = obs_mul_to_db(volume);
}
if (volume == -INFINITY) {
volume = -100.0;
}
OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "name", obs_source_get_name(source));
obs_data_set_double(response, "volume", volume);
@ -204,7 +277,7 @@ RpcResponse WSRequestHandler::GetVolume(const RpcRequest& request)
* Set the volume of the specified source. Default request format uses mul, NOT SLIDER PERCENTAGE.
*
* @param {String} `source` Source name.
* @param {double} `volume` Desired volume. Must be between `0.0` and `1.0` for mul, and under 0.0 for dB. Note: OBS will interpret dB values under -100.0 as Inf.
* @param {double} `volume` Desired volume. Must be between `0.0` and `20.0` for mul, and under 26.0 for dB. OBS will interpret dB values under -100.0 as Inf. Note: The OBS volume sliders only reach a maximum of 1.0mul/0.0dB, however OBS actually supports larger values.
* @param {boolean (optional)} `useDecibel` Interperet `volume` data as decibels instead of amplitude/mul.
*
* @api requests
@ -223,8 +296,8 @@ RpcResponse WSRequestHandler::SetVolume(const RpcRequest& request)
QString sourceName = obs_data_get_string(request.parameters(), "source");
float sourceVolume = obs_data_get_double(request.parameters(), "volume");
bool isNotValidDecibel = (useDecibel && sourceVolume > 0.0);
bool isNotValidMul = (!useDecibel && (sourceVolume < 0.0 || sourceVolume > 1.0));
bool isNotValidDecibel = (useDecibel && sourceVolume > 26.0);
bool isNotValidMul = (!useDecibel && (sourceVolume < 0.0 || sourceVolume > 20.0));
if (sourceName.isEmpty() || isNotValidDecibel || isNotValidMul) {
return request.failed("invalid request parameters");
}
@ -341,10 +414,44 @@ RpcResponse WSRequestHandler::ToggleMute(const RpcRequest& request)
return request.success();
}
/**
* Get the audio's active status of a specified source.
*
* @param {String} `sourceName` Source name.
*
* @return {boolean} `audioActive` Audio active status of the source.
*
* @api requests
* @name GetAudioActive
* @category sources
* @since 4.9.0
*/
RpcResponse WSRequestHandler::GetAudioActive(const RpcRequest& request)
{
if (!request.hasField("sourceName")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
if (sourceName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
OBSDataAutoRelease response = obs_data_create();
obs_data_set_bool(response, "audioActive", obs_source_audio_active(source));
return request.success(response);
}
/**
* Sets (aka rename) the name of a source. Also works with scenes since scenes are technically sources in OBS.
*
* Note: If the new name already exists as a source, OBS will automatically modify the name to not interfere.
* Note: If the new name already exists as a source, obs-websocket will return an error.
*
* @param {String} `sourceName` Source name.
* @param {String} `newName` New source name.
@ -377,7 +484,7 @@ RpcResponse WSRequestHandler::SetSourceName(const RpcRequest& request)
return request.success();
} else {
return request.failed("a source with that newSourceName already exists");
return request.failed("a source with that name already exists");
}
}
@ -473,6 +580,7 @@ RpcResponse WSRequestHandler::GetSourceSettings(const RpcRequest& request)
const char* sourceName = obs_data_get_string(request.parameters(), "sourceName");
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName);
if (!source) {
return request.failed("specified source doesn't exist");
}
@ -533,21 +641,17 @@ RpcResponse WSRequestHandler::SetSourceSettings(const RpcRequest& request)
}
}
OBSDataAutoRelease currentSettings = obs_source_get_settings(source);
OBSDataAutoRelease newSettings = obs_data_get_obj(request.parameters(), "sourceSettings");
OBSDataAutoRelease sourceSettings = obs_data_create();
obs_data_apply(sourceSettings, currentSettings);
obs_data_apply(sourceSettings, newSettings);
obs_source_update(source, sourceSettings);
obs_source_update(source, newSettings);
obs_source_update_properties(source);
OBSDataAutoRelease updatedSettings = obs_source_get_settings(source);
OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "sourceName", obs_source_get_name(source));
obs_data_set_string(response, "sourceType", obs_source_get_id(source));
obs_data_set_obj(response, "sourceSettings", sourceSettings);
obs_data_set_obj(response, "sourceSettings", updatedSettings);
return request.success(response);
}
@ -558,8 +662,8 @@ RpcResponse WSRequestHandler::SetSourceSettings(const RpcRequest& request)
*
* @return {String} `source` Source name.
* @return {String} `align` Text Alignment ("left", "center", "right").
* @return {int} `bk-color` Background color.
* @return {int} `bk-opacity` Background opacity (0-100).
* @return {int} `bk_color` Background color.
* @return {int} `bk_opacity` Background opacity (0-100).
* @return {boolean} `chatlog` Chat log.
* @return {int} `chatlog_lines` Chat log lines.
* @return {int} `color` Text color.
@ -618,8 +722,8 @@ RpcResponse WSRequestHandler::GetTextGDIPlusProperties(const RpcRequest& request
*
* @param {String} `source` Name of the source.
* @param {String (optional)} `align` Text Alignment ("left", "center", "right").
* @param {int (optional)} `bk-color` Background color.
* @param {int (optional)} `bk-opacity` Background opacity (0-100).
* @param {int (optional)} `bk_color` Background color.
* @param {int (optional)} `bk_opacity` Background opacity (0-100).
* @param {boolean (optional)} `chatlog` Chat log.
* @param {int (optional)} `chatlog_lines` Chat log lines.
* @param {int (optional)} `color` Text color.
@ -975,7 +1079,7 @@ RpcResponse WSRequestHandler::SetTextFreetype2Properties(const RpcRequest& reque
* @name GetBrowserSourceProperties
* @category sources
* @since 4.1.0
* @deprecated Since 4.8.0. Prefer the use of GetSourceSettings.
* @deprecated Since 4.8.0. Prefer the use of GetSourceSettings. Will be removed in v5.0.0
*/
RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& request)
{
@ -1017,7 +1121,7 @@ RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& reque
* @api requests
* @name SetBrowserSourceProperties
* @category sources
* @deprecated Since 4.8.0. Prefer the use of SetSourceSettings.
* @deprecated Since 4.8.0. Prefer the use of SetSourceSettings. Will be removed in v5.0.0
* @since 4.1.0
*/
RpcResponse WSRequestHandler::SetBrowserSourceProperties(const RpcRequest& request)
@ -1575,6 +1679,43 @@ RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request)
return request.success();
}
/**
* Get the default settings for a given source type.
*
* @param {String} `sourceKind` Source kind. Also called "source id" in libobs terminology.
*
* @return {String} `sourceKind` Source kind. Same value as the `sourceKind` parameter.
* @return {Object} `defaultSettings` Settings object for source.
*
* @api requests
* @name GetSourceDefaultSettings
* @category sources
* @since 4.9.0
*/
RpcResponse WSRequestHandler::GetSourceDefaultSettings(const RpcRequest& request)
{
if (!request.hasField("sourceKind")) {
return request.failed("missing request parameters");
}
QString sourceKind = obs_data_get_string(request.parameters(), "sourceKind");
if (sourceKind.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSDataAutoRelease defaultData = obs_get_source_defaults(sourceKind.toUtf8());
if (!defaultData) {
return request.failed("invalid sourceKind");
}
OBSDataAutoRelease defaultSettings = Utils::OBSDataGetDefaults(defaultData);
OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "sourceKind", sourceKind.toUtf8().constData());
obs_data_set_obj(response, "defaultSettings", defaultSettings);
return request.success(response);
}
/**
* Takes a picture snapshot of a source and then can either or both:
* - Send it over as a Data URI (base64-encoded data) in the response (by specifying `embedPictureFormat` in the request)
@ -1585,7 +1726,7 @@ RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request)
* Clients can specify `width` and `height` parameters to receive scaled pictures. Aspect ratio is
* preserved if only one of these two parameters is specified.
*
* @param {String} `sourceName` Source name. Note that, since scenes are also sources, you can also provide a scene name.
* @param {String (optional)} `sourceName` Source name. Note: Since scenes are also sources, you can also provide a scene name. If not provided, the currently active scene is used.
* @param {String (optional)} `embedPictureFormat` Format of the Data URI encoded picture. Can be "png", "jpg", "jpeg" or "bmp" (or any other value supported by Qt's Image module)
* @param {String (optional)} `saveToFilePath` Full file path (file extension included) where the captured image is to be saved. Can be in a format different from `pictureFormat`. Can be a relative path.
* @param {String (optional)} `fileFormat` Format to save the image file as (one of the values provided in the `supported-image-export-formats` response field of `GetVersion`). If not specified, tries to guess based on file extension.
@ -1603,19 +1744,20 @@ RpcResponse WSRequestHandler::SetAudioMonitorType(const RpcRequest& request)
* @since 4.6.0
*/
RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) {
if (!request.hasField("sourceName")) {
return request.failed("missing request parameters");
}
if (!request.hasField("embedPictureFormat") && !request.hasField("saveToFilePath")) {
return request.failed("At least 'embedPictureFormat' or 'saveToFilePath' must be specified");
}
OBSSourceAutoRelease source;
if (!request.hasField("sourceName")) {
source = obs_frontend_get_current_scene();
} else {
const char* sourceName = obs_data_get_string(request.parameters(), "sourceName");
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName);
source = obs_get_source_by_name(sourceName);
if (!source) {
return request.failed("specified source doesn't exist");;
}
}
const uint32_t sourceWidth = obs_source_get_base_width(source);
const uint32_t sourceHeight = obs_source_get_base_height(source);
@ -1752,3 +1894,38 @@ RpcResponse WSRequestHandler::TakeSourceScreenshot(const RpcRequest& request) {
obs_data_set_string(response, "sourceName", obs_source_get_name(source));
return request.success(response);
}
/**
* Refreshes the specified browser source.
*
* @param {String} `sourceName` Source name.
*
* @api requests
* @name RefreshBrowserSource
* @category sources
* @since 4.9.0
*/
RpcResponse WSRequestHandler::RefreshBrowserSource(const RpcRequest& request)
{
if (!request.hasField("sourceName")) {
return request.failed("missing request parameters");
}
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
if (!source) {
return request.failed("specified source doesn't exist");
}
if (strcmp(obs_source_get_id(source), "browser_source") != 0) {
return request.failed("specified source is not a browser");
}
obs_properties_t *sourceProperties = obs_source_properties(source);
obs_property_t *property = obs_properties_get(sourceProperties, "refreshnocache");
obs_property_button_clicked(property, source); // This returns a boolean but we ignore it because the browser plugin always returns `false`.
obs_properties_destroy(sourceProperties);
return request.success();
}

View File

@ -11,9 +11,10 @@
*
* @return {boolean} `streaming` Current streaming status.
* @return {boolean} `recording` Current recording status.
* @return {boolean} `recording-paused` If recording is paused.
* @return {boolean} `preview-only` Always false. Retrocompatibility with OBSRemote.
* @return {String (optional)} `stream-timecode` Time elapsed since streaming started (only present if currently streaming).
* @return {String (optional)} `rec-timecode` Time elapsed since recording started (only present if currently recording).
* @return {boolean} `preview-only` Always false. Retrocompatibility with OBSRemote.
*
* @api requests
* @name GetStreamingStatus
@ -43,7 +44,7 @@ RpcResponse WSRequestHandler::GetStreamingStatus(const RpcRequest& request) {
}
/**
* Toggle streaming on or off.
* Toggle streaming on or off (depending on the current stream state).
*
* @api requests
* @name StartStopStreaming
@ -61,7 +62,7 @@ RpcResponse WSRequestHandler::StartStopStreaming(const RpcRequest& request) {
* Start streaming.
* Will return an `error` if streaming is already active.
*
* @param {Object (optional)} `stream` Special stream configuration. Please note: these won't be saved to OBS' configuration.
* @param {Object (optional)} `stream` Special stream configuration. Note: these won't be saved to OBS' configuration.
* @param {String (optional)} `stream.type` If specified ensures the type of stream matches the given type (usually 'rtmp_custom' or 'rtmp_common'). If the currently configured stream type does not match the given stream type, all settings must be specified in the `settings` object or an error will occur when starting the stream.
* @param {Object (optional)} `stream.metadata` Adds the given object parameters as encoded query string parameters to the 'key' of the RTMP stream. Used to pass data to the RTMP service about the streaming. May be any String, Numeric, or Boolean field.
* @param {Object (optional)} `stream.settings` Settings for the stream.
@ -292,7 +293,6 @@ RpcResponse WSRequestHandler::SaveStreamSettings(const RpcRequest& request) {
/**
* Send the provided text as embedded CEA-608 caption data.
* As of OBS Studio 23.1, captions are not yet available on Linux.
*
* @param {String} `text` Captions text
*
@ -301,7 +301,6 @@ RpcResponse WSRequestHandler::SaveStreamSettings(const RpcRequest& request) {
* @category streaming
* @since 4.6.0
*/
#if BUILD_CAPTIONS
RpcResponse WSRequestHandler::SendCaptions(const RpcRequest& request) {
if (!request.hasField("text")) {
return request.failed("missing request parameters");
@ -316,5 +315,4 @@ RpcResponse WSRequestHandler::SendCaptions(const RpcRequest& request) {
return request.success();
}
#endif

View File

@ -166,7 +166,7 @@ RpcResponse WSRequestHandler::DisableStudioMode(const RpcRequest& request) {
}
/**
* Toggles Studio Mode.
* Toggles Studio Mode (depending on the current state of studio mode).
*
* @api requests
* @name ToggleStudioMode

View File

@ -129,7 +129,7 @@ RpcResponse WSRequestHandler::GetTransitionDuration(const RpcRequest& request) {
* @api requests
* @name GetTransitionPosition
* @category transitions
* @since 4.8.0
* @since 4.9.0
*/
RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) {
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
@ -139,3 +139,136 @@ RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) {
return request.success(response);
}
/**
* Get the current settings of a transition
*
* @param {String} `transitionName` Transition name
*
* @return {Object} `transitionSettings` Current transition settings
*
* @api requests
* @name GetTransitionSettings
* @category transitions
* @since 4.9.0
*/
RpcResponse WSRequestHandler::GetTransitionSettings(const RpcRequest& request) {
if (!request.hasField("transitionName")) {
return request.failed("missing request parameters");
}
const char* transitionName = obs_data_get_string(request.parameters(), "transitionName");
OBSSourceAutoRelease transition = Utils::GetTransitionFromName(transitionName);
if (!transition) {
return request.failed("specified transition doesn't exist");
}
OBSDataAutoRelease transitionSettings = obs_source_get_settings(transition);
OBSDataAutoRelease response = obs_data_create();
obs_data_set_obj(response, "transitionSettings", transitionSettings);
return request.success(response);
}
/**
* Change the current settings of a transition
*
* @param {String} `transitionName` Transition name
* @param {Object} `transitionSettings` Transition settings (they can be partial)
*
* @return {Object} `transitionSettings` Updated transition settings
*
* @api requests
* @name SetTransitionSettings
* @category transitions
* @since 4.9.0
*/
RpcResponse WSRequestHandler::SetTransitionSettings(const RpcRequest& request) {
if (!request.hasField("transitionName") || !request.hasField("transitionSettings")) {
return request.failed("missing request parameters");
}
const char* transitionName = obs_data_get_string(request.parameters(), "transitionName");
OBSSourceAutoRelease transition = Utils::GetTransitionFromName(transitionName);
if (!transition) {
return request.failed("specified transition doesn't exist");
}
OBSDataAutoRelease newSettings = obs_data_get_obj(request.parameters(), "transitionSettings");
obs_source_update(transition, newSettings);
obs_source_update_properties(transition);
OBSDataAutoRelease updatedSettings = obs_source_get_settings(transition);
OBSDataAutoRelease response = obs_data_create();
obs_data_set_obj(response, "transitionSettings", updatedSettings);
return request.success(response);
}
/**
* Release the T-Bar (like a user releasing their mouse button after moving it).
* *YOU MUST CALL THIS if you called `SetTBarPosition` with the `release` parameter set to `false`.*
*
* @api requests
* @name ReleaseTBar
* @category transitions
* @since 4.9.0
*/
RpcResponse WSRequestHandler::ReleaseTBar(const RpcRequest& request) {
if (!obs_frontend_preview_program_mode_active()) {
return request.failed("studio mode not enabled");
}
if (obs_transition_fixed(obs_frontend_get_current_transition())) {
return request.failed("current transition doesn't support t-bar control");
}
obs_frontend_release_tbar();
return request.success();
}
/**
* Set the manual position of the T-Bar (in Studio Mode) to the specified value. Will return an error if OBS is not in studio mode
* or if the current transition doesn't support T-Bar control.
*
* If your code needs to perform multiple successive T-Bar moves (e.g. : in an animation, or in response to a user moving a T-Bar control in your User Interface), set `release` to false and call `ReleaseTBar` later once the animation/interaction is over.
*
* @param {double} `position` T-Bar position. This value must be between 0.0 and 1.0.
* @param {boolean (optional)} `release` Whether or not the T-Bar gets released automatically after setting its new position (like a user releasing their mouse button after moving the T-Bar). Call `ReleaseTBar` manually if you set `release` to false. Defaults to true.
*
* @api requests
* @name SetTBarPosition
* @category transitions
* @since 4.9.0
*/
RpcResponse WSRequestHandler::SetTBarPosition(const RpcRequest& request) {
if (!obs_frontend_preview_program_mode_active()) {
return request.failed("studio mode not enabled");
}
if (obs_transition_fixed(obs_frontend_get_current_transition())) {
return request.failed("current transition doesn't support t-bar control");
}
if (!request.hasField("position")) {
return request.failed("missing request parameters");
}
double position = obs_data_get_double(request.parameters(), "position");
if (position < 0.0 || position > 1.0) {
return request.failed("position is out of range");
}
bool release = true;
if (request.hasField("release")) {
release = obs_data_get_bool(request.parameters(), "release");
}
obs_frontend_set_tbar_position((int)((float)position * 1024.0));
if (release) {
obs_frontend_release_tbar();
}
return request.success();
}

View File

@ -60,10 +60,10 @@ WSServer::~WSServer()
stop();
}
void WSServer::start(quint16 port)
void WSServer::start(quint16 port, bool lockToIPv4)
{
if (_server.is_listening() && port == _serverPort) {
blog(LOG_INFO, "WSServer::start: server already on this port. no restart needed");
if (_server.is_listening() && (port == _serverPort && _lockToIPv4 == lockToIPv4)) {
blog(LOG_INFO, "WSServer::start: server already on this port and protocol mode. no restart needed");
return;
}
@ -74,9 +74,16 @@ void WSServer::start(quint16 port)
_server.reset();
_serverPort = port;
_lockToIPv4 = lockToIPv4;
websocketpp::lib::error_code errorCode;
if (lockToIPv4) {
blog(LOG_INFO, "WSServer::start: Locked to IPv4 bindings");
_server.listen(websocketpp::lib::asio::ip::tcp::v4(), _serverPort, errorCode);
} else {
blog(LOG_INFO, "WSServer::start: Not locked to IPv4 bindings");
_server.listen(_serverPort, errorCode);
}
if (errorCode) {
std::string errorCodeMessage = errorCode.message();
@ -114,13 +121,11 @@ void WSServer::stop()
for (connection_hdl hdl : _connections) {
_server.close(hdl, websocketpp::close::status::going_away, "Server stopping");
}
_connections.clear();
_connectionProperties.clear();
_threadPool.waitForDone();
while (!_server.stopped()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
while (_connections.size() > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
blog(LOG_INFO, "server stopped successfully");
@ -128,8 +133,7 @@ void WSServer::stop()
void WSServer::broadcast(const RpcEvent& event)
{
OBSRemoteProtocol protocol;
std::string message = protocol.encodeEvent(event);
std::string message = OBSRemoteProtocol::encodeEvent(event);
if (GetConfig()->DebugEnabled) {
blog(LOG_INFO, "Update << '%s'", message.c_str());
@ -185,8 +189,7 @@ void WSServer::onMessage(connection_hdl hdl, server::message_ptr message)
}
WSRequestHandler requestHandler(connProperties);
OBSRemoteProtocol protocol;
std::string response = protocol.processMessage(requestHandler, payload);
std::string response = OBSRemoteProtocol::processMessage(requestHandler, payload);
if (GetConfig()->DebugEnabled) {
blog(LOG_INFO, "Response << '%s'", response.c_str());

View File

@ -44,7 +44,7 @@ Q_OBJECT
public:
explicit WSServer();
virtual ~WSServer();
void start(quint16 port);
void start(quint16 port, bool lockToIPv4);
void stop();
void broadcast(const RpcEvent& event);
QThreadPool* threadPool() {
@ -62,6 +62,7 @@ private:
server _server;
quint16 _serverPort;
bool _lockToIPv4;
std::set<connection_hdl, std::owner_less<connection_hdl>> _connections;
std::map<connection_hdl, ConnectionProperties, std::owner_less<connection_hdl>> _connectionProperties;
QMutex _clMutex;

View File

@ -16,12 +16,16 @@ 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 "settings-dialog.h"
#include <obs-frontend-api.h>
#include <obs-module.h>
#include <QtWidgets/QMessageBox>
#include "../obs-websocket.h"
#include "../Config.h"
#include "../WSServer.h"
#include "settings-dialog.h"
#define CHANGE_ME "changeme"
@ -35,9 +39,6 @@ SettingsDialog::SettingsDialog(QWidget* parent) :
this, &SettingsDialog::AuthCheckboxChanged);
connect(ui->buttonBox, &QDialogButtonBox::accepted,
this, &SettingsDialog::FormAccepted);
AuthCheckboxChanged();
}
void SettingsDialog::showEvent(QShowEvent* event) {
@ -45,12 +46,17 @@ void SettingsDialog::showEvent(QShowEvent* event) {
ui->serverEnabled->setChecked(conf->ServerEnabled);
ui->serverPort->setValue(conf->ServerPort);
ui->lockToIPv4->setChecked(conf->LockToIPv4);
ui->debugEnabled->setChecked(conf->DebugEnabled);
ui->alertsEnabled->setChecked(conf->AlertsEnabled);
ui->authRequired->blockSignals(true);
ui->authRequired->setChecked(conf->AuthRequired);
ui->authRequired->blockSignals(false);
ui->password->setText(CHANGE_ME);
ui->password->setEnabled(ui->authRequired->isChecked());
}
void SettingsDialog::ToggleShowHide() {
@ -60,11 +66,30 @@ void SettingsDialog::ToggleShowHide() {
setVisible(false);
}
void SettingsDialog::AuthCheckboxChanged() {
if (ui->authRequired->isChecked())
void SettingsDialog::PreparePasswordEntry() {
ui->authRequired->blockSignals(true);
ui->authRequired->setChecked(true);
ui->authRequired->blockSignals(false);
ui->password->setEnabled(true);
else
ui->password->setFocus();
}
void SettingsDialog::AuthCheckboxChanged() {
if (ui->authRequired->isChecked()) {
ui->password->setEnabled(true);
}
else {
obs_frontend_push_ui_translation(obs_module_get_string);
QString authDisabledWarning = QObject::tr("OBSWebsocket.Settings.AuthDisabledWarning");
obs_frontend_pop_ui_translation();
QMessageBox::StandardButton response = QMessageBox::question(this, "obs-websocket", authDisabledWarning);
if (response == QMessageBox::Yes) {
ui->password->setEnabled(false);
} else {
ui->authRequired->setChecked(true);
}
}
}
void SettingsDialog::FormAccepted() {
@ -72,6 +97,7 @@ void SettingsDialog::FormAccepted() {
conf->ServerEnabled = ui->serverEnabled->isChecked();
conf->ServerPort = ui->serverPort->value();
conf->LockToIPv4 = ui->lockToIPv4->isChecked();
conf->DebugEnabled = ui->debugEnabled->isChecked();
conf->AlertsEnabled = ui->alertsEnabled->isChecked();
@ -95,7 +121,7 @@ void SettingsDialog::FormAccepted() {
auto server = GetServer();
if (conf->ServerEnabled) {
server->start(conf->ServerPort);
server->start(conf->ServerPort, conf->LockToIPv4);
} else {
server->stop();
}

View File

@ -31,6 +31,7 @@ public:
~SettingsDialog();
void showEvent(QShowEvent* event);
void ToggleShowHide();
void PreparePasswordEntry();
private Q_SLOTS:
void AuthCheckboxChanged();

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>407</width>
<height>195</height>
<height>216</height>
</rect>
</property>
<property name="sizePolicy">
@ -79,7 +79,7 @@
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<widget class="QCheckBox" name="alertsEnabled">
<property name="text">
<string>OBSWebsocket.Settings.AlertsEnable</string>
@ -89,7 +89,7 @@
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QCheckBox" name="debugEnabled">
<property name="text">
<string>OBSWebsocket.Settings.DebugEnable</string>
@ -99,6 +99,13 @@
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="lockToIPv4">
<property name="text">
<string>OBSWebsocket.Settings.LockToIPv4</string>
</property>
</widget>
</item>
</layout>
</item>
<item>

View File

@ -47,6 +47,7 @@ OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
ConfigPtr _config;
WSServerPtr _server;
WSEventsPtr _eventsSystem;
SettingsDialog* settingsDialog = nullptr;
bool obs_module_load(void) {
blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION);
@ -64,14 +65,14 @@ bool obs_module_load(void) {
// UI setup
obs_frontend_push_ui_translation(obs_module_get_string);
QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window();
SettingsDialog* settingsDialog = new SettingsDialog(mainWindow);
settingsDialog = new SettingsDialog(mainWindow);
obs_frontend_pop_ui_translation();
const char* menuActionText =
obs_module_text("OBSWebsocket.Settings.DialogTitle");
QAction* menuAction =
(QAction*)obs_frontend_add_tools_menu_qaction(menuActionText);
QObject::connect(menuAction, &QAction::triggered, [settingsDialog] {
QObject::connect(menuAction, &QAction::triggered, [] {
// The settings dialog belongs to the main window. Should be ok
// to pass the pointer to this QAction belonging to the main window
settingsDialog->ToggleShowHide();
@ -81,7 +82,7 @@ bool obs_module_load(void) {
auto eventCallback = [](enum obs_frontend_event event, void *param) {
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
if (_config->ServerEnabled) {
_server->start(_config->ServerPort);
_server->start(_config->ServerPort, _config->LockToIPv4);
}
obs_frontend_remove_event_callback((obs_frontend_event_cb)param, nullptr);
}
@ -115,3 +116,10 @@ WSServerPtr GetServer() {
WSEventsPtr GetEventsSystem() {
return _eventsSystem;
}
void ShowPasswordSetting() {
if (settingsDialog) {
settingsDialog->PreparePasswordEntry();
settingsDialog->setVisible(true);
}
}

View File

@ -55,7 +55,8 @@ typedef std::shared_ptr<WSEvents> WSEventsPtr;
ConfigPtr GetConfig();
WSServerPtr GetServer();
WSEventsPtr GetEventsSystem();
void ShowPasswordSetting();
#define OBS_WEBSOCKET_VERSION "4.8.0"
#define OBS_WEBSOCKET_VERSION "4.9.0"
#define blog(level, msg, ...) blog(level, "[obs-websocket] " msg, ##__VA_ARGS__)

View File

@ -31,11 +31,15 @@ std::string OBSRemoteProtocol::processMessage(WSRequestHandler& requestHandler,
OBSDataAutoRelease data = obs_data_create_from_json(msg);
if (!data) {
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
return errorResponse(QString::Null(), "invalid JSON payload");
return jsonDataToString(
errorResponse(nullptr, "invalid JSON payload")
);
}
if (!obs_data_has_user_value(data, "request-type") || !obs_data_has_user_value(data, "message-id")) {
return errorResponse(QString::Null(), "missing request parameters");
return jsonDataToString(
errorResponse(nullptr, "missing request parameters")
);
}
QString methodName = obs_data_get_string(data, "request-type");
@ -49,15 +53,8 @@ std::string OBSRemoteProtocol::processMessage(WSRequestHandler& requestHandler,
RpcRequest request(messageId, methodName, params);
RpcResponse response = requestHandler.processRequest(request);
OBSData additionalFields = response.additionalFields();
switch (response.status()) {
case RpcResponse::Status::Ok:
return successResponse(messageId, additionalFields);
case RpcResponse::Status::Error:
return errorResponse(messageId, response.errorMessage(), additionalFields);
}
return std::string();
OBSDataAutoRelease responseData = rpcResponseToJsonData(response);
return jsonDataToString(responseData);
}
std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event)
@ -87,33 +84,56 @@ std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event)
return std::string(obs_data_get_json(eventData));
}
std::string OBSRemoteProtocol::buildResponse(QString messageId, QString status, obs_data_t* fields)
obs_data_t* OBSRemoteProtocol::rpcResponseToJsonData(const RpcResponse& response)
{
OBSDataAutoRelease response = obs_data_create();
if (!messageId.isNull()) {
obs_data_set_string(response, "message-id", messageId.toUtf8().constData());
}
obs_data_set_string(response, "status", status.toUtf8().constData());
QByteArray messageIdBytes = response.messageId().toUtf8();
const char* messageId = messageIdBytes.constData();
if (fields) {
obs_data_apply(response, fields);
OBSData additionalFields = response.additionalFields();
switch (response.status()) {
case RpcResponse::Status::Ok:
return successResponse(messageId, additionalFields);
case RpcResponse::Status::Error:
return errorResponse(messageId, response.errorMessage().toUtf8().constData(), additionalFields);
default:
assert(false);
}
std::string responseString = obs_data_get_json(response);
return responseString;
return nullptr;
}
std::string OBSRemoteProtocol::successResponse(QString messageId, obs_data_t* fields)
obs_data_t* OBSRemoteProtocol::successResponse(const char* messageId, obs_data_t* fields)
{
return buildResponse(messageId, "ok", fields);
}
std::string OBSRemoteProtocol::errorResponse(QString messageId, QString errorMessage, obs_data_t* additionalFields)
obs_data_t* OBSRemoteProtocol::errorResponse(const char* messageId, const char* errorMessage, obs_data_t* additionalFields)
{
OBSDataAutoRelease fields = obs_data_create();
if (additionalFields) {
obs_data_apply(fields, additionalFields);
}
obs_data_set_string(fields, "error", errorMessage.toUtf8().constData());
obs_data_set_string(fields, "error", errorMessage);
return buildResponse(messageId, "error", fields);
}
obs_data_t* OBSRemoteProtocol::buildResponse(const char* messageId, const char* status, obs_data_t* fields)
{
obs_data_t* response = obs_data_create();
if (messageId) {
obs_data_set_string(response, "message-id", messageId);
}
obs_data_set_string(response, "status", status);
if (fields) {
obs_data_apply(response, fields);
}
return response;
}
std::string OBSRemoteProtocol::jsonDataToString(OBSDataAutoRelease data)
{
std::string responseString = obs_data_get_json(data);
return responseString;
}

View File

@ -20,7 +20,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <string>
#include <obs-data.h>
#include <QtCore/QString>
#include "../rpc/RpcResponse.h"
class WSRequestHandler;
class RpcEvent;
@ -28,11 +29,13 @@ class RpcEvent;
class OBSRemoteProtocol
{
public:
std::string processMessage(WSRequestHandler& requestHandler, std::string message);
std::string encodeEvent(const RpcEvent& event);
static std::string processMessage(WSRequestHandler& requestHandler, std::string message);
static std::string encodeEvent(const RpcEvent& event);
static obs_data_t* rpcResponseToJsonData(const RpcResponse& response);
private:
std::string buildResponse(QString messageId, QString status, obs_data_t* fields = nullptr);
std::string successResponse(QString messageId, obs_data_t* fields = nullptr);
std::string errorResponse(QString messageId, QString errorMessage, obs_data_t* additionalFields = nullptr);
static obs_data_t* successResponse(const char* messageId, obs_data_t* fields = nullptr);
static obs_data_t* errorResponse(const char* messageId, const char* errorMessage, obs_data_t* additionalFields = nullptr);
static obs_data_t* buildResponse(const char* messageId, const char*, obs_data_t* fields = nullptr);
static std::string jsonDataToString(OBSDataAutoRelease data);
};

View File

@ -36,7 +36,7 @@ public:
obs_data_t* additionalFields = nullptr
);
Status status() {
const Status status() const {
return _status;
}