mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Compare commits
447 Commits
Author | SHA1 | Date | |
---|---|---|---|
9a8f248a75 | |||
6845cda146 | |||
4488b2b7a5 | |||
7dd8cb5d8d | |||
ebf631a15f | |||
3770b75a78 | |||
5d81b61325 | |||
a8e5171b40 | |||
a3e0abb6ce | |||
c51f20eb99 | |||
be897c4df2 | |||
11b948fb69 | |||
070660848b | |||
61af0ec9c6 | |||
897f115363 | |||
b8fcf0355c | |||
5bdff87e3f | |||
31991a3567 | |||
25c369d422 | |||
8d396b1518 | |||
ba327b746a | |||
7d11f912c9 | |||
919cdfb5a9 | |||
da9dd6f775 | |||
8b841f026e | |||
5342b39640 | |||
fc08add504 | |||
20379ac61e | |||
af40aa59ab | |||
f9afc5597a | |||
40178e4661 | |||
ad56abd6f5 | |||
9073a09d84 | |||
2a4e6fce3f | |||
0a3a407217 | |||
9ca9cf80df | |||
6c881a5da7 | |||
5322acd58c | |||
ce489e53c6 | |||
0c95e509dc | |||
46a006ed54 | |||
0cc9378d05 | |||
f3edb2ee68 | |||
a539f23194 | |||
6b6dec79c5 | |||
d7f5c042bf | |||
2fe9812b26 | |||
1c15c3c7a3 | |||
1ee5857139 | |||
2f9c2b0d65 | |||
6e5161f43b | |||
d492d74a64 | |||
26506731c5 | |||
b0c03d4f47 | |||
3bc8348c32 | |||
a034fbba83 | |||
73b5261c49 | |||
cee0cbebc2 | |||
9723147429 | |||
dca385ae87 | |||
71f792944c | |||
6041c4acd2 | |||
0c7529705e | |||
63f1ea1cec | |||
4bbaa75f41 | |||
5fe9314b74 | |||
bfd26493ab | |||
5d74d5d03e | |||
3142e097a5 | |||
b25d8d8201 | |||
7e68cd016a | |||
9e4fc9eb73 | |||
fb0d13a171 | |||
f097b36c2e | |||
57ce3cf80b | |||
c1da62391f | |||
d8882ce979 | |||
9c1871494e | |||
ac26c9114a | |||
fb0a57003c | |||
cda22233a6 | |||
add307577d | |||
4af46ac2b9 | |||
8d39752bda | |||
e3ff9c013e | |||
635870ba4b | |||
a827afb05b | |||
0195e13bdc | |||
76d8b688fd | |||
b4857e2c79 | |||
ac2ae90e8a | |||
0b5cb76b5d | |||
6eed9606ae | |||
29679ff94c | |||
65a1f8b746 | |||
48182d35bd | |||
b526ed47be | |||
2225dec2a9 | |||
011458d642 | |||
e32fff3f61 | |||
0b42e3d0f3 | |||
2b746d1353 | |||
6fed6f9f7d | |||
480945073a | |||
ef6df94838 | |||
fb05848426 | |||
e5be6b96fb | |||
2d706a245a | |||
1abc4f491b | |||
b21a45b43e | |||
585f8450e1 | |||
d3401db5fe | |||
45dc6447bf | |||
409c270ba2 | |||
aed0234d47 | |||
07ebbe56e6 | |||
95bbeb103e | |||
bfb5570b7a | |||
1e19cf7ccc | |||
0d8999d64f | |||
91fde777cf | |||
061ab12b10 | |||
c516e87180 | |||
de6bfdca0a | |||
788f70d5f3 | |||
b0a35789f6 | |||
eb7fbf011f | |||
50bb6e7204 | |||
15610bb296 | |||
ae0ffdc4d5 | |||
033a6929c3 | |||
7c8292a88d | |||
ab5dad7d91 | |||
0744d215c6 | |||
149ddfa8f8 | |||
3830870a83 | |||
b94be080f6 | |||
83382d8dcf | |||
f815228677 | |||
d7d8d23de7 | |||
5834c6ed54 | |||
b0512b3ba7 | |||
d3a7a6ef55 | |||
99fad5bc83 | |||
a54171e1cd | |||
84629f6e63 | |||
b18d597bdc | |||
9125dc4cce | |||
b0e3ea8765 | |||
7c457546f1 | |||
83bef1a840 | |||
b4d89d5666 | |||
3ba8a77d9a | |||
82c31dc47a | |||
7fce694577 | |||
e352d9750d | |||
3d9eac8e6d | |||
6c85b33539 | |||
d979ada4fe | |||
faeeae17d1 | |||
82d1a2b4f9 | |||
cac3496145 | |||
53bfa077ae | |||
0b9366fa28 | |||
c95b709f39 | |||
5745da5466 | |||
ef853e34d6 | |||
7177913d9b | |||
86b6ddb625 | |||
53b9a46feb | |||
d46c1f8687 | |||
c92477b457 | |||
059244bb32 | |||
f10c0d2558 | |||
f5ae89500f | |||
2e3a24d32e | |||
f955d81c5e | |||
4f1fa3bc32 | |||
8305d907e3 | |||
ab73d61178 | |||
8f88d1b7c4 | |||
c5cdbc48a9 | |||
fe00f31147 | |||
79ddb43ac3 | |||
89f95f6459 | |||
87c13b6f26 | |||
d16ea653fe | |||
51dc7fceb0 | |||
bc1c6f7b9a | |||
3fdf77d29b | |||
0b97502029 | |||
cba2bf5cb8 | |||
d8b37328a1 | |||
bb9cf83744 | |||
56fc6ae47c | |||
0cf17cf3db | |||
1bd3297055 | |||
6a10662bc4 | |||
2d84ad8963 | |||
1ec69cbc0d | |||
0057744e57 | |||
45892b4347 | |||
3d41e8882a | |||
436b8216ec | |||
03ba2a680e | |||
2821962d12 | |||
43d38a2e63 | |||
ea28d217e0 | |||
89fe6d57c9 | |||
1d0e1143bf | |||
1a99353559 | |||
ad97d04e37 | |||
25c3abe873 | |||
8eeea50ede | |||
0ded11c6a2 | |||
b6c542212d | |||
182bde4884 | |||
eb84f677cf | |||
3ecb637e70 | |||
bfc826b898 | |||
efc6d4c15a | |||
a96da35f11 | |||
2bff3798af | |||
0fb50a3273 | |||
1c17eee125 | |||
14a43a9cd2 | |||
7bbc7366f2 | |||
879bedd781 | |||
8b79bfab4f | |||
f022e09938 | |||
a63b6a0e3d | |||
dd1facec06 | |||
a46b5716be | |||
e96b7bfd62 | |||
a60c1c1365 | |||
4c39fcd614 | |||
e6b341f2a0 | |||
bdf9e76a6b | |||
e0fc395fbe | |||
107c03653d | |||
a5058cf951 | |||
ed2726c9d2 | |||
6b31ff7e79 | |||
86d3925bf4 | |||
8c2cde4c13 | |||
2d6e34ee6d | |||
adb5577b01 | |||
84ef3f223d | |||
fa50008f3d | |||
6a5537f90f | |||
e0d33a9683 | |||
4092c9a473 | |||
fcaac3d515 | |||
e86d1aad02 | |||
2c94b4c332 | |||
2b6933f6e2 | |||
2e2a7f073e | |||
515c7852ba | |||
5577277944 | |||
ed9e4ff168 | |||
d10915c7a8 | |||
40763fc15d | |||
88d3271c40 | |||
21940c922d | |||
93adc8587d | |||
1907f8d1d1 | |||
ca8848827e | |||
3a7473ba91 | |||
a99da27ff6 | |||
c476649f3e | |||
062473d6f4 | |||
d0ed43a8e5 | |||
701098d19d | |||
c33a0ab439 | |||
b8af848d3a | |||
3dd7fe5d4a | |||
81ab199308 | |||
92938d2c35 | |||
c914632663 | |||
c95104fada | |||
2209fe30e2 | |||
979e0ddc88 | |||
9fab714674 | |||
2eb6463ab0 | |||
5e4d6fbd3f | |||
30df5f3558 | |||
252dd7e09f | |||
2915690d22 | |||
1993596232 | |||
8946e8997f | |||
dfeb156da9 | |||
c10c35e38e | |||
67bfdde113 | |||
c62178a7fe | |||
c12a4323e7 | |||
fd1c4abad7 | |||
bd4fe5a1a7 | |||
190f5ebfc4 | |||
79493df32e | |||
71d523437e | |||
268f503875 | |||
5ac47b823f | |||
ec572da822 | |||
37f96b8cf2 | |||
545db60b98 | |||
f65fdcdbc1 | |||
579acabe5e | |||
f2a9ff8551 | |||
2e40e07563 | |||
cb3af837c6 | |||
3cce89ea3f | |||
5f2dfb24ca | |||
4bc02a7389 | |||
eeeca8afd0 | |||
4f607df5fc | |||
e06b3e2052 | |||
b14b18e4be | |||
fb616b4b53 | |||
a1c5bc00bc | |||
0921632f87 | |||
38ad465233 | |||
881de01073 | |||
2d973e0b90 | |||
e9b43b9b2a | |||
b9193989b0 | |||
be6d9791f5 | |||
baac1b1d80 | |||
40e2d410dd | |||
11617eea99 | |||
5748c4d0ec | |||
2e5b903eae | |||
9405b17e14 | |||
7d1f0e2a69 | |||
c245c24752 | |||
5b0410a207 | |||
62e4c42aa6 | |||
fe1b14ff57 | |||
c074088f2f | |||
0391280c13 | |||
4f98b9e41b | |||
a8de9ac472 | |||
ec7f3fa057 | |||
5cfefd8b15 | |||
7e6b53311d | |||
16bc68f2f9 | |||
974d6b48b2 | |||
db2b1e2dc7 | |||
98656b5d2f | |||
3c7570d814 | |||
fc3e30a826 | |||
2f0476b43c | |||
e310c7d744 | |||
8a649b89c8 | |||
95f52987ef | |||
82b8c66d51 | |||
83fb1843ee | |||
58b10069ab | |||
9cda739672 | |||
276bba050b | |||
147e49b362 | |||
bc338c1f4a | |||
e8fbb18a71 | |||
682c349831 | |||
14b311f6ab | |||
8a40f355c8 | |||
ae2f90c5c2 | |||
7aff773e2c | |||
0cdfa6e7f6 | |||
fc637eef6d | |||
b4c3141170 | |||
41257f7af5 | |||
b204f3ec90 | |||
b4926b3535 | |||
77d63e9848 | |||
94dcd58c2e | |||
689ce16f1b | |||
edc64b8336 | |||
03db5bfd8d | |||
9ad340ab02 | |||
962e26040d | |||
b07884c1da | |||
bad0fb62ed | |||
28e522ce97 | |||
c206cdfa4c | |||
c31ec077f5 | |||
afc6a60746 | |||
0a495b67e6 | |||
37ea7073d5 | |||
feaeef5a70 | |||
5586670d38 | |||
65a9139ffe | |||
3d76f078cd | |||
a1de1b11bc | |||
b5a3e3a4f0 | |||
d7b0ad4916 | |||
2a80a6b217 | |||
9df72f54d5 | |||
ef75ca36c9 | |||
84c0b698f5 | |||
3d9a4ef1e6 | |||
cf51fdceef | |||
85a52ab01f | |||
f2792c0b40 | |||
97109087a4 | |||
953f07f21f | |||
03f1035690 | |||
a561c60f7e | |||
7963b328f9 | |||
3b7e216409 | |||
9ae43a6f75 | |||
6b86de1fb9 | |||
4e6d4ac437 | |||
3b197651cc | |||
c675f1c20c | |||
e87955d59a | |||
c718d8d803 | |||
454a68d1b7 | |||
45f6f74cbe | |||
cb7412a457 | |||
a9fc82365c | |||
edc0fed9e2 | |||
1c718963ea | |||
cd40ccdb9d | |||
80e1dc2446 | |||
d03c4cc4b9 | |||
7bd434e755 | |||
640bcb90c6 | |||
08e86a1378 | |||
fefcc3937a | |||
25210dfa52 | |||
56fbb7b9cf | |||
c55d33b956 | |||
0c5bce101e | |||
0a50e2a95c | |||
5ad940924b | |||
6eb49930bf | |||
3a0d5fb190 | |||
8c2eee2e25 | |||
2e19c5f08a | |||
548b53437f | |||
e1ca9a8029 | |||
389ef2aea9 | |||
357691bad5 | |||
beadb56b05 | |||
745fb5ea29 | |||
58434cac3b | |||
c34dff17ff |
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[*]
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{c,cpp,h,hpp}]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -5,5 +5,5 @@
|
|||||||
/build64/
|
/build64/
|
||||||
/release/
|
/release/
|
||||||
/installer/Output/
|
/installer/Output/
|
||||||
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
9
.gitmodules
vendored
9
.gitmodules
vendored
@ -1,3 +1,6 @@
|
|||||||
[submodule "deps/mbedtls"]
|
[submodule "deps/websocketpp"]
|
||||||
path = deps/mbedtls
|
path = deps/websocketpp
|
||||||
url = https://github.com/ARMmbed/mbedtls
|
url = https://github.com/zaphoyd/websocketpp.git
|
||||||
|
[submodule "deps/asio"]
|
||||||
|
path = deps/asio
|
||||||
|
url = https://github.com/chriskohlhoff/asio.git
|
||||||
|
22
.travis.yml
22
.travis.yml
@ -32,14 +32,6 @@ matrix:
|
|||||||
after_success:
|
after_success:
|
||||||
- docker exec -it xenial /root/obs-websocket/CI/package-xenial.sh
|
- docker exec -it xenial /root/obs-websocket/CI/package-xenial.sh
|
||||||
|
|
||||||
- os: osx
|
|
||||||
env: _macos_build
|
|
||||||
osx_image: xcode8.3
|
|
||||||
before_install: "./CI/install-dependencies-macos.sh"
|
|
||||||
script: "./CI/build-macos.sh"
|
|
||||||
after_success:
|
|
||||||
- ./CI/package-macos.sh
|
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
- provider: s3
|
- provider: s3
|
||||||
region: eu-central-1
|
region: eu-central-1
|
||||||
@ -51,19 +43,7 @@ deploy:
|
|||||||
acl: public_read
|
acl: public_read
|
||||||
on:
|
on:
|
||||||
repo: Palakis/obs-websocket
|
repo: Palakis/obs-websocket
|
||||||
condition:
|
condition:
|
||||||
- "$TRAVIS_OS_NAME = linux"
|
- "$TRAVIS_OS_NAME = linux"
|
||||||
- "-d /home/travis/package"
|
- "-d /home/travis/package"
|
||||||
all_branches: true
|
all_branches: true
|
||||||
- provider: s3
|
|
||||||
region: eu-central-1
|
|
||||||
bucket: obs-websocket-osx-builds
|
|
||||||
access_key_id: "$AWS_ID"
|
|
||||||
secret_access_key: "$AWS_SECRET"
|
|
||||||
local_dir: release
|
|
||||||
skip_cleanup: true
|
|
||||||
acl: public_read
|
|
||||||
on:
|
|
||||||
repo: Palakis/obs-websocket
|
|
||||||
condition: "$TRAVIS_OS_NAME = osx"
|
|
||||||
all_branches: true
|
|
||||||
|
42
BUILDING.md
42
BUILDING.md
@ -1,18 +1,26 @@
|
|||||||
# Compiling obs-websocket
|
# Compiling obs-websocket
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
You'll need [QT 5.9.0](https://download.qt.io/official_releases/qt/5.7/5.7.0/), CMake, and a working development environment for OBS Studio installed on your computer.
|
|
||||||
|
You'll need [Qt 5.10.x](https://download.qt.io/official_releases/qt/5.10/),
|
||||||
|
[CMake](https://cmake.org/download/) and a working [OBS Studio development environment](https://obsproject.com/wiki/install-instructions) installed on your
|
||||||
|
computer.
|
||||||
|
|
||||||
## Windows
|
## Windows
|
||||||
|
|
||||||
In cmake-gui, you'll have to set the following variables :
|
In cmake-gui, you'll have to set the following variables :
|
||||||
|
|
||||||
- **QTDIR** (path) : location of the Qt environment suited for your compiler and architecture
|
- **QTDIR** (path) : location of the Qt environment suited for your compiler and architecture
|
||||||
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source code of OBS Studio
|
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source code of OBS Studio
|
||||||
- **LIBOBS_LIB** (filepath) : location of the obs.lib file
|
- **LIBOBS_LIB** (filepath) : location of the obs.lib file
|
||||||
- **OBS_FRONTEND_LIB** (filepath) : location of the obs-frontend-api.lib file
|
- **OBS_FRONTEND_LIB** (filepath) : location of the obs-frontend-api.lib file
|
||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
On Debian/Ubuntu :
|
|
||||||
```
|
On Debian/Ubuntu :
|
||||||
sudo apt-get install libqt5websockets5-dev
|
|
||||||
|
```shell
|
||||||
|
sudo apt-get install libboost-all-dev
|
||||||
git clone --recursive https://github.com/Palakis/obs-websocket.git
|
git clone --recursive https://github.com/Palakis/obs-websocket.git
|
||||||
cd obs-websocket
|
cd obs-websocket
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
@ -22,17 +30,33 @@ sudo make install
|
|||||||
```
|
```
|
||||||
|
|
||||||
## OS X
|
## OS X
|
||||||
Use of the Travis 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-dependencies-macos.sh` will install it for you.
|
|
||||||
Of course, you're encouraged to dig through the contents of these scripts to look for issues or specificities.
|
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
|
||||||
|
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
|
||||||
|
working obs-studio development environment and have built obs-studio, you can
|
||||||
|
skip that script.
|
||||||
|
|
||||||
|
Of course, you're encouraged to dig through the contents of these scripts to
|
||||||
|
look for issues or specificities.
|
||||||
|
|
||||||
|
```shell
|
||||||
git clone --recursive https://github.com/Palakis/obs-websocket.git
|
git clone --recursive https://github.com/Palakis/obs-websocket.git
|
||||||
cd obs-websocket
|
cd obs-websocket
|
||||||
./CI/install-dependencies-macos.sh
|
./CI/install-dependencies-macos.sh
|
||||||
|
./CI/install-build-obs-macos.sh
|
||||||
./CI/build-macos.sh
|
./CI/build-macos.sh
|
||||||
./CI/package-macos.sh
|
./CI/package-macos.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
This will result in a ready-to-use `obs-websocket.pkg` installer in the `release` subfolder.
|
This will result in a ready-to-use `obs-websocket.pkg` installer in the `release` subfolder.
|
||||||
|
|
||||||
## Automated Builds
|
## Automated Builds
|
||||||
- Windows : [](https://ci.appveyor.com/project/Palakis/obs-websocket/history)
|
|
||||||
- Linux & OS X : [](https://travis-ci.org/Palakis/obs-websocket)
|
- Windows: [](https://ci.appveyor.com/project/Palakis/obs-websocket/history)
|
||||||
|
- Linux: [](https://travis-ci.org/Palakis/obs-websocket)
|
||||||
|
- macOS: [](https://dev.azure.com/Palakis/obs-websocket/_build)
|
||||||
|
@ -1,13 +1,28 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -ex
|
|
||||||
|
OSTYPE=$(uname)
|
||||||
|
|
||||||
|
if [ "${OSTYPE}" != "Darwin" ]; then
|
||||||
|
echo "[obs-websocket - Error] macOS build script can be run on Darwin-type OS only."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
HAS_CMAKE=$(type cmake 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "${HAS_CMAKE}" = "" ]; then
|
||||||
|
echo "[obs-websocket - Error] CMake not installed - please run 'install-dependencies-macos.sh' first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
#export QT_PREFIX="$(find /usr/local/Cellar/qt5 -d 1 | tail -n 1)"
|
#export QT_PREFIX="$(find /usr/local/Cellar/qt5 -d 1 | tail -n 1)"
|
||||||
|
|
||||||
mkdir build && cd build
|
echo "[obs-websocket] Building 'obs-websocket' for macOS."
|
||||||
|
mkdir -p build && cd build
|
||||||
cmake .. \
|
cmake .. \
|
||||||
-DQTDIR=/usr/local/opt/qt \
|
-DQTDIR=/usr/local/opt/qt \
|
||||||
-DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \
|
-DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \
|
||||||
-DLIBOBS_LIB=../../obs-studio/libobs \
|
-DLIBOBS_LIB=../../obs-studio/libobs \
|
||||||
-DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \
|
-DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
&& make -j4
|
&& make -j4
|
||||||
|
@ -4,5 +4,5 @@ set -ex
|
|||||||
cd /root/obs-websocket
|
cd /root/obs-websocket
|
||||||
|
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake -DLIBOBS_INCLUDE_DIR="../../obs-studio/libobs" -DCMAKE_INSTALL_PREFIX=/usr ..
|
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
make -j4
|
make -j4
|
||||||
|
@ -11,13 +11,13 @@ npm run build
|
|||||||
echo "-- Documentation successfully generated."
|
echo "-- Documentation successfully generated."
|
||||||
|
|
||||||
if git diff --quiet; then
|
if git diff --quiet; then
|
||||||
echo "-- No documentation changes to commit."
|
echo "-- No documentation changes to commit."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "master" ]; then
|
if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "4.x-current" ]; then
|
||||||
echo "-- Skipping documentation deployment because this is either a pull request or a non-master branch."
|
echo "-- Skipping documentation deployment because this is either a pull request or a non-master branch."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
REMOTE_URL="$(git config remote.origin.url)"
|
REMOTE_URL="$(git config remote.origin.url)"
|
||||||
|
43
CI/install-build-obs-macos.sh
Executable file
43
CI/install-build-obs-macos.sh
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
OSTYPE=$(uname)
|
||||||
|
|
||||||
|
if [ "${OSTYPE}" != "Darwin" ]; then
|
||||||
|
echo "[obs-websocket - Error] macOS obs-studio build script can be run on Darwin-type OS only."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
HAS_CMAKE=$(type cmake 2>/dev/null)
|
||||||
|
HAS_GIT=$(type git 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "${HAS_CMAKE}" = "" ]; then
|
||||||
|
echo "[obs-websocket - Error] CMake not installed - please run 'install-dependencies-macos.sh' first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${HAS_GIT}" = "" ]; then
|
||||||
|
echo "[obs-websocket - Error] Git not installed - please install Xcode developer tools or via Homebrew."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[obs-websocket] Downloading and unpacking OBS dependencies"
|
||||||
|
wget --quiet --retry-connrefused --waitretry=1 https://obs-nightly.s3.amazonaws.com/osx-deps-2018-08-09.tar.gz
|
||||||
|
tar -xf ./osx-deps-2018-08-09.tar.gz -C /tmp
|
||||||
|
|
||||||
|
# Build obs-studio
|
||||||
|
cd ..
|
||||||
|
echo "[obs-websocket] Cloning obs-studio from GitHub.."
|
||||||
|
git clone https://github.com/obsproject/obs-studio
|
||||||
|
cd obs-studio
|
||||||
|
OBSLatestTag=$(git describe --tags --abbrev=0)
|
||||||
|
git checkout $OBSLatestTag
|
||||||
|
mkdir build && cd build
|
||||||
|
echo "[obs-websocket] Building obs-studio.."
|
||||||
|
cmake .. \
|
||||||
|
-DBUILD_CAPTIONS=true \
|
||||||
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \
|
||||||
|
-DDISABLE_PLUGINS=true \
|
||||||
|
-DENABLE_SCRIPTING=0 \
|
||||||
|
-DDepsPath=/tmp/obsdeps \
|
||||||
|
-DCMAKE_PREFIX_PATH=/usr/local/opt/qt/lib/cmake \
|
||||||
|
&& make -j4
|
@ -19,52 +19,63 @@ set "BuildOBS="
|
|||||||
|
|
||||||
REM Check the last tag successfully built by CI.
|
REM Check the last tag successfully built by CI.
|
||||||
if exist C:\projects\obs-studio-last-tag-built.txt (
|
if exist C:\projects\obs-studio-last-tag-built.txt (
|
||||||
set /p OBSLastTagBuilt=<C:\projects\obs-studio-last-tag-built.txt
|
set /p OBSLastTagBuilt=<C:\projects\obs-studio-last-tag-built.txt
|
||||||
) else (
|
) else (
|
||||||
set OBSLastTagBuilt=0
|
set OBSLastTagBuilt=0
|
||||||
)
|
)
|
||||||
|
|
||||||
REM If obs-studio directory exists, run git pull and get the latest tag number.
|
REM If obs-studio directory exists, run git pull and get the latest tag number.
|
||||||
if exist C:\projects\obs-studio\ (
|
if exist C:\projects\obs-studio\ (
|
||||||
echo obs-studio directory exists
|
echo obs-studio directory exists
|
||||||
echo Updating tag info
|
echo Updating tag info
|
||||||
cd C:\projects\obs-studio\
|
cd C:\projects\obs-studio\
|
||||||
git describe --tags --abbrev=0 > C:\projects\latest-obs-studio-tag-pre-pull.txt
|
git describe --tags --abbrev=0 --exclude="*-rc*" > C:\projects\latest-obs-studio-tag-pre-pull.txt
|
||||||
set /p OBSLatestTagPrePull=<C:\projects\latest-obs-studio-tag-pre-pull.txt
|
set /p OBSLatestTagPrePull=<C:\projects\latest-obs-studio-tag-pre-pull.txt
|
||||||
git checkout master
|
git checkout master
|
||||||
git pull
|
git pull
|
||||||
git describe --tags --abbrev=0 > C:\projects\latest-obs-studio-tag-post-pull.txt
|
git describe --tags --abbrev=0 --exclude="*-rc*" > C:\projects\latest-obs-studio-tag-post-pull.txt
|
||||||
set /p OBSLatestTagPostPull=<C:\projects\latest-obs-studio-tag-post-pull.txt
|
set /p OBSLatestTagPostPull=<C:\projects\latest-obs-studio-tag-post-pull.txt
|
||||||
set /p OBSLatestTag=<C:\projects\latest-obs-studio-tag-post-pull.txt
|
set /p OBSLatestTag=<C:\projects\latest-obs-studio-tag-post-pull.txt
|
||||||
echo %OBSLatestTagPostPull%> C:\projects\latest-obs-studio-tag.txt
|
echo %OBSLatestTagPostPull%> C:\projects\latest-obs-studio-tag.txt
|
||||||
)
|
)
|
||||||
|
|
||||||
REM Check the obs-studio tags for mismatches.
|
REM Check the obs-studio tags for mismatches.
|
||||||
REM If a new tag was pulled, set the build flag.
|
REM If a new tag was pulled, set the build flag.
|
||||||
if not %OBSLatestTagPrePull%==%OBSLatestTagPostPull% (
|
if not %OBSLatestTagPrePull%==%OBSLatestTagPostPull% (
|
||||||
echo Latest tag pre-pull: %OBSLatestTagPrePull%
|
echo Latest tag pre-pull: %OBSLatestTagPrePull%
|
||||||
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
||||||
echo Tags do not match. Need to rebuild OBS.
|
echo Tags do not match. Need to rebuild OBS.
|
||||||
set BuildOBS=true
|
set BuildOBS=true
|
||||||
)
|
)
|
||||||
|
|
||||||
REM If the latest git tag doesn't match the last built tag, set the build flag.
|
REM If the latest git tag doesn't match the last built tag, set the build flag.
|
||||||
if not %OBSLatestTagPostPull%==%OBSLastTagBuilt% (
|
if not %OBSLatestTagPostPull%==%OBSLastTagBuilt% (
|
||||||
echo Last built OBS tag: %OBSLastTagBuilt%
|
echo Last built OBS tag: %OBSLastTagBuilt%
|
||||||
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
||||||
echo Tags do not match. Need to rebuild OBS.
|
echo Tags do not match. Need to rebuild OBS.
|
||||||
set BuildOBS=true
|
set BuildOBS=true
|
||||||
)
|
)
|
||||||
|
|
||||||
REM If obs-studio directory does not exist, clone the git repo, get the latest
|
REM If obs-studio directory does not exist, clone the git repo, get the latest
|
||||||
REM tag number, and set the build flag.
|
REM tag number, and set the build flag.
|
||||||
if not exist C:\projects\obs-studio (
|
if not exist C:\projects\obs-studio (
|
||||||
echo obs-studio directory does not exist
|
echo obs-studio directory does not exist
|
||||||
git clone --recursive https://github.com/jp9000/obs-studio
|
git clone https://github.com/obsproject/obs-studio
|
||||||
cd C:\projects\obs-studio\
|
cd C:\projects\obs-studio\
|
||||||
git describe --tags --abbrev=0 > C:\projects\obs-studio-latest-tag.txt
|
git describe --tags --abbrev=0 --exclude="*-rc*" > C:\projects\obs-studio-latest-tag.txt
|
||||||
set /p OBSLatestTag=<C:\projects\obs-studio-latest-tag.txt
|
set /p OBSLatestTag=<C:\projects\obs-studio-latest-tag.txt
|
||||||
set BuildOBS=true
|
set BuildOBS=true
|
||||||
|
)
|
||||||
|
|
||||||
|
REM If the needed obs-studio libs for this build_config do not exist,
|
||||||
|
REM set the build flag.
|
||||||
|
if not exist C:\projects\obs-studio\build32\libobs\%build_config%\obs.lib (
|
||||||
|
echo obs-studio\build32\libobs\%build_config%\obs.lib does not exist
|
||||||
|
set BuildOBS=true
|
||||||
|
)
|
||||||
|
if not exist C:\projects\obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib (
|
||||||
|
echo obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib does not exist
|
||||||
|
set BuildOBS=true
|
||||||
)
|
)
|
||||||
|
|
||||||
REM Some debug info
|
REM Some debug info
|
||||||
@ -75,44 +86,44 @@ echo Latest tag: %OBSLatestTag%
|
|||||||
echo Last built OBS tag: %OBSLastTagBuilt%
|
echo Last built OBS tag: %OBSLastTagBuilt%
|
||||||
|
|
||||||
if defined BuildOBS (
|
if defined BuildOBS (
|
||||||
echo BuildOBS: true
|
echo BuildOBS: true
|
||||||
) else (
|
) else (
|
||||||
echo BuildOBS: false
|
echo BuildOBS: false
|
||||||
)
|
)
|
||||||
echo:
|
echo:
|
||||||
|
|
||||||
REM If the build flag is set, build obs-studio.
|
REM If the build flag is set, build obs-studio.
|
||||||
if defined BuildOBS (
|
if defined BuildOBS (
|
||||||
echo Building obs-studio...
|
echo Building obs-studio...
|
||||||
echo git checkout %OBSLatestTag%
|
echo git checkout %OBSLatestTag%
|
||||||
git checkout %OBSLatestTag%
|
git checkout %OBSLatestTag%
|
||||||
echo:
|
echo:
|
||||||
echo Removing previous build dirs...
|
echo Removing previous build dirs...
|
||||||
if exist build rmdir /s /q C:\projects\obs-studio\build
|
if exist build rmdir /s /q C:\projects\obs-studio\build
|
||||||
if exist build32 rmdir /s /q C:\projects\obs-studio\build32
|
if exist build32 rmdir /s /q C:\projects\obs-studio\build32
|
||||||
if exist build64 rmdir /s /q C:\projects\obs-studio\build64
|
if exist build64 rmdir /s /q C:\projects\obs-studio\build64
|
||||||
echo Making new build dirs...
|
echo Making new build dirs...
|
||||||
mkdir build
|
mkdir build
|
||||||
mkdir build32
|
mkdir build32
|
||||||
mkdir build64
|
mkdir build64
|
||||||
echo Running cmake for obs-studio %OBSLatestTag% 32-bit...
|
echo Running cmake for obs-studio %OBSLatestTag% 32-bit...
|
||||||
cd ./build32
|
cd ./build32
|
||||||
cmake -G "Visual Studio 12 2013" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
cmake -G "Visual Studio 14 2015" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
||||||
echo:
|
echo:
|
||||||
echo:
|
echo:
|
||||||
echo Running cmake for obs-studio %OBSLatestTag% 64-bit...
|
echo Running cmake for obs-studio %OBSLatestTag% 64-bit...
|
||||||
cd ../build64
|
cd ../build64
|
||||||
cmake -G "Visual Studio 12 2013 Win64" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
cmake -G "Visual Studio 14 2015 Win64" -DBUILD_CAPTIONS=true -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
||||||
echo:
|
echo:
|
||||||
echo:
|
echo:
|
||||||
echo Building obs-studio %OBSLatestTag% 32-bit ^(Build Config: %build_config%^)...
|
echo Building obs-studio %OBSLatestTag% 32-bit ^(Build Config: %build_config%^)...
|
||||||
call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build32\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build32\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
echo Building obs-studio %OBSLatestTag% 64-bit ^(Build Config: %build_config%^)...
|
echo Building obs-studio %OBSLatestTag% 64-bit ^(Build Config: %build_config%^)...
|
||||||
call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build64\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build64\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
cd ..
|
cd ..
|
||||||
git describe --tags --abbrev=0 > C:\projects\obs-studio-last-tag-built.txt
|
git describe --tags --abbrev=0 > C:\projects\obs-studio-last-tag-built.txt
|
||||||
set /p OBSLastTagBuilt=<C:\projects\obs-studio-last-tag-built.txt
|
set /p OBSLastTagBuilt=<C:\projects\obs-studio-last-tag-built.txt
|
||||||
) else (
|
) else (
|
||||||
echo Last OBS tag built is: %OBSLastTagBuilt%
|
echo Last OBS tag built is: %OBSLastTagBuilt%
|
||||||
echo No need to rebuild OBS.
|
echo No need to rebuild OBS.
|
||||||
)
|
)
|
||||||
|
@ -1,32 +1,61 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -ex
|
|
||||||
|
|
||||||
|
|
||||||
|
OSTYPE=$(uname)
|
||||||
|
|
||||||
|
if [ "${OSTYPE}" != "Darwin" ]; then
|
||||||
|
echo "[obs-websocket - Error] macOS install dependencies script can be run on Darwin-type OS only."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
HAS_BREW=$(type brew 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "${HAS_BREW}" = "" ]; then
|
||||||
|
echo "[obs-websocket - Error] Please install Homebrew (https://www.brew.sh/) to build obs-websocket on macOS."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# OBS Studio deps
|
# OBS Studio deps
|
||||||
brew update
|
echo "[obs-websocket] Updating Homebrew.."
|
||||||
brew install ffmpeg
|
brew update >/dev/null
|
||||||
brew install libav
|
echo "[obs-websocket] Checking installed Homebrew formulas.."
|
||||||
|
BREW_PACKAGES=$(brew list)
|
||||||
|
BREW_DEPENDENCIES="jack speexdsp ccache swig mbedtls"
|
||||||
|
|
||||||
|
for DEPENDENCY in ${BREW_DEPENDENCIES}; do
|
||||||
|
if echo "${BREW_PACKAGES}" | grep -q "^${DEPENDENCY}\$"; then
|
||||||
|
echo "[obs-websocket] Upgrading OBS-Studio dependency '${DEPENDENCY}'.."
|
||||||
|
brew upgrade ${DEPENDENCY} 2>/dev/null
|
||||||
|
else
|
||||||
|
echo "[obs-websocket] Installing OBS-Studio dependency '${DEPENDENCY}'.."
|
||||||
|
brew install ${DEPENDENCY} 2>/dev/null
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# qtwebsockets deps
|
# qtwebsockets deps
|
||||||
# qt latest
|
echo "[obs-websocket] Installing obs-websocket dependency 'QT 5.10.1'.."
|
||||||
#brew install qt5
|
# =!= 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 =!=
|
||||||
|
|
||||||
# qt 5.9.2
|
brew install https://gist.githubusercontent.com/DDRBoxman/b3956fab6073335a4bf151db0dcbd4ad/raw/ed1342a8a86793ea8c10d8b4d712a654da121ace/qt.rb
|
||||||
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/2b121c9a96e58a5da14228630cb71d5bead7137e/Formula/qt.rb
|
|
||||||
|
|
||||||
#echo "Qt path: $(find /usr/local/Cellar/qt5 -d 1 | tail -n 1)"
|
# Pin this version of QT5 to avoid `brew upgrade`
|
||||||
|
# upgrading it to incompatible version
|
||||||
|
brew pin qt
|
||||||
|
|
||||||
# Build obs-studio
|
# Fetch and install Packages app
|
||||||
cd ..
|
# =!= NOTICE =!=
|
||||||
git clone --recursive https://github.com/jp9000/obs-studio
|
# Installs a LaunchDaemon under /Library/LaunchDaemons/fr.whitebox.packages.build.dispatcher.plist
|
||||||
cd obs-studio
|
# =!= NOTICE =!=
|
||||||
git checkout 20.1.0
|
|
||||||
mkdir build && cd build
|
|
||||||
cmake .. \
|
|
||||||
-DCMAKE_PREFIX_PATH=/usr/local/opt/qt/lib/cmake \
|
|
||||||
&& make -j4
|
|
||||||
|
|
||||||
# Packages app
|
HAS_PACKAGES=$(type packagesbuild 2>/dev/null)
|
||||||
cd ..
|
|
||||||
curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg -f --retry 5 -C -
|
if [ "${HAS_PACKAGES}" = "" ]; then
|
||||||
hdiutil attach ./Packages.dmg
|
echo "[obs-websocket] Installing Packaging app (might require password due to 'sudo').."
|
||||||
sudo installer -pkg /Volumes/Packages\ 1.2.2/packages/Packages.pkg -target /
|
curl -o './Packages.pkg' --retry-connrefused -s --retry-delay 1 'https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg'
|
||||||
|
sudo installer -pkg ./Packages.pkg -target /
|
||||||
|
fi
|
||||||
|
@ -1,57 +1,19 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
# OBS Studio deps
|
add-apt-repository -y ppa:obsproject/obs-studio
|
||||||
apt-get -qq update
|
apt-get -qq update
|
||||||
apt-get install -y \
|
|
||||||
libc-dev-bin libc6-dev \
|
|
||||||
git \
|
|
||||||
build-essential
|
|
||||||
|
|
||||||
apt-get install -y \
|
apt-get install -y \
|
||||||
build-essential \
|
libc-dev-bin \
|
||||||
checkinstall \
|
libc6-dev git \
|
||||||
cmake \
|
build-essential \
|
||||||
libasound2-dev \
|
checkinstall \
|
||||||
libavcodec-dev \
|
cmake \
|
||||||
libavdevice-dev \
|
obs-studio \
|
||||||
libavfilter-dev \
|
qtbase5-dev
|
||||||
libavformat-dev \
|
|
||||||
libavutil-dev \
|
|
||||||
libcurl4-openssl-dev \
|
|
||||||
libfontconfig-dev \
|
|
||||||
libfreetype6-dev \
|
|
||||||
libgl1-mesa-dev \
|
|
||||||
libjack-jackd2-dev \
|
|
||||||
libjansson-dev \
|
|
||||||
libpulse-dev \
|
|
||||||
libqt5x11extras5-dev \
|
|
||||||
libspeexdsp-dev \
|
|
||||||
libswresample-dev \
|
|
||||||
libswscale-dev \
|
|
||||||
libudev-dev \
|
|
||||||
libv4l-dev \
|
|
||||||
libvlc-dev \
|
|
||||||
libx11-dev \
|
|
||||||
libx264-dev \
|
|
||||||
libxcb-shm0-dev \
|
|
||||||
libxcb-xinerama0-dev \
|
|
||||||
libxcomposite-dev \
|
|
||||||
libxinerama-dev \
|
|
||||||
pkg-config \
|
|
||||||
qtbase5-dev
|
|
||||||
|
|
||||||
# obs-websocket deps
|
# Dirty hack
|
||||||
apt-get install -y libqt5websockets5-dev
|
wget -O /usr/include/obs/obs-frontend-api.h https://raw.githubusercontent.com/obsproject/obs-studio/master/UI/obs-frontend-api/obs-frontend-api.h
|
||||||
|
|
||||||
# Build obs-studio
|
|
||||||
cd /root
|
|
||||||
git clone https://github.com/jp9000/obs-studio ./obs-studio
|
|
||||||
cd obs-studio
|
|
||||||
git checkout 20.1.0
|
|
||||||
mkdir build && cd build
|
|
||||||
cmake -DUNIX_STRUCTURE=1 -DCMAKE_INSTALL_PREFIX=/usr ..
|
|
||||||
make -j4
|
|
||||||
make install
|
|
||||||
|
|
||||||
ldconfig
|
ldconfig
|
||||||
|
@ -1,20 +1,6 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
REM Set default values to use AppVeyor's built-in Qt.
|
REM Set default values to use AppVeyor's built-in Qt.
|
||||||
set QTDIR32=C:\Qt\5.7\msvc2013
|
set QTDIR32=C:\Qt\5.10.1\msvc2015
|
||||||
set QTDIR64=C:\Qt\5.7\msvc2013_64
|
set QTDIR64=C:\Qt\5.10.1\msvc2015_64
|
||||||
set QTCompileVersion=5.7.1
|
set QTCompileVersion=5.10.1
|
||||||
|
|
||||||
REM If the AppVeyor cache couldn't recover qt570.7z,
|
|
||||||
REM try to fetch Qt 5.7.0 from slepin.fr.
|
|
||||||
if not exist qt570.7z (
|
|
||||||
curl -kLO https://www.slepin.fr/obs-plugins/deps/qt570.7z -f --retry 5 -C -
|
|
||||||
)
|
|
||||||
|
|
||||||
REM If qt570.7z exists now, use that instead of AppVeyor's built-in Qt.
|
|
||||||
if exist qt570.7z (
|
|
||||||
7z x qt570.7z -o"Qt5.7.0"
|
|
||||||
set QTDIR32=%CD%\Qt5.7.0\msvc2013
|
|
||||||
set QTDIR64=%CD%\Qt5.7.0\msvc2013_64
|
|
||||||
set QTCompileVersion=5.7.0
|
|
||||||
)
|
|
||||||
|
@ -12,123 +12,6 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>CHILDREN</key>
|
<key>CHILDREN</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
|
||||||
<key>CHILDREN</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CHILDREN</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CHILDREN</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CHILDREN</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CHILDREN</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CHILDREN</key>
|
|
||||||
<array/>
|
|
||||||
<key>GID</key>
|
|
||||||
<integer>80</integer>
|
|
||||||
<key>PATH</key>
|
|
||||||
<string>../../build/QtNetwork</string>
|
|
||||||
<key>PATH_TYPE</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
<key>PERMISSIONS</key>
|
|
||||||
<integer>292</integer>
|
|
||||||
<key>TYPE</key>
|
|
||||||
<integer>3</integer>
|
|
||||||
<key>UID</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CHILDREN</key>
|
|
||||||
<array/>
|
|
||||||
<key>GID</key>
|
|
||||||
<integer>80</integer>
|
|
||||||
<key>PATH</key>
|
|
||||||
<string>../../build/QtWebSockets</string>
|
|
||||||
<key>PATH_TYPE</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
<key>PERMISSIONS</key>
|
|
||||||
<integer>292</integer>
|
|
||||||
<key>TYPE</key>
|
|
||||||
<integer>3</integer>
|
|
||||||
<key>UID</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>GID</key>
|
|
||||||
<integer>80</integer>
|
|
||||||
<key>PATH</key>
|
|
||||||
<string>bin</string>
|
|
||||||
<key>PATH_TYPE</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>PERMISSIONS</key>
|
|
||||||
<integer>509</integer>
|
|
||||||
<key>TYPE</key>
|
|
||||||
<integer>2</integer>
|
|
||||||
<key>UID</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>GID</key>
|
|
||||||
<integer>80</integer>
|
|
||||||
<key>PATH</key>
|
|
||||||
<string>Resources</string>
|
|
||||||
<key>PATH_TYPE</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>PERMISSIONS</key>
|
|
||||||
<integer>509</integer>
|
|
||||||
<key>TYPE</key>
|
|
||||||
<integer>2</integer>
|
|
||||||
<key>UID</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>GID</key>
|
|
||||||
<integer>80</integer>
|
|
||||||
<key>PATH</key>
|
|
||||||
<string>Contents</string>
|
|
||||||
<key>PATH_TYPE</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>PERMISSIONS</key>
|
|
||||||
<integer>509</integer>
|
|
||||||
<key>TYPE</key>
|
|
||||||
<integer>2</integer>
|
|
||||||
<key>UID</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>GID</key>
|
|
||||||
<integer>80</integer>
|
|
||||||
<key>PATH</key>
|
|
||||||
<string>OBS.app</string>
|
|
||||||
<key>PATH_TYPE</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>PERMISSIONS</key>
|
|
||||||
<integer>509</integer>
|
|
||||||
<key>TYPE</key>
|
|
||||||
<integer>2</integer>
|
|
||||||
<key>UID</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>GID</key>
|
|
||||||
<integer>80</integer>
|
|
||||||
<key>PATH</key>
|
|
||||||
<string>Applications</string>
|
|
||||||
<key>PATH_TYPE</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>PERMISSIONS</key>
|
|
||||||
<integer>509</integer>
|
|
||||||
<key>TYPE</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
<key>UID</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>CHILDREN</key>
|
<key>CHILDREN</key>
|
||||||
<array>
|
<array>
|
||||||
@ -635,7 +518,7 @@
|
|||||||
<key>OVERWRITE_PERMISSIONS</key>
|
<key>OVERWRITE_PERMISSIONS</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>VERSION</key>
|
<key>VERSION</key>
|
||||||
<string>4.3.0</string>
|
<string>4.7.0</string>
|
||||||
</dict>
|
</dict>
|
||||||
<key>PROJECT_COMMENTS</key>
|
<key>PROJECT_COMMENTS</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
163
CI/macos/qt.rb
Normal file
163
CI/macos/qt.rb
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
# Patches for Qt must be at the very least submitted to Qt's Gerrit codereview
|
||||||
|
# rather than their bug-report Jira. The latter is rarely reviewed by Qt.
|
||||||
|
class Qt < Formula
|
||||||
|
desc "Cross-platform application and UI framework"
|
||||||
|
homepage "https://www.qt.io/"
|
||||||
|
url "https://download.qt.io/archive/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz"
|
||||||
|
mirror "https://mirrorservice.org/sites/download.qt-project.org/official_releases/qt/5.10/5.10.1/single/qt-everywhere-src-5.10.1.tar.xz"
|
||||||
|
sha256 "05ffba7b811b854ed558abf2be2ddbd3bb6ddd0b60ea4b5da75d277ac15e740a"
|
||||||
|
head "https://code.qt.io/qt/qt5.git", :branch => "5.10.1", :shallow => false
|
||||||
|
|
||||||
|
bottle do
|
||||||
|
sha256 "8b4bad005596a5f8790150fe455db998ac2406f4e0f04140d6656205d844d266" => :high_sierra
|
||||||
|
sha256 "9c488554935fb573554a4e36d36d3c81e47245b7fefc4b61edef894e67ba1740" => :sierra
|
||||||
|
sha256 "c0407afba5951df6cc4c6f6c1c315972bd41c99cecb4e029919c4c15ab6f7bdc" => :el_capitan
|
||||||
|
end
|
||||||
|
|
||||||
|
keg_only "Qt 5 has CMake issues when linked"
|
||||||
|
|
||||||
|
option "with-docs", "Build documentation"
|
||||||
|
option "with-examples", "Build examples"
|
||||||
|
|
||||||
|
deprecated_option "with-mysql" => "with-mysql-client"
|
||||||
|
|
||||||
|
# OS X 10.7 Lion is still supported in Qt 5.5, but is no longer a reference
|
||||||
|
# configuration and thus untested in practice. Builds on OS X 10.7 have been
|
||||||
|
# reported to fail: <https://github.com/Homebrew/homebrew/issues/45284>.
|
||||||
|
depends_on :macos => :mountain_lion
|
||||||
|
|
||||||
|
depends_on "pkg-config" => :build
|
||||||
|
depends_on :xcode => :build
|
||||||
|
depends_on "mysql-client" => :optional
|
||||||
|
depends_on "postgresql" => :optional
|
||||||
|
|
||||||
|
# Restore `.pc` files for framework-based build of Qt 5 on OS X. This
|
||||||
|
# partially reverts <https://codereview.qt-project.org/#/c/140954/> merged
|
||||||
|
# between the 5.5.1 and 5.6.0 releases. (Remove this as soon as feasible!)
|
||||||
|
#
|
||||||
|
# Core formulae known to fail without this patch (as of 2016-10-15):
|
||||||
|
# * gnuplot (with `--with-qt` option)
|
||||||
|
# * mkvtoolnix (with `--with-qt` option, silent build failure)
|
||||||
|
# * poppler (with `--with-qt` option)
|
||||||
|
patch do
|
||||||
|
url "https://raw.githubusercontent.com/Homebrew/formula-patches/e8fe6567/qt5/restore-pc-files.patch"
|
||||||
|
sha256 "48ff18be2f4050de7288bddbae7f47e949512ac4bcd126c2f504be2ac701158b"
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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
|
||||||
|
-prefix #{prefix}
|
||||||
|
-release
|
||||||
|
-opensource -confirm-license
|
||||||
|
-system-zlib
|
||||||
|
-qt-libpng
|
||||||
|
-qt-libjpeg
|
||||||
|
-qt-freetype
|
||||||
|
-qt-pcre
|
||||||
|
-nomake tests
|
||||||
|
-no-rpath
|
||||||
|
-pkg-config
|
||||||
|
-dbus-runtime
|
||||||
|
-no-assimp
|
||||||
|
]
|
||||||
|
|
||||||
|
args << "-nomake" << "examples" if build.without? "examples"
|
||||||
|
|
||||||
|
if build.with? "mysql-client"
|
||||||
|
args << "-plugin-sql-mysql"
|
||||||
|
(buildpath/"brew_shim/mysql_config").write <<~EOS
|
||||||
|
#!/bin/sh
|
||||||
|
if [ x"$1" = x"--libs" ]; then
|
||||||
|
mysql_config --libs | sed "s/-lssl -lcrypto//"
|
||||||
|
else
|
||||||
|
exec mysql_config "$@"
|
||||||
|
fi
|
||||||
|
EOS
|
||||||
|
chmod 0755, "brew_shim/mysql_config"
|
||||||
|
args << "-mysql_config" << buildpath/"brew_shim/mysql_config"
|
||||||
|
end
|
||||||
|
|
||||||
|
args << "-plugin-sql-psql" if build.with? "postgresql"
|
||||||
|
|
||||||
|
system "./configure", *args
|
||||||
|
system "make"
|
||||||
|
ENV.deparallelize
|
||||||
|
system "make", "install"
|
||||||
|
|
||||||
|
if build.with? "docs"
|
||||||
|
system "make", "docs"
|
||||||
|
system "make", "install_docs"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Some config scripts will only find Qt in a "Frameworks" folder
|
||||||
|
frameworks.install_symlink Dir["#{lib}/*.framework"]
|
||||||
|
|
||||||
|
# The pkg-config files installed suggest that headers can be found in the
|
||||||
|
# `include` directory. Make this so by creating symlinks from `include` to
|
||||||
|
# the Frameworks' Headers folders.
|
||||||
|
Pathname.glob("#{lib}/*.framework/Headers") do |path|
|
||||||
|
include.install_symlink path => path.parent.basename(".framework")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Move `*.app` bundles into `libexec` to expose them to `brew linkapps` and
|
||||||
|
# because we don't like having them in `bin`.
|
||||||
|
# (Note: This move breaks invocation of Assistant via the Help menu
|
||||||
|
# of both Designer and Linguist as that relies on Assistant being in `bin`.)
|
||||||
|
libexec.mkpath
|
||||||
|
Pathname.glob("#{bin}/*.app") { |app| mv app, libexec }
|
||||||
|
end
|
||||||
|
|
||||||
|
def caveats; <<~EOS
|
||||||
|
We agreed to the Qt opensource license for you.
|
||||||
|
If this is unacceptable you should uninstall.
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
|
||||||
|
test do
|
||||||
|
(testpath/"hello.pro").write <<~EOS
|
||||||
|
QT += core
|
||||||
|
QT -= gui
|
||||||
|
TARGET = hello
|
||||||
|
CONFIG += console
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
TEMPLATE = app
|
||||||
|
SOURCES += main.cpp
|
||||||
|
EOS
|
||||||
|
|
||||||
|
(testpath/"main.cpp").write <<~EOS
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication a(argc, argv);
|
||||||
|
qDebug() << "Hello World!";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOS
|
||||||
|
|
||||||
|
system bin/"qmake", testpath/"hello.pro"
|
||||||
|
system "make"
|
||||||
|
assert_predicate testpath/"hello", :exist?
|
||||||
|
assert_predicate testpath/"main.o", :exist?
|
||||||
|
system "./hello"
|
||||||
|
end
|
||||||
|
end
|
@ -2,65 +2,42 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "-- Preparing package build"
|
OSTYPE=$(uname)
|
||||||
export QT_CELLAR_PREFIX="$(find /usr/local/Cellar/qt -d 1 | tail -n 1)"
|
|
||||||
|
|
||||||
export WS_LIB="/usr/local/opt/qt/lib/QtWebSockets.framework/QtWebSockets"
|
if [ "${OSTYPE}" != "Darwin" ]; then
|
||||||
export NET_LIB="/usr/local/opt/qt/lib/QtNetwork.framework/QtNetwork"
|
echo "[obs-websocket - Error] macOS package script can be run on Darwin-type OS only."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[obs-websocket] Preparing package build"
|
||||||
|
export QT_CELLAR_PREFIX="$(/usr/bin/find /usr/local/Cellar/qt -d 1 | sort -t '.' -k 1,1n -k 2,2n -k 3,3n | tail -n 1)"
|
||||||
|
|
||||||
export GIT_HASH=$(git rev-parse --short HEAD)
|
export GIT_HASH=$(git rev-parse --short HEAD)
|
||||||
|
export GIT_BRANCH_OR_TAG=$(git name-rev --name-only HEAD | awk -F/ '{print $NF}')
|
||||||
|
|
||||||
export VERSION="$GIT_HASH-$TRAVIS_BRANCH"
|
export VERSION="$GIT_HASH-$GIT_BRANCH_OR_TAG"
|
||||||
export LATEST_VERSION="$TRAVIS_BRANCH"
|
export LATEST_VERSION="$GIT_BRANCH_OR_TAG"
|
||||||
if [ -n "${TRAVIS_TAG}" ]; then
|
|
||||||
export VERSION="$TRAVIS_TAG"
|
|
||||||
export LATEST_VERSION="$TRAVIS_TAG"
|
|
||||||
fi
|
|
||||||
|
|
||||||
export FILENAME="obs-websocket-$VERSION.pkg"
|
export FILENAME="obs-websocket-$VERSION.pkg"
|
||||||
export LATEST_FILENAME="obs-websocket-latest-$LATEST_VERSION.pkg"
|
export LATEST_FILENAME="obs-websocket-latest-$LATEST_VERSION.pkg"
|
||||||
|
|
||||||
echo "-- Copying Qt dependencies"
|
echo "[obs-websocket] Modifying obs-websocket.so"
|
||||||
cp $WS_LIB ./build
|
|
||||||
cp $NET_LIB ./build
|
|
||||||
|
|
||||||
chmod +rw ./build/QtWebSockets ./build/QtNetwork
|
|
||||||
|
|
||||||
echo "-- Modifying QtNetwork"
|
|
||||||
install_name_tool \
|
install_name_tool \
|
||||||
-change /usr/local/opt/qt/lib/QtNetwork.framework/Versions/5/QtNetwork @rpath/QtNetwork \
|
-add_rpath @executable_path/../Frameworks/QtWidgets.framework/Versions/5/ \
|
||||||
-change $QT_CELLAR_PREFIX/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
|
-add_rpath @executable_path/../Frameworks/QtGui.framework/Versions/5/ \
|
||||||
./build/QtNetwork
|
-add_rpath @executable_path/../Frameworks/QtCore.framework/Versions/5/ \
|
||||||
|
|
||||||
echo "-- Modifying QtWebSockets"
|
|
||||||
install_name_tool \
|
|
||||||
-change /usr/local/opt/qt/lib/QtWebSockets.framework/Versions/5/QtWebSockets @rpath/QtWebSockets \
|
|
||||||
-change $QT_CELLAR_PREFIX/lib/QtNetwork.framework/Versions/5/QtNetwork @rpath/QtNetwork \
|
|
||||||
-change $QT_CELLAR_PREFIX/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
|
|
||||||
./build/QtWebSockets
|
|
||||||
|
|
||||||
echo "-- Modifying obs-websocket.so"
|
|
||||||
install_name_tool \
|
|
||||||
-change /usr/local/opt/qt/lib/QtWebSockets.framework/Versions/5/QtWebSockets @rpath/QtWebSockets \
|
|
||||||
-change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @rpath/QtWidgets \
|
-change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @rpath/QtWidgets \
|
||||||
-change /usr/local/opt/qt/lib/QtNetwork.framework/Versions/5/QtNetwork @rpath/QtNetwork \
|
|
||||||
-change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @rpath/QtGui \
|
-change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @rpath/QtGui \
|
||||||
-change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
|
-change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
|
||||||
./build/obs-websocket.so
|
./build/obs-websocket.so
|
||||||
|
|
||||||
# Check if replacement worked
|
# Check if replacement worked
|
||||||
echo "-- Dependencies for QtNetwork"
|
echo "[obs-websocket] Dependencies for obs-websocket"
|
||||||
otool -L ./build/QtNetwork
|
|
||||||
echo "-- Dependencies for QtWebSockets"
|
|
||||||
otool -L ./build/QtWebSockets
|
|
||||||
echo "-- Dependencies for obs-websocket"
|
|
||||||
otool -L ./build/obs-websocket.so
|
otool -L ./build/obs-websocket.so
|
||||||
|
|
||||||
chmod -w ./build/QtWebSockets ./build/QtNetwork
|
echo "[obs-websocket] Actual package build"
|
||||||
|
|
||||||
echo "-- Actual package build"
|
|
||||||
packagesbuild ./CI/macos/obs-websocket.pkgproj
|
packagesbuild ./CI/macos/obs-websocket.pkgproj
|
||||||
|
|
||||||
echo "-- Renaming obs-websocket.pkg to $FILENAME"
|
echo "[obs-websocket] Renaming obs-websocket.pkg to $FILENAME"
|
||||||
mv ./release/obs-websocket.pkg ./release/$FILENAME
|
mv ./release/obs-websocket.pkg ./release/$FILENAME
|
||||||
cp ./release/$FILENAME ./release/$LATEST_FILENAME
|
cp ./release/$FILENAME ./release/$LATEST_FILENAME
|
||||||
|
@ -17,7 +17,7 @@ PAGER=cat checkinstall -y --type=debian --fstrans=no --nodoc \
|
|||||||
--backup=no --deldoc=yes --install=no \
|
--backup=no --deldoc=yes --install=no \
|
||||||
--pkgname=obs-websocket --pkgversion="$PKG_VERSION" \
|
--pkgname=obs-websocket --pkgversion="$PKG_VERSION" \
|
||||||
--pkglicense="GPLv2.0" --maintainer="contact@slepin.fr" \
|
--pkglicense="GPLv2.0" --maintainer="contact@slepin.fr" \
|
||||||
--requires="libqt5websockets5" --pkggroup="video" \
|
--pkggroup="video" \
|
||||||
--pkgsource="https://github.com/Palakis/obs-websocket" \
|
--pkgsource="https://github.com/Palakis/obs-websocket" \
|
||||||
--pakdir="/package"
|
--pakdir="/package"
|
||||||
|
|
||||||
|
100
CMakeLists.txt
100
CMakeLists.txt
@ -6,19 +6,30 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
|||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
if (WIN32 OR APPLE)
|
||||||
include(external/FindLibObs.cmake)
|
include(external/FindLibObs.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_definitions(-DASIO_STANDALONE)
|
||||||
|
|
||||||
|
if (UNIX)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WIN32 OR APPLE)
|
||||||
|
include(external/FindLibObs.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(LibObs REQUIRED)
|
find_package(LibObs REQUIRED)
|
||||||
find_package(Qt5Core REQUIRED)
|
find_package(Qt5Core REQUIRED)
|
||||||
find_package(Qt5WebSockets REQUIRED)
|
|
||||||
find_package(Qt5Widgets REQUIRED)
|
find_package(Qt5Widgets REQUIRED)
|
||||||
|
|
||||||
add_subdirectory(deps/mbedtls EXCLUDE_FROM_ALL)
|
set(obs-websocket_SOURCES
|
||||||
set(ENABLE_PROGRAMS false)
|
|
||||||
|
|
||||||
set(obs-websocket_SOURCES
|
|
||||||
src/obs-websocket.cpp
|
src/obs-websocket.cpp
|
||||||
src/WSServer.cpp
|
src/WSServer.cpp
|
||||||
|
src/ConnectionProperties.cpp
|
||||||
src/WSRequestHandler.cpp
|
src/WSRequestHandler.cpp
|
||||||
src/WSRequestHandler_General.cpp
|
src/WSRequestHandler_General.cpp
|
||||||
src/WSRequestHandler_Profiles.cpp
|
src/WSRequestHandler_Profiles.cpp
|
||||||
@ -31,6 +42,7 @@ set(obs-websocket_SOURCES
|
|||||||
src/WSRequestHandler_Streaming.cpp
|
src/WSRequestHandler_Streaming.cpp
|
||||||
src/WSRequestHandler_StudioMode.cpp
|
src/WSRequestHandler_StudioMode.cpp
|
||||||
src/WSRequestHandler_Transitions.cpp
|
src/WSRequestHandler_Transitions.cpp
|
||||||
|
src/WSRequestHandler_Outputs.cpp
|
||||||
src/WSEvents.cpp
|
src/WSEvents.cpp
|
||||||
src/Config.cpp
|
src/Config.cpp
|
||||||
src/Utils.cpp
|
src/Utils.cpp
|
||||||
@ -39,6 +51,7 @@ set(obs-websocket_SOURCES
|
|||||||
set(obs-websocket_HEADERS
|
set(obs-websocket_HEADERS
|
||||||
src/obs-websocket.h
|
src/obs-websocket.h
|
||||||
src/WSServer.h
|
src/WSServer.h
|
||||||
|
src/ConnectionProperties.h
|
||||||
src/WSRequestHandler.h
|
src/WSRequestHandler.h
|
||||||
src/WSEvents.h
|
src/WSEvents.h
|
||||||
src/Config.h
|
src/Config.h
|
||||||
@ -46,26 +59,21 @@ set(obs-websocket_HEADERS
|
|||||||
src/forms/settings-dialog.h)
|
src/forms/settings-dialog.h)
|
||||||
|
|
||||||
# --- Platform-independent build settings ---
|
# --- Platform-independent build settings ---
|
||||||
add_library(obs-websocket MODULE
|
add_library(obs-websocket MODULE
|
||||||
${obs-websocket_SOURCES}
|
${obs-websocket_SOURCES}
|
||||||
${obs-websocket_HEADERS})
|
${obs-websocket_HEADERS})
|
||||||
|
|
||||||
add_dependencies(obs-websocket mbedcrypto)
|
include_directories(
|
||||||
|
|
||||||
include_directories(
|
|
||||||
"${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api"
|
"${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api"
|
||||||
${Qt5Core_INCLUDES}
|
${Qt5Core_INCLUDES}
|
||||||
${Qt5WebSockets_INCLUDES}
|
|
||||||
${Qt5Widgets_INCLUDES}
|
${Qt5Widgets_INCLUDES}
|
||||||
${mbedcrypto_INCLUDES}
|
"${CMAKE_SOURCE_DIR}/deps/asio/asio/include"
|
||||||
"${CMAKE_SOURCE_DIR}/deps/mbedtls/include")
|
"${CMAKE_SOURCE_DIR}/deps/websocketpp")
|
||||||
|
|
||||||
target_link_libraries(obs-websocket
|
target_link_libraries(obs-websocket
|
||||||
libobs
|
libobs
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
Qt5::WebSockets
|
Qt5::Widgets)
|
||||||
Qt5::Widgets
|
|
||||||
mbedcrypto)
|
|
||||||
|
|
||||||
# --- End of section ---
|
# --- End of section ---
|
||||||
|
|
||||||
@ -73,9 +81,11 @@ target_link_libraries(obs-websocket
|
|||||||
if(WIN32)
|
if(WIN32)
|
||||||
if(NOT DEFINED OBS_FRONTEND_LIB)
|
if(NOT DEFINED OBS_FRONTEND_LIB)
|
||||||
set(OBS_FRONTEND_LIB "OBS_FRONTEND_LIB-NOTFOUND" CACHE FILEPATH "OBS frontend library")
|
set(OBS_FRONTEND_LIB "OBS_FRONTEND_LIB-NOTFOUND" CACHE FILEPATH "OBS frontend library")
|
||||||
message(FATAL_ERROR "Could not find OBS Frontend API\'s library !")
|
message(FATAL_ERROR "Could not find OBS Frontend API's library !")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
|
||||||
|
|
||||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
set(ARCH_NAME "64bit")
|
set(ARCH_NAME "64bit")
|
||||||
set(OBS_BUILDDIR_ARCH "build64")
|
set(OBS_BUILDDIR_ARCH "build64")
|
||||||
@ -91,44 +101,52 @@ if(WIN32)
|
|||||||
target_link_libraries(obs-websocket
|
target_link_libraries(obs-websocket
|
||||||
"${OBS_FRONTEND_LIB}")
|
"${OBS_FRONTEND_LIB}")
|
||||||
|
|
||||||
add_custom_command(TARGET obs-websocket POST_BUILD
|
|
||||||
COMMAND if $<CONFIG:Release>==1 ("${CMAKE_COMMAND}" -E copy
|
|
||||||
"${QTDIR}/bin/Qt5WebSockets.dll"
|
|
||||||
"${QTDIR}/bin/Qt5Network.dll"
|
|
||||||
"${CMAKE_BINARY_DIR}/$<CONFIG>")
|
|
||||||
|
|
||||||
COMMAND if $<CONFIG:Debug>==1 ("${CMAKE_COMMAND}" -E copy
|
|
||||||
"${QTDIR}/bin/Qt5WebSocketsd.dll"
|
|
||||||
"${QTDIR}/bin/Qt5Networkd.dll"
|
|
||||||
"${CMAKE_BINARY_DIR}/$<CONFIG>")
|
|
||||||
)
|
|
||||||
|
|
||||||
# --- Release package helper ---
|
# --- Release package helper ---
|
||||||
# The "release" folder has a structure similar OBS' one on Windows
|
# The "release" folder has a structure similar OBS' one on Windows
|
||||||
set(RELEASE_DIR "${PROJECT_SOURCE_DIR}/release")
|
set(RELEASE_DIR "${PROJECT_SOURCE_DIR}/release")
|
||||||
|
|
||||||
add_custom_command(TARGET obs-websocket POST_BUILD
|
add_custom_command(TARGET obs-websocket POST_BUILD
|
||||||
|
# If config is Release, package release files
|
||||||
COMMAND if $<CONFIG:Release>==1 (
|
COMMAND if $<CONFIG:Release>==1 (
|
||||||
"${CMAKE_COMMAND}" -E make_directory
|
"${CMAKE_COMMAND}" -E make_directory
|
||||||
"${RELEASE_DIR}/data/obs-plugins/obs-websocket"
|
"${RELEASE_DIR}/data/obs-plugins/obs-websocket"
|
||||||
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
||||||
|
|
||||||
COMMAND if $<CONFIG:Release>==1 ("${CMAKE_COMMAND}" -E copy_directory
|
COMMAND if $<CONFIG:Release>==1 ("${CMAKE_COMMAND}" -E copy_directory
|
||||||
"${PROJECT_SOURCE_DIR}/data"
|
"${PROJECT_SOURCE_DIR}/data"
|
||||||
"${RELEASE_DIR}/data/obs-plugins/obs-websocket")
|
"${RELEASE_DIR}/data/obs-plugins/obs-websocket")
|
||||||
|
|
||||||
COMMAND if $<CONFIG:Release>==1 ("${CMAKE_COMMAND}" -E copy
|
COMMAND if $<CONFIG:Release>==1 ("${CMAKE_COMMAND}" -E copy
|
||||||
"$<TARGET_FILE:obs-websocket>"
|
"$<TARGET_FILE:obs-websocket>"
|
||||||
"${QTDIR}/bin/Qt5WebSockets.dll"
|
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
||||||
"${QTDIR}/bin/Qt5Network.dll"
|
|
||||||
|
# If config is RelWithDebInfo, package release files
|
||||||
|
COMMAND if $<CONFIG:RelWithDebInfo>==1 (
|
||||||
|
"${CMAKE_COMMAND}" -E make_directory
|
||||||
|
"${RELEASE_DIR}/data/obs-plugins/obs-websocket"
|
||||||
|
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
||||||
|
|
||||||
|
COMMAND if $<CONFIG:RelWithDebInfo>==1 ("${CMAKE_COMMAND}" -E copy_directory
|
||||||
|
"${PROJECT_SOURCE_DIR}/data"
|
||||||
|
"${RELEASE_DIR}/data/obs-plugins/obs-websocket")
|
||||||
|
|
||||||
|
COMMAND if $<CONFIG:RelWithDebInfo>==1 ("${CMAKE_COMMAND}" -E copy
|
||||||
|
"$<TARGET_FILE:obs-websocket>"
|
||||||
|
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
||||||
|
|
||||||
|
COMMAND if $<CONFIG:RelWithDebInfo>==1 ("${CMAKE_COMMAND}" -E copy
|
||||||
|
"$<TARGET_PDB_FILE:obs-websocket>"
|
||||||
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
||||||
|
|
||||||
# Copy to obs-studio dev environment for immediate testing
|
# Copy to obs-studio dev environment for immediate testing
|
||||||
COMMAND if $<CONFIG:Debug>==1 (
|
COMMAND if $<CONFIG:Debug>==1 (
|
||||||
"${CMAKE_COMMAND}" -E copy
|
"${CMAKE_COMMAND}" -E copy
|
||||||
"$<TARGET_FILE:obs-websocket>"
|
"$<TARGET_FILE:obs-websocket>"
|
||||||
"${QTDIR}/bin/Qt5WebSocketsd.dll"
|
"${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$<CONFIG>/obs-plugins/${ARCH_NAME}")
|
||||||
"${QTDIR}/bin/Qt5Networkd.dll"
|
|
||||||
|
COMMAND if $<CONFIG:Debug>==1 (
|
||||||
|
"${CMAKE_COMMAND}" -E copy
|
||||||
|
"$<TARGET_PDB_FILE:obs-websocket>"
|
||||||
"${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$<CONFIG>/obs-plugins/${ARCH_NAME}")
|
"${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$<CONFIG>/obs-plugins/${ARCH_NAME}")
|
||||||
|
|
||||||
COMMAND if $<CONFIG:Debug>==1 (
|
COMMAND if $<CONFIG:Debug>==1 (
|
||||||
@ -147,17 +165,18 @@ endif()
|
|||||||
|
|
||||||
# --- Linux-specific build settings and tasks ---
|
# --- Linux-specific build settings and tasks ---
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
|
||||||
|
|
||||||
target_compile_options(mbedcrypto PRIVATE -fPIC)
|
|
||||||
set_target_properties(obs-websocket PROPERTIES PREFIX "")
|
set_target_properties(obs-websocket PROPERTIES PREFIX "")
|
||||||
target_link_libraries(obs-websocket
|
target_link_libraries(obs-websocket obs-frontend-api)
|
||||||
obs-frontend-api)
|
|
||||||
|
|
||||||
file(GLOB locale_files data/locale/*.ini)
|
file(GLOB locale_files data/locale/*.ini)
|
||||||
|
execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE UNAME_MACHINE)
|
||||||
|
|
||||||
install(TARGETS obs-websocket
|
install(TARGETS obs-websocket
|
||||||
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/obs-plugins")
|
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/obs-plugins")
|
||||||
|
# Dirty fix for Ubuntu
|
||||||
|
install(TARGETS obs-websocket
|
||||||
|
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/${UNAME_MACHINE}-linux-gnu/obs-plugins")
|
||||||
|
|
||||||
install(FILES ${locale_files}
|
install(FILES ${locale_files}
|
||||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/obs-websocket/locale")
|
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/obs-websocket/locale")
|
||||||
endif()
|
endif()
|
||||||
@ -167,6 +186,7 @@ endif()
|
|||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -fvisibility=default")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -fvisibility=default")
|
||||||
|
|
||||||
|
set(CMAKE_SKIP_RPATH TRUE)
|
||||||
set_target_properties(obs-websocket PROPERTIES PREFIX "")
|
set_target_properties(obs-websocket PROPERTIES PREFIX "")
|
||||||
target_link_libraries(obs-websocket "${OBS_FRONTEND_LIB}")
|
target_link_libraries(obs-websocket "${OBS_FRONTEND_LIB}")
|
||||||
endif()
|
endif()
|
||||||
|
109
README.md
109
README.md
@ -1,26 +1,31 @@
|
|||||||
obs-websocket
|
obs-websocket
|
||||||
==============
|
==============
|
||||||
Remote control of OBS Studio made easy.
|
|
||||||
|
|
||||||
Follow the project on Twitter for news & updates : [@obswebsocket](https://twitter.com/obswebsocket)
|
WebSockets API for OBS Studio.
|
||||||
|
|
||||||
[](https://gitter.im/obs-websocket/obs-websocket) [](https://ci.appveyor.com/project/Palakis/obs-websocket/history) [](https://travis-ci.org/Palakis/obs-websocket)
|
Follow the main author on Twitter for news & updates : [@LePalakis](https://twitter.com/LePalakis)
|
||||||
|
|
||||||
|
[](https://ci.appveyor.com/project/Palakis/obs-websocket/history) [](https://travis-ci.org/Palakis/obs-websocket)
|
||||||
|
|
||||||
## Downloads
|
## Downloads
|
||||||
|
|
||||||
Binaries for Windows and Linux are available in the [Releases](https://github.com/Palakis/obs-websocket/releases) section.
|
Binaries for Windows and Linux are available in the [Releases](https://github.com/Palakis/obs-websocket/releases) section.
|
||||||
|
|
||||||
## Using 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/
|
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/
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
### Possible use cases
|
### Possible use cases
|
||||||
|
|
||||||
- Remote control OBS from a phone or tablet on the same local network
|
- Remote control OBS from a phone or tablet on the same local network
|
||||||
- Change your stream overlay/graphics based on the current scene (like the AGDQ overlay does)
|
- Change your stream overlay/graphics based on the current scene (like the AGDQ overlay does)
|
||||||
- Automate scene switching with a third-party program (e.g. : auto-pilot, foot pedal, ...)
|
- Automate scene switching with a third-party program (e.g. : auto-pilot, foot pedal, ...)
|
||||||
|
|
||||||
### For developers
|
### For developers
|
||||||
The server is a typical Websockets server running by default on port 4444 (the port number can be changed in the Settings dialog).
|
|
||||||
|
The server is a typical Websockets server running by default on port 4444 (the port number can be changed in the Settings dialog).
|
||||||
The protocol understood by the server is documented in [PROTOCOL.md](docs/generated/protocol.md).
|
The protocol understood by the server is documented in [PROTOCOL.md](docs/generated/protocol.md).
|
||||||
|
|
||||||
Here's a list of available language APIs for obs-websocket :
|
Here's a list of available language APIs for obs-websocket :
|
||||||
@ -28,38 +33,94 @@ Here's a list of available language APIs for obs-websocket :
|
|||||||
- C#/VB.NET: [obs-websocket-dotnet](https://github.com/Palakis/obs-websocket-dotnet)
|
- C#/VB.NET: [obs-websocket-dotnet](https://github.com/Palakis/obs-websocket-dotnet)
|
||||||
- Python 2 and 3: [obs-websocket-py](https://github.com/Elektordi/obs-websocket-py) by Guillaume Genty a.k.a Elektordi
|
- Python 2 and 3: [obs-websocket-py](https://github.com/Elektordi/obs-websocket-py) by Guillaume Genty a.k.a Elektordi
|
||||||
- Python 3.5+ with asyncio: [obs-ws-rc](https://github.com/KirillMysnik/obs-ws-rc) by Kirill Mysnik
|
- Python 3.5+ with asyncio: [obs-ws-rc](https://github.com/KirillMysnik/obs-ws-rc) by Kirill Mysnik
|
||||||
|
- Java 8+: [obs-websocket-java](https://github.com/Twasi/websocket-obs-java) by TwasiNET
|
||||||
|
|
||||||
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 `contact at slepin dot fr` !
|
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` !
|
||||||
|
|
||||||
## Compiling obs-websocket
|
## Compiling obs-websocket
|
||||||
|
|
||||||
See the [build instructions](BUILDING.md).
|
See the [build instructions](BUILDING.md).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
### Branches
|
||||||
|
|
||||||
|
The two main development branches are:
|
||||||
|
|
||||||
|
- `4.x-current`: actively-maintained codebase for 4.x releases. Backwards-compatible (unless stated otherwise) with existing clients until 5.0.
|
||||||
|
- `5.x`: upcoming 5.0 version
|
||||||
|
|
||||||
|
**New features and fixes must be based off and contributed to `4.x-current`**, as obs-websocket 5.0 is not in active development yet.
|
||||||
|
|
||||||
|
### Pull Requests
|
||||||
|
|
||||||
|
Pull Requests must never be based off your fork's main branch (in our case, `4.x-current` or `5.x`). Start your work in a new branch
|
||||||
|
based on the main one (e.g.: `cool-new-feature`, `fix-palakis-mistakes`, ...) and open a Pull Request once you feel ready to show your work.
|
||||||
|
|
||||||
|
If your Pull Request is not ready to merge yet, tag it with the `work in progress` label. You can also use the `help needed` label if you have questions, need a hand or want to ask for input.
|
||||||
|
|
||||||
|
### 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();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
**We need your help 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
|
## Special thanks
|
||||||
In order of appearance:
|
|
||||||
- [Brendan H.](https://github.com/haganbmj) : Code contributions and gooder English in the Protocol specification
|
In (almost) order of appearance:
|
||||||
- [Mikhail Swift](https://github.com/mikhailswift) : Code contributions
|
|
||||||
- [Tobias Frahmer](https://github.com/Frahmer) : German localization
|
- [Brendan H.](https://github.com/haganbmj): Code contributions and gooder English in the Protocol specification
|
||||||
- [Genture](https://github.com/Genteure) : Simplified Chinese and Traditional Chinese localizations
|
- [Mikhail Swift](https://github.com/mikhailswift): Code contributions
|
||||||
- [Larissa Gabilan](https://github.com/laris151) : Portuguese localization
|
- [Tobias Frahmer](https://github.com/Frahmer): Initial German localization
|
||||||
- [Andy Asquelt](https://github.com/asquelt) : Polish localization
|
- [Genture](https://github.com/Genteure): Initial Simplified Chinese and Traditional Chinese localizations
|
||||||
- [Marcel Haazen](https://github.com/inpothet) : Dutch localization
|
- [Larissa Gabilan](https://github.com/laris151): Initial Portuguese localization
|
||||||
- [Peter Antonvich](https://github.com/pantonvich) : Code contributions
|
- [Andy Asquelt](https://github.com/asquelt): Initial Polish localization
|
||||||
- [yinzara](https://github.com/yinzara) : Code contributions
|
- [Marcel Haazen](https://github.com/nekocentral): Initial Dutch localization
|
||||||
- [Chris Angelico](https://github.com/Rosuav) : Code contributions
|
- [Peter Antonvich](https://github.com/pantonvich): Code contributions
|
||||||
- [Guillaume "Elektordi" Genty](https://github.com/Elektordi) : Code contributions
|
- [yinzara](https://github.com/yinzara): Code contributions
|
||||||
- [Marwin M](https://github.com/dragonbane0) : Code contributions
|
- [Chris Angelico](https://github.com/Rosuav): Code contributions
|
||||||
- [Logan S.](https://github.com/lsdaniel) : Code contributions
|
- [Guillaume "Elektordi" Genty](https://github.com/Elektordi): Code contributions
|
||||||
- [RainbowEK](https://github.com/RainbowEK) : Code contributions
|
- [Marwin M](https://github.com/dragonbane0): Code contributions
|
||||||
- [RytoEX](https://github.com/RytoEX) : CI script and code contributions
|
- [Logan S.](https://github.com/lsdaniel): Code contributions
|
||||||
- [Theodore Stoddard](https://github.com/TStod) : Code contributions
|
- [RainbowEK](https://github.com/RainbowEK): Code contributions
|
||||||
- [Philip Loche](https://github.com/PicoCentauri) : 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
|
||||||
|
|
||||||
And also: special thanks to supporters of the project!
|
And also: special thanks to supporters of the project!
|
||||||
|
|
||||||
## Supporters
|
## Supporters
|
||||||
|
|
||||||
They have contributed financially to the project and made possible the addition of several features into obs-websocket. Many thanks to them!
|
They have contributed financially to the project and made possible the addition of several features into obs-websocket. Many thanks to them!
|
||||||
|
|
||||||
---
|
---
|
||||||
|
22
appveyor.yml
22
appveyor.yml
@ -4,20 +4,20 @@ environment:
|
|||||||
install:
|
install:
|
||||||
- git submodule update --init --recursive
|
- git submodule update --init --recursive
|
||||||
- cd C:\projects\
|
- cd C:\projects\
|
||||||
- if not exist dependencies2013.zip curl -kLO https://obsproject.com/downloads/dependencies2013.zip -f --retry 5 -C -
|
- if not exist dependencies2015.zip curl -kLO https://obsproject.com/downloads/dependencies2015.zip -f --retry 5 -C -
|
||||||
- 7z x dependencies2013.zip -odependencies2013
|
- 7z x dependencies2015.zip -odependencies2015
|
||||||
- set DepsPath32=%CD%\dependencies2013\win32
|
- set DepsPath32=%CD%\dependencies2015\win32
|
||||||
- set DepsPath64=%CD%\dependencies2013\win64
|
- set DepsPath64=%CD%\dependencies2015\win64
|
||||||
- call C:\projects\obs-websocket\CI\install-setup-qt.cmd
|
- call C:\projects\obs-websocket\CI\install-setup-qt.cmd
|
||||||
- set build_config=Release
|
- set build_config=RelWithDebInfo
|
||||||
- call C:\projects\obs-websocket\CI\install-build-obs.cmd
|
- call C:\projects\obs-websocket\CI\install-build-obs.cmd
|
||||||
- cd C:\projects\obs-websocket\
|
- cd C:\projects\obs-websocket\
|
||||||
- mkdir build32
|
- mkdir build32
|
||||||
- mkdir build64
|
- mkdir build64
|
||||||
- cd ./build32
|
- cd ./build32
|
||||||
- cmake -G "Visual Studio 12 2013" -DQTDIR="%QTDIR32%" -DLibObs_DIR="C:\projects\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="C:\projects\obs-studio\libobs" -DLIBOBS_LIB="C:\projects\obs-studio\build32\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="C:\projects\obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
|
- cmake -G "Visual Studio 14 2015" -DQTDIR="%QTDIR32%" -DLibObs_DIR="C:\projects\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="C:\projects\obs-studio\libobs" -DLIBOBS_LIB="C:\projects\obs-studio\build32\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="C:\projects\obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
|
||||||
- cd ../build64
|
- cd ../build64
|
||||||
- cmake -G "Visual Studio 12 2013 Win64" -DQTDIR="%QTDIR64%" -DLibObs_DIR="C:\projects\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="C:\projects\obs-studio\libobs" -DLIBOBS_LIB="C:\projects\obs-studio\build64\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="C:\projects\obs-studio\build64\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
|
- cmake -G "Visual Studio 14 2015 Win64" -DQTDIR="%QTDIR64%" -DLibObs_DIR="C:\projects\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="C:\projects\obs-studio\libobs" -DLIBOBS_LIB="C:\projects\obs-studio\build64\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="C:\projects\obs-studio\build64\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- call msbuild /m /p:Configuration=%build_config% C:\projects\obs-websocket\build32\obs-websocket.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
- call msbuild /m /p:Configuration=%build_config% C:\projects\obs-websocket\build32\obs-websocket.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
@ -25,14 +25,16 @@ build_script:
|
|||||||
|
|
||||||
before_deploy:
|
before_deploy:
|
||||||
- 7z a "C:\projects\obs-websocket\build.zip" C:\projects\obs-websocket\release\*
|
- 7z a "C:\projects\obs-websocket\build.zip" C:\projects\obs-websocket\release\*
|
||||||
|
- set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5"
|
||||||
|
- iscc "C:\projects\obs-websocket\installer\installer.iss"
|
||||||
|
|
||||||
deploy_script:
|
deploy_script:
|
||||||
- ps: Push-AppveyorArtifact "C:\projects\obs-websocket\build.zip" -FileName "obs-websocket-$(git log --pretty=format:'%h' -n 1).zip"
|
- ps: Push-AppveyorArtifact "C:\projects\obs-websocket\build.zip" -FileName "obs-websocket-$(git log --pretty=format:'%h' -n 1)-Windows.zip"
|
||||||
|
- ps: Push-AppveyorArtifact "C:\projects\obs-websocket\installer\Output\obs-websocket-Windows-Installer.exe" -FileName "obs-websocket-$(git log --pretty=format:'%h' -n 1)-Windows-Installer.exe"
|
||||||
|
|
||||||
test: off
|
test: off
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
- C:\projects\dependencies2013.zip
|
- C:\projects\dependencies2015.zip
|
||||||
- C:\projects\qt570.7z
|
|
||||||
- C:\projects\obs-studio-last-tag-built.txt
|
- C:\projects\obs-studio-last-tag-built.txt
|
||||||
- C:\projects\obs-studio\
|
- C:\projects\obs-studio\
|
||||||
|
23
azure-pipelines.yml
Normal file
23
azure-pipelines.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
pool:
|
||||||
|
vmImage: 'macOS-10.13'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- checkout: self
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- script: ./CI/install-dependencies-macos.sh
|
||||||
|
displayName: 'Install Dependencies'
|
||||||
|
|
||||||
|
- script: ./CI/install-build-obs-macos.sh
|
||||||
|
displayName: 'Build OBS'
|
||||||
|
|
||||||
|
- script: ./CI/build-macos.sh
|
||||||
|
displayName: 'Build obs-websocket'
|
||||||
|
|
||||||
|
- script: ./CI/package-macos.sh
|
||||||
|
displayName: 'Package'
|
||||||
|
|
||||||
|
- task: PublishBuildArtifacts@1
|
||||||
|
inputs:
|
||||||
|
pathtoPublish: './release'
|
||||||
|
artifactName: 'build'
|
0
data/locale/ar-SA.ini
Normal file
0
data/locale/ar-SA.ini
Normal file
@ -1,14 +1,16 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Websocket-Server Einstellungen"
|
OBSWebsocket.Settings.DialogTitle="WebSockets-Servereinstellungen"
|
||||||
OBSWebsocket.Settings.DialogTitle="Websocket-Server Einstellungen"
|
OBSWebsocket.Settings.ServerEnable="WebSockets-Server aktivieren"
|
||||||
OBSWebsocket.Settings.ServerEnable="Websocket-Server aktivieren"
|
OBSWebsocket.Settings.ServerPort="Server-Port"
|
||||||
OBSWebsocket.Settings.ServerPort="Server Port"
|
OBSWebsocket.Settings.AuthRequired="Authentifizierung aktivieren"
|
||||||
OBSWebsocket.Settings.AuthRequired="Authentifizierung erforderlich"
|
|
||||||
OBSWebsocket.Settings.Password="Passwort"
|
OBSWebsocket.Settings.Password="Passwort"
|
||||||
OBSWebsocket.Settings.DebugEnable="Debug-Protokollierung aktivieren"
|
OBSWebsocket.Settings.DebugEnable="Debug-Protokollierung aktivieren"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Infobereich-Benachrichtigungen aktivieren"
|
OBSWebsocket.Settings.AlertsEnable="Infobereichbenachrichtigungen aktivieren"
|
||||||
OBSWebsocket.NotifyConnect.Title="Neue WebSocket Verbindung"
|
OBSWebsocket.NotifyConnect.Title="Neue Websocket-Verbindung"
|
||||||
OBSWebsocket.NotifyConnect.Message="Client %1 verbunden"
|
OBSWebsocket.NotifyConnect.Message="Client %1 verbunden"
|
||||||
OBSWebsocket.NotifyDisconnect.Title="WebSocket-Client getrennt"
|
OBSWebsocket.NotifyDisconnect.Title="Websocket-Client getrennt"
|
||||||
OBSWebsocket.NotifyDisconnect.Message="Client %1 getrennt"
|
OBSWebsocket.NotifyDisconnect.Message="Client %1 getrennt"
|
||||||
OBSWebsocket.Server.StartFailed.Title="WebSocket-Server Fehler"
|
OBSWebsocket.Server.StartFailed.Title="Websocket-Serverfehler"
|
||||||
OBSWebsocket.Server.StartFailed.Message="Der WebSocket-Server konnte nicht gestartet werden, mögliche Gründe:\n - TCP Port %1 wird möglicherweise gerade von einem anderen Programm verwendet. Versuchen Sie einen anderen Port in den WebSocket-Server Einstellungen zu setzten oder alle Programme zu beenden, die den Port möglicherweise verwenden.\n - Ein unbekannter Netzwerkfehler ist aufgetreten. Versuchen Sie es erneut mit anderen Einstellungen, einem OBS neustart oder einem System neustart."
|
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.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."
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Websocket server settings"
|
OBSWebsocket.Settings.DialogTitle="WebSockets Server Settings"
|
||||||
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
OBSWebsocket.Settings.ServerEnable="Enable WebSockets server"
|
||||||
OBSWebsocket.Settings.ServerEnable="Enable Websocket server"
|
|
||||||
OBSWebsocket.Settings.ServerPort="Server Port"
|
OBSWebsocket.Settings.ServerPort="Server Port"
|
||||||
OBSWebsocket.Settings.AuthRequired="Enable authentication"
|
OBSWebsocket.Settings.AuthRequired="Enable authentication"
|
||||||
OBSWebsocket.Settings.Password="Password"
|
OBSWebsocket.Settings.Password="Password"
|
||||||
@ -10,5 +9,8 @@ OBSWebsocket.NotifyConnect.Title="New WebSocket connection"
|
|||||||
OBSWebsocket.NotifyConnect.Message="Client %1 connected"
|
OBSWebsocket.NotifyConnect.Message="Client %1 connected"
|
||||||
OBSWebsocket.NotifyDisconnect.Title="WebSocket client disconnected"
|
OBSWebsocket.NotifyDisconnect.Title="WebSocket client disconnected"
|
||||||
OBSWebsocket.NotifyDisconnect.Message="Client %1 disconnected"
|
OBSWebsocket.NotifyDisconnect.Message="Client %1 disconnected"
|
||||||
OBSWebsocket.Server.StartFailed.Title="WebSocket Server failure"
|
OBSWebsocket.Server.StartFailed.Title="WebSockets Server failure"
|
||||||
OBSWebsocket.Server.StartFailed.Message="The obs-websocket server failed to start, maybe because:\n - TCP port %1 may currently be in use elsewhere on this system, possibly by another application. Try setting a different TCP port in the WebSocket server settings, or stop any application that could be using this port.\n - An unknown network error happened on your system. Try again by changing settings, restarting OBS or restarting your system."
|
OBSWebsocket.Server.StartFailed.Message="The WebSockets server failed to start, maybe because:\n - TCP port %1 may currently be in use elsewhere on this system, possibly by another application. Try setting a different TCP port in the WebSocket server settings, or stop any application that could be using this port.\n - An unknown network error happened on your system. Try again by changing settings, restarting OBS or restarting your system."
|
||||||
|
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."
|
@ -1,14 +1,12 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Configuración del servidor obs-websocket"
|
OBSWebsocket.Settings.ServerEnable="Habilitar el servidor WebSockets"
|
||||||
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
|
||||||
OBSWebsocket.Settings.ServerEnable="Habilitar el servidor Websocket"
|
|
||||||
OBSWebsocket.Settings.ServerPort="Puerto del Servidor"
|
OBSWebsocket.Settings.ServerPort="Puerto del Servidor"
|
||||||
OBSWebsocket.Settings.AuthRequired="Habilitar autenticación"
|
OBSWebsocket.Settings.AuthRequired="Habilitar autenticación"
|
||||||
OBSWebsocket.Settings.Password="Contraseña"
|
OBSWebsocket.Settings.Password="Contraseña"
|
||||||
OBSWebsocket.Settings.DebugEnable="Habilitar registro de depuración"
|
OBSWebsocket.Settings.DebugEnable="Habilitar registro de depuración"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Habilitar alertas de la bandeja de sistema"
|
OBSWebsocket.Settings.AlertsEnable="Habilitar alertas en la bandeja de sistema"
|
||||||
OBSWebsocket.NotifyConnect.Title="Nueva conexión WebSocket"
|
OBSWebsocket.NotifyConnect.Title="Nueva conexión WebSocket"
|
||||||
OBSWebsocket.NotifyConnect.Message="Cliente %1 conectado"
|
OBSWebsocket.NotifyConnect.Message="Cliente %1 conectado"
|
||||||
OBSWebsocket.NotifyDisconnect.Title="Cliente WebSocket desconectado"
|
OBSWebsocket.NotifyDisconnect.Title="Cliente WebSocket desconectado"
|
||||||
OBSWebsocket.NotifyDisconnect.Message="Cliente %1 desconectado"
|
OBSWebsocket.NotifyDisconnect.Message="Cliente %1 desconectado"
|
||||||
OBSWebsocket.Server.StartFailed.Title="Fallo del servidor WebSocket"
|
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 en uso en 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 ocurrido en su sistema. Inténtalo de nuevo cambiando la configuración, reiniciando OBS o reiniciando su sistema."
|
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."
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Paramètres du serveur Websocket"
|
OBSWebsocket.Settings.DialogTitle="Paramètres du serveur WebSockets"
|
||||||
OBSWebsocket.Settings.ServerEnable="Activer le serveur WebSocket"
|
OBSWebsocket.Settings.ServerEnable="Activer le serveur WebSocket"
|
||||||
OBSWebsocket.Settings.ServerPort="Port du serveur"
|
OBSWebsocket.Settings.ServerPort="Port du serveur"
|
||||||
OBSWebsocket.Settings.AuthRequired="Activer l'authentification"
|
OBSWebsocket.Settings.AuthRequired="Activer l'authentification"
|
||||||
@ -9,5 +9,8 @@ OBSWebsocket.NotifyConnect.Title="Nouvelle connexion WebSocket"
|
|||||||
OBSWebsocket.NotifyConnect.Message="Le client %1 s'est connecté"
|
OBSWebsocket.NotifyConnect.Message="Le client %1 s'est connecté"
|
||||||
OBSWebsocket.NotifyDisconnect.Title="Déconnexion WebSocket"
|
OBSWebsocket.NotifyDisconnect.Title="Déconnexion WebSocket"
|
||||||
OBSWebsocket.NotifyDisconnect.Message="Le client %1 s'est déconnecté"
|
OBSWebsocket.NotifyDisconnect.Message="Le client %1 s'est déconnecté"
|
||||||
OBSWebsocket.Server.StartFailed.Title="Impossible de démarrer le serveur WebSocket"
|
OBSWebsocket.Server.StartFailed.Title="Impossible de démarrer le serveur WebSockets"
|
||||||
OBSWebsocket.Server.StartFailed.Message="Le serveur WebSocket 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.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é."
|
||||||
|
0
data/locale/hi-IN.ini
Normal file
0
data/locale/hi-IN.ini
Normal file
@ -1,6 +1,4 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Impostazioni del server di WebSocket"
|
OBSWebsocket.Settings.ServerEnable="Abilitare il server WebSockets"
|
||||||
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
|
||||||
OBSWebsocket.Settings.ServerEnable="Abilitare il server Websocket"
|
|
||||||
OBSWebsocket.Settings.ServerPort="Porta del server"
|
OBSWebsocket.Settings.ServerPort="Porta del server"
|
||||||
OBSWebsocket.Settings.AuthRequired="Abilitare l'autenticazione"
|
OBSWebsocket.Settings.AuthRequired="Abilitare l'autenticazione"
|
||||||
OBSWebsocket.Settings.Password="Password"
|
OBSWebsocket.Settings.Password="Password"
|
||||||
@ -11,4 +9,4 @@ OBSWebsocket.NotifyConnect.Message="%1 cliente collegato"
|
|||||||
OBSWebsocket.NotifyDisconnect.Title="WebSocket cliente disconnesso"
|
OBSWebsocket.NotifyDisconnect.Title="WebSocket cliente disconnesso"
|
||||||
OBSWebsocket.NotifyDisconnect.Message="%1 cliente disconnesso"
|
OBSWebsocket.NotifyDisconnect.Message="%1 cliente disconnesso"
|
||||||
OBSWebsocket.Server.StartFailed.Title="Errore del WebSocket Server"
|
OBSWebsocket.Server.StartFailed.Title="Errore del WebSocket Server"
|
||||||
OBSWebsocket.Server.StartFailed.Message="Impossibile avviare, forse perché il server di obs-websocket: \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 WebSocket, 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="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."
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Websocket サーバー設定"
|
OBSWebsocket.Settings.ServerEnable="WebSockets サーバーを有効にする"
|
||||||
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
|
||||||
OBSWebsocket.Settings.ServerEnable="Websocket サーバーを有効にする"
|
|
||||||
OBSWebsocket.Settings.ServerPort="サーバーポート"
|
OBSWebsocket.Settings.ServerPort="サーバーポート"
|
||||||
OBSWebsocket.Settings.AuthRequired="認証を有効にする"
|
OBSWebsocket.Settings.AuthRequired="認証を有効にする"
|
||||||
OBSWebsocket.Settings.Password="パスワード"
|
OBSWebsocket.Settings.Password="パスワード"
|
||||||
OBSWebsocket.Settings.DebugEnable="デバッグログを有効にする"
|
OBSWebsocket.Settings.DebugEnable="デバッグログを有効にする"
|
||||||
OBSWebsocket.NotifyConnect.Title="新しいWebSocket接続"
|
OBSWebsocket.Settings.AlertsEnable="システムトレイ通知を有効にする"
|
||||||
|
OBSWebsocket.NotifyConnect.Title="新しい WebSocket 接続"
|
||||||
|
OBSWebsocket.NotifyConnect.Message="接続されているクライアント %1"
|
||||||
|
OBSWebsocket.NotifyDisconnect.Title="WebSocket クライアントが切断されました"
|
||||||
|
OBSWebsocket.NotifyDisconnect.Message="切断されたクライアント %1"
|
||||||
|
OBSWebsocket.Server.StartFailed.Title="WebSockets サーバー障害"
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Websocket server instellingen"
|
|
||||||
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
|
||||||
OBSWebsocket.Settings.ServerEnable="Activeer Websocket server"
|
|
||||||
OBSWebsocket.Settings.ServerPort="Serverpoort"
|
OBSWebsocket.Settings.ServerPort="Serverpoort"
|
||||||
OBSWebsocket.Settings.AuthRequired="Activeer authenticatie"
|
|
||||||
OBSWebsocket.Settings.Password="Wachtwoord"
|
|
||||||
OBSWebsocket.Settings.DebugEnable="Activeer debug logs"
|
OBSWebsocket.Settings.DebugEnable="Activeer debug logs"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Systemvak waarschuwingen inschakelen"
|
OBSWebsocket.Settings.AlertsEnable="Systemvak waarschuwingen inschakelen"
|
||||||
OBSWebsocket.NotifyConnect.Title="Nieuwe WebSocket verbinding"
|
OBSWebsocket.NotifyConnect.Title="Nieuwe WebSocket verbinding"
|
||||||
OBSWebsocket.NotifyConnect.Message="Client %1 verbonden"
|
OBSWebsocket.NotifyConnect.Message="Client %1 verbonden"
|
||||||
OBSWebsocket.NotifyDisconnect.Title="WebSocket client connectie verbroken"
|
OBSWebsocket.NotifyDisconnect.Title="WebSocket client connectie verbroken"
|
||||||
OBSWebsocket.NotifyDisconnect.Message="Client %1 losgekoppeld"
|
OBSWebsocket.NotifyDisconnect.Message="Client %1 losgekoppeld"
|
||||||
OBSWebsocket.Server.StartFailed.Title="WebSocket Server mislukt"
|
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 wordt gebruikt elders op dit systeem, eventueel door een andere toepassing. Probeer een andere TCP-poort instellen in de WebSocket Server-instellingen of stoppen van elke toepassing die deze poort zouden kunnen gebruiken.\n Een onbekende netwerkfout gebeurde op uw systeem. Probeer het opnieuw door de instellingen wijzigen, OBS herstarten of opnieuw opstarten van uw systeem."
|
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."
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Ustawienia serwera zdalnego sterowania"
|
OBSWebsocket.Settings.ServerEnable="Włącz serwer WebSockets"
|
||||||
OBSWebsocket.Settings.DialogTitle="Serwer zdalnego sterowania"
|
OBSWebsocket.Settings.AuthRequired="Wymagaj uwierzytelniania"
|
||||||
OBSWebsocket.Settings.ServerEnable="Włącz serwer zdalnego sterowania (Websocket)"
|
|
||||||
OBSWebsocket.Settings.ServerPort="Port serwera"
|
|
||||||
OBSWebsocket.Settings.AuthRequired="Wymagaj hasła"
|
|
||||||
OBSWebsocket.Settings.Password="Hasło"
|
|
||||||
OBSWebsocket.Settings.DebugEnable="Włącz rejestrowanie debugowania"
|
OBSWebsocket.Settings.DebugEnable="Włącz rejestrowanie debugowania"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Włącz powiadomienia o zasobniku systemowym"
|
OBSWebsocket.Settings.AlertsEnable="Włącz powiadomienia w zasobniku systemowym"
|
||||||
OBSWebsocket.NotifyConnect.Title="Nowe połączenie WebSocket"
|
OBSWebsocket.NotifyConnect.Title="Nowe połączenie WebSocket"
|
||||||
OBSWebsocket.NotifyConnect.Message="Klient %1 połączony"
|
OBSWebsocket.NotifyConnect.Message="Klient %1 połączony"
|
||||||
OBSWebsocket.NotifyDisconnect.Title="Klient WebSocket odłączony"
|
OBSWebsocket.NotifyDisconnect.Title="Klient WebSocket odłączony"
|
||||||
OBSWebsocket.NotifyDisconnect.Message="Klient %1 połączony"
|
OBSWebsocket.NotifyDisconnect.Message="Klient %1 rozłączony"
|
||||||
OBSWebsocket.Server.StartFailed.Title="Awaria serwera WebSocket"
|
OBSWebsocket.Server.StartFailed.Title="Awaria serwera WebSockets"
|
||||||
OBSWebsocket.Server.StartFailed.Message="Nie udało się uruchomić serwera obs-websocket, 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 WebSocket, 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.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."
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Configuraçes do Servidor Websocket"
|
|
||||||
OBSWebsocket.Settings.ServerEnable="Habilitar o Servidor Websocket"
|
|
||||||
OBSWebsocket.Settings.ServerPort="Porta do Servidor"
|
|
||||||
OBSWebsocket.Settings.AuthRequired="Autenticação Requerida"
|
|
||||||
OBSWebsocket.Settings.Password="Senha"
|
|
@ -1,5 +1,3 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Configurações do servidor Websocket"
|
|
||||||
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
|
||||||
OBSWebsocket.Settings.ServerEnable="Habilitar servidor de WebSockets"
|
OBSWebsocket.Settings.ServerEnable="Habilitar servidor de WebSockets"
|
||||||
OBSWebsocket.Settings.ServerPort="Porta do Servidor"
|
OBSWebsocket.Settings.ServerPort="Porta do Servidor"
|
||||||
OBSWebsocket.Settings.AuthRequired="Activar autenticação"
|
OBSWebsocket.Settings.AuthRequired="Activar autenticação"
|
||||||
@ -11,4 +9,4 @@ OBSWebsocket.NotifyConnect.Message="Cliente %1 conectado"
|
|||||||
OBSWebsocket.NotifyDisconnect.Title="WebSocket cliente desconectado"
|
OBSWebsocket.NotifyDisconnect.Title="WebSocket cliente desconectado"
|
||||||
OBSWebsocket.NotifyDisconnect.Message="Cliente %1 desconectado"
|
OBSWebsocket.NotifyDisconnect.Message="Cliente %1 desconectado"
|
||||||
OBSWebsocket.Server.StartFailed.Title="Falha do servidor de WebSocket"
|
OBSWebsocket.Server.StartFailed.Title="Falha do servidor de WebSocket"
|
||||||
OBSWebsocket.Server.StartFailed.Message="O servidor obs-websocket 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 WebSocket, 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, 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."
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Параметры сервера Websocket"
|
OBSWebsocket.Settings.DialogTitle="Настройки сервера WebSockets"
|
||||||
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
OBSWebsocket.Settings.ServerEnable="Включить сервер WebSockets"
|
||||||
OBSWebsocket.Settings.ServerEnable="Включить сервер Websocket"
|
|
||||||
OBSWebsocket.Settings.ServerPort="Порт сервера"
|
OBSWebsocket.Settings.ServerPort="Порт сервера"
|
||||||
OBSWebsocket.Settings.AuthRequired="Включить аутентификацию"
|
OBSWebsocket.Settings.AuthRequired="Включить авторизацию"
|
||||||
OBSWebsocket.Settings.Password="Пароль"
|
OBSWebsocket.Settings.Password="Пароль"
|
||||||
OBSWebsocket.Settings.DebugEnable="Включить ведение журнала отладки"
|
OBSWebsocket.Settings.DebugEnable="Включить ведение журнала отладки"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Включить оповещения в системном трее"
|
OBSWebsocket.Settings.AlertsEnable="Включить оповещения в системном трее"
|
||||||
@ -10,5 +9,5 @@ OBSWebsocket.NotifyConnect.Title="Новое соединение WebSocket"
|
|||||||
OBSWebsocket.NotifyConnect.Message="Клиент %1 подключен"
|
OBSWebsocket.NotifyConnect.Message="Клиент %1 подключен"
|
||||||
OBSWebsocket.NotifyDisconnect.Title="Клиент WebSocket отключён"
|
OBSWebsocket.NotifyDisconnect.Title="Клиент WebSocket отключён"
|
||||||
OBSWebsocket.NotifyDisconnect.Message="Клиент %1 отключен"
|
OBSWebsocket.NotifyDisconnect.Message="Клиент %1 отключен"
|
||||||
OBSWebsocket.Server.StartFailed.Title="Сбой сервера WebSocket"
|
OBSWebsocket.Server.StartFailed.Title="Сбой сервера WebSockets"
|
||||||
OBSWebsocket.Server.StartFailed.Message="Сбой запуска сервера obs-websocket. Вероятные причины:\n - Возможно, TCP-порт %1 занят другим приложением в системе. Попробуйте задать другой TCP-порт в настройках сервера WebSocket или закройте приложение, которое может использовать данный порт.\n - Произошла неизвестная сетевая ошибка в системе. Попробуйте снова после изменения настроек, перезапуска OBS или перезапуска системы."
|
OBSWebsocket.Server.StartFailed.Message="Ошибка запуска сервера WebSockets. Вероятные причины:\n - Возможно, TCP-порт %1 занят другим приложением в системе. Попробуйте задать другой TCP-порт в настройках сервера WebSockets или закройте приложение, которое может использовать данный порт.\n - Произошла неизвестная сетевая ошибка в системе. Попробуйте снова после изменения настроек, перезапуска OBS или системы."
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Websocket 服务器设置"
|
OBSWebsocket.Settings.ServerEnable="启用 WebSockets 服务器"
|
||||||
OBSWebsocket.Settings.DialogTitle="obs-websocket 设置"
|
|
||||||
OBSWebsocket.Settings.ServerEnable="启用 Websocket 服务器"
|
|
||||||
OBSWebsocket.Settings.ServerPort="服务器端口"
|
OBSWebsocket.Settings.ServerPort="服务器端口"
|
||||||
OBSWebsocket.Settings.AuthRequired="启用密码认证"
|
OBSWebsocket.Settings.AuthRequired="启用身份验证"
|
||||||
OBSWebsocket.Settings.Password="密码"
|
OBSWebsocket.Settings.Password="密码"
|
||||||
|
OBSWebsocket.Settings.DebugEnable="启用调试日志"
|
||||||
|
OBSWebsocket.Settings.AlertsEnable="启用系统托盘通知"
|
||||||
|
OBSWebsocket.NotifyConnect.Title="新 WebSocket 连接"
|
||||||
|
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 或重新启动系统。"
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
OBSWebsocket.Menu.SettingsItem="Websocket 伺服器設定"
|
OBSWebsocket.Settings.ServerPort="伺服器連接埠"
|
||||||
OBSWebsocket.Settings.DialogTitle="obs-websocket 設定"
|
OBSWebsocket.Settings.DebugEnable="啟用除錯日誌"
|
||||||
OBSWebsocket.Settings.ServerEnable="啟用 Websocket 伺服器"
|
OBSWebsocket.Settings.AlertsEnable="啟用系統列通知"
|
||||||
OBSWebsocket.Settings.ServerPort="伺服器端口"
|
OBSWebsocket.NotifyConnect.Title="新的 WebSocket 連線"
|
||||||
OBSWebsocket.Settings.AuthRequired="啟用密碼認證"
|
OBSWebsocket.NotifyConnect.Message="客戶端 %1 已連線"
|
||||||
OBSWebsocket.Settings.Password="密碼"
|
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 或是重新啟動您的系統。"
|
||||||
|
1
deps/asio
vendored
Submodule
1
deps/asio
vendored
Submodule
Submodule deps/asio added at b73dc1d2c0
1
deps/mbedtls
vendored
1
deps/mbedtls
vendored
Submodule deps/mbedtls deleted from 1a6a15c795
1
deps/websocketpp
vendored
Submodule
1
deps/websocketpp
vendored
Submodule
Submodule deps/websocketpp added at c6d7e295bf
@ -30,6 +30,14 @@ const processComments = comments => {
|
|||||||
let errors = [];
|
let errors = [];
|
||||||
|
|
||||||
comments.forEach(comment => {
|
comments.forEach(comment => {
|
||||||
|
if (comment.typedef) {
|
||||||
|
comment.comment = undefined;
|
||||||
|
comment.context = undefined;
|
||||||
|
sorted['typedefs'] = sorted['typedefs'] || [];
|
||||||
|
sorted['typedefs'].push(comment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof comment.api === 'undefined') return;
|
if (typeof comment.api === 'undefined') return;
|
||||||
let validationFailures = validateComment(comment);
|
let validationFailures = validateComment(comment);
|
||||||
|
|
||||||
@ -84,9 +92,7 @@ const validateComment = comment => {
|
|||||||
fullContext: Object.assign({}, comment)
|
fullContext: Object.assign({}, comment)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
};
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const files = glob.sync(config.srcGlob);
|
const files = glob.sync(config.srcGlob);
|
||||||
const comments = processComments(parseFiles(files));
|
const comments = processComments(parseFiles(files));
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,11 @@
|
|||||||
# obs-websocket 4.2.1 protocol reference
|
# obs-websocket 4.7.0 protocol reference
|
||||||
|
|
||||||
**This is the reference for the unreleased obs-websocket 4.2.1. See the list below for older versions.**
|
|
||||||
- [4.2.0 protocol reference](https://github.com/Palakis/obs-websocket/blob/4.2.0/docs/generated/protocol.md)
|
|
||||||
- [4.1.0 protocol reference](https://github.com/Palakis/obs-websocket/blob/4.1.0/PROTOCOL.md)
|
|
||||||
- [4.0.0 protocol reference](https://github.com/Palakis/obs-websocket/blob/4.0.0/PROTOCOL.md)
|
|
||||||
|
|
||||||
# General Introduction
|
# General Introduction
|
||||||
Messages are exchanged between the client and the server as JSON objects.
|
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.
|
||||||
|
|
||||||
|
|
||||||
# Authentication
|
# Authentication
|
||||||
OBSWebSocket uses SHA256 to transmit credentials.
|
`obs-websocket` uses SHA256 to transmit credentials.
|
||||||
|
|
||||||
A request for [`GetAuthRequired`](#getauthrequired) returns two elements:
|
A request for [`GetAuthRequired`](#getauthrequired) returns two elements:
|
||||||
- A `challenge`: a random string that will be used to generate the auth response.
|
- A `challenge`: a random string that will be used to generate the auth response.
|
||||||
|
2
docs/partials/typedefsHeader.md
Normal file
2
docs/partials/typedefsHeader.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Typedefs
|
||||||
|
These are complex types, such as `Source` and `Scene`, which are used as arguments or return values in multiple requests and/or events.
|
@ -7,6 +7,19 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{{#read "partials/typedefsHeader.md"}}{{/read}}
|
||||||
|
|
||||||
|
{{#each typedefs}}
|
||||||
|
## {{typedefs.0.name}}
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ---- | :---: | ------------|
|
||||||
|
{{#each properties}}
|
||||||
|
| `{{name}}` | _{{depipe type}}_ | {{{depipe description}}} |
|
||||||
|
{{/each}}
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{{#read "partials/eventsHeader.md"}}{{/read}}
|
{{#read "partials/eventsHeader.md"}}{{/read}}
|
||||||
|
|
||||||
{{#each events}}
|
{{#each events}}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||||
|
|
||||||
#define MyAppName "obs-websocket"
|
#define MyAppName "obs-websocket"
|
||||||
#define MyAppVersion "4.3.0"
|
#define MyAppVersion "4.7.0"
|
||||||
#define MyAppPublisher "St<EFBFBD>phane Lepin"
|
#define MyAppPublisher "Stephane Lepin"
|
||||||
#define MyAppURL "http://github.com/Palakis/obs-websocket"
|
#define MyAppURL "http://github.com/Palakis/obs-websocket"
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
@ -20,17 +20,16 @@ AppSupportURL={#MyAppURL}
|
|||||||
AppUpdatesURL={#MyAppURL}
|
AppUpdatesURL={#MyAppURL}
|
||||||
DefaultDirName={code:GetDirName}
|
DefaultDirName={code:GetDirName}
|
||||||
DefaultGroupName={#MyAppName}
|
DefaultGroupName={#MyAppName}
|
||||||
OutputBaseFilename=obs-websocket-{#MyAppVersion}-Windows-Installer
|
OutputBaseFilename=obs-websocket-Windows-Installer
|
||||||
Compression=lzma
|
Compression=lzma
|
||||||
SolidCompression=yes
|
SolidCompression=yes
|
||||||
LicenseFile=..\LICENSE
|
|
||||||
|
|
||||||
[Languages]
|
[Languages]
|
||||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||||
Name: "french"; MessagesFile: "compiler:Languages\French.isl"
|
|
||||||
|
|
||||||
[Files]
|
[Files]
|
||||||
Source: "..\release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
Source: "..\release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
|
Source: "..\LICENSE"; Flags: dontcopy
|
||||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||||
|
|
||||||
[Icons]
|
[Icons]
|
||||||
@ -38,17 +37,32 @@ Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}"
|
|||||||
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
|
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
|
||||||
|
|
||||||
[Code]
|
[Code]
|
||||||
|
procedure InitializeWizard();
|
||||||
|
var
|
||||||
|
GPLText: AnsiString;
|
||||||
|
Page: TOutputMsgMemoWizardPage;
|
||||||
|
begin
|
||||||
|
ExtractTemporaryFile('LICENSE');
|
||||||
|
LoadStringFromFile(ExpandConstant('{tmp}\LICENSE'), GPLText);
|
||||||
|
|
||||||
|
Page := CreateOutputMsgMemoPage(wpWelcome,
|
||||||
|
'License Information', 'Please review the license terms before installing obs-websocket',
|
||||||
|
'Press Page Down to see the rest of the agreement. Once you are aware of your rights, click Next to continue.',
|
||||||
|
String(GPLText)
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
|
||||||
// credit where it's due :
|
// credit where it's due :
|
||||||
// following function come from https://github.com/Xaymar/obs-studio_amf-encoder-plugin/blob/master/%23Resources/Installer.in.iss#L45
|
// following function come from https://github.com/Xaymar/obs-studio_amf-encoder-plugin/blob/master/%23Resources/Installer.in.iss#L45
|
||||||
function GetDirName(Value: string): string;
|
function GetDirName(Value: string): string;
|
||||||
var
|
var
|
||||||
InstallPath: string;
|
InstallPath: string;
|
||||||
begin
|
begin
|
||||||
// initialize default path, which will be returned when the following registry
|
// initialize default path, which will be returned when the following registry
|
||||||
// key queries fail due to missing keys or for some different reason
|
// key queries fail due to missing keys or for some different reason
|
||||||
Result := '{pf}\obs-studio';
|
Result := '{pf}\obs-studio';
|
||||||
// query the first registry value; if this succeeds, return the obtained value
|
// query the first registry value; if this succeeds, return the obtained value
|
||||||
if RegQueryStringValue(HKLM32, 'SOFTWARE\OBS Studio', '', InstallPath) then
|
if RegQueryStringValue(HKLM32, 'SOFTWARE\OBS Studio', '', InstallPath) then
|
||||||
Result := InstallPath
|
Result := InstallPath
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
348
src/Config.cpp
348
src/Config.cpp
@ -16,11 +16,11 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <mbedtls/base64.h>
|
|
||||||
#include <mbedtls/sha256.h>
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <util/config-file.h>
|
|
||||||
#include <string>
|
#include <QtCore/QCryptographicHash>
|
||||||
|
#include <QtCore/QTime>
|
||||||
|
#include <QtWidgets/QSystemTrayIcon>
|
||||||
|
|
||||||
#define SECTION_NAME "WebsocketAPI"
|
#define SECTION_NAME "WebsocketAPI"
|
||||||
#define PARAM_ENABLE "ServerEnabled"
|
#define PARAM_ENABLE "ServerEnabled"
|
||||||
@ -31,164 +31,256 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#define PARAM_SECRET "AuthSecret"
|
#define PARAM_SECRET "AuthSecret"
|
||||||
#define PARAM_SALT "AuthSalt"
|
#define PARAM_SALT "AuthSalt"
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
#include "WSServer.h"
|
||||||
|
|
||||||
Config* Config::_instance = new Config();
|
#include "Config.h"
|
||||||
|
|
||||||
|
#define QT_TO_UTF8(str) str.toUtf8().constData()
|
||||||
|
|
||||||
Config::Config() :
|
Config::Config() :
|
||||||
ServerEnabled(true),
|
ServerEnabled(true),
|
||||||
ServerPort(4444),
|
ServerPort(4444),
|
||||||
DebugEnabled(false),
|
DebugEnabled(false),
|
||||||
AlertsEnabled(true),
|
AlertsEnabled(true),
|
||||||
AuthRequired(false),
|
AuthRequired(false),
|
||||||
Secret(""),
|
Secret(""),
|
||||||
Salt(""),
|
Salt(""),
|
||||||
SettingsLoaded(false)
|
SettingsLoaded(false)
|
||||||
{
|
{
|
||||||
// OBS Config defaults
|
qsrand(QTime::currentTime().msec());
|
||||||
config_t* obsConfig = obs_frontend_get_global_config();
|
|
||||||
if (obsConfig) {
|
|
||||||
config_set_default_bool(obsConfig,
|
|
||||||
SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
|
||||||
config_set_default_uint(obsConfig,
|
|
||||||
SECTION_NAME, PARAM_PORT, ServerPort);
|
|
||||||
|
|
||||||
config_set_default_bool(obsConfig,
|
SetDefaults();
|
||||||
SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
SessionChallenge = GenerateSalt();
|
||||||
config_set_default_bool(obsConfig,
|
|
||||||
SECTION_NAME, PARAM_ALERT, AlertsEnabled);
|
|
||||||
|
|
||||||
config_set_default_bool(obsConfig,
|
obs_frontend_add_event_callback(OnFrontendEvent, this);
|
||||||
SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
|
||||||
config_set_default_string(obsConfig,
|
|
||||||
SECTION_NAME, PARAM_SECRET, qstring_data_copy(Secret));
|
|
||||||
config_set_default_string(obsConfig,
|
|
||||||
SECTION_NAME, PARAM_SALT, qstring_data_copy(Salt));
|
|
||||||
}
|
|
||||||
|
|
||||||
mbedtls_entropy_init(&entropy);
|
|
||||||
mbedtls_ctr_drbg_init(&rng);
|
|
||||||
mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &entropy, nullptr, 0);
|
|
||||||
|
|
||||||
SessionChallenge = GenerateSalt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::~Config() {
|
Config::~Config()
|
||||||
mbedtls_ctr_drbg_free(&rng);
|
{
|
||||||
mbedtls_entropy_free(&entropy);
|
obs_frontend_remove_event_callback(OnFrontendEvent, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Load() {
|
void Config::Load()
|
||||||
config_t* obsConfig = obs_frontend_get_global_config();
|
{
|
||||||
|
config_t* obsConfig = GetConfigStore();
|
||||||
|
|
||||||
ServerEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ENABLE);
|
ServerEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ENABLE);
|
||||||
ServerPort = config_get_uint(obsConfig, SECTION_NAME, PARAM_PORT);
|
ServerPort = config_get_uint(obsConfig, SECTION_NAME, PARAM_PORT);
|
||||||
|
|
||||||
DebugEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_DEBUG);
|
DebugEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_DEBUG);
|
||||||
AlertsEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ALERT);
|
AlertsEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ALERT);
|
||||||
|
|
||||||
AuthRequired = config_get_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED);
|
AuthRequired = config_get_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED);
|
||||||
Secret = config_get_string(obsConfig, SECTION_NAME, PARAM_SECRET);
|
Secret = config_get_string(obsConfig, SECTION_NAME, PARAM_SECRET);
|
||||||
Salt = config_get_string(obsConfig, SECTION_NAME, PARAM_SALT);
|
Salt = config_get_string(obsConfig, SECTION_NAME, PARAM_SALT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Save() {
|
void Config::Save()
|
||||||
config_t* obsConfig = obs_frontend_get_global_config();
|
{
|
||||||
|
config_t* obsConfig = GetConfigStore();
|
||||||
|
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
||||||
config_set_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort);
|
config_set_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
|
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_ALERT, AlertsEnabled);
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_ALERT, AlertsEnabled);
|
||||||
|
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
||||||
config_set_string(obsConfig, SECTION_NAME, PARAM_SECRET,
|
config_set_string(obsConfig, SECTION_NAME, PARAM_SECRET,
|
||||||
qstring_data_copy(Secret));
|
QT_TO_UTF8(Secret));
|
||||||
config_set_string(obsConfig, SECTION_NAME, PARAM_SALT,
|
config_set_string(obsConfig, SECTION_NAME, PARAM_SALT,
|
||||||
qstring_data_copy(Salt));
|
QT_TO_UTF8(Salt));
|
||||||
|
|
||||||
config_save(obsConfig);
|
config_save(obsConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Config::GenerateSalt() {
|
void Config::SetDefaults()
|
||||||
// Generate 32 random chars
|
{
|
||||||
unsigned char* randomChars = (unsigned char*)bzalloc(32);
|
// OBS Config defaults
|
||||||
mbedtls_ctr_drbg_random(&rng, randomChars, 32);
|
config_t* obsConfig = GetConfigStore();
|
||||||
|
if (obsConfig) {
|
||||||
|
config_set_default_bool(obsConfig,
|
||||||
|
SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
||||||
|
config_set_default_uint(obsConfig,
|
||||||
|
SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
|
|
||||||
// Convert the 32 random chars to a base64 string
|
config_set_default_bool(obsConfig,
|
||||||
char* salt = (char*)bzalloc(64);
|
SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
||||||
size_t saltBytes;
|
config_set_default_bool(obsConfig,
|
||||||
mbedtls_base64_encode(
|
SECTION_NAME, PARAM_ALERT, AlertsEnabled);
|
||||||
(unsigned char*)salt, 64, &saltBytes,
|
|
||||||
randomChars, 32);
|
|
||||||
|
|
||||||
bfree(randomChars);
|
config_set_default_bool(obsConfig,
|
||||||
return salt;
|
SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
||||||
|
config_set_default_string(obsConfig,
|
||||||
|
SECTION_NAME, PARAM_SECRET, QT_TO_UTF8(Secret));
|
||||||
|
config_set_default_string(obsConfig,
|
||||||
|
SECTION_NAME, PARAM_SALT, QT_TO_UTF8(Salt));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Config::GenerateSecret(QString password, QString salt) {
|
config_t* Config::GetConfigStore()
|
||||||
// Concatenate the password and the salt
|
{
|
||||||
QString passAndSalt = "";
|
return obs_frontend_get_profile_config();
|
||||||
passAndSalt += password;
|
|
||||||
passAndSalt += salt;
|
|
||||||
|
|
||||||
// Generate a SHA256 hash of the password
|
|
||||||
unsigned char* challengeHash = (unsigned char*)bzalloc(32);
|
|
||||||
mbedtls_sha256(
|
|
||||||
(unsigned char*)passAndSalt.toUtf8().constData(), passAndSalt.length(),
|
|
||||||
challengeHash, 0);
|
|
||||||
|
|
||||||
// Encode SHA256 hash to Base64
|
|
||||||
char* challenge = (char*)bzalloc(64);
|
|
||||||
size_t challengeBytes = 0;
|
|
||||||
mbedtls_base64_encode(
|
|
||||||
(unsigned char*)challenge, 64, &challengeBytes,
|
|
||||||
challengeHash, 32);
|
|
||||||
|
|
||||||
bfree(challengeHash);
|
|
||||||
return challenge;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::SetPassword(QString password) {
|
QString Config::GenerateSalt()
|
||||||
QString newSalt = GenerateSalt();
|
{
|
||||||
QString newChallenge = GenerateSecret(password, newSalt);
|
// Generate 32 random chars
|
||||||
|
const size_t randomCount = 32;
|
||||||
|
QByteArray randomChars;
|
||||||
|
for (size_t i = 0; i < randomCount; i++) {
|
||||||
|
randomChars.append((char)qrand());
|
||||||
|
}
|
||||||
|
|
||||||
this->Salt = newSalt;
|
// Convert the 32 random chars to a base64 string
|
||||||
this->Secret = newChallenge;
|
QString salt = randomChars.toBase64();
|
||||||
|
|
||||||
|
return salt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Config::CheckAuth(QString response) {
|
QString Config::GenerateSecret(QString password, QString salt)
|
||||||
// Concatenate auth secret with the challenge sent to the user
|
{
|
||||||
QString challengeAndResponse = "";
|
// Concatenate the password and the salt
|
||||||
challengeAndResponse += Secret;
|
QString passAndSalt = "";
|
||||||
challengeAndResponse += SessionChallenge;
|
passAndSalt += password;
|
||||||
|
passAndSalt += salt;
|
||||||
|
|
||||||
// Generate a SHA256 hash of challengeAndResponse
|
// Generate a SHA256 hash of the password and salt
|
||||||
unsigned char* hash = (unsigned char*)bzalloc(32);
|
auto challengeHash = QCryptographicHash::hash(
|
||||||
mbedtls_sha256(
|
passAndSalt.toUtf8(),
|
||||||
(unsigned char*)challengeAndResponse.toUtf8().constData(),
|
QCryptographicHash::Algorithm::Sha256
|
||||||
challengeAndResponse.length(),
|
);
|
||||||
hash, 0);
|
|
||||||
|
|
||||||
// Encode the SHA256 hash to Base64
|
// Encode SHA256 hash to Base64
|
||||||
char* expectedResponse = (char*)bzalloc(64);
|
QString challenge = challengeHash.toBase64();
|
||||||
size_t base64_size = 0;
|
|
||||||
mbedtls_base64_encode(
|
|
||||||
(unsigned char*)expectedResponse, 64, &base64_size,
|
|
||||||
hash, 32);
|
|
||||||
|
|
||||||
bool authSuccess = false;
|
return challenge;
|
||||||
if (response == QString(expectedResponse)) {
|
|
||||||
SessionChallenge = GenerateSalt();
|
|
||||||
authSuccess = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bfree(hash);
|
|
||||||
bfree(expectedResponse);
|
|
||||||
return authSuccess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Config* Config::Current() {
|
void Config::SetPassword(QString password)
|
||||||
return _instance;
|
{
|
||||||
|
QString newSalt = GenerateSalt();
|
||||||
|
QString newChallenge = GenerateSecret(password, newSalt);
|
||||||
|
|
||||||
|
this->Salt = newSalt;
|
||||||
|
this->Secret = newChallenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Config::CheckAuth(QString response)
|
||||||
|
{
|
||||||
|
// Concatenate auth secret with the challenge sent to the user
|
||||||
|
QString challengeAndResponse = "";
|
||||||
|
challengeAndResponse += Secret;
|
||||||
|
challengeAndResponse += SessionChallenge;
|
||||||
|
|
||||||
|
// Generate a SHA256 hash of challengeAndResponse
|
||||||
|
auto hash = QCryptographicHash::hash(
|
||||||
|
challengeAndResponse.toUtf8(),
|
||||||
|
QCryptographicHash::Algorithm::Sha256
|
||||||
|
);
|
||||||
|
|
||||||
|
// Encode the SHA256 hash to Base64
|
||||||
|
QString expectedResponse = hash.toBase64();
|
||||||
|
|
||||||
|
bool authSuccess = false;
|
||||||
|
if (response == expectedResponse) {
|
||||||
|
SessionChallenge = GenerateSalt();
|
||||||
|
authSuccess = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return authSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::OnFrontendEvent(enum obs_frontend_event event, void* param)
|
||||||
|
{
|
||||||
|
auto config = reinterpret_cast<Config*>(param);
|
||||||
|
|
||||||
|
if (event == OBS_FRONTEND_EVENT_PROFILE_CHANGED) {
|
||||||
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
|
QString startMessage = QObject::tr("OBSWebsocket.ProfileChanged.Started");
|
||||||
|
QString stopMessage = QObject::tr("OBSWebsocket.ProfileChanged.Stopped");
|
||||||
|
QString restartMessage = QObject::tr("OBSWebsocket.ProfileChanged.Restarted");
|
||||||
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
|
bool previousEnabled = config->ServerEnabled;
|
||||||
|
uint64_t previousPort = config->ServerPort;
|
||||||
|
|
||||||
|
config->SetDefaults();
|
||||||
|
config->Load();
|
||||||
|
|
||||||
|
if (config->ServerEnabled != previousEnabled || config->ServerPort != previousPort) {
|
||||||
|
auto server = GetServer();
|
||||||
|
server->stop();
|
||||||
|
|
||||||
|
if (config->ServerEnabled) {
|
||||||
|
server->start(config->ServerPort);
|
||||||
|
|
||||||
|
if (previousEnabled != config->ServerEnabled) {
|
||||||
|
Utils::SysTrayNotify(startMessage, QSystemTrayIcon::MessageIcon::Information);
|
||||||
|
} else {
|
||||||
|
Utils::SysTrayNotify(restartMessage, QSystemTrayIcon::MessageIcon::Information);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Utils::SysTrayNotify(stopMessage, QSystemTrayIcon::MessageIcon::Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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);
|
||||||
}
|
}
|
||||||
|
63
src/Config.h
63
src/Config.h
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,45 +16,42 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CONFIG_H
|
#pragma once
|
||||||
#define CONFIG_H
|
|
||||||
|
|
||||||
#include <QString>
|
#include <obs-frontend-api.h>
|
||||||
|
#include <util/config-file.h>
|
||||||
#include <mbedtls/entropy.h>
|
#include <QtCore/QString>
|
||||||
#include <mbedtls/ctr_drbg.h>
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
class Config {
|
class Config {
|
||||||
public:
|
public:
|
||||||
Config();
|
Config();
|
||||||
~Config();
|
~Config();
|
||||||
void Load();
|
void Load();
|
||||||
void Save();
|
void Save();
|
||||||
|
void SetDefaults();
|
||||||
|
config_t* GetConfigStore();
|
||||||
|
|
||||||
void SetPassword(QString password);
|
void MigrateFromGlobalSettings();
|
||||||
bool CheckAuth(QString userChallenge);
|
|
||||||
QString GenerateSalt();
|
|
||||||
static QString GenerateSecret(
|
|
||||||
QString password, QString salt);
|
|
||||||
|
|
||||||
bool ServerEnabled;
|
void SetPassword(QString password);
|
||||||
uint64_t ServerPort;
|
bool CheckAuth(QString userChallenge);
|
||||||
|
QString GenerateSalt();
|
||||||
|
static QString GenerateSecret(
|
||||||
|
QString password, QString salt);
|
||||||
|
|
||||||
bool DebugEnabled;
|
bool ServerEnabled;
|
||||||
bool AlertsEnabled;
|
uint64_t ServerPort;
|
||||||
|
|
||||||
bool AuthRequired;
|
bool DebugEnabled;
|
||||||
QString Secret;
|
bool AlertsEnabled;
|
||||||
QString Salt;
|
|
||||||
QString SessionChallenge;
|
|
||||||
bool SettingsLoaded;
|
|
||||||
|
|
||||||
static Config* Current();
|
bool AuthRequired;
|
||||||
|
QString Secret;
|
||||||
|
QString Salt;
|
||||||
|
QString SessionChallenge;
|
||||||
|
bool SettingsLoaded;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Config* _instance;
|
static void OnFrontendEvent(enum obs_frontend_event event, void* param);
|
||||||
mbedtls_entropy_context entropy;
|
|
||||||
mbedtls_ctr_drbg_context rng;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONFIG_H
|
|
34
src/ConnectionProperties.cpp
Normal file
34
src/ConnectionProperties.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
obs-websocket
|
||||||
|
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ConnectionProperties.h"
|
||||||
|
|
||||||
|
ConnectionProperties::ConnectionProperties()
|
||||||
|
: _authenticated(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionProperties::isAuthenticated()
|
||||||
|
{
|
||||||
|
return _authenticated.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionProperties::setAuthenticated(bool authenticated)
|
||||||
|
{
|
||||||
|
_authenticated.store(authenticated);
|
||||||
|
}
|
31
src/ConnectionProperties.h
Normal file
31
src/ConnectionProperties.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
obs-websocket
|
||||||
|
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
class ConnectionProperties
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ConnectionProperties();
|
||||||
|
bool isAuthenticated();
|
||||||
|
void setAuthenticated(bool authenticated);
|
||||||
|
private:
|
||||||
|
std::atomic<bool> _authenticated;
|
||||||
|
};
|
1026
src/Utils.cpp
1026
src/Utils.cpp
File diff suppressed because it is too large
Load Diff
96
src/Utils.h
96
src/Utils.h
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,74 +16,74 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef UTILS_H
|
#pragma once
|
||||||
#define UTILS_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <QSpinBox>
|
#include <QtCore/QString>
|
||||||
#include <QPushButton>
|
#include <QtWidgets/QSpinBox>
|
||||||
#include <QLayout>
|
#include <QtWidgets/QPushButton>
|
||||||
#include <QListWidget>
|
#include <QtWidgets/QLayout>
|
||||||
#include <QSystemTrayIcon>
|
#include <QtWidgets/QListWidget>
|
||||||
#include <QHostAddress>
|
#include <QtWidgets/QSystemTrayIcon>
|
||||||
|
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
#include <util/config-file.h>
|
#include <util/config-file.h>
|
||||||
|
|
||||||
const char* qstring_data_copy(QString value);
|
typedef void(*PauseRecordingFunction)(bool);
|
||||||
|
typedef bool(*RecordingPausedFunction)();
|
||||||
|
|
||||||
class Utils {
|
class Utils {
|
||||||
public:
|
public:
|
||||||
static obs_data_array_t* StringListToArray(char** strings, char* key);
|
static obs_data_array_t* StringListToArray(char** strings, const char* key);
|
||||||
static obs_data_array_t* GetSceneItems(obs_source_t* source);
|
static obs_data_array_t* GetSceneItems(obs_source_t* source);
|
||||||
static obs_data_t* GetSceneItemData(obs_sceneitem_t* item);
|
static obs_data_t* GetSceneItemData(obs_sceneitem_t* item);
|
||||||
static obs_sceneitem_t* GetSceneItemFromName(
|
|
||||||
obs_source_t* source, QString name);
|
|
||||||
static obs_source_t* GetTransitionFromName(QString transitionName);
|
|
||||||
static obs_source_t* GetSceneFromNameOrCurrent(QString sceneName);
|
|
||||||
|
|
||||||
static bool IsValidAlignment(const uint32_t alignment);
|
// These two functions support nested lookup into groups
|
||||||
|
static obs_sceneitem_t* GetSceneItemFromName(obs_scene_t* scene, QString name);
|
||||||
|
static obs_sceneitem_t* GetSceneItemFromId(obs_scene_t* scene, int64_t id);
|
||||||
|
|
||||||
static obs_data_array_t* GetScenes();
|
static obs_sceneitem_t* GetSceneItemFromItem(obs_scene_t* scene, obs_data_t* item);
|
||||||
static obs_data_t* GetSceneData(obs_source_t* source);
|
static obs_scene_t* GetSceneFromNameOrCurrent(QString sceneName);
|
||||||
|
static obs_data_t* GetSceneItemPropertiesData(obs_sceneitem_t* item);
|
||||||
|
|
||||||
static obs_data_array_t* GetProfiles();
|
static obs_data_t* GetSourceFilterInfo(obs_source_t* filter, bool includeSettings);
|
||||||
|
static obs_data_array_t* GetSourceFiltersList(obs_source_t* source, bool includeSettings);
|
||||||
|
|
||||||
static QSpinBox* GetTransitionDurationControl();
|
static bool IsValidAlignment(const uint32_t alignment);
|
||||||
static int GetTransitionDuration();
|
|
||||||
static void SetTransitionDuration(int ms);
|
|
||||||
|
|
||||||
static bool SetTransitionByName(QString transitionName);
|
static obs_data_array_t* GetScenes();
|
||||||
|
static obs_data_t* GetSceneData(obs_source_t* source);
|
||||||
|
|
||||||
static QPushButton* GetPreviewModeButtonControl();
|
// TODO contribute a proper frontend API method for this to OBS and remove this hack
|
||||||
static QLayout* GetPreviewLayout();
|
static QSpinBox* GetTransitionDurationControl();
|
||||||
static QListWidget* GetSceneListControl();
|
static int GetTransitionDuration(obs_source_t* transition);
|
||||||
static obs_scene_t* SceneListItemToScene(QListWidgetItem* item);
|
static obs_source_t* GetTransitionFromName(QString transitionName);
|
||||||
|
static bool SetTransitionByName(QString transitionName);
|
||||||
|
|
||||||
static void TransitionToProgram();
|
static QString OBSVersionString();
|
||||||
|
|
||||||
static QString OBSVersionString();
|
static QSystemTrayIcon* GetTrayIcon();
|
||||||
|
static void SysTrayNotify(
|
||||||
|
QString text,
|
||||||
|
QSystemTrayIcon::MessageIcon n,
|
||||||
|
QString title = QString("obs-websocket"));
|
||||||
|
|
||||||
static QSystemTrayIcon* GetTrayIcon();
|
static const char* GetRecordingFolder();
|
||||||
static void SysTrayNotify(
|
static bool SetRecordingFolder(const char* path);
|
||||||
QString &text,
|
|
||||||
QSystemTrayIcon::MessageIcon n,
|
|
||||||
QString title = QString("obs-websocket"));
|
|
||||||
|
|
||||||
static QString FormatIPAddress(QHostAddress &addr);
|
static QString ParseDataToQueryString(obs_data_t* data);
|
||||||
|
static obs_hotkey_t* FindHotkeyByName(QString name);
|
||||||
|
|
||||||
static const char* GetRecordingFolder();
|
static bool ReplayBufferEnabled();
|
||||||
static bool SetRecordingFolder(const char* path);
|
static void StartReplayBuffer();
|
||||||
|
static bool IsRPHotkeySet();
|
||||||
|
|
||||||
static QString ParseDataToQueryString(obs_data_t* data);
|
static const char* GetFilenameFormatting();
|
||||||
static obs_hotkey_t* FindHotkeyByName(QString name);
|
static bool SetFilenameFormatting(const char* filenameFormatting);
|
||||||
static bool ReplayBufferEnabled();
|
|
||||||
static void StartReplayBuffer();
|
static bool RecordingPauseSupported();
|
||||||
static bool IsRPHotkeySet();
|
static bool RecordingPaused();
|
||||||
static const char* GetFilenameFormatting();
|
static void PauseRecording(bool pause);
|
||||||
static bool SetFilenameFormatting(const char* filenameFormatting);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // UTILS_H
|
|
||||||
|
1569
src/WSEvents.cpp
1569
src/WSEvents.cpp
File diff suppressed because it is too large
Load Diff
167
src/WSEvents.h
167
src/WSEvents.h
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
Copyright (C) 2017 Brendan Hagan <https://github.com/haganbmj>
|
Copyright (C) 2017 Brendan Hagan <https://github.com/haganbmj>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
@ -17,95 +17,126 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef WSEVENTS_H
|
#pragma once
|
||||||
#define WSEVENTS_H
|
|
||||||
|
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <QListWidgetItem>
|
#include <util/platform.h>
|
||||||
|
|
||||||
|
#include <QtWidgets/QListWidgetItem>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
#include "WSServer.h"
|
#include "WSServer.h"
|
||||||
|
|
||||||
class WSEvents : public QObject {
|
class WSEvents : public QObject
|
||||||
Q_OBJECT
|
{
|
||||||
public:
|
Q_OBJECT
|
||||||
explicit WSEvents(WSServer* srv);
|
|
||||||
~WSEvents();
|
|
||||||
static void FrontendEventHandler(
|
|
||||||
enum obs_frontend_event event, void* privateData);
|
|
||||||
static WSEvents* Instance;
|
|
||||||
void connectTransitionSignals(obs_source_t* transition);
|
|
||||||
void connectSceneSignals(obs_source_t* scene);
|
|
||||||
|
|
||||||
uint64_t GetStreamingTime();
|
public:
|
||||||
const char* GetStreamingTimecode();
|
explicit WSEvents(WSServerPtr srv);
|
||||||
uint64_t GetRecordingTime();
|
~WSEvents();
|
||||||
const char* GetRecordingTimecode();
|
|
||||||
|
|
||||||
bool HeartbeatIsActive;
|
void connectSourceSignals(obs_source_t* source);
|
||||||
|
void disconnectSourceSignals(obs_source_t* source);
|
||||||
|
|
||||||
private slots:
|
void connectFilterSignals(obs_source_t* filter);
|
||||||
void deferredInitOperations();
|
void disconnectFilterSignals(obs_source_t* filter);
|
||||||
void StreamStatus();
|
|
||||||
void Heartbeat();
|
|
||||||
void TransitionDurationChanged(int ms);
|
|
||||||
void SelectedSceneChanged(
|
|
||||||
QListWidgetItem* current, QListWidgetItem* prev);
|
|
||||||
|
|
||||||
private:
|
void hookTransitionBeginEvent();
|
||||||
WSServer* _srv;
|
void unhookTransitionBeginEvent();
|
||||||
signal_handler_t* transitionHandler;
|
|
||||||
signal_handler_t* sceneHandler;
|
|
||||||
|
|
||||||
bool pulse;
|
uint64_t getStreamingTime();
|
||||||
|
uint64_t getRecordingTime();
|
||||||
|
|
||||||
bool _streamingActive;
|
QString getStreamingTimecode();
|
||||||
bool _recordingActive;
|
QString getRecordingTimecode();
|
||||||
|
|
||||||
|
obs_data_t* GetStats();
|
||||||
|
|
||||||
uint64_t _streamStarttime;
|
void OnBroadcastCustomMessage(QString realm, obs_data_t* data);
|
||||||
uint64_t _recStarttime;
|
|
||||||
|
|
||||||
uint64_t _lastBytesSent;
|
bool HeartbeatIsActive;
|
||||||
uint64_t _lastBytesSentTime;
|
|
||||||
|
|
||||||
void broadcastUpdate(const char* updateType,
|
private slots:
|
||||||
obs_data_t* additionalFields);
|
void StreamStatus();
|
||||||
|
void Heartbeat();
|
||||||
|
void TransitionDurationChanged(int ms);
|
||||||
|
|
||||||
void OnSceneChange();
|
private:
|
||||||
void OnSceneListChange();
|
WSServerPtr _srv;
|
||||||
void OnSceneCollectionChange();
|
QTimer streamStatusTimer;
|
||||||
void OnSceneCollectionListChange();
|
QTimer heartbeatTimer;
|
||||||
|
os_cpu_usage_info_t* cpuUsageInfo;
|
||||||
|
|
||||||
void OnTransitionChange();
|
bool pulse;
|
||||||
void OnTransitionListChange();
|
|
||||||
|
|
||||||
void OnProfileChange();
|
uint64_t _streamStarttime;
|
||||||
void OnProfileListChange();
|
|
||||||
|
|
||||||
void OnStreamStarting();
|
uint64_t _lastBytesSent;
|
||||||
void OnStreamStarted();
|
uint64_t _lastBytesSentTime;
|
||||||
void OnStreamStopping();
|
|
||||||
void OnStreamStopped();
|
|
||||||
|
|
||||||
void OnRecordingStarting();
|
void broadcastUpdate(const char* updateType,
|
||||||
void OnRecordingStarted();
|
obs_data_t* additionalFields);
|
||||||
void OnRecordingStopping();
|
|
||||||
void OnRecordingStopped();
|
|
||||||
|
|
||||||
void OnReplayStarting();
|
void OnSceneChange();
|
||||||
void OnReplayStarted();
|
void OnSceneListChange();
|
||||||
void OnReplayStopping();
|
void OnSceneCollectionChange();
|
||||||
void OnReplayStopped();
|
void OnSceneCollectionListChange();
|
||||||
|
|
||||||
void OnStudioModeSwitched(bool enabled);
|
void OnTransitionChange();
|
||||||
|
void OnTransitionListChange();
|
||||||
|
|
||||||
void OnExit();
|
void OnProfileChange();
|
||||||
|
void OnProfileListChange();
|
||||||
|
|
||||||
static void OnTransitionBegin(void* param, calldata_t* data);
|
void OnStreamStarting();
|
||||||
|
void OnStreamStarted();
|
||||||
|
void OnStreamStopping();
|
||||||
|
void OnStreamStopped();
|
||||||
|
|
||||||
static void OnSceneReordered(void* param, calldata_t* data);
|
void OnRecordingStarting();
|
||||||
static void OnSceneItemAdd(void* param, calldata_t* data);
|
void OnRecordingStarted();
|
||||||
static void OnSceneItemDelete(void* param, calldata_t* data);
|
void OnRecordingStopping();
|
||||||
static void OnSceneItemVisibilityChanged(void* param, calldata_t* data);
|
void OnRecordingStopped();
|
||||||
|
void OnRecordingPaused();
|
||||||
|
void OnRecordingResumed();
|
||||||
|
|
||||||
|
void OnReplayStarting();
|
||||||
|
void OnReplayStarted();
|
||||||
|
void OnReplayStopping();
|
||||||
|
void OnReplayStopped();
|
||||||
|
|
||||||
|
void OnStudioModeSwitched(bool enabled);
|
||||||
|
void OnPreviewSceneChanged();
|
||||||
|
|
||||||
|
void OnExit();
|
||||||
|
|
||||||
|
static void FrontendEventHandler(
|
||||||
|
enum obs_frontend_event event, void* privateData);
|
||||||
|
|
||||||
|
static void OnTransitionBegin(void* param, calldata_t* data);
|
||||||
|
|
||||||
|
static void OnSourceCreate(void* param, calldata_t* data);
|
||||||
|
static void OnSourceDestroy(void* param, calldata_t* data);
|
||||||
|
|
||||||
|
static void OnSourceVolumeChange(void* param, calldata_t* data);
|
||||||
|
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 OnSourceRename(void* param, calldata_t* data);
|
||||||
|
|
||||||
|
static void OnSourceFilterAdded(void* param, calldata_t* data);
|
||||||
|
static void OnSourceFilterRemoved(void* param, calldata_t* data);
|
||||||
|
static void OnSourceFilterVisibilityChanged(void* param, calldata_t* data);
|
||||||
|
static void OnSourceFilterOrderChanged(void* param, calldata_t* data);
|
||||||
|
|
||||||
|
static void OnSceneReordered(void* param, calldata_t* data);
|
||||||
|
static void OnSceneItemAdd(void* param, calldata_t* data);
|
||||||
|
static void OnSceneItemDelete(void* param, calldata_t* data);
|
||||||
|
static void OnSceneItemVisibilityChanged(void* param, calldata_t* data);
|
||||||
|
static void OnSceneItemTransform(void* param, calldata_t* data);
|
||||||
|
static void OnSceneItemSelected(void* param, calldata_t* data);
|
||||||
|
static void OnSceneItemDeselected(void* param, calldata_t* data);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WSEVENTS_H
|
|
@ -24,194 +24,265 @@
|
|||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
QHash<QString, void(*)(WSRequestHandler*)> WSRequestHandler::messageMap {
|
QHash<QString, HandlerResponse(*)(WSRequestHandler*)> WSRequestHandler::messageMap{
|
||||||
{ "GetVersion", WSRequestHandler::HandleGetVersion },
|
{ "GetVersion", WSRequestHandler::HandleGetVersion },
|
||||||
{ "GetAuthRequired", WSRequestHandler::HandleGetAuthRequired },
|
{ "GetAuthRequired", WSRequestHandler::HandleGetAuthRequired },
|
||||||
{ "Authenticate", WSRequestHandler::HandleAuthenticate },
|
{ "Authenticate", WSRequestHandler::HandleAuthenticate },
|
||||||
|
|
||||||
{ "SetHeartbeat", WSRequestHandler::HandleSetHeartbeat },
|
{ "GetStats", WSRequestHandler::HandleGetStats },
|
||||||
|
{ "SetHeartbeat", WSRequestHandler::HandleSetHeartbeat },
|
||||||
|
{ "GetVideoInfo", WSRequestHandler::HandleGetVideoInfo },
|
||||||
|
|
||||||
{ "SetFilenameFormatting", WSRequestHandler::HandleSetFilenameFormatting },
|
{ "SetFilenameFormatting", WSRequestHandler::HandleSetFilenameFormatting },
|
||||||
{ "GetFilenameFormatting", WSRequestHandler::HandleGetFilenameFormatting },
|
{ "GetFilenameFormatting", WSRequestHandler::HandleGetFilenameFormatting },
|
||||||
|
|
||||||
{ "SetCurrentScene", WSRequestHandler::HandleSetCurrentScene },
|
{ "BroadcastCustomMessage", WSRequestHandler::HandleBroadcastCustomMessage },
|
||||||
{ "GetCurrentScene", WSRequestHandler::HandleGetCurrentScene },
|
|
||||||
{ "GetSceneList", WSRequestHandler::HandleGetSceneList },
|
|
||||||
|
|
||||||
{ "SetSourceRender", WSRequestHandler::HandleSetSceneItemRender }, // Retrocompat
|
{ "SetCurrentScene", WSRequestHandler::HandleSetCurrentScene },
|
||||||
{ "SetSceneItemRender", WSRequestHandler::HandleSetSceneItemRender },
|
{ "GetCurrentScene", WSRequestHandler::HandleGetCurrentScene },
|
||||||
{ "SetSceneItemPosition", WSRequestHandler::HandleSetSceneItemPosition },
|
{ "GetSceneList", WSRequestHandler::HandleGetSceneList },
|
||||||
{ "SetSceneItemTransform", WSRequestHandler::HandleSetSceneItemTransform },
|
|
||||||
{ "SetSceneItemCrop", WSRequestHandler::HandleSetSceneItemCrop },
|
|
||||||
{ "GetSceneItemProperties", WSRequestHandler::HandleGetSceneItemProperties },
|
|
||||||
{ "SetSceneItemProperties", WSRequestHandler::HandleSetSceneItemProperties },
|
|
||||||
{ "ResetSceneItem", WSRequestHandler::HandleResetSceneItem },
|
|
||||||
|
|
||||||
{ "GetStreamingStatus", WSRequestHandler::HandleGetStreamingStatus },
|
{ "SetSourceRender", WSRequestHandler::HandleSetSceneItemRender }, // Retrocompat
|
||||||
{ "StartStopStreaming", WSRequestHandler::HandleStartStopStreaming },
|
{ "SetSceneItemRender", WSRequestHandler::HandleSetSceneItemRender },
|
||||||
{ "StartStopRecording", WSRequestHandler::HandleStartStopRecording },
|
{ "SetSceneItemPosition", WSRequestHandler::HandleSetSceneItemPosition },
|
||||||
{ "StartStreaming", WSRequestHandler::HandleStartStreaming },
|
{ "SetSceneItemTransform", WSRequestHandler::HandleSetSceneItemTransform },
|
||||||
{ "StopStreaming", WSRequestHandler::HandleStopStreaming },
|
{ "SetSceneItemCrop", WSRequestHandler::HandleSetSceneItemCrop },
|
||||||
{ "StartRecording", WSRequestHandler::HandleStartRecording },
|
{ "GetSceneItemProperties", WSRequestHandler::HandleGetSceneItemProperties },
|
||||||
{ "StopRecording", WSRequestHandler::HandleStopRecording },
|
{ "SetSceneItemProperties", WSRequestHandler::HandleSetSceneItemProperties },
|
||||||
|
{ "ResetSceneItem", WSRequestHandler::HandleResetSceneItem },
|
||||||
|
{ "DeleteSceneItem", WSRequestHandler::HandleDeleteSceneItem },
|
||||||
|
{ "DuplicateSceneItem", WSRequestHandler::HandleDuplicateSceneItem },
|
||||||
|
{ "ReorderSceneItems", WSRequestHandler::HandleReorderSceneItems },
|
||||||
|
|
||||||
{ "StartStopReplayBuffer", WSRequestHandler::HandleStartStopReplayBuffer },
|
{ "GetStreamingStatus", WSRequestHandler::HandleGetStreamingStatus },
|
||||||
{ "StartReplayBuffer", WSRequestHandler::HandleStartReplayBuffer },
|
{ "StartStopStreaming", WSRequestHandler::HandleStartStopStreaming },
|
||||||
{ "StopReplayBuffer", WSRequestHandler::HandleStopReplayBuffer },
|
{ "StartStopRecording", WSRequestHandler::HandleStartStopRecording },
|
||||||
{ "SaveReplayBuffer", WSRequestHandler::HandleSaveReplayBuffer },
|
|
||||||
|
|
||||||
{ "SetRecordingFolder", WSRequestHandler::HandleSetRecordingFolder },
|
{ "StartStreaming", WSRequestHandler::HandleStartStreaming },
|
||||||
{ "GetRecordingFolder", WSRequestHandler::HandleGetRecordingFolder },
|
{ "StopStreaming", WSRequestHandler::HandleStopStreaming },
|
||||||
|
|
||||||
{ "GetTransitionList", WSRequestHandler::HandleGetTransitionList },
|
{ "StartRecording", WSRequestHandler::HandleStartRecording },
|
||||||
{ "GetCurrentTransition", WSRequestHandler::HandleGetCurrentTransition },
|
{ "StopRecording", WSRequestHandler::HandleStopRecording },
|
||||||
{ "SetCurrentTransition", WSRequestHandler::HandleSetCurrentTransition },
|
{ "PauseRecording", WSRequestHandler::HandlePauseRecording },
|
||||||
{ "SetTransitionDuration", WSRequestHandler::HandleSetTransitionDuration },
|
{ "ResumeRecording", WSRequestHandler::HandleResumeRecording },
|
||||||
{ "GetTransitionDuration", WSRequestHandler::HandleGetTransitionDuration },
|
|
||||||
|
|
||||||
{ "SetVolume", WSRequestHandler::HandleSetVolume },
|
{ "StartStopReplayBuffer", WSRequestHandler::HandleStartStopReplayBuffer },
|
||||||
{ "GetVolume", WSRequestHandler::HandleGetVolume },
|
{ "StartReplayBuffer", WSRequestHandler::HandleStartReplayBuffer },
|
||||||
{ "ToggleMute", WSRequestHandler::HandleToggleMute },
|
{ "StopReplayBuffer", WSRequestHandler::HandleStopReplayBuffer },
|
||||||
{ "SetMute", WSRequestHandler::HandleSetMute },
|
{ "SaveReplayBuffer", WSRequestHandler::HandleSaveReplayBuffer },
|
||||||
{ "GetMute", WSRequestHandler::HandleGetMute },
|
|
||||||
{ "SetSyncOffset", WSRequestHandler::HandleSetSyncOffset },
|
|
||||||
{ "GetSyncOffset", WSRequestHandler::HandleGetSyncOffset },
|
|
||||||
{ "GetSpecialSources", WSRequestHandler::HandleGetSpecialSources },
|
|
||||||
{ "GetSourcesList", WSRequestHandler::HandleGetSourcesList },
|
|
||||||
{ "GetSourceTypesList", WSRequestHandler::HandleGetSourceTypesList },
|
|
||||||
{ "GetSourceSettings", WSRequestHandler::HandleGetSourceSettings },
|
|
||||||
{ "SetSourceSettings", WSRequestHandler::HandleSetSourceSettings },
|
|
||||||
|
|
||||||
{ "SetCurrentSceneCollection", WSRequestHandler::HandleSetCurrentSceneCollection },
|
{ "SetRecordingFolder", WSRequestHandler::HandleSetRecordingFolder },
|
||||||
{ "GetCurrentSceneCollection", WSRequestHandler::HandleGetCurrentSceneCollection },
|
{ "GetRecordingFolder", WSRequestHandler::HandleGetRecordingFolder },
|
||||||
{ "ListSceneCollections", WSRequestHandler::HandleListSceneCollections },
|
|
||||||
|
|
||||||
{ "SetCurrentProfile", WSRequestHandler::HandleSetCurrentProfile },
|
{ "GetTransitionList", WSRequestHandler::HandleGetTransitionList },
|
||||||
{ "GetCurrentProfile", WSRequestHandler::HandleGetCurrentProfile },
|
{ "GetCurrentTransition", WSRequestHandler::HandleGetCurrentTransition },
|
||||||
{ "ListProfiles", WSRequestHandler::HandleListProfiles },
|
{ "SetCurrentTransition", WSRequestHandler::HandleSetCurrentTransition },
|
||||||
|
{ "SetTransitionDuration", WSRequestHandler::HandleSetTransitionDuration },
|
||||||
|
{ "GetTransitionDuration", WSRequestHandler::HandleGetTransitionDuration },
|
||||||
|
|
||||||
{ "SetStreamSettings", WSRequestHandler::HandleSetStreamSettings },
|
{ "SetVolume", WSRequestHandler::HandleSetVolume },
|
||||||
{ "GetStreamSettings", WSRequestHandler::HandleGetStreamSettings },
|
{ "GetVolume", WSRequestHandler::HandleGetVolume },
|
||||||
{ "SaveStreamSettings", WSRequestHandler::HandleSaveStreamSettings },
|
{ "ToggleMute", WSRequestHandler::HandleToggleMute },
|
||||||
|
{ "SetMute", WSRequestHandler::HandleSetMute },
|
||||||
|
{ "GetMute", WSRequestHandler::HandleGetMute },
|
||||||
|
{ "SetSyncOffset", WSRequestHandler::HandleSetSyncOffset },
|
||||||
|
{ "GetSyncOffset", WSRequestHandler::HandleGetSyncOffset },
|
||||||
|
{ "GetSpecialSources", WSRequestHandler::HandleGetSpecialSources },
|
||||||
|
{ "GetSourcesList", WSRequestHandler::HandleGetSourcesList },
|
||||||
|
{ "GetSourceTypesList", WSRequestHandler::HandleGetSourceTypesList },
|
||||||
|
{ "GetSourceSettings", WSRequestHandler::HandleGetSourceSettings },
|
||||||
|
{ "SetSourceSettings", WSRequestHandler::HandleSetSourceSettings },
|
||||||
|
{ "TakeSourceScreenshot", WSRequestHandler::HandleTakeSourceScreenshot },
|
||||||
|
|
||||||
{ "GetStudioModeStatus", WSRequestHandler::HandleGetStudioModeStatus },
|
{ "GetSourceFilters", WSRequestHandler::HandleGetSourceFilters },
|
||||||
{ "GetPreviewScene", WSRequestHandler::HandleGetPreviewScene },
|
{ "GetSourceFilterInfo", WSRequestHandler::HandleGetSourceFilterInfo },
|
||||||
{ "SetPreviewScene", WSRequestHandler::HandleSetPreviewScene },
|
{ "AddFilterToSource", WSRequestHandler::HandleAddFilterToSource },
|
||||||
{ "TransitionToProgram", WSRequestHandler::HandleTransitionToProgram },
|
{ "RemoveFilterFromSource", WSRequestHandler::HandleRemoveFilterFromSource },
|
||||||
{ "EnableStudioMode", WSRequestHandler::HandleEnableStudioMode },
|
{ "ReorderSourceFilter", WSRequestHandler::HandleReorderSourceFilter },
|
||||||
{ "DisableStudioMode", WSRequestHandler::HandleDisableStudioMode },
|
{ "MoveSourceFilter", WSRequestHandler::HandleMoveSourceFilter },
|
||||||
{ "ToggleStudioMode", WSRequestHandler::HandleToggleStudioMode },
|
{ "SetSourceFilterSettings", WSRequestHandler::HandleSetSourceFilterSettings },
|
||||||
|
{ "SetSourceFilterVisibility", WSRequestHandler::HandleSetSourceFilterVisibility },
|
||||||
|
|
||||||
{ "SetTextGDIPlusProperties", WSRequestHandler::HandleSetTextGDIPlusProperties },
|
{ "SetCurrentSceneCollection", WSRequestHandler::HandleSetCurrentSceneCollection },
|
||||||
{ "GetTextGDIPlusProperties", WSRequestHandler::HandleGetTextGDIPlusProperties },
|
{ "GetCurrentSceneCollection", WSRequestHandler::HandleGetCurrentSceneCollection },
|
||||||
|
{ "ListSceneCollections", WSRequestHandler::HandleListSceneCollections },
|
||||||
|
|
||||||
{ "GetBrowserSourceProperties", WSRequestHandler::HandleGetBrowserSourceProperties },
|
{ "SetCurrentProfile", WSRequestHandler::HandleSetCurrentProfile },
|
||||||
{ "SetBrowserSourceProperties", WSRequestHandler::HandleSetBrowserSourceProperties }
|
{ "GetCurrentProfile", WSRequestHandler::HandleGetCurrentProfile },
|
||||||
|
{ "ListProfiles", WSRequestHandler::HandleListProfiles },
|
||||||
|
|
||||||
|
{ "SetStreamSettings", WSRequestHandler::HandleSetStreamSettings },
|
||||||
|
{ "GetStreamSettings", WSRequestHandler::HandleGetStreamSettings },
|
||||||
|
{ "SaveStreamSettings", WSRequestHandler::HandleSaveStreamSettings },
|
||||||
|
#if BUILD_CAPTIONS
|
||||||
|
{ "SendCaptions", WSRequestHandler::HandleSendCaptions },
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{ "GetStudioModeStatus", WSRequestHandler::HandleGetStudioModeStatus },
|
||||||
|
{ "GetPreviewScene", WSRequestHandler::HandleGetPreviewScene },
|
||||||
|
{ "SetPreviewScene", WSRequestHandler::HandleSetPreviewScene },
|
||||||
|
{ "TransitionToProgram", WSRequestHandler::HandleTransitionToProgram },
|
||||||
|
{ "EnableStudioMode", WSRequestHandler::HandleEnableStudioMode },
|
||||||
|
{ "DisableStudioMode", WSRequestHandler::HandleDisableStudioMode },
|
||||||
|
{ "ToggleStudioMode", WSRequestHandler::HandleToggleStudioMode },
|
||||||
|
|
||||||
|
{ "SetTextGDIPlusProperties", WSRequestHandler::HandleSetTextGDIPlusProperties },
|
||||||
|
{ "GetTextGDIPlusProperties", WSRequestHandler::HandleGetTextGDIPlusProperties },
|
||||||
|
|
||||||
|
{ "SetTextFreetype2Properties", WSRequestHandler::HandleSetTextFreetype2Properties },
|
||||||
|
{ "GetTextFreetype2Properties", WSRequestHandler::HandleGetTextFreetype2Properties },
|
||||||
|
|
||||||
|
{ "GetBrowserSourceProperties", WSRequestHandler::HandleGetBrowserSourceProperties },
|
||||||
|
{ "SetBrowserSourceProperties", WSRequestHandler::HandleSetBrowserSourceProperties },
|
||||||
|
|
||||||
|
{ "ListOutputs", WSRequestHandler::HandleListOutputs },
|
||||||
|
{ "GetOutputInfo", WSRequestHandler::HandleGetOutputInfo },
|
||||||
|
{ "StartOutput", WSRequestHandler::HandleStartOutput },
|
||||||
|
{ "StopOutput", WSRequestHandler::HandleStopOutput }
|
||||||
};
|
};
|
||||||
|
|
||||||
QSet<QString> WSRequestHandler::authNotRequired {
|
QSet<QString> WSRequestHandler::authNotRequired {
|
||||||
"GetVersion",
|
"GetVersion",
|
||||||
"GetAuthRequired",
|
"GetAuthRequired",
|
||||||
"Authenticate"
|
"Authenticate"
|
||||||
};
|
};
|
||||||
|
|
||||||
WSRequestHandler::WSRequestHandler(QWebSocket* client) :
|
WSRequestHandler::WSRequestHandler(ConnectionProperties& connProperties) :
|
||||||
_messageId(0),
|
_messageId(0),
|
||||||
_requestType(""),
|
_requestType(""),
|
||||||
data(nullptr),
|
data(nullptr),
|
||||||
_client(client)
|
_connProperties(connProperties)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::processIncomingMessage(QString textMessage) {
|
std::string WSRequestHandler::processIncomingMessage(std::string& textMessage) {
|
||||||
QByteArray msgData = textMessage.toUtf8();
|
if (GetConfig()->DebugEnabled) {
|
||||||
const char* msg = msgData.constData();
|
blog(LOG_INFO, "Request >> '%s'", textMessage.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
data = obs_data_create_from_json(msg);
|
OBSDataAutoRelease responseData = processRequest(textMessage);
|
||||||
if (!data) {
|
std::string response = obs_data_get_json(responseData);
|
||||||
if (!msg)
|
|
||||||
msg = "<null pointer>";
|
|
||||||
|
|
||||||
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
|
if (GetConfig()->DebugEnabled) {
|
||||||
SendErrorResponse("invalid JSON payload");
|
blog(LOG_INFO, "Response << '%s'", response.c_str());
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (Config::Current()->DebugEnabled) {
|
return response;
|
||||||
blog(LOG_DEBUG, "Request >> '%s'", msg);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasField("request-type")
|
HandlerResponse WSRequestHandler::processRequest(std::string& textMessage){
|
||||||
|| !hasField("message-id"))
|
std::string msgContainer(textMessage);
|
||||||
{
|
const char* msg = msgContainer.c_str();
|
||||||
SendErrorResponse("missing request parameters");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_requestType = obs_data_get_string(data, "request-type");
|
data = obs_data_create_from_json(msg);
|
||||||
_messageId = obs_data_get_string(data, "message-id");
|
if (!data) {
|
||||||
|
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
|
||||||
|
return SendErrorResponse("invalid JSON payload");
|
||||||
|
}
|
||||||
|
|
||||||
if (Config::Current()->AuthRequired
|
if (!hasField("request-type") || !hasField("message-id")) {
|
||||||
&& (_client->property(PROP_AUTHENTICATED).toBool() == false)
|
return SendErrorResponse("missing request parameters");
|
||||||
&& (authNotRequired.find(_requestType) == authNotRequired.end()))
|
}
|
||||||
{
|
|
||||||
SendErrorResponse("Not Authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void (*handlerFunc)(WSRequestHandler*) = (messageMap[_requestType]);
|
_requestType = obs_data_get_string(data, "request-type");
|
||||||
|
_messageId = obs_data_get_string(data, "message-id");
|
||||||
|
|
||||||
if (handlerFunc != nullptr)
|
if (GetConfig()->AuthRequired
|
||||||
handlerFunc(this);
|
&& (!authNotRequired.contains(_requestType))
|
||||||
else
|
&& (!_connProperties.isAuthenticated()))
|
||||||
SendErrorResponse("invalid request type");
|
{
|
||||||
|
return SendErrorResponse("Not Authenticated");
|
||||||
|
}
|
||||||
|
|
||||||
|
HandlerResponse (*handlerFunc)(WSRequestHandler*) = (messageMap[_requestType]);
|
||||||
|
if (!handlerFunc) {
|
||||||
|
return SendErrorResponse("invalid request type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return handlerFunc(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
WSRequestHandler::~WSRequestHandler() {
|
WSRequestHandler::~WSRequestHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::SendOKResponse(obs_data_t* additionalFields) {
|
HandlerResponse WSRequestHandler::SendOKResponse(obs_data_t* additionalFields) {
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
return SendResponse("ok", additionalFields);
|
||||||
obs_data_set_string(response, "status", "ok");
|
|
||||||
obs_data_set_string(response, "message-id", _messageId);
|
|
||||||
|
|
||||||
if (additionalFields)
|
|
||||||
obs_data_apply(response, additionalFields);
|
|
||||||
|
|
||||||
SendResponse(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::SendErrorResponse(const char* errorMessage) {
|
HandlerResponse WSRequestHandler::SendErrorResponse(QString errorMessage) {
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease fields = obs_data_create();
|
||||||
obs_data_set_string(response, "status", "error");
|
obs_data_set_string(fields, "error", errorMessage.toUtf8().constData());
|
||||||
obs_data_set_string(response, "error", errorMessage);
|
|
||||||
obs_data_set_string(response, "message-id", _messageId);
|
|
||||||
|
|
||||||
SendResponse(response);
|
return SendResponse("error", fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::SendErrorResponse(obs_data_t* additionalFields) {
|
HandlerResponse WSRequestHandler::SendErrorResponse(obs_data_t* additionalFields) {
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
return SendResponse("error", additionalFields);
|
||||||
obs_data_set_string(response, "status", "error");
|
|
||||||
obs_data_set_string(response, "message-id", _messageId);
|
|
||||||
|
|
||||||
if (additionalFields)
|
|
||||||
obs_data_set_obj(response, "error", additionalFields);
|
|
||||||
|
|
||||||
SendResponse(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::SendResponse(obs_data_t* response) {
|
HandlerResponse WSRequestHandler::SendResponse(const char* status, obs_data_t* fields) {
|
||||||
QString json = obs_data_get_json(response);
|
obs_data_t* response = obs_data_create();
|
||||||
_client->sendTextMessage(json);
|
obs_data_set_string(response, "message-id", _messageId);
|
||||||
|
obs_data_set_string(response, "status", status);
|
||||||
|
|
||||||
if (Config::Current()->DebugEnabled)
|
if (fields) {
|
||||||
blog(LOG_DEBUG, "Response << '%s'", json.toUtf8().constData());
|
obs_data_apply(response, fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WSRequestHandler::hasField(QString name) {
|
bool WSRequestHandler::hasField(QString name, obs_data_type expectedFieldType, obs_data_number_type expectedNumberType) {
|
||||||
if (!data || name.isEmpty() || name.isNull())
|
if (!data || name.isEmpty() || name.isNull()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return obs_data_has_user_value(data, name.toUtf8());
|
OBSDataItemAutoRelease dataItem = obs_data_item_byname(data, name.toUtf8());
|
||||||
|
if (!dataItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectedFieldType != OBS_DATA_NULL) {
|
||||||
|
obs_data_type fieldType = obs_data_item_gettype(dataItem);
|
||||||
|
if (fieldType != expectedFieldType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldType == OBS_DATA_NUMBER && expectedNumberType != OBS_DATA_NUM_INVALID) {
|
||||||
|
obs_data_number_type numberType = obs_data_item_numtype(dataItem);
|
||||||
|
if (numberType != expectedNumberType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WSRequestHandler::hasBool(QString fieldName) {
|
||||||
|
return this->hasField(fieldName, OBS_DATA_BOOLEAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WSRequestHandler::hasString(QString fieldName) {
|
||||||
|
return this->hasField(fieldName, OBS_DATA_STRING);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WSRequestHandler::hasNumber(QString fieldName, obs_data_number_type expectedNumberType) {
|
||||||
|
return this->hasField(fieldName, OBS_DATA_NUMBER, expectedNumberType);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WSRequestHandler::hasInteger(QString fieldName) {
|
||||||
|
return this->hasNumber(fieldName, OBS_DATA_NUM_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WSRequestHandler::hasDouble(QString fieldName) {
|
||||||
|
return this->hasNumber(fieldName, OBS_DATA_NUM_DOUBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WSRequestHandler::hasArray(QString fieldName) {
|
||||||
|
return this->hasField(fieldName, OBS_DATA_ARRAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WSRequestHandler::hasObject(QString fieldName) {
|
||||||
|
return this->hasField(fieldName, OBS_DATA_OBJECT);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
Copyright (C) 2017 Mikhail Swift <https://github.com/mikhailswift>
|
Copyright (C) 2017 Mikhail Swift <https://github.com/mikhailswift>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
@ -17,123 +17,173 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef WSREQUESTHANDLER_H
|
#pragma once
|
||||||
#define WSREQUESTHANDLER_H
|
|
||||||
|
|
||||||
#include <QHash>
|
#include <QtCore/QHash>
|
||||||
#include <QSet>
|
#include <QtCore/QSet>
|
||||||
#include <QWebSocket>
|
#include <QtCore/QVariantHash>
|
||||||
#include <QWebSocketServer>
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
|
#include "ConnectionProperties.h"
|
||||||
|
|
||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
|
|
||||||
|
typedef obs_data_t* HandlerResponse;
|
||||||
|
|
||||||
class WSRequestHandler : public QObject {
|
class WSRequestHandler : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit WSRequestHandler(QWebSocket* client);
|
explicit WSRequestHandler(ConnectionProperties& connProperties);
|
||||||
~WSRequestHandler();
|
~WSRequestHandler();
|
||||||
void processIncomingMessage(QString textMessage);
|
std::string processIncomingMessage(std::string& textMessage);
|
||||||
bool hasField(QString name);
|
|
||||||
|
|
||||||
private:
|
bool hasField(QString fieldName, obs_data_type expectedFieldType = OBS_DATA_NULL,
|
||||||
QWebSocket* _client;
|
obs_data_number_type expectedNumberType = OBS_DATA_NUM_INVALID);
|
||||||
const char* _messageId;
|
bool hasBool(QString fieldName);
|
||||||
const char* _requestType;
|
bool hasString(QString fieldName);
|
||||||
OBSDataAutoRelease data;
|
bool hasNumber(QString fieldName, obs_data_number_type expectedNumberType = OBS_DATA_NUM_INVALID);
|
||||||
|
bool hasInteger(QString fieldName);
|
||||||
|
bool hasDouble(QString fieldName);
|
||||||
|
bool hasArray(QString fieldName);
|
||||||
|
bool hasObject(QString fieldName);
|
||||||
|
|
||||||
void SendOKResponse(obs_data_t* additionalFields = NULL);
|
HandlerResponse SendOKResponse(obs_data_t* additionalFields = nullptr);
|
||||||
void SendErrorResponse(const char* errorMessage);
|
HandlerResponse SendErrorResponse(QString errorMessage);
|
||||||
void SendErrorResponse(obs_data_t* additionalFields = NULL);
|
HandlerResponse SendErrorResponse(obs_data_t* additionalFields = nullptr);
|
||||||
void SendResponse(obs_data_t* response);
|
HandlerResponse SendResponse(const char* status, obs_data_t* additionalFields = nullptr);
|
||||||
|
|
||||||
static QHash<QString, void(*)(WSRequestHandler*)> messageMap;
|
obs_data_t* parameters() {
|
||||||
static QSet<QString> authNotRequired;
|
return this->data;
|
||||||
|
}
|
||||||
|
|
||||||
static void HandleGetVersion(WSRequestHandler* req);
|
private:
|
||||||
static void HandleGetAuthRequired(WSRequestHandler* req);
|
const char* _messageId;
|
||||||
static void HandleAuthenticate(WSRequestHandler* req);
|
const char* _requestType;
|
||||||
|
ConnectionProperties& _connProperties;
|
||||||
|
OBSDataAutoRelease data;
|
||||||
|
|
||||||
static void HandleSetHeartbeat(WSRequestHandler* req);
|
HandlerResponse processRequest(std::string& textMessage);
|
||||||
|
|
||||||
static void HandleSetFilenameFormatting(WSRequestHandler* req);
|
static QHash<QString, HandlerResponse(*)(WSRequestHandler*)> messageMap;
|
||||||
static void HandleGetFilenameFormatting(WSRequestHandler* req);
|
static QSet<QString> authNotRequired;
|
||||||
|
|
||||||
static void HandleSetCurrentScene(WSRequestHandler* req);
|
static HandlerResponse HandleGetVersion(WSRequestHandler* req);
|
||||||
static void HandleGetCurrentScene(WSRequestHandler* req);
|
static HandlerResponse HandleGetAuthRequired(WSRequestHandler* req);
|
||||||
static void HandleGetSceneList(WSRequestHandler* req);
|
static HandlerResponse HandleAuthenticate(WSRequestHandler* req);
|
||||||
|
|
||||||
static void HandleSetSceneItemRender(WSRequestHandler* req);
|
static HandlerResponse HandleGetStats(WSRequestHandler* req);
|
||||||
static void HandleSetSceneItemPosition(WSRequestHandler* req);
|
static HandlerResponse HandleSetHeartbeat(WSRequestHandler* req);
|
||||||
static void HandleSetSceneItemTransform(WSRequestHandler* req);
|
static HandlerResponse HandleGetVideoInfo(WSRequestHandler* req);
|
||||||
static void HandleSetSceneItemCrop(WSRequestHandler* req);
|
|
||||||
static void HandleGetSceneItemProperties(WSRequestHandler* req);
|
|
||||||
static void HandleSetSceneItemProperties(WSRequestHandler* req);
|
|
||||||
static void HandleResetSceneItem(WSRequestHandler* req);
|
|
||||||
|
|
||||||
static void HandleGetStreamingStatus(WSRequestHandler* req);
|
static HandlerResponse HandleSetFilenameFormatting(WSRequestHandler* req);
|
||||||
static void HandleStartStopStreaming(WSRequestHandler* req);
|
static HandlerResponse HandleGetFilenameFormatting(WSRequestHandler* req);
|
||||||
static void HandleStartStopRecording(WSRequestHandler* req);
|
|
||||||
static void HandleStartStreaming(WSRequestHandler* req);
|
|
||||||
static void HandleStopStreaming(WSRequestHandler* req);
|
|
||||||
static void HandleStartRecording(WSRequestHandler* req);
|
|
||||||
static void HandleStopRecording(WSRequestHandler* req);
|
|
||||||
|
|
||||||
static void HandleStartStopReplayBuffer(WSRequestHandler* req);
|
static HandlerResponse HandleBroadcastCustomMessage(WSRequestHandler* req);
|
||||||
static void HandleStartReplayBuffer(WSRequestHandler* req);
|
|
||||||
static void HandleStopReplayBuffer(WSRequestHandler* req);
|
|
||||||
static void HandleSaveReplayBuffer(WSRequestHandler* req);
|
|
||||||
|
|
||||||
static void HandleSetRecordingFolder(WSRequestHandler* req);
|
static HandlerResponse HandleSetCurrentScene(WSRequestHandler* req);
|
||||||
static void HandleGetRecordingFolder(WSRequestHandler* req);
|
static HandlerResponse HandleGetCurrentScene(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetSceneList(WSRequestHandler* req);
|
||||||
|
|
||||||
static void HandleGetTransitionList(WSRequestHandler* req);
|
static HandlerResponse HandleSetSceneItemRender(WSRequestHandler* req);
|
||||||
static void HandleGetCurrentTransition(WSRequestHandler* req);
|
static HandlerResponse HandleSetSceneItemPosition(WSRequestHandler* req);
|
||||||
static void HandleSetCurrentTransition(WSRequestHandler* req);
|
static HandlerResponse HandleSetSceneItemTransform(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleSetSceneItemCrop(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetSceneItemProperties(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleSetSceneItemProperties(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleResetSceneItem(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleDuplicateSceneItem(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleDeleteSceneItem(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleReorderSceneItems(WSRequestHandler* req);
|
||||||
|
|
||||||
static void HandleSetVolume(WSRequestHandler* req);
|
static HandlerResponse HandleGetStreamingStatus(WSRequestHandler* req);
|
||||||
static void HandleGetVolume(WSRequestHandler* req);
|
static HandlerResponse HandleStartStopStreaming(WSRequestHandler* req);
|
||||||
static void HandleToggleMute(WSRequestHandler* req);
|
static HandlerResponse HandleStartStopRecording(WSRequestHandler* req);
|
||||||
static void HandleSetMute(WSRequestHandler* req);
|
|
||||||
static void HandleGetMute(WSRequestHandler* req);
|
|
||||||
static void HandleSetSyncOffset(WSRequestHandler* req);
|
|
||||||
static void HandleGetSyncOffset(WSRequestHandler* req);
|
|
||||||
static void HandleGetSpecialSources(WSRequestHandler* req);
|
|
||||||
static void HandleGetSourcesList(WSRequestHandler* req);
|
|
||||||
static void HandleGetSourceTypesList(WSRequestHandler* req);
|
|
||||||
static void HandleGetSourceSettings(WSRequestHandler* req);
|
|
||||||
static void HandleSetSourceSettings(WSRequestHandler* req);
|
|
||||||
|
|
||||||
static void HandleSetCurrentSceneCollection(WSRequestHandler* req);
|
static HandlerResponse HandleStartStreaming(WSRequestHandler* req);
|
||||||
static void HandleGetCurrentSceneCollection(WSRequestHandler* req);
|
static HandlerResponse HandleStopStreaming(WSRequestHandler* req);
|
||||||
static void HandleListSceneCollections(WSRequestHandler* req);
|
|
||||||
|
|
||||||
static void HandleSetCurrentProfile(WSRequestHandler* req);
|
static HandlerResponse HandleStartRecording(WSRequestHandler* req);
|
||||||
static void HandleGetCurrentProfile(WSRequestHandler* req);
|
static HandlerResponse HandleStopRecording(WSRequestHandler* req);
|
||||||
static void HandleListProfiles(WSRequestHandler* req);
|
static HandlerResponse HandlePauseRecording(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleResumeRecording(WSRequestHandler* req);
|
||||||
|
|
||||||
static void HandleSetStreamSettings(WSRequestHandler* req);
|
static HandlerResponse HandleStartStopReplayBuffer(WSRequestHandler* req);
|
||||||
static void HandleGetStreamSettings(WSRequestHandler* req);
|
static HandlerResponse HandleStartReplayBuffer(WSRequestHandler* req);
|
||||||
static void HandleSaveStreamSettings(WSRequestHandler* req);
|
static HandlerResponse HandleStopReplayBuffer(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleSaveReplayBuffer(WSRequestHandler* req);
|
||||||
|
|
||||||
static void HandleSetTransitionDuration(WSRequestHandler* req);
|
static HandlerResponse HandleSetRecordingFolder(WSRequestHandler* req);
|
||||||
static void HandleGetTransitionDuration(WSRequestHandler* req);
|
static HandlerResponse HandleGetRecordingFolder(WSRequestHandler* req);
|
||||||
|
|
||||||
static void HandleGetStudioModeStatus(WSRequestHandler* req);
|
static HandlerResponse HandleGetTransitionList(WSRequestHandler* req);
|
||||||
static void HandleGetPreviewScene(WSRequestHandler* req);
|
static HandlerResponse HandleGetCurrentTransition(WSRequestHandler* req);
|
||||||
static void HandleSetPreviewScene(WSRequestHandler* req);
|
static HandlerResponse HandleSetCurrentTransition(WSRequestHandler* req);
|
||||||
static void HandleTransitionToProgram(WSRequestHandler* req);
|
|
||||||
static void HandleEnableStudioMode(WSRequestHandler* req);
|
|
||||||
static void HandleDisableStudioMode(WSRequestHandler* req);
|
|
||||||
static void HandleToggleStudioMode(WSRequestHandler* req);
|
|
||||||
|
|
||||||
static void HandleSetTextGDIPlusProperties(WSRequestHandler* req);
|
static HandlerResponse HandleSetVolume(WSRequestHandler* req);
|
||||||
static void HandleGetTextGDIPlusProperties(WSRequestHandler* req);
|
static HandlerResponse HandleGetVolume(WSRequestHandler* req);
|
||||||
static void HandleSetBrowserSourceProperties(WSRequestHandler* req);
|
static HandlerResponse HandleToggleMute(WSRequestHandler* req);
|
||||||
static void HandleGetBrowserSourceProperties(WSRequestHandler* req);
|
static HandlerResponse HandleSetMute(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetMute(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleSetSyncOffset(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetSyncOffset(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetSpecialSources(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetSourcesList(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetSourceTypesList(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetSourceSettings(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleSetSourceSettings(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleTakeSourceScreenshot(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static HandlerResponse HandleGetSourceFilters(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetSourceFilterInfo(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleAddFilterToSource(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleRemoveFilterFromSource(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleReorderSourceFilter(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleMoveSourceFilter(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleSetSourceFilterSettings(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleSetSourceFilterVisibility(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static HandlerResponse HandleSetCurrentSceneCollection(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetCurrentSceneCollection(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleListSceneCollections(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static HandlerResponse HandleSetCurrentProfile(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetCurrentProfile(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleListProfiles(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static HandlerResponse HandleSetStreamSettings(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetStreamSettings(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleSaveStreamSettings(WSRequestHandler* req);
|
||||||
|
#if BUILD_CAPTIONS
|
||||||
|
static HandlerResponse HandleSendCaptions(WSRequestHandler * req);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static HandlerResponse HandleSetTransitionDuration(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetTransitionDuration(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static HandlerResponse HandleGetStudioModeStatus(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetPreviewScene(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleSetPreviewScene(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleTransitionToProgram(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleEnableStudioMode(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleDisableStudioMode(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleToggleStudioMode(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static HandlerResponse HandleSetTextGDIPlusProperties(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetTextGDIPlusProperties(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static HandlerResponse HandleSetTextFreetype2Properties(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetTextFreetype2Properties(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static HandlerResponse HandleSetBrowserSourceProperties(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetBrowserSourceProperties(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static HandlerResponse HandleListOutputs(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleGetOutputInfo(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleStartOutput(WSRequestHandler* req);
|
||||||
|
static HandlerResponse HandleStopOutput(WSRequestHandler* req);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WSPROTOCOL_H
|
|
||||||
|
@ -1,11 +1,58 @@
|
|||||||
#include <QString>
|
#include "obs-websocket.h"
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "WSEvents.h"
|
#include "WSEvents.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
|
#define CASE(x) case x: return #x;
|
||||||
|
const char *describe_output_format(int format) {
|
||||||
|
switch (format) {
|
||||||
|
default:
|
||||||
|
CASE(VIDEO_FORMAT_NONE)
|
||||||
|
CASE(VIDEO_FORMAT_I420)
|
||||||
|
CASE(VIDEO_FORMAT_NV12)
|
||||||
|
CASE(VIDEO_FORMAT_YVYU)
|
||||||
|
CASE(VIDEO_FORMAT_YUY2)
|
||||||
|
CASE(VIDEO_FORMAT_UYVY)
|
||||||
|
CASE(VIDEO_FORMAT_RGBA)
|
||||||
|
CASE(VIDEO_FORMAT_BGRA)
|
||||||
|
CASE(VIDEO_FORMAT_BGRX)
|
||||||
|
CASE(VIDEO_FORMAT_Y800)
|
||||||
|
CASE(VIDEO_FORMAT_I444)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *describe_color_space(int cs) {
|
||||||
|
switch (cs) {
|
||||||
|
default:
|
||||||
|
CASE(VIDEO_CS_DEFAULT)
|
||||||
|
CASE(VIDEO_CS_601)
|
||||||
|
CASE(VIDEO_CS_709)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *describe_color_range(int range) {
|
||||||
|
switch (range) {
|
||||||
|
default:
|
||||||
|
CASE(VIDEO_RANGE_DEFAULT)
|
||||||
|
CASE(VIDEO_RANGE_PARTIAL)
|
||||||
|
CASE(VIDEO_RANGE_FULL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *describe_scale_type(int scale) {
|
||||||
|
switch (scale) {
|
||||||
|
default:
|
||||||
|
CASE(VIDEO_SCALE_DEFAULT)
|
||||||
|
CASE(VIDEO_SCALE_POINT)
|
||||||
|
CASE(VIDEO_SCALE_FAST_BILINEAR)
|
||||||
|
CASE(VIDEO_SCALE_BILINEAR)
|
||||||
|
CASE(VIDEO_SCALE_BICUBIC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef CASE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the latest version of the plugin and the API.
|
* Returns the latest version of the plugin and the API.
|
||||||
*
|
*
|
||||||
@ -19,25 +66,26 @@
|
|||||||
* @category general
|
* @category general
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetVersion(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetVersion(WSRequestHandler* req) {
|
||||||
QString obsVersion = Utils::OBSVersionString();
|
QString obsVersion = Utils::OBSVersionString();
|
||||||
|
|
||||||
QList<QString> names = req->messageMap.keys();
|
QList<QString> names = req->messageMap.keys();
|
||||||
names.sort(Qt::CaseInsensitive);
|
names.sort(Qt::CaseInsensitive);
|
||||||
|
|
||||||
// (Palakis) OBS' data arrays only support object arrays, so I improvised.
|
// (Palakis) OBS' data arrays only support object arrays, so I improvised.
|
||||||
QString requests;
|
QString requests;
|
||||||
requests += names.takeFirst();
|
requests += names.takeFirst();
|
||||||
for (QString reqName : names) {
|
for (QString reqName : names) {
|
||||||
requests += ("," + reqName);
|
requests += ("," + reqName);
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
obs_data_set_string(data, "obs-websocket-version", OBS_WEBSOCKET_VERSION);
|
obs_data_set_double(data, "version", 1.1);
|
||||||
obs_data_set_string(data, "obs-studio-version", obsVersion.toUtf8());
|
obs_data_set_string(data, "obs-websocket-version", OBS_WEBSOCKET_VERSION);
|
||||||
obs_data_set_string(data, "available-requests", requests.toUtf8());
|
obs_data_set_string(data, "obs-studio-version", obsVersion.toUtf8());
|
||||||
|
obs_data_set_string(data, "available-requests", requests.toUtf8());
|
||||||
|
|
||||||
req->SendOKResponse(data);
|
return req->SendOKResponse(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,20 +101,21 @@
|
|||||||
* @category general
|
* @category general
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetAuthRequired(WSRequestHandler* req) {
|
||||||
bool authRequired = Config::Current()->AuthRequired;
|
bool authRequired = GetConfig()->AuthRequired;
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
obs_data_set_bool(data, "authRequired", authRequired);
|
obs_data_set_bool(data, "authRequired", authRequired);
|
||||||
|
|
||||||
if (authRequired) {
|
if (authRequired) {
|
||||||
obs_data_set_string(data, "challenge",
|
auto config = GetConfig();
|
||||||
Config::Current()->SessionChallenge.toUtf8());
|
obs_data_set_string(data, "challenge",
|
||||||
obs_data_set_string(data, "salt",
|
config->SessionChallenge.toUtf8());
|
||||||
Config::Current()->Salt.toUtf8());
|
obs_data_set_string(data, "salt",
|
||||||
}
|
config->Salt.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
req->SendOKResponse(data);
|
return req->SendOKResponse(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,26 +128,26 @@ void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler* req) {
|
|||||||
* @category general
|
* @category general
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) {
|
||||||
if (!req->hasField("auth")) {
|
if (!req->hasField("auth")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
QString auth = obs_data_get_string(req->data, "auth");
|
if (req->_connProperties.isAuthenticated()) {
|
||||||
if (auth.isEmpty()) {
|
return req->SendErrorResponse("already authenticated");
|
||||||
req->SendErrorResponse("auth not specified!");
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((req->_client->property(PROP_AUTHENTICATED).toBool() == false)
|
QString auth = obs_data_get_string(req->data, "auth");
|
||||||
&& Config::Current()->CheckAuth(auth))
|
if (auth.isEmpty()) {
|
||||||
{
|
return req->SendErrorResponse("auth not specified!");
|
||||||
req->_client->setProperty(PROP_AUTHENTICATED, true);
|
}
|
||||||
req->SendOKResponse();
|
|
||||||
} else {
|
if (GetConfig()->CheckAuth(auth) == false) {
|
||||||
req->SendErrorResponse("Authentication Failed.");
|
return req->SendErrorResponse("Authentication Failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req->_connProperties.setAuthenticated(true);
|
||||||
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,19 +160,17 @@ void WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) {
|
|||||||
* @category general
|
* @category general
|
||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetHeartbeat(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetHeartbeat(WSRequestHandler* req) {
|
||||||
if (!req->hasField("enable")) {
|
if (!req->hasField("enable")) {
|
||||||
req->SendErrorResponse("Heartbeat <enable> parameter missing");
|
return req->SendErrorResponse("Heartbeat <enable> parameter missing");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
WSEvents::Instance->HeartbeatIsActive =
|
auto events = GetEventsSystem();
|
||||||
obs_data_get_bool(req->data, "enable");
|
events->HeartbeatIsActive = obs_data_get_bool(req->data, "enable");
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_bool(response, "enable",
|
obs_data_set_bool(response, "enable", events->HeartbeatIsActive);
|
||||||
WSEvents::Instance->HeartbeatIsActive);
|
return req->SendOKResponse(response);
|
||||||
req->SendOKResponse(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,19 +183,18 @@ void WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) {
|
|||||||
* @category general
|
* @category general
|
||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetFilenameFormatting(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetFilenameFormatting(WSRequestHandler* req) {
|
||||||
if (!req->hasField("filename-formatting")) {
|
if (!req->hasField("filename-formatting")) {
|
||||||
req->SendErrorResponse("<filename-formatting> parameter missing");
|
return req->SendErrorResponse("<filename-formatting> parameter missing");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
QString filenameFormatting = obs_data_get_string(req->data, "filename-formatting");
|
QString filenameFormatting = obs_data_get_string(req->data, "filename-formatting");
|
||||||
if (!filenameFormatting.isEmpty()) {
|
if (filenameFormatting.isEmpty()) {
|
||||||
Utils::SetFilenameFormatting(filenameFormatting.toUtf8());
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
req->SendOKResponse();
|
}
|
||||||
} else {
|
|
||||||
req->SendErrorResponse("invalid request parameters");
|
Utils::SetFilenameFormatting(filenameFormatting.toUtf8());
|
||||||
}
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,8 +207,94 @@ void WSRequestHandler::HandleSetFilenameFormatting(WSRequestHandler* req) {
|
|||||||
* @category general
|
* @category general
|
||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetFilenameFormatting(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetFilenameFormatting(WSRequestHandler* req) {
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_string(response, "filename-formatting", Utils::GetFilenameFormatting());
|
obs_data_set_string(response, "filename-formatting", Utils::GetFilenameFormatting());
|
||||||
req->SendOKResponse(response);
|
return req->SendOKResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get OBS stats (almost the same info as provided in OBS' stats window)
|
||||||
|
*
|
||||||
|
* @return {OBSStats} `stats` OBS stats
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetStats
|
||||||
|
* @category general
|
||||||
|
* @since 4.6.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandleGetStats(WSRequestHandler* req) {
|
||||||
|
OBSDataAutoRelease stats = GetEventsSystem()->GetStats();
|
||||||
|
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_obj(response, "stats", stats);
|
||||||
|
return req->SendOKResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast custom message to all connected WebSocket clients
|
||||||
|
*
|
||||||
|
* @param {String} `realm` Identifier to be choosen by the client
|
||||||
|
* @param {Object} `data` User-defined data
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name BroadcastCustomMessage
|
||||||
|
* @category general
|
||||||
|
* @since 4.7.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandleBroadcastCustomMessage(WSRequestHandler* req) {
|
||||||
|
if (!req->hasField("realm") || !req->hasField("data")) {
|
||||||
|
return req->SendErrorResponse("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString realm = obs_data_get_string(req->data, "realm");
|
||||||
|
OBSDataAutoRelease data = obs_data_get_obj(req->data, "data");
|
||||||
|
|
||||||
|
if (realm.isEmpty()) {
|
||||||
|
return req->SendErrorResponse("realm not specified!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return req->SendErrorResponse("data not specified!");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto events = GetEventsSystem();
|
||||||
|
events->OnBroadcastCustomMessage(realm, data);
|
||||||
|
|
||||||
|
return req->SendOKResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get basic OBS video information
|
||||||
|
*
|
||||||
|
* @return {int} `baseWidth` Base (canvas) width
|
||||||
|
* @return {int} `baseHeight` Base (canvas) height
|
||||||
|
* @return {int} `outputWidth` Output width
|
||||||
|
* @return {int} `outputHeight` Output height
|
||||||
|
* @return {String} `scaleType` Scaling method used if output size differs from base size
|
||||||
|
* @return {double} `fps` Frames rendered per second
|
||||||
|
* @return {String} `videoFormat` Video color format
|
||||||
|
* @return {String} `colorSpace` Color space for YUV
|
||||||
|
* @return {String} `colorRange` Color range (full or partial)
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetVideoInfo
|
||||||
|
* @category general
|
||||||
|
* @since 4.6.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandleGetVideoInfo(WSRequestHandler* req) {
|
||||||
|
obs_video_info ovi;
|
||||||
|
obs_get_video_info(&ovi);
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_int(response, "baseWidth", ovi.base_width);
|
||||||
|
obs_data_set_int(response, "baseHeight", ovi.base_height);
|
||||||
|
obs_data_set_int(response, "outputWidth", ovi.output_width);
|
||||||
|
obs_data_set_int(response, "outputHeight", ovi.output_height);
|
||||||
|
obs_data_set_double(response, "fps", (double)ovi.fps_num / ovi.fps_den);
|
||||||
|
obs_data_set_string(response, "videoFormat", describe_output_format(ovi.output_format));
|
||||||
|
obs_data_set_string(response, "colorSpace", describe_color_space(ovi.colorspace));
|
||||||
|
obs_data_set_string(response, "colorRange", describe_color_range(ovi.range));
|
||||||
|
obs_data_set_string(response, "scaleType", describe_scale_type(ovi.scale_type));
|
||||||
|
return req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
181
src/WSRequestHandler_Outputs.cpp
Normal file
181
src/WSRequestHandler_Outputs.cpp
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} `Output`
|
||||||
|
* @property {String} `name` Output name
|
||||||
|
* @property {String} `type` Output type/kind
|
||||||
|
* @property {int} `width` Video output width
|
||||||
|
* @property {int} `height` Video output height
|
||||||
|
* @property {Object} `flags` Output flags
|
||||||
|
* @property {int} `flags.rawValue` Raw flags value
|
||||||
|
* @property {boolean} `flags.audio` Output uses audio
|
||||||
|
* @property {boolean} `flags.video` Output uses video
|
||||||
|
* @property {boolean} `flags.encoded` Output is encoded
|
||||||
|
* @property {boolean} `flags.multiTrack` Output uses several audio tracks
|
||||||
|
* @property {boolean} `flags.service` Output uses a service
|
||||||
|
* @property {Object} `settings` Output name
|
||||||
|
* @property {boolean} `active` Output status (active or not)
|
||||||
|
* @property {boolean} `reconnecting` Output reconnection status (reconnecting or not)
|
||||||
|
* @property {double} `congestion` Output congestion
|
||||||
|
* @property {int} `totalFrames` Number of frames sent
|
||||||
|
* @property {int} `droppedFrames` Number of frames dropped
|
||||||
|
* @property {int} `totalBytes` Total bytes sent
|
||||||
|
*/
|
||||||
|
obs_data_t* getOutputInfo(obs_output_t* output)
|
||||||
|
{
|
||||||
|
if (!output) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease settings = obs_output_get_settings(output);
|
||||||
|
|
||||||
|
uint32_t rawFlags = obs_output_get_flags(output);
|
||||||
|
OBSDataAutoRelease flags = obs_data_create();
|
||||||
|
obs_data_set_int(flags, "rawValue", rawFlags);
|
||||||
|
obs_data_set_bool(flags, "audio", rawFlags & OBS_OUTPUT_AUDIO);
|
||||||
|
obs_data_set_bool(flags, "video", rawFlags & OBS_OUTPUT_VIDEO);
|
||||||
|
obs_data_set_bool(flags, "encoded", rawFlags & OBS_OUTPUT_ENCODED);
|
||||||
|
obs_data_set_bool(flags, "multiTrack", rawFlags & OBS_OUTPUT_MULTI_TRACK);
|
||||||
|
obs_data_set_bool(flags, "service", rawFlags & OBS_OUTPUT_SERVICE);
|
||||||
|
|
||||||
|
obs_data_t* data = obs_data_create();
|
||||||
|
|
||||||
|
obs_data_set_string(data, "name", obs_output_get_name(output));
|
||||||
|
obs_data_set_string(data, "type", obs_output_get_id(output));
|
||||||
|
obs_data_set_int(data, "width", obs_output_get_width(output));
|
||||||
|
obs_data_set_int(data, "height", obs_output_get_height(output));
|
||||||
|
obs_data_set_obj(data, "flags", flags);
|
||||||
|
obs_data_set_obj(data, "settings", settings);
|
||||||
|
|
||||||
|
obs_data_set_bool(data, "active", obs_output_active(output));
|
||||||
|
obs_data_set_bool(data, "reconnecting", obs_output_reconnecting(output));
|
||||||
|
obs_data_set_double(data, "congestion", obs_output_get_congestion(output));
|
||||||
|
obs_data_set_int(data, "totalFrames", obs_output_get_total_frames(output));
|
||||||
|
obs_data_set_int(data, "droppedFrames", obs_output_get_frames_dropped(output));
|
||||||
|
obs_data_set_int(data, "totalBytes", obs_output_get_total_bytes(output));
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandlerResponse findOutputOrFail(WSRequestHandler* req, std::function<HandlerResponse (obs_output_t*)> callback)
|
||||||
|
{
|
||||||
|
if (!req->hasField("outputName")) {
|
||||||
|
return req->SendErrorResponse("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* outputName = obs_data_get_string(req->parameters(), "outputName");
|
||||||
|
OBSOutputAutoRelease output = obs_get_output_by_name(outputName);
|
||||||
|
if (!output) {
|
||||||
|
return req->SendErrorResponse("specified output doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List existing outputs
|
||||||
|
*
|
||||||
|
* @return {Array<Output>} `outputs` Outputs list
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name ListOutputs
|
||||||
|
* @category outputs
|
||||||
|
* @since 4.7.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandleListOutputs(WSRequestHandler* req)
|
||||||
|
{
|
||||||
|
OBSDataArrayAutoRelease outputs = obs_data_array_create();
|
||||||
|
|
||||||
|
obs_enum_outputs([](void* param, obs_output_t* output) {
|
||||||
|
obs_data_array_t* outputs = reinterpret_cast<obs_data_array_t*>(param);
|
||||||
|
|
||||||
|
OBSDataAutoRelease outputInfo = getOutputInfo(output);
|
||||||
|
obs_data_array_push_back(outputs, outputInfo);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}, outputs);
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = obs_data_create();
|
||||||
|
obs_data_set_array(fields, "outputs", outputs);
|
||||||
|
return req->SendOKResponse(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get information about a single output
|
||||||
|
*
|
||||||
|
* @param {String} `outputName` Output name
|
||||||
|
*
|
||||||
|
* @return {Output} `outputInfo` Output info
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name GetOutputInfo
|
||||||
|
* @category outputs
|
||||||
|
* @since 4.7.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandleGetOutputInfo(WSRequestHandler* req)
|
||||||
|
{
|
||||||
|
return findOutputOrFail(req, [req](obs_output_t* output) {
|
||||||
|
OBSDataAutoRelease outputInfo = getOutputInfo(output);
|
||||||
|
|
||||||
|
OBSDataAutoRelease fields = obs_data_create();
|
||||||
|
obs_data_set_obj(fields, "outputInfo", outputInfo);
|
||||||
|
return req->SendOKResponse(fields);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start an output
|
||||||
|
*
|
||||||
|
* @param {String} `outputName` Output name
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name StartOutput
|
||||||
|
* @category outputs
|
||||||
|
* @since 4.7.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandleStartOutput(WSRequestHandler* req)
|
||||||
|
{
|
||||||
|
return findOutputOrFail(req, [req](obs_output_t* output) {
|
||||||
|
if (obs_output_active(output)) {
|
||||||
|
return req->SendErrorResponse("output already active");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = obs_output_start(output);
|
||||||
|
if (!success) {
|
||||||
|
QString lastError = obs_output_get_last_error(output);
|
||||||
|
QString errorMessage = QString("output start failed: %1").arg(lastError);
|
||||||
|
return req->SendErrorResponse(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return req->SendOKResponse();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop an output
|
||||||
|
*
|
||||||
|
* @param {String} `outputName` Output name
|
||||||
|
* @param {boolean (optional)} `force` Force stop (default: false)
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name StopOutput
|
||||||
|
* @category outputs
|
||||||
|
* @since 4.7.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandleStopOutput(WSRequestHandler* req)
|
||||||
|
{
|
||||||
|
return findOutputOrFail(req, [req](obs_output_t* output) {
|
||||||
|
if (!obs_output_active(output)) {
|
||||||
|
return req->SendErrorResponse("output not active");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool forceStop = obs_data_get_bool(req->data, "force");
|
||||||
|
if (forceStop) {
|
||||||
|
obs_output_force_stop(output);
|
||||||
|
} else {
|
||||||
|
obs_output_stop(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
return req->SendOKResponse();
|
||||||
|
});
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
#include <QString>
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
@ -13,20 +12,19 @@
|
|||||||
* @category profiles
|
* @category profiles
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetCurrentProfile(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetCurrentProfile(WSRequestHandler* req) {
|
||||||
if (!req->hasField("profile-name")) {
|
if (!req->hasField("profile-name")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
QString profileName = obs_data_get_string(req->data, "profile-name");
|
QString profileName = obs_data_get_string(req->data, "profile-name");
|
||||||
if (!profileName.isEmpty()) {
|
if (profileName.isEmpty()) {
|
||||||
// TODO : check if profile exists
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
obs_frontend_set_current_profile(profileName.toUtf8());
|
}
|
||||||
req->SendOKResponse();
|
|
||||||
} else {
|
// TODO : check if profile exists
|
||||||
req->SendErrorResponse("invalid request parameters");
|
obs_frontend_set_current_profile(profileName.toUtf8());
|
||||||
}
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,32 +37,29 @@
|
|||||||
* @category profiles
|
* @category profiles
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler* req) {
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_string(response, "profile-name",
|
obs_data_set_string(response, "profile-name", obs_frontend_get_current_profile());
|
||||||
obs_frontend_get_current_profile());
|
return req->SendOKResponse(response);
|
||||||
|
|
||||||
req->SendOKResponse(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of available profiles.
|
* Get a list of available profiles.
|
||||||
*
|
*
|
||||||
* @return {Object|Array} `profiles` List of available profiles.
|
* @return {Array<Object>} `profiles` List of available profiles.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name ListProfiles
|
* @name ListProfiles
|
||||||
* @category profiles
|
* @category profiles
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleListProfiles(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleListProfiles(WSRequestHandler* req) {
|
||||||
char** profiles = obs_frontend_get_profiles();
|
char** profiles = obs_frontend_get_profiles();
|
||||||
OBSDataArrayAutoRelease list =
|
OBSDataArrayAutoRelease list = Utils::StringListToArray(profiles, "profile-name");
|
||||||
Utils::StringListToArray(profiles, "profile-name");
|
bfree(profiles);
|
||||||
bfree(profiles);
|
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_array(response, "profiles", list);
|
obs_data_set_array(response, "profiles", list);
|
||||||
|
|
||||||
req->SendOKResponse(response);
|
return req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,20 @@
|
|||||||
#include <QString>
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
|
#include <util/platform.h>
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
HandlerResponse ifCanPause(WSRequestHandler* req, std::function<HandlerResponse()> callback)
|
||||||
|
{
|
||||||
|
if (!obs_frontend_recording_active()) {
|
||||||
|
return req->SendErrorResponse("recording is not active");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Utils::RecordingPauseSupported()) {
|
||||||
|
return req->SendErrorResponse("recording pauses are not available in this version of OBS Studio");
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle recording on or off.
|
* Toggle recording on or off.
|
||||||
@ -11,13 +24,9 @@
|
|||||||
* @category recording
|
* @category recording
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req) {
|
||||||
if (obs_frontend_recording_active())
|
(obs_frontend_recording_active() ? obs_frontend_recording_stop() : obs_frontend_recording_start());
|
||||||
obs_frontend_recording_stop();
|
return req->SendOKResponse();
|
||||||
else
|
|
||||||
obs_frontend_recording_start();
|
|
||||||
|
|
||||||
req->SendOKResponse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,13 +38,13 @@
|
|||||||
* @category recording
|
* @category recording
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
|
||||||
if (obs_frontend_recording_active() == false) {
|
if (obs_frontend_recording_active()) {
|
||||||
obs_frontend_recording_start();
|
return req->SendErrorResponse("recording already active");
|
||||||
req->SendOKResponse();
|
}
|
||||||
} else {
|
|
||||||
req->SendErrorResponse("recording already active");
|
obs_frontend_recording_start();
|
||||||
}
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,18 +56,63 @@
|
|||||||
* @category recording
|
* @category recording
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleStopRecording(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleStopRecording(WSRequestHandler* req) {
|
||||||
if (obs_frontend_recording_active() == true) {
|
if (!obs_frontend_recording_active()) {
|
||||||
obs_frontend_recording_stop();
|
return req->SendErrorResponse("recording not active");
|
||||||
req->SendOKResponse();
|
}
|
||||||
} else {
|
|
||||||
req->SendErrorResponse("recording not active");
|
obs_frontend_recording_stop();
|
||||||
}
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the current recording folder.
|
* Pause the current recording.
|
||||||
*
|
* Returns an error if recording is not active or already paused.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name PauseRecording
|
||||||
|
* @category recording
|
||||||
|
* @since 4.7.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandlePauseRecording(WSRequestHandler* req) {
|
||||||
|
return ifCanPause(req, [req]() {
|
||||||
|
if (Utils::RecordingPaused()) {
|
||||||
|
return req->SendErrorResponse("recording already paused");
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::PauseRecording(true);
|
||||||
|
return req->SendOKResponse();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resume/unpause the current recording (if paused).
|
||||||
|
* Returns an error if recording is not active or not paused.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name ResumeRecording
|
||||||
|
* @category recording
|
||||||
|
* @since 4.7.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandleResumeRecording(WSRequestHandler* req) {
|
||||||
|
return ifCanPause(req, [req]() {
|
||||||
|
if (!Utils::RecordingPaused()) {
|
||||||
|
return req->SendErrorResponse("recording is not paused");
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::PauseRecording(false);
|
||||||
|
return req->SendOKResponse();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* in progress, the change won't be applied immediately and will be
|
||||||
|
* effective on the next recording.
|
||||||
|
*
|
||||||
* @param {String} `rec-folder` Path of the recording folder.
|
* @param {String} `rec-folder` Path of the recording folder.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
@ -66,18 +120,18 @@ void WSRequestHandler::HandleStopRecording(WSRequestHandler* req) {
|
|||||||
* @category recording
|
* @category recording
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetRecordingFolder(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetRecordingFolder(WSRequestHandler* req) {
|
||||||
if (!req->hasField("rec-folder")) {
|
if (!req->hasField("rec-folder")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const char* newRecFolder = obs_data_get_string(req->data, "rec-folder");
|
const char* newRecFolder = obs_data_get_string(req->data, "rec-folder");
|
||||||
bool success = Utils::SetRecordingFolder(newRecFolder);
|
bool success = Utils::SetRecordingFolder(newRecFolder);
|
||||||
if (success)
|
if (!success) {
|
||||||
req->SendOKResponse();
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
else
|
}
|
||||||
req->SendErrorResponse("invalid request parameters");
|
|
||||||
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,11 +144,11 @@ void WSRequestHandler::HandleStopRecording(WSRequestHandler* req) {
|
|||||||
* @category recording
|
* @category recording
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetRecordingFolder(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetRecordingFolder(WSRequestHandler* req) {
|
||||||
const char* recFolder = Utils::GetRecordingFolder();
|
const char* recFolder = Utils::GetRecordingFolder();
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_string(response, "rec-folder", recFolder);
|
obs_data_set_string(response, "rec-folder", recFolder);
|
||||||
|
|
||||||
req->SendOKResponse(response);
|
return req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
#include <QString>
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
@ -11,13 +10,13 @@
|
|||||||
* @category replay buffer
|
* @category replay buffer
|
||||||
* @since 4.2.0
|
* @since 4.2.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleStartStopReplayBuffer(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleStartStopReplayBuffer(WSRequestHandler* req) {
|
||||||
if (obs_frontend_replay_buffer_active()) {
|
if (obs_frontend_replay_buffer_active()) {
|
||||||
obs_frontend_replay_buffer_stop();
|
obs_frontend_replay_buffer_stop();
|
||||||
} else {
|
} else {
|
||||||
Utils::StartReplayBuffer();
|
Utils::StartReplayBuffer();
|
||||||
}
|
}
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,19 +31,17 @@ void WSRequestHandler::HandleStartStopReplayBuffer(WSRequestHandler* req) {
|
|||||||
* @category replay buffer
|
* @category replay buffer
|
||||||
* @since 4.2.0
|
* @since 4.2.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleStartReplayBuffer(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleStartReplayBuffer(WSRequestHandler* req) {
|
||||||
if (!Utils::ReplayBufferEnabled()) {
|
if (!Utils::ReplayBufferEnabled()) {
|
||||||
req->SendErrorResponse("replay buffer disabled in settings");
|
return req->SendErrorResponse("replay buffer disabled in settings");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (obs_frontend_replay_buffer_active() == true) {
|
if (obs_frontend_replay_buffer_active() == true) {
|
||||||
req->SendErrorResponse("replay buffer already active");
|
return req->SendErrorResponse("replay buffer already active");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Utils::StartReplayBuffer();
|
Utils::StartReplayBuffer();
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,13 +53,13 @@ void WSRequestHandler::HandleStartReplayBuffer(WSRequestHandler* req) {
|
|||||||
* @category replay buffer
|
* @category replay buffer
|
||||||
* @since 4.2.0
|
* @since 4.2.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleStopReplayBuffer(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleStopReplayBuffer(WSRequestHandler* req) {
|
||||||
if (obs_frontend_replay_buffer_active() == true) {
|
if (obs_frontend_replay_buffer_active() == true) {
|
||||||
obs_frontend_replay_buffer_stop();
|
obs_frontend_replay_buffer_stop();
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
} else {
|
} else {
|
||||||
req->SendErrorResponse("replay buffer not active");
|
return req->SendErrorResponse("replay buffer not active");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,18 +72,17 @@ void WSRequestHandler::HandleStopReplayBuffer(WSRequestHandler* req) {
|
|||||||
* @category replay buffer
|
* @category replay buffer
|
||||||
* @since 4.2.0
|
* @since 4.2.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSaveReplayBuffer(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSaveReplayBuffer(WSRequestHandler* req) {
|
||||||
if (!obs_frontend_replay_buffer_active()) {
|
if (!obs_frontend_replay_buffer_active()) {
|
||||||
req->SendErrorResponse("replay buffer not active");
|
return req->SendErrorResponse("replay buffer not active");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
OBSOutputAutoRelease replayOutput = obs_frontend_get_replay_buffer_output();
|
OBSOutputAutoRelease replayOutput = obs_frontend_get_replay_buffer_output();
|
||||||
|
|
||||||
calldata_t cd = { 0 };
|
calldata_t cd = { 0 };
|
||||||
proc_handler_t* ph = obs_output_get_proc_handler(replayOutput);
|
proc_handler_t* ph = obs_output_get_proc_handler(replayOutput);
|
||||||
proc_handler_call(ph, "save", &cd);
|
proc_handler_call(ph, "save", &cd);
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
#include <QString>
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
@ -13,20 +12,19 @@
|
|||||||
* @category scene collections
|
* @category scene collections
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetCurrentSceneCollection(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetCurrentSceneCollection(WSRequestHandler* req) {
|
||||||
if (!req->hasField("sc-name")) {
|
if (!req->hasField("sc-name")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
QString sceneCollection = obs_data_get_string(req->data, "sc-name");
|
QString sceneCollection = obs_data_get_string(req->data, "sc-name");
|
||||||
if (!sceneCollection.isEmpty()) {
|
if (sceneCollection.isEmpty()) {
|
||||||
// TODO : Check if specified profile exists and if changing is allowed
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
obs_frontend_set_current_scene_collection(sceneCollection.toUtf8());
|
}
|
||||||
req->SendOKResponse();
|
|
||||||
} else {
|
// TODO : Check if specified profile exists and if changing is allowed
|
||||||
req->SendErrorResponse("invalid request parameters");
|
obs_frontend_set_current_scene_collection(sceneCollection.toUtf8());
|
||||||
}
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,33 +37,32 @@
|
|||||||
* @category scene collections
|
* @category scene collections
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetCurrentSceneCollection(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetCurrentSceneCollection(WSRequestHandler* req) {
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_string(response, "sc-name",
|
obs_data_set_string(response, "sc-name",
|
||||||
obs_frontend_get_current_scene_collection());
|
obs_frontend_get_current_scene_collection());
|
||||||
|
|
||||||
req->SendOKResponse(response);
|
return req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List available scene collections
|
* List available scene collections
|
||||||
*
|
*
|
||||||
* @return {Object|Array} `scene-collections` Scene collections list
|
* @return {Array<String>} `scene-collections` Scene collections list
|
||||||
* @return {String} `scene-collections.*.`
|
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name ListSceneCollections
|
* @name ListSceneCollections
|
||||||
* @category scene collections
|
* @category scene collections
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleListSceneCollections(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleListSceneCollections(WSRequestHandler* req) {
|
||||||
char** sceneCollections = obs_frontend_get_scene_collections();
|
char** sceneCollections = obs_frontend_get_scene_collections();
|
||||||
OBSDataArrayAutoRelease list =
|
OBSDataArrayAutoRelease list =
|
||||||
Utils::StringListToArray(sceneCollections, "sc-name");
|
Utils::StringListToArray(sceneCollections, "sc-name");
|
||||||
bfree(sceneCollections);
|
bfree(sceneCollections);
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_array(response, "scene-collections", list);
|
obs_data_set_array(response, "scene-collections", list);
|
||||||
|
|
||||||
req->SendOKResponse(response);
|
return req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#include <QString>
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the scene specific properties of the specified source item.
|
* Gets the scene specific properties of the specified source item.
|
||||||
|
* Coordinates are relative to the item's parent (the scene or group it belongs to).
|
||||||
*
|
*
|
||||||
* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.
|
* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.
|
||||||
* @param {String} `item` The name of the source.
|
* @param {String} `item` The name of the source.
|
||||||
@ -21,171 +21,105 @@
|
|||||||
* @return {int} `crop.bottom` The number of pixels cropped off the bottom of the source before scaling.
|
* @return {int} `crop.bottom` The number of pixels cropped off the bottom of the source before scaling.
|
||||||
* @return {int} `crop.left` The number of pixels cropped off the left of the source before scaling.
|
* @return {int} `crop.left` The number of pixels cropped off the left of the source before scaling.
|
||||||
* @return {bool} `visible` If the source is visible.
|
* @return {bool} `visible` If the source is visible.
|
||||||
* @return {String} `bounds.type` Type of bounding box.
|
* @return {bool} `locked` If the source's transform is locked.
|
||||||
|
* @return {String} `bounds.type` Type of bounding box. Can be "OBS_BOUNDS_STRETCH", "OBS_BOUNDS_SCALE_INNER", "OBS_BOUNDS_SCALE_OUTER", "OBS_BOUNDS_SCALE_TO_WIDTH", "OBS_BOUNDS_SCALE_TO_HEIGHT", "OBS_BOUNDS_MAX_ONLY" or "OBS_BOUNDS_NONE".
|
||||||
* @return {int} `bounds.alignment` Alignment of the bounding box.
|
* @return {int} `bounds.alignment` Alignment of the bounding box.
|
||||||
* @return {double} `bounds.x` Width of the bounding box.
|
* @return {double} `bounds.x` Width of the bounding box.
|
||||||
* @return {double} `bounds.y` Height of the bounding box.
|
* @return {double} `bounds.y` Height of the bounding box.
|
||||||
*
|
* @return {int} `sourceWidth` Base width (without scaling) of the source
|
||||||
|
* @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)
|
||||||
|
* @property {String (optional)} `parentGroupName` Name of the item's parent (if this item belongs to a group)
|
||||||
|
* @property {Array<SceneItemTransform> (optional)} `groupChildren` List of children (if this item is a group)
|
||||||
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name GetSceneItemProperties
|
* @name GetSceneItemProperties
|
||||||
* @category scene items
|
* @category scene items
|
||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetSceneItemProperties(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetSceneItemProperties(WSRequestHandler* req) {
|
||||||
if (!req->hasField("item")) {
|
if (!req->hasField("item")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString itemName = obs_data_get_string(req->data, "item");
|
QString itemName = obs_data_get_string(req->data, "item");
|
||||||
if (itemName.isEmpty()) {
|
if (itemName.isEmpty()) {
|
||||||
req->SendErrorResponse("invalid request parameters");
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString sceneName = obs_data_get_string(req->data, "scene-name");
|
QString sceneName = obs_data_get_string(req->data, "scene-name");
|
||||||
OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
req->SendErrorResponse("requested scene doesn't exist");
|
return req->SendErrorResponse("requested scene doesn't exist");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSSceneItemAutoRelease sceneItem =
|
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
|
||||||
Utils::GetSceneItemFromName(scene, itemName);
|
|
||||||
if (!sceneItem) {
|
if (!sceneItem) {
|
||||||
req->SendErrorResponse("specified scene item doesn't exist");
|
return req->SendErrorResponse("specified scene item doesn't exist");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
OBSDataAutoRelease data = Utils::GetSceneItemPropertiesData(sceneItem);
|
||||||
obs_data_set_string(data, "name", itemName.toUtf8());
|
obs_data_set_string(data, "name", itemName.toUtf8());
|
||||||
|
|
||||||
OBSDataAutoRelease posData = obs_data_create();
|
return req->SendOKResponse(data);
|
||||||
vec2 pos;
|
|
||||||
obs_sceneitem_get_pos(sceneItem, &pos);
|
|
||||||
obs_data_set_double(posData, "x", pos.x);
|
|
||||||
obs_data_set_double(posData, "y", pos.y);
|
|
||||||
obs_data_set_int(posData, "alignment", obs_sceneitem_get_alignment(sceneItem));
|
|
||||||
obs_data_set_obj(data, "position", posData);
|
|
||||||
|
|
||||||
obs_data_set_double(data, "rotation", obs_sceneitem_get_rot(sceneItem));
|
|
||||||
|
|
||||||
OBSDataAutoRelease scaleData = obs_data_create();
|
|
||||||
vec2 scale;
|
|
||||||
obs_sceneitem_get_scale(sceneItem, &scale);
|
|
||||||
obs_data_set_double(scaleData, "x", scale.x);
|
|
||||||
obs_data_set_double(scaleData, "y", scale.y);
|
|
||||||
obs_data_set_obj(data, "scale", scaleData);
|
|
||||||
|
|
||||||
OBSDataAutoRelease cropData = obs_data_create();
|
|
||||||
obs_sceneitem_crop crop;
|
|
||||||
obs_sceneitem_get_crop(sceneItem, &crop);
|
|
||||||
obs_data_set_int(cropData, "left", crop.left);
|
|
||||||
obs_data_set_int(cropData, "top", crop.top);
|
|
||||||
obs_data_set_int(cropData, "right", crop.right);
|
|
||||||
obs_data_set_int(cropData, "bottom", crop.bottom);
|
|
||||||
obs_data_set_obj(data, "crop", cropData);
|
|
||||||
|
|
||||||
obs_data_set_bool(data, "visible", obs_sceneitem_visible(sceneItem));
|
|
||||||
|
|
||||||
OBSDataAutoRelease boundsData = obs_data_create();
|
|
||||||
obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(sceneItem);
|
|
||||||
if (boundsType == OBS_BOUNDS_NONE) {
|
|
||||||
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_NONE");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
switch (boundsType) {
|
|
||||||
case OBS_BOUNDS_STRETCH: {
|
|
||||||
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_STRETCH");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OBS_BOUNDS_SCALE_INNER: {
|
|
||||||
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_SCALE_INNER");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OBS_BOUNDS_SCALE_OUTER: {
|
|
||||||
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_SCALE_OUTER");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OBS_BOUNDS_SCALE_TO_WIDTH: {
|
|
||||||
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_SCALE_TO_WIDTH");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OBS_BOUNDS_SCALE_TO_HEIGHT: {
|
|
||||||
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_SCALE_TO_HEIGHT");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OBS_BOUNDS_MAX_ONLY: {
|
|
||||||
obs_data_set_string(boundsData, "type", "OBS_BOUNDS_MAX_ONLY");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obs_data_set_int(boundsData, "alignment", obs_sceneitem_get_bounds_alignment(sceneItem));
|
|
||||||
vec2 bounds;
|
|
||||||
obs_sceneitem_get_bounds(sceneItem, &bounds);
|
|
||||||
obs_data_set_double(boundsData, "x", bounds.x);
|
|
||||||
obs_data_set_double(boundsData, "y", bounds.y);
|
|
||||||
}
|
|
||||||
obs_data_set_obj(data, "bounds", boundsData);
|
|
||||||
|
|
||||||
req->SendOKResponse(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the scene specific properties of a source. Unspecified properties will remain unchanged.
|
* Sets the scene specific properties of a source. Unspecified properties will remain unchanged.
|
||||||
|
* Coordinates are relative to the item's parent (the scene or group it belongs to).
|
||||||
*
|
*
|
||||||
* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.
|
* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.
|
||||||
* @param {String} `item` The name of the source.
|
* @param {String} `item` The name of the source.
|
||||||
* @param {int} `position.x` The new x position of the source.
|
* @param {int (optional)} `position.x` The new x position of the source.
|
||||||
* @param {int} `position.y` The new y position of the source.
|
* @param {int (optional)} `position.y` The new y position of the source.
|
||||||
* @param {int} `position.alignment` The new alignment of the source.
|
* @param {int (optional)} `position.alignment` The new alignment of the source.
|
||||||
* @param {double} `rotation` The new clockwise rotation of the item in degrees.
|
* @param {double (optional)} `rotation` The new clockwise rotation of the item in degrees.
|
||||||
* @param {double} `scale.x` The new x scale of the item.
|
* @param {double (optional)} `scale.x` The new x scale of the item.
|
||||||
* @param {double} `scale.y` The new y scale of the item.
|
* @param {double (optional)} `scale.y` The new y scale of the item.
|
||||||
* @param {int} `crop.top` The new amount of pixels cropped off the top of the source before scaling.
|
* @param {int (optional)} `crop.top` The new amount of pixels cropped off the top of the source before scaling.
|
||||||
* @param {int} `crop.bottom` The new amount of pixels cropped off the bottom of the source before scaling.
|
* @param {int (optional)} `crop.bottom` The new amount of pixels cropped off the bottom of the source before scaling.
|
||||||
* @param {int} `crop.left` The new amount of pixels cropped off the left of the source before scaling.
|
* @param {int (optional)} `crop.left` The new amount of pixels cropped off the left of the source before scaling.
|
||||||
* @param {int} `crop.right` The new amount of pixels cropped off the right of the source before scaling.
|
* @param {int (optional)} `crop.right` The new amount of pixels cropped off the right of the source before scaling.
|
||||||
* @param {bool} `visible` The new visibility of the source. 'true' shows source, 'false' hides source.
|
* @param {bool (optional)} `visible` The new visibility of the source. 'true' shows source, 'false' hides source.
|
||||||
* @param {String} `bounds.type` The new bounds type of the source.
|
* @param {bool (optional)} `locked` The new locked status of the source. 'true' keeps it in its current position, 'false' allows movement.
|
||||||
* @param {int} `bounds.alignment` The new alignment of the bounding box. (0-2, 4-6, 8-10)
|
* @param {String (optional)} `bounds.type` The new bounds type of the source. Can be "OBS_BOUNDS_STRETCH", "OBS_BOUNDS_SCALE_INNER", "OBS_BOUNDS_SCALE_OUTER", "OBS_BOUNDS_SCALE_TO_WIDTH", "OBS_BOUNDS_SCALE_TO_HEIGHT", "OBS_BOUNDS_MAX_ONLY" or "OBS_BOUNDS_NONE".
|
||||||
* @param {double} `bounds.x` The new width of the bounding box.
|
* @param {int (optional)} `bounds.alignment` The new alignment of the bounding box. (0-2, 4-6, 8-10)
|
||||||
* @param {double} `bounds.y` The new height of the bounding box.
|
* @param {double (optional)} `bounds.x` The new width of the bounding box.
|
||||||
|
* @param {double (optional)} `bounds.y` The new height of the bounding box.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name SetSceneItemProperties
|
* @name SetSceneItemProperties
|
||||||
* @category scene items
|
* @category scene items
|
||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler* req) {
|
||||||
if (!req->hasField("item")) {
|
if (!req->hasField("item")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString itemName = obs_data_get_string(req->data, "item");
|
QString itemName = obs_data_get_string(req->data, "item");
|
||||||
if (itemName.isEmpty()) {
|
if (itemName.isEmpty()) {
|
||||||
req->SendErrorResponse("invalid request parameters");
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString sceneName = obs_data_get_string(req->data, "scene-name");
|
QString sceneName = obs_data_get_string(req->data, "scene-name");
|
||||||
OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
req->SendErrorResponse("requested scene doesn't exist");
|
return req->SendErrorResponse("requested scene doesn't exist");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSSceneItemAutoRelease sceneItem =
|
OBSSceneItemAutoRelease sceneItem =
|
||||||
Utils::GetSceneItemFromName(scene, itemName);
|
Utils::GetSceneItemFromName(scene, itemName);
|
||||||
if (!sceneItem) {
|
if (!sceneItem) {
|
||||||
req->SendErrorResponse("specified scene item doesn't exist");
|
return req->SendErrorResponse("specified scene item doesn't exist");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool badRequest = false;
|
bool badRequest = false;
|
||||||
OBSDataAutoRelease errorMessage = obs_data_create();
|
OBSDataAutoRelease errorMessage = obs_data_create();
|
||||||
|
|
||||||
|
obs_sceneitem_defer_update_begin(sceneItem);
|
||||||
|
|
||||||
if (req->hasField("position")) {
|
if (req->hasField("position")) {
|
||||||
vec2 oldPosition;
|
vec2 oldPosition;
|
||||||
OBSDataAutoRelease positionError = obs_data_create();
|
OBSDataAutoRelease positionError = obs_data_create();
|
||||||
@ -254,12 +188,16 @@ void WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler* req) {
|
|||||||
obs_sceneitem_set_visible(sceneItem, obs_data_get_bool(req->data, "visible"));
|
obs_sceneitem_set_visible(sceneItem, obs_data_get_bool(req->data, "visible"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req->hasField("locked")) {
|
||||||
|
obs_sceneitem_set_locked(sceneItem, obs_data_get_bool(req->data, "locked"));
|
||||||
|
}
|
||||||
|
|
||||||
if (req->hasField("bounds")) {
|
if (req->hasField("bounds")) {
|
||||||
bool badBounds = false;
|
bool badBounds = false;
|
||||||
OBSDataAutoRelease boundsError = obs_data_create();
|
OBSDataAutoRelease boundsError = obs_data_create();
|
||||||
OBSDataAutoRelease reqBounds = obs_data_get_obj(req->data, "bounds");
|
OBSDataAutoRelease reqBounds = obs_data_get_obj(req->data, "bounds");
|
||||||
if (obs_data_has_user_value(reqBounds, "type")) {
|
if (obs_data_has_user_value(reqBounds, "type")) {
|
||||||
const char* newBoundsType = obs_data_get_string(reqBounds, "type");
|
QString newBoundsType = obs_data_get_string(reqBounds, "type");
|
||||||
if (newBoundsType == "OBS_BOUNDS_NONE") {
|
if (newBoundsType == "OBS_BOUNDS_NONE") {
|
||||||
obs_sceneitem_set_bounds_type(sceneItem, OBS_BOUNDS_NONE);
|
obs_sceneitem_set_bounds_type(sceneItem, OBS_BOUNDS_NONE);
|
||||||
}
|
}
|
||||||
@ -311,18 +249,19 @@ void WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler* req) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_defer_update_end(sceneItem);
|
||||||
|
|
||||||
if (badRequest) {
|
if (badRequest) {
|
||||||
req->SendErrorResponse(errorMessage);
|
return req->SendErrorResponse(errorMessage);
|
||||||
}
|
|
||||||
else {
|
|
||||||
req->SendOKResponse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset a scene item.
|
* Reset a scene item.
|
||||||
*
|
*
|
||||||
* @param {String (optional)} `scene-name` Name of the scene the source belogns to. Defaults to the current scene.
|
* @param {String (optional)} `scene-name` Name of the scene the source belongs to. Defaults to the current scene.
|
||||||
* @param {String} `item` Name of the source item.
|
* @param {String} `item` Name of the source item.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
@ -330,39 +269,35 @@ void WSRequestHandler::HandleSetSceneItemProperties(WSRequestHandler* req) {
|
|||||||
* @category scene items
|
* @category scene items
|
||||||
* @since 4.2.0
|
* @since 4.2.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleResetSceneItem(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleResetSceneItem(WSRequestHandler* req) {
|
||||||
// TODO: remove this request, or refactor it to ResetSource
|
// TODO: remove this request, or refactor it to ResetSource
|
||||||
|
|
||||||
if (!req->hasField("item")) {
|
if (!req->hasField("item")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* itemName = obs_data_get_string(req->data, "item");
|
const char* itemName = obs_data_get_string(req->data, "item");
|
||||||
if (!itemName) {
|
if (!itemName) {
|
||||||
req->SendErrorResponse("invalid request parameters");
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* sceneName = obs_data_get_string(req->data, "scene-name");
|
const char* sceneName = obs_data_get_string(req->data, "scene-name");
|
||||||
OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
req->SendErrorResponse("requested scene doesn't exist");
|
return req->SendErrorResponse("requested scene doesn't exist");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
|
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
|
||||||
if (sceneItem) {
|
if (!sceneItem) {
|
||||||
OBSSource sceneItemSource = obs_sceneitem_get_source(sceneItem);
|
return req->SendErrorResponse("specified scene item doesn't exist");
|
||||||
|
|
||||||
OBSDataAutoRelease settings = obs_source_get_settings(sceneItemSource);
|
|
||||||
obs_source_update(sceneItemSource, settings);
|
|
||||||
|
|
||||||
req->SendOKResponse();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
req->SendErrorResponse("specified scene item doesn't exist");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OBSSource sceneItemSource = obs_sceneitem_get_source(sceneItem);
|
||||||
|
|
||||||
|
OBSDataAutoRelease settings = obs_source_get_settings(sceneItemSource);
|
||||||
|
obs_source_update(sceneItemSource, settings);
|
||||||
|
|
||||||
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -378,38 +313,34 @@ void WSRequestHandler::HandleResetSceneItem(WSRequestHandler* req) {
|
|||||||
* @since 0.3
|
* @since 0.3
|
||||||
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
|
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetSceneItemRender(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetSceneItemRender(WSRequestHandler* req) {
|
||||||
if (!req->hasField("source") ||
|
if (!req->hasField("source") ||
|
||||||
!req->hasField("render"))
|
!req->hasField("render"))
|
||||||
{
|
{
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* itemName = obs_data_get_string(req->data, "source");
|
const char* itemName = obs_data_get_string(req->data, "source");
|
||||||
bool isVisible = obs_data_get_bool(req->data, "render");
|
bool isVisible = obs_data_get_bool(req->data, "render");
|
||||||
|
|
||||||
if (!itemName) {
|
if (!itemName) {
|
||||||
req->SendErrorResponse("invalid request parameters");
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* sceneName = obs_data_get_string(req->data, "scene-name");
|
const char* sceneName = obs_data_get_string(req->data, "scene-name");
|
||||||
OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
req->SendErrorResponse("requested scene doesn't exist");
|
return req->SendErrorResponse("requested scene doesn't exist");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSSceneItemAutoRelease sceneItem =
|
OBSSceneItemAutoRelease sceneItem =
|
||||||
Utils::GetSceneItemFromName(scene, itemName);
|
Utils::GetSceneItemFromName(scene, itemName);
|
||||||
if (sceneItem) {
|
if (!sceneItem) {
|
||||||
obs_sceneitem_set_visible(sceneItem, isVisible);
|
return req->SendErrorResponse("specified scene item doesn't exist");
|
||||||
req->SendOKResponse();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
req->SendErrorResponse("specified scene item doesn't exist");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_set_visible(sceneItem, isVisible);
|
||||||
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -427,38 +358,34 @@ void WSRequestHandler::HandleSetSceneItemRender(WSRequestHandler* req) {
|
|||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
|
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetSceneItemPosition(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetSceneItemPosition(WSRequestHandler* req) {
|
||||||
if (!req->hasField("item") ||
|
if (!req->hasField("item") ||
|
||||||
!req->hasField("x") || !req->hasField("y")) {
|
!req->hasField("x") || !req->hasField("y")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString itemName = obs_data_get_string(req->data, "item");
|
QString itemName = obs_data_get_string(req->data, "item");
|
||||||
if (itemName.isEmpty()) {
|
if (itemName.isEmpty()) {
|
||||||
req->SendErrorResponse("invalid request parameters");
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString sceneName = obs_data_get_string(req->data, "scene-name");
|
QString sceneName = obs_data_get_string(req->data, "scene-name");
|
||||||
OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
req->SendErrorResponse("requested scene could not be found");
|
return req->SendErrorResponse("requested scene could not be found");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSSceneItem sceneItem = Utils::GetSceneItemFromName(scene, itemName);
|
OBSSceneItem sceneItem = Utils::GetSceneItemFromName(scene, itemName);
|
||||||
if (sceneItem) {
|
if (!sceneItem) {
|
||||||
vec2 item_position = { 0 };
|
return req->SendErrorResponse("specified scene item doesn't exist");
|
||||||
item_position.x = obs_data_get_double(req->data, "x");
|
}
|
||||||
item_position.y = obs_data_get_double(req->data, "y");
|
|
||||||
obs_sceneitem_set_pos(sceneItem, &item_position);
|
|
||||||
|
|
||||||
req->SendOKResponse();
|
vec2 item_position = { 0 };
|
||||||
}
|
item_position.x = obs_data_get_double(req->data, "x");
|
||||||
else {
|
item_position.y = obs_data_get_double(req->data, "y");
|
||||||
req->SendErrorResponse("specified scene item doesn't exist");
|
obs_sceneitem_set_pos(sceneItem, &item_position);
|
||||||
}
|
|
||||||
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -476,27 +403,24 @@ void WSRequestHandler::HandleSetSceneItemPosition(WSRequestHandler* req) {
|
|||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
|
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler* req) {
|
||||||
if (!req->hasField("item") ||
|
if (!req->hasField("item") ||
|
||||||
!req->hasField("x-scale") ||
|
!req->hasField("x-scale") ||
|
||||||
!req->hasField("y-scale") ||
|
!req->hasField("y-scale") ||
|
||||||
!req->hasField("rotation"))
|
!req->hasField("rotation"))
|
||||||
{
|
{
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString itemName = obs_data_get_string(req->data, "item");
|
QString itemName = obs_data_get_string(req->data, "item");
|
||||||
if (itemName.isEmpty()) {
|
if (itemName.isEmpty()) {
|
||||||
req->SendErrorResponse("invalid request parameters");
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString sceneName = obs_data_get_string(req->data, "scene-name");
|
QString sceneName = obs_data_get_string(req->data, "scene-name");
|
||||||
OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
req->SendErrorResponse("requested scene doesn't exist");
|
return req->SendErrorResponse("requested scene doesn't exist");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 scale;
|
vec2 scale;
|
||||||
@ -505,14 +429,18 @@ void WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler* req) {
|
|||||||
float rotation = obs_data_get_double(req->data, "rotation");
|
float rotation = obs_data_get_double(req->data, "rotation");
|
||||||
|
|
||||||
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
|
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
|
||||||
if (sceneItem) {
|
if (!sceneItem) {
|
||||||
obs_sceneitem_set_scale(sceneItem, &scale);
|
return req->SendErrorResponse("specified scene item doesn't exist");
|
||||||
obs_sceneitem_set_rot(sceneItem, rotation);
|
|
||||||
req->SendOKResponse();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
req->SendErrorResponse("specified scene item doesn't exist");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_defer_update_begin(sceneItem);
|
||||||
|
|
||||||
|
obs_sceneitem_set_scale(sceneItem, &scale);
|
||||||
|
obs_sceneitem_set_rot(sceneItem, rotation);
|
||||||
|
|
||||||
|
obs_sceneitem_defer_update_end(sceneItem);
|
||||||
|
|
||||||
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -531,38 +459,147 @@ void WSRequestHandler::HandleSetSceneItemTransform(WSRequestHandler* req) {
|
|||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
|
* @deprecated Since 4.3.0. Prefer the use of SetSceneItemProperties.
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetSceneItemCrop(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetSceneItemCrop(WSRequestHandler* req) {
|
||||||
if (!req->hasField("item")) {
|
if (!req->hasField("item")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString itemName = obs_data_get_string(req->data, "item");
|
QString itemName = obs_data_get_string(req->data, "item");
|
||||||
if (itemName.isEmpty()) {
|
if (itemName.isEmpty()) {
|
||||||
req->SendErrorResponse("invalid request parameters");
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString sceneName = obs_data_get_string(req->data, "scene-name");
|
QString sceneName = obs_data_get_string(req->data, "scene-name");
|
||||||
OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
req->SendErrorResponse("requested scene doesn't exist");
|
return req->SendErrorResponse("requested scene doesn't exist");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
|
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName);
|
||||||
if (sceneItem) {
|
if (!sceneItem) {
|
||||||
struct obs_sceneitem_crop crop = { 0 };
|
return req->SendErrorResponse("specified scene item doesn't exist");
|
||||||
crop.top = obs_data_get_int(req->data, "top");
|
|
||||||
crop.bottom = obs_data_get_int(req->data, "bottom");
|
|
||||||
crop.left = obs_data_get_int(req->data, "left");
|
|
||||||
crop.right = obs_data_get_int(req->data, "right");
|
|
||||||
|
|
||||||
obs_sceneitem_set_crop(sceneItem, &crop);
|
|
||||||
|
|
||||||
req->SendOKResponse();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
req->SendErrorResponse("specified scene item doesn't exist");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct obs_sceneitem_crop crop = { 0 };
|
||||||
|
crop.top = obs_data_get_int(req->data, "top");
|
||||||
|
crop.bottom = obs_data_get_int(req->data, "bottom");
|
||||||
|
crop.left = obs_data_get_int(req->data, "left");
|
||||||
|
crop.right = obs_data_get_int(req->data, "right");
|
||||||
|
|
||||||
|
obs_sceneitem_set_crop(sceneItem, &crop);
|
||||||
|
|
||||||
|
return req->SendOKResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a scene item.
|
||||||
|
*
|
||||||
|
* @param {String (optional)} `scene` Name of the scene the source belongs to. Defaults to the current scene.
|
||||||
|
* @param {Object} `item` item to delete (required)
|
||||||
|
* @param {String} `item.name` name of the scene item (prefer `id`, including both is acceptable).
|
||||||
|
* @param {int} `item.id` id of the scene item.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name DeleteSceneItem
|
||||||
|
* @category scene items
|
||||||
|
* @since 4.5.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandleDeleteSceneItem(WSRequestHandler* req) {
|
||||||
|
if (!req->hasField("item")) {
|
||||||
|
return req->SendErrorResponse("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* sceneName = obs_data_get_string(req->data, "scene");
|
||||||
|
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
||||||
|
if (!scene) {
|
||||||
|
return req->SendErrorResponse("requested scene doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease item = obs_data_get_obj(req->data, "item");
|
||||||
|
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item);
|
||||||
|
if (!sceneItem) {
|
||||||
|
return req->SendErrorResponse("item with id/name combination not found in specified scene");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_remove(sceneItem);
|
||||||
|
|
||||||
|
return req->SendOKResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DuplicateSceneItemData {
|
||||||
|
obs_sceneitem_t *referenceItem;
|
||||||
|
obs_source_t *fromSource;
|
||||||
|
obs_sceneitem_t *newItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void DuplicateSceneItem(void *_data, obs_scene_t *scene) {
|
||||||
|
DuplicateSceneItemData *data = (DuplicateSceneItemData *)_data;
|
||||||
|
data->newItem = obs_scene_add(scene, data->fromSource);
|
||||||
|
obs_sceneitem_set_visible(data->newItem, obs_sceneitem_visible(data->referenceItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicates a scene item.
|
||||||
|
*
|
||||||
|
* @param {String (optional)} `fromScene` Name of the scene to copy the item from. Defaults to the current scene.
|
||||||
|
* @param {String (optional)} `toScene` Name of the scene to create the item in. Defaults to the current scene.
|
||||||
|
* @param {Object} `item` item to duplicate (required)
|
||||||
|
* @param {String} `item.name` name of the scene item (prefer `id`, including both is acceptable).
|
||||||
|
* @param {int} `item.id` id of the scene item.
|
||||||
|
*
|
||||||
|
* @return {String} `scene` Name of the scene where the new item was created
|
||||||
|
* @return {Object} `item` New item info
|
||||||
|
* @return {int} `item.id` New item ID
|
||||||
|
* @return {String} `item.name` New item name
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name DuplicateSceneItem
|
||||||
|
* @category scene items
|
||||||
|
* @since 4.5.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandleDuplicateSceneItem(WSRequestHandler* req) {
|
||||||
|
if (!req->hasField("item")) {
|
||||||
|
return req->SendErrorResponse("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* fromSceneName = obs_data_get_string(req->data, "fromScene");
|
||||||
|
OBSScene fromScene = Utils::GetSceneFromNameOrCurrent(fromSceneName);
|
||||||
|
if (!fromScene) {
|
||||||
|
return req->SendErrorResponse("requested fromScene doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* toSceneName = obs_data_get_string(req->data, "toScene");
|
||||||
|
OBSScene toScene = Utils::GetSceneFromNameOrCurrent(toSceneName);
|
||||||
|
if (!toScene) {
|
||||||
|
return req->SendErrorResponse("requested toScene doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease item = obs_data_get_obj(req->data, "item");
|
||||||
|
OBSSceneItemAutoRelease referenceItem = Utils::GetSceneItemFromItem(fromScene, item);
|
||||||
|
if (!referenceItem) {
|
||||||
|
return req->SendErrorResponse("item with id/name combination not found in specified scene");
|
||||||
|
}
|
||||||
|
|
||||||
|
DuplicateSceneItemData data;
|
||||||
|
data.fromSource = obs_sceneitem_get_source(referenceItem);
|
||||||
|
data.referenceItem = referenceItem;
|
||||||
|
|
||||||
|
obs_enter_graphics();
|
||||||
|
obs_scene_atomic_update(toScene, DuplicateSceneItem, &data);
|
||||||
|
obs_leave_graphics();
|
||||||
|
|
||||||
|
obs_sceneitem_t *newItem = data.newItem;
|
||||||
|
if (!newItem) {
|
||||||
|
return req->SendErrorResponse("Error duplicating scene item");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease itemData = obs_data_create();
|
||||||
|
obs_data_set_int(itemData, "id", obs_sceneitem_get_id(newItem));
|
||||||
|
obs_data_set_string(itemData, "name", obs_source_get_name(obs_sceneitem_get_source(newItem)));
|
||||||
|
|
||||||
|
OBSDataAutoRelease responseData = obs_data_create();
|
||||||
|
obs_data_set_obj(responseData, "item", itemData);
|
||||||
|
obs_data_set_string(responseData, "scene", obs_source_get_name(obs_scene_get_source(toScene)));
|
||||||
|
|
||||||
|
return req->SendOKResponse(responseData);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
#include <QString>
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} `Scene`
|
||||||
|
* @property {String} `name` Name of the currently active scene.
|
||||||
|
* @property {Array<SceneItem>} `sources` Ordered list of the current scene's source items.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch to the specified scene.
|
* Switch to the specified scene.
|
||||||
*
|
*
|
||||||
@ -13,64 +18,133 @@
|
|||||||
* @category scenes
|
* @category scenes
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetCurrentScene(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetCurrentScene(WSRequestHandler* req) {
|
||||||
if (!req->hasField("scene-name")) {
|
if (!req->hasField("scene-name")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const char* sceneName = obs_data_get_string(req->data, "scene-name");
|
const char* sceneName = obs_data_get_string(req->data, "scene-name");
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName);
|
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName);
|
||||||
|
|
||||||
if (source) {
|
if (source) {
|
||||||
obs_frontend_set_current_scene(source);
|
obs_frontend_set_current_scene(source);
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
} else {
|
} else {
|
||||||
req->SendErrorResponse("requested scene does not exist");
|
return req->SendErrorResponse("requested scene does not exist");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current scene's name and source items.
|
* Get the current scene's name and source items.
|
||||||
*
|
*
|
||||||
* @return {String} `name` Name of the currently active scene.
|
* @return {String} `name` Name of the currently active scene.
|
||||||
* @return {Source|Array} `sources` Ordered list of the current scene's source items.
|
* @return {Array<SceneItem>} `sources` Ordered list of the current scene's source items.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name GetCurrentScene
|
* @name GetCurrentScene
|
||||||
* @category scenes
|
* @category scenes
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetCurrentScene(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetCurrentScene(WSRequestHandler* req) {
|
||||||
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
|
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
|
||||||
OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(currentScene);
|
OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(currentScene);
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
obs_data_set_string(data, "name", obs_source_get_name(currentScene));
|
obs_data_set_string(data, "name", obs_source_get_name(currentScene));
|
||||||
obs_data_set_array(data, "sources", sceneItems);
|
obs_data_set_array(data, "sources", sceneItems);
|
||||||
|
|
||||||
req->SendOKResponse(data);
|
return req->SendOKResponse(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of scenes in the currently active profile.
|
* Get a list of scenes in the currently active profile.
|
||||||
*
|
*
|
||||||
* @return {String} `current-scene` Name of the currently active scene.
|
* @return {String} `current-scene` Name of the currently active scene.
|
||||||
* @return {Scene|Array} `scenes` Ordered list of the current profile's scenes (See `[GetCurrentScene](#getcurrentscene)` for more information).
|
* @return {Array<Scene>} `scenes` Ordered list of the current profile's scenes (See `[GetCurrentScene](#getcurrentscene)` for more information).
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name GetSceneList
|
* @name GetSceneList
|
||||||
* @category scenes
|
* @category scenes
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetSceneList(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetSceneList(WSRequestHandler* req) {
|
||||||
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
|
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
|
||||||
OBSDataArrayAutoRelease scenes = Utils::GetScenes();
|
OBSDataArrayAutoRelease scenes = Utils::GetScenes();
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
obs_data_set_string(data, "current-scene",
|
obs_data_set_string(data, "current-scene",
|
||||||
obs_source_get_name(currentScene));
|
obs_source_get_name(currentScene));
|
||||||
obs_data_set_array(data, "scenes", scenes);
|
obs_data_set_array(data, "scenes", scenes);
|
||||||
|
|
||||||
req->SendOKResponse(data);
|
return req->SendOKResponse(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name ReorderSceneItems
|
||||||
|
* @category scenes
|
||||||
|
* @since 4.5.0
|
||||||
|
*/
|
||||||
|
HandlerResponse WSRequestHandler::HandleReorderSceneItems(WSRequestHandler* req) {
|
||||||
|
QString sceneName = obs_data_get_string(req->data, "scene");
|
||||||
|
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
||||||
|
if (!scene) {
|
||||||
|
return req->SendErrorResponse("requested scene doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataArrayAutoRelease items = obs_data_get_array(req->data, "items");
|
||||||
|
if (!items) {
|
||||||
|
return req->SendErrorResponse("sceneItem order not specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct reorder_context {
|
||||||
|
obs_data_array_t* items;
|
||||||
|
bool success;
|
||||||
|
QString errorMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct reorder_context ctx;
|
||||||
|
ctx.success = false;
|
||||||
|
ctx.items = items;
|
||||||
|
|
||||||
|
obs_scene_atomic_update(scene, [](void* param, obs_scene_t* scene) {
|
||||||
|
auto ctx = reinterpret_cast<struct reorder_context*>(param);
|
||||||
|
|
||||||
|
QVector<struct obs_sceneitem_order_info> orderList;
|
||||||
|
struct obs_sceneitem_order_info info;
|
||||||
|
|
||||||
|
size_t itemCount = obs_data_array_count(ctx->items);
|
||||||
|
for (int i = 0; i < itemCount; i++) {
|
||||||
|
OBSDataAutoRelease item = obs_data_array_item(ctx->items, i);
|
||||||
|
|
||||||
|
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item);
|
||||||
|
if (!sceneItem) {
|
||||||
|
ctx->success = false;
|
||||||
|
ctx->errorMessage = "Invalid sceneItem id or name specified";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.group = nullptr;
|
||||||
|
info.item = sceneItem;
|
||||||
|
orderList.insert(0, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->success = obs_scene_reorder_items2(scene, orderList.data(), orderList.size());
|
||||||
|
if (!ctx->success) {
|
||||||
|
ctx->errorMessage = "Invalid sceneItem order";
|
||||||
|
}
|
||||||
|
}, &ctx);
|
||||||
|
|
||||||
|
if (!ctx.success) {
|
||||||
|
return req->SendErrorResponse(ctx.errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
#include <QString>
|
#include "obs-websocket.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "WSEvents.h"
|
#include "WSEvents.h"
|
||||||
|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current streaming and recording status.
|
* Get current streaming and recording status.
|
||||||
*
|
*
|
||||||
* @return {boolean} `streaming` Current streaming status.
|
* @return {boolean} `streaming` Current streaming status.
|
||||||
* @return {boolean} `recording` Current recording status.
|
* @return {boolean} `recording` Current recording status.
|
||||||
* @return {String (optional)} `stream-timecode` Time elapsed since streaming started (only present if currently streaming).
|
* @return {String (optional)} `stream-timecode` Time elapsed since streaming started (only present if currently streaming).
|
||||||
@ -20,26 +20,26 @@
|
|||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req) {
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
auto events = GetEventsSystem();
|
||||||
obs_data_set_bool(data, "streaming", obs_frontend_streaming_active());
|
|
||||||
obs_data_set_bool(data, "recording", obs_frontend_recording_active());
|
|
||||||
obs_data_set_bool(data, "preview-only", false);
|
|
||||||
|
|
||||||
const char* tc = nullptr;
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
if (obs_frontend_streaming_active()) {
|
obs_data_set_bool(data, "streaming", obs_frontend_streaming_active());
|
||||||
tc = WSEvents::Instance->GetStreamingTimecode();
|
obs_data_set_bool(data, "recording", obs_frontend_recording_active());
|
||||||
obs_data_set_string(data, "stream-timecode", tc);
|
obs_data_set_bool(data, "recording-paused", Utils::RecordingPaused());
|
||||||
bfree((void*)tc);
|
obs_data_set_bool(data, "preview-only", false);
|
||||||
}
|
|
||||||
|
|
||||||
if (obs_frontend_recording_active()) {
|
if (obs_frontend_streaming_active()) {
|
||||||
tc = WSEvents::Instance->GetRecordingTimecode();
|
QString streamingTimecode = events->getStreamingTimecode();
|
||||||
obs_data_set_string(data, "rec-timecode", tc);
|
obs_data_set_string(data, "stream-timecode", streamingTimecode.toUtf8().constData());
|
||||||
bfree((void*)tc);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
req->SendOKResponse(data);
|
if (obs_frontend_recording_active()) {
|
||||||
|
QString recordingTimecode = events->getRecordingTimecode();
|
||||||
|
obs_data_set_string(data, "rec-timecode", recordingTimecode.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
return req->SendOKResponse(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,11 +50,11 @@
|
|||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleStartStopStreaming(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleStartStopStreaming(WSRequestHandler* req) {
|
||||||
if (obs_frontend_streaming_active())
|
if (obs_frontend_streaming_active())
|
||||||
HandleStopStreaming(req);
|
return HandleStopStreaming(req);
|
||||||
else
|
else
|
||||||
HandleStartStreaming(req);
|
return HandleStartStreaming(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,7 +63,7 @@
|
|||||||
*
|
*
|
||||||
* @param {Object (optional)} `stream` Special stream configuration. Please note: these won't be saved to OBS' configuration.
|
* @param {Object (optional)} `stream` Special stream configuration. Please 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 {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.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.
|
* @param {Object (optional)} `stream.settings` Settings for the stream.
|
||||||
* @param {String (optional)} `stream.settings.server` The publish URL.
|
* @param {String (optional)} `stream.settings.server` The publish URL.
|
||||||
* @param {String (optional)} `stream.settings.key` The publish key of the stream.
|
* @param {String (optional)} `stream.settings.key` The publish key of the stream.
|
||||||
@ -76,91 +76,91 @@
|
|||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleStartStreaming(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleStartStreaming(WSRequestHandler* req) {
|
||||||
if (obs_frontend_streaming_active() == false) {
|
if (obs_frontend_streaming_active() == false) {
|
||||||
OBSService configuredService = obs_frontend_get_streaming_service();
|
OBSService configuredService = obs_frontend_get_streaming_service();
|
||||||
OBSService newService = nullptr;
|
OBSService newService = nullptr;
|
||||||
|
|
||||||
// TODO: fix service memory leak
|
// TODO: fix service memory leak
|
||||||
|
|
||||||
if (req->hasField("stream")) {
|
if (req->hasField("stream")) {
|
||||||
OBSDataAutoRelease streamData = obs_data_get_obj(req->data, "stream");
|
OBSDataAutoRelease streamData = obs_data_get_obj(req->data, "stream");
|
||||||
OBSDataAutoRelease newSettings = obs_data_get_obj(streamData, "settings");
|
OBSDataAutoRelease newSettings = obs_data_get_obj(streamData, "settings");
|
||||||
OBSDataAutoRelease newMetadata = obs_data_get_obj(streamData, "metadata");
|
OBSDataAutoRelease newMetadata = obs_data_get_obj(streamData, "metadata");
|
||||||
|
|
||||||
OBSDataAutoRelease csHotkeys =
|
OBSDataAutoRelease csHotkeys =
|
||||||
obs_hotkeys_save_service(configuredService);
|
obs_hotkeys_save_service(configuredService);
|
||||||
|
|
||||||
QString currentType = obs_service_get_type(configuredService);
|
QString currentType = obs_service_get_type(configuredService);
|
||||||
QString newType = obs_data_get_string(streamData, "type");
|
QString newType = obs_data_get_string(streamData, "type");
|
||||||
if (newType.isEmpty() || newType.isNull()) {
|
if (newType.isEmpty() || newType.isNull()) {
|
||||||
newType = currentType;
|
newType = currentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Supporting adding metadata parameters to key query string
|
//Supporting adding metadata parameters to key query string
|
||||||
QString query = Utils::ParseDataToQueryString(newMetadata);
|
QString query = Utils::ParseDataToQueryString(newMetadata);
|
||||||
if (!query.isEmpty()
|
if (!query.isEmpty()
|
||||||
&& obs_data_has_user_value(newSettings, "key"))
|
&& obs_data_has_user_value(newSettings, "key"))
|
||||||
{
|
{
|
||||||
const char* key = obs_data_get_string(newSettings, "key");
|
const char* key = obs_data_get_string(newSettings, "key");
|
||||||
int keylen = strlen(key);
|
int keylen = strlen(key);
|
||||||
|
|
||||||
bool hasQuestionMark = false;
|
bool hasQuestionMark = false;
|
||||||
for (int i = 0; i < keylen; i++) {
|
for (int i = 0; i < keylen; i++) {
|
||||||
if (key[i] == '?') {
|
if (key[i] == '?') {
|
||||||
hasQuestionMark = true;
|
hasQuestionMark = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasQuestionMark) {
|
if (hasQuestionMark) {
|
||||||
query.prepend('&');
|
query.prepend('&');
|
||||||
} else {
|
} else {
|
||||||
query.prepend('?');
|
query.prepend('?');
|
||||||
}
|
}
|
||||||
|
|
||||||
query.prepend(key);
|
query.prepend(key);
|
||||||
obs_data_set_string(newSettings, "key", query.toUtf8());
|
obs_data_set_string(newSettings, "key", query.toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newType == currentType) {
|
if (newType == currentType) {
|
||||||
// Service type doesn't change: apply settings to current service
|
// Service type doesn't change: apply settings to current service
|
||||||
|
|
||||||
// By doing this, you can send a request to the websocket
|
// By doing this, you can send a request to the websocket
|
||||||
// that only contains settings you want to change, instead of
|
// that only contains settings you want to change, instead of
|
||||||
// having to do a get and then change them
|
// having to do a get and then change them
|
||||||
|
|
||||||
OBSDataAutoRelease currentSettings = obs_service_get_settings(configuredService);
|
OBSDataAutoRelease currentSettings = obs_service_get_settings(configuredService);
|
||||||
OBSDataAutoRelease updatedSettings = obs_data_create();
|
OBSDataAutoRelease updatedSettings = obs_data_create();
|
||||||
|
|
||||||
obs_data_apply(updatedSettings, currentSettings); //first apply the existing settings
|
obs_data_apply(updatedSettings, currentSettings); //first apply the existing settings
|
||||||
obs_data_apply(updatedSettings, newSettings); //then apply the settings from the request should they exist
|
obs_data_apply(updatedSettings, newSettings); //then apply the settings from the request should they exist
|
||||||
|
|
||||||
newService = obs_service_create(
|
newService = obs_service_create(
|
||||||
newType.toUtf8(), STREAM_SERVICE_ID,
|
newType.toUtf8(), STREAM_SERVICE_ID,
|
||||||
updatedSettings, csHotkeys);
|
updatedSettings, csHotkeys);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Service type changed: override service settings
|
// Service type changed: override service settings
|
||||||
newService = obs_service_create(
|
newService = obs_service_create(
|
||||||
newType.toUtf8(), STREAM_SERVICE_ID,
|
newType.toUtf8(), STREAM_SERVICE_ID,
|
||||||
newSettings, csHotkeys);
|
newSettings, csHotkeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_frontend_set_streaming_service(newService);
|
obs_frontend_set_streaming_service(newService);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_frontend_streaming_start();
|
obs_frontend_streaming_start();
|
||||||
|
|
||||||
// Stream settings provided in StartStreaming are not persisted to disk
|
// Stream settings provided in StartStreaming are not persisted to disk
|
||||||
if (newService != nullptr) {
|
if (newService != nullptr) {
|
||||||
obs_frontend_set_streaming_service(configuredService);
|
obs_frontend_set_streaming_service(configuredService);
|
||||||
}
|
}
|
||||||
|
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
} else {
|
} else {
|
||||||
req->SendErrorResponse("streaming already active");
|
return req->SendErrorResponse("streaming already active");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,18 +172,18 @@
|
|||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) {
|
||||||
if (obs_frontend_streaming_active() == true) {
|
if (obs_frontend_streaming_active() == true) {
|
||||||
obs_frontend_streaming_stop();
|
obs_frontend_streaming_stop();
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
} else {
|
} else {
|
||||||
req->SendErrorResponse("streaming not active");
|
return req->SendErrorResponse("streaming not active");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets one or more attributes of the current streaming server settings. Any options not passed will remain unchanged. Returns the updated settings in response. If 'type' is different than the current streaming service type, all settings are required. Returns the full settings of the stream (the same as GetStreamSettings).
|
* Sets one or more attributes of the current streaming server settings. Any options not passed will remain unchanged. Returns the updated settings in response. If 'type' is different than the current streaming service type, all settings are required. Returns the full settings of the stream (the same as GetStreamSettings).
|
||||||
*
|
*
|
||||||
* @param {String} `type` The type of streaming service configuration, usually `rtmp_custom` or `rtmp_common`.
|
* @param {String} `type` The type of streaming service configuration, usually `rtmp_custom` or `rtmp_common`.
|
||||||
* @param {Object} `settings` The actual settings of the stream.
|
* @param {Object} `settings` The actual settings of the stream.
|
||||||
* @param {String (optional)} `settings.server` The publish URL.
|
* @param {String (optional)} `settings.server` The publish URL.
|
||||||
@ -198,51 +198,50 @@ void WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) {
|
|||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetStreamSettings(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetStreamSettings(WSRequestHandler* req) {
|
||||||
OBSService service = obs_frontend_get_streaming_service();
|
OBSService service = obs_frontend_get_streaming_service();
|
||||||
|
|
||||||
OBSDataAutoRelease requestSettings = obs_data_get_obj(req->data, "settings");
|
OBSDataAutoRelease requestSettings = obs_data_get_obj(req->data, "settings");
|
||||||
if (!requestSettings) {
|
if (!requestSettings) {
|
||||||
req->SendErrorResponse("'settings' are required'");
|
return req->SendErrorResponse("'settings' are required'");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
QString serviceType = obs_service_get_type(service);
|
QString serviceType = obs_service_get_type(service);
|
||||||
QString requestedType = obs_data_get_string(req->data, "type");
|
QString requestedType = obs_data_get_string(req->data, "type");
|
||||||
|
|
||||||
if (requestedType != nullptr && requestedType != serviceType) {
|
if (requestedType != nullptr && requestedType != serviceType) {
|
||||||
OBSDataAutoRelease hotkeys = obs_hotkeys_save_service(service);
|
OBSDataAutoRelease hotkeys = obs_hotkeys_save_service(service);
|
||||||
service = obs_service_create(
|
service = obs_service_create(
|
||||||
requestedType.toUtf8(), STREAM_SERVICE_ID, requestSettings, hotkeys);
|
requestedType.toUtf8(), STREAM_SERVICE_ID, requestSettings, hotkeys);
|
||||||
} else {
|
} else {
|
||||||
// If type isn't changing, we should overlay the settings we got
|
// If type isn't changing, we should overlay the settings we got
|
||||||
// to the existing settings. By doing so, you can send a request that
|
// to the existing settings. By doing so, you can send a request that
|
||||||
// only contains the settings you want to change, instead of having to
|
// only contains the settings you want to change, instead of having to
|
||||||
// do a get and then change them
|
// do a get and then change them
|
||||||
|
|
||||||
OBSDataAutoRelease existingSettings = obs_service_get_settings(service);
|
OBSDataAutoRelease existingSettings = obs_service_get_settings(service);
|
||||||
OBSDataAutoRelease newSettings = obs_data_create();
|
OBSDataAutoRelease newSettings = obs_data_create();
|
||||||
|
|
||||||
// Apply existing settings
|
// Apply existing settings
|
||||||
obs_data_apply(newSettings, existingSettings);
|
obs_data_apply(newSettings, existingSettings);
|
||||||
// Then apply the settings from the request
|
// Then apply the settings from the request
|
||||||
obs_data_apply(newSettings, requestSettings);
|
obs_data_apply(newSettings, requestSettings);
|
||||||
|
|
||||||
obs_service_update(service, newSettings);
|
obs_service_update(service, newSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if save is specified we should immediately save the streaming service
|
//if save is specified we should immediately save the streaming service
|
||||||
if (obs_data_get_bool(req->data, "save")) {
|
if (obs_data_get_bool(req->data, "save")) {
|
||||||
obs_frontend_save_streaming_service();
|
obs_frontend_save_streaming_service();
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSDataAutoRelease serviceSettings = obs_service_get_settings(service);
|
OBSDataAutoRelease serviceSettings = obs_service_get_settings(service);
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_string(response, "type", requestedType.toUtf8());
|
obs_data_set_string(response, "type", requestedType.toUtf8());
|
||||||
obs_data_set_obj(response, "settings", serviceSettings);
|
obs_data_set_obj(response, "settings", serviceSettings);
|
||||||
|
|
||||||
req->SendOKResponse(response);
|
return req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -252,7 +251,7 @@ void WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) {
|
|||||||
* @return {Object} `settings` Stream settings object.
|
* @return {Object} `settings` Stream settings object.
|
||||||
* @return {String} `settings.server` The publish URL.
|
* @return {String} `settings.server` The publish URL.
|
||||||
* @return {String} `settings.key` The publish key of the stream.
|
* @return {String} `settings.key` The publish key of the stream.
|
||||||
* @return {boolean} `settings.use-auth` Indicates whether audentication should be used when connecting to the streaming server.
|
* @return {boolean} `settings.use-auth` Indicates whether authentication should be used when connecting to the streaming server.
|
||||||
* @return {String} `settings.username` The username to use when accessing the streaming server. Only present if `use-auth` is `true`.
|
* @return {String} `settings.username` The username to use when accessing the streaming server. Only present if `use-auth` is `true`.
|
||||||
* @return {String} `settings.password` The password to use when accessing the streaming server. Only present if `use-auth` is `true`.
|
* @return {String} `settings.password` The password to use when accessing the streaming server. Only present if `use-auth` is `true`.
|
||||||
*
|
*
|
||||||
@ -261,17 +260,17 @@ void WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) {
|
|||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetStreamSettings(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetStreamSettings(WSRequestHandler* req) {
|
||||||
OBSService service = obs_frontend_get_streaming_service();
|
OBSService service = obs_frontend_get_streaming_service();
|
||||||
|
|
||||||
const char* serviceType = obs_service_get_type(service);
|
const char* serviceType = obs_service_get_type(service);
|
||||||
OBSDataAutoRelease settings = obs_service_get_settings(service);
|
OBSDataAutoRelease settings = obs_service_get_settings(service);
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_string(response, "type", serviceType);
|
obs_data_set_string(response, "type", serviceType);
|
||||||
obs_data_set_obj(response, "settings", settings);
|
obs_data_set_obj(response, "settings", settings);
|
||||||
|
|
||||||
req->SendOKResponse(response);
|
return req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -282,7 +281,36 @@ void WSRequestHandler::HandleGetStreamSettings(WSRequestHandler* req) {
|
|||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSaveStreamSettings(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSaveStreamSettings(WSRequestHandler* req) {
|
||||||
obs_frontend_save_streaming_service();
|
obs_frontend_save_streaming_service();
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* @api requests
|
||||||
|
* @name SendCaptions
|
||||||
|
* @category streaming
|
||||||
|
* @since 4.6.0
|
||||||
|
*/
|
||||||
|
#if BUILD_CAPTIONS
|
||||||
|
HandlerResponse WSRequestHandler::HandleSendCaptions(WSRequestHandler* req) {
|
||||||
|
if (!req->hasField("text")) {
|
||||||
|
return req->SendErrorResponse("missing request parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSOutputAutoRelease output = obs_frontend_get_streaming_output();
|
||||||
|
if (output) {
|
||||||
|
const char* caption = obs_data_get_string(req->data, "text");
|
||||||
|
obs_output_output_caption_text1(output, caption);
|
||||||
|
}
|
||||||
|
|
||||||
|
return req->SendOKResponse();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
#include <QString>
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
@ -13,13 +12,13 @@
|
|||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler* req) {
|
||||||
bool previewActive = obs_frontend_preview_program_mode_active();
|
bool previewActive = obs_frontend_preview_program_mode_active();
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_bool(response, "studio-mode", previewActive);
|
obs_data_set_bool(response, "studio-mode", previewActive);
|
||||||
|
|
||||||
req->SendOKResponse(response);
|
return req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,27 +26,26 @@
|
|||||||
* Will return an `error` if Studio Mode is not enabled.
|
* Will return an `error` if Studio Mode is not enabled.
|
||||||
*
|
*
|
||||||
* @return {String} `name` The name of the active preview scene.
|
* @return {String} `name` The name of the active preview scene.
|
||||||
* @return {Source|Array} `sources`
|
* @return {Array<SceneItem>} `sources`
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name GetPreviewScene
|
* @name GetPreviewScene
|
||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetPreviewScene(WSRequestHandler* req) {
|
||||||
if (!obs_frontend_preview_program_mode_active()) {
|
if (!obs_frontend_preview_program_mode_active()) {
|
||||||
req->SendErrorResponse("studio mode not enabled");
|
return req->SendErrorResponse("studio mode not enabled");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease scene = obs_frontend_get_current_preview_scene();
|
OBSSourceAutoRelease scene = obs_frontend_get_current_preview_scene();
|
||||||
OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(scene);
|
OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(scene);
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
obs_data_set_string(data, "name", obs_source_get_name(scene));
|
obs_data_set_string(data, "name", obs_source_get_name(scene));
|
||||||
obs_data_set_array(data, "sources", sceneItems);
|
obs_data_set_array(data, "sources", sceneItems);
|
||||||
|
|
||||||
req->SendOKResponse(data);
|
return req->SendOKResponse(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,26 +59,23 @@ void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler* req) {
|
|||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetPreviewScene(WSRequestHandler* req) {
|
||||||
if (!obs_frontend_preview_program_mode_active()) {
|
if (!obs_frontend_preview_program_mode_active()) {
|
||||||
req->SendErrorResponse("studio mode not enabled");
|
return req->SendErrorResponse("studio mode not enabled");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!req->hasField("scene-name")) {
|
if (!req->hasField("scene-name")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const char* scene_name = obs_data_get_string(req->data, "scene-name");
|
const char* scene_name = obs_data_get_string(req->data, "scene-name");
|
||||||
OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(scene_name);
|
OBSScene scene = Utils::GetSceneFromNameOrCurrent(scene_name);
|
||||||
|
if (!scene) {
|
||||||
|
return req->SendErrorResponse("specified scene doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
if (scene) {
|
obs_frontend_set_current_preview_scene(obs_scene_get_source(scene));
|
||||||
obs_frontend_set_current_preview_scene(scene);
|
return req->SendOKResponse();
|
||||||
req->SendOKResponse();
|
|
||||||
} else {
|
|
||||||
req->SendErrorResponse("specified scene doesn't exist");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,40 +91,37 @@ void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler* req) {
|
|||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleTransitionToProgram(WSRequestHandler* req) {
|
||||||
if (!obs_frontend_preview_program_mode_active()) {
|
if (!obs_frontend_preview_program_mode_active()) {
|
||||||
req->SendErrorResponse("studio mode not enabled");
|
return req->SendErrorResponse("studio mode not enabled");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (req->hasField("with-transition")) {
|
if (req->hasField("with-transition")) {
|
||||||
OBSDataAutoRelease transitionInfo =
|
OBSDataAutoRelease transitionInfo =
|
||||||
obs_data_get_obj(req->data, "with-transition");
|
obs_data_get_obj(req->data, "with-transition");
|
||||||
|
|
||||||
if (obs_data_has_user_value(transitionInfo, "name")) {
|
if (obs_data_has_user_value(transitionInfo, "name")) {
|
||||||
QString transitionName =
|
QString transitionName =
|
||||||
obs_data_get_string(transitionInfo, "name");
|
obs_data_get_string(transitionInfo, "name");
|
||||||
if (transitionName.isEmpty()) {
|
if (transitionName.isEmpty()) {
|
||||||
req->SendErrorResponse("invalid request parameters");
|
return req->SendErrorResponse("invalid request parameters");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool success = Utils::SetTransitionByName(transitionName);
|
bool success = Utils::SetTransitionByName(transitionName);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
req->SendErrorResponse("specified transition doesn't exist");
|
return req->SendErrorResponse("specified transition doesn't exist");
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (obs_data_has_user_value(transitionInfo, "duration")) {
|
if (obs_data_has_user_value(transitionInfo, "duration")) {
|
||||||
int transitionDuration =
|
int transitionDuration =
|
||||||
obs_data_get_int(transitionInfo, "duration");
|
obs_data_get_int(transitionInfo, "duration");
|
||||||
Utils::SetTransitionDuration(transitionDuration);
|
obs_frontend_set_transition_duration(transitionDuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::TransitionToProgram();
|
obs_frontend_preview_program_trigger_transition();
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -140,9 +132,9 @@ void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler* req) {
|
|||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleEnableStudioMode(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleEnableStudioMode(WSRequestHandler* req) {
|
||||||
obs_frontend_set_preview_program_mode(true);
|
obs_frontend_set_preview_program_mode(true);
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,9 +145,9 @@ void WSRequestHandler::HandleEnableStudioMode(WSRequestHandler* req) {
|
|||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleDisableStudioMode(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleDisableStudioMode(WSRequestHandler* req) {
|
||||||
obs_frontend_set_preview_program_mode(false);
|
obs_frontend_set_preview_program_mode(false);
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,8 +158,8 @@ void WSRequestHandler::HandleDisableStudioMode(WSRequestHandler* req) {
|
|||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleToggleStudioMode(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleToggleStudioMode(WSRequestHandler* req) {
|
||||||
bool previewProgramMode = obs_frontend_preview_program_mode_active();
|
bool previewProgramMode = obs_frontend_preview_program_mode_active();
|
||||||
obs_frontend_set_preview_program_mode(!previewProgramMode);
|
obs_frontend_set_preview_program_mode(!previewProgramMode);
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
#include <QString>
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
@ -7,35 +6,35 @@
|
|||||||
* List of all transitions available in the frontend's dropdown menu.
|
* List of all transitions available in the frontend's dropdown menu.
|
||||||
*
|
*
|
||||||
* @return {String} `current-transition` Name of the currently active transition.
|
* @return {String} `current-transition` Name of the currently active transition.
|
||||||
* @return {Object|Array} `transitions` List of transitions.
|
* @return {Array<Object>} `transitions` List of transitions.
|
||||||
* @return {String} `transitions[].name` Name of the transition.
|
* @return {String} `transitions.*.name` Name of the transition.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name GetTransitionList
|
* @name GetTransitionList
|
||||||
* @category transitions
|
* @category transitions
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetTransitionList(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetTransitionList(WSRequestHandler* req) {
|
||||||
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
|
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
|
||||||
obs_frontend_source_list transitionList = {};
|
obs_frontend_source_list transitionList = {};
|
||||||
obs_frontend_get_transitions(&transitionList);
|
obs_frontend_get_transitions(&transitionList);
|
||||||
|
|
||||||
OBSDataArrayAutoRelease transitions = obs_data_array_create();
|
OBSDataArrayAutoRelease transitions = obs_data_array_create();
|
||||||
for (size_t i = 0; i < transitionList.sources.num; i++) {
|
for (size_t i = 0; i < transitionList.sources.num; i++) {
|
||||||
OBSSource transition = transitionList.sources.array[i];
|
OBSSource transition = transitionList.sources.array[i];
|
||||||
|
|
||||||
OBSDataAutoRelease obj = obs_data_create();
|
OBSDataAutoRelease obj = obs_data_create();
|
||||||
obs_data_set_string(obj, "name", obs_source_get_name(transition));
|
obs_data_set_string(obj, "name", obs_source_get_name(transition));
|
||||||
obs_data_array_push_back(transitions, obj);
|
obs_data_array_push_back(transitions, obj);
|
||||||
}
|
}
|
||||||
obs_frontend_source_list_free(&transitionList);
|
obs_frontend_source_list_free(&transitionList);
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_string(response, "current-transition",
|
obs_data_set_string(response, "current-transition",
|
||||||
obs_source_get_name(currentTransition));
|
obs_source_get_name(currentTransition));
|
||||||
obs_data_set_array(response, "transitions", transitions);
|
obs_data_set_array(response, "transitions", transitions);
|
||||||
|
|
||||||
req->SendOKResponse(response);
|
return req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,17 +48,17 @@
|
|||||||
* @category transitions
|
* @category transitions
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler* req) {
|
||||||
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
|
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_string(response, "name",
|
obs_data_set_string(response, "name",
|
||||||
obs_source_get_name(currentTransition));
|
obs_source_get_name(currentTransition));
|
||||||
|
|
||||||
if (!obs_transition_fixed(currentTransition))
|
if (!obs_transition_fixed(currentTransition))
|
||||||
obs_data_set_int(response, "duration", Utils::GetTransitionDuration());
|
obs_data_set_int(response, "duration", obs_frontend_get_transition_duration());
|
||||||
|
|
||||||
req->SendOKResponse(response);
|
return req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,18 +71,18 @@ void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler* req) {
|
|||||||
* @category transitions
|
* @category transitions
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler* req) {
|
||||||
if (!req->hasField("transition-name")) {
|
if (!req->hasField("transition-name")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
QString name = obs_data_get_string(req->data, "transition-name");
|
QString name = obs_data_get_string(req->data, "transition-name");
|
||||||
bool success = Utils::SetTransitionByName(name);
|
bool success = Utils::SetTransitionByName(name);
|
||||||
if (success)
|
if (!success) {
|
||||||
req->SendOKResponse();
|
return req->SendErrorResponse("requested transition does not exist");
|
||||||
else
|
}
|
||||||
req->SendErrorResponse("requested transition does not exist");
|
|
||||||
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,15 +95,14 @@ void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler* req) {
|
|||||||
* @category transitions
|
* @category transitions
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler* req) {
|
||||||
if (!req->hasField("duration")) {
|
if (!req->hasField("duration")) {
|
||||||
req->SendErrorResponse("missing request parameters");
|
return req->SendErrorResponse("missing request parameters");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int ms = obs_data_get_int(req->data, "duration");
|
int ms = obs_data_get_int(req->data, "duration");
|
||||||
Utils::SetTransitionDuration(ms);
|
obs_frontend_set_transition_duration(ms);
|
||||||
req->SendOKResponse();
|
return req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,10 +115,8 @@ void WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler* req) {
|
|||||||
* @category transitions
|
* @category transitions
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
void WSRequestHandler::HandleGetTransitionDuration(WSRequestHandler* req) {
|
HandlerResponse WSRequestHandler::HandleGetTransitionDuration(WSRequestHandler* req) {
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_int(response, "transition-duration",
|
obs_data_set_int(response, "transition-duration", obs_frontend_get_transition_duration());
|
||||||
Utils::GetTransitionDuration());
|
return req->SendOKResponse(response);
|
||||||
|
|
||||||
req->SendOKResponse(response);
|
|
||||||
}
|
}
|
||||||
|
283
src/WSServer.cpp
283
src/WSServer.cpp
@ -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/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QtWebSockets/QWebSocket>
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
#include <QMainWindow>
|
#include <QtWidgets/QMainWindow>
|
||||||
#include <QMessageBox>
|
#include <QtWidgets/QMessageBox>
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
#include <util/platform.h>
|
||||||
|
|
||||||
#include "WSServer.h"
|
#include "WSServer.h"
|
||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
@ -30,139 +34,196 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
QT_USE_NAMESPACE
|
QT_USE_NAMESPACE
|
||||||
|
|
||||||
WSServer* WSServer::Instance = nullptr;
|
using websocketpp::lib::placeholders::_1;
|
||||||
|
using websocketpp::lib::placeholders::_2;
|
||||||
|
using websocketpp::lib::bind;
|
||||||
|
|
||||||
WSServer::WSServer(QObject* parent)
|
WSServer::WSServer()
|
||||||
: QObject(parent),
|
: QObject(nullptr),
|
||||||
_wsServer(Q_NULLPTR),
|
_connections(),
|
||||||
_clients(),
|
_clMutex(QMutex::Recursive)
|
||||||
_clMutex(QMutex::Recursive)
|
|
||||||
{
|
{
|
||||||
_wsServer = new QWebSocketServer(
|
_server.init_asio();
|
||||||
QStringLiteral("obs-websocket"),
|
#ifndef _WIN32
|
||||||
QWebSocketServer::NonSecureMode);
|
_server.set_reuse_addr(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_server.set_open_handler(bind(&WSServer::onOpen, this, ::_1));
|
||||||
|
_server.set_close_handler(bind(&WSServer::onClose, this, ::_1));
|
||||||
|
_server.set_message_handler(bind(&WSServer::onMessage, this, ::_1, ::_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
WSServer::~WSServer() {
|
WSServer::~WSServer()
|
||||||
Stop();
|
{
|
||||||
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::Start(quint16 port) {
|
void WSServer::start(quint16 port)
|
||||||
if (port == _wsServer->serverPort())
|
{
|
||||||
return;
|
if (_server.is_listening() && port == _serverPort) {
|
||||||
|
blog(LOG_INFO, "WSServer::start: server already on this port. no restart needed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(_wsServer->isListening())
|
if (_server.is_listening()) {
|
||||||
Stop();
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
bool serverStarted = _wsServer->listen(QHostAddress::Any, port);
|
_server.reset();
|
||||||
if (serverStarted) {
|
|
||||||
blog(LOG_INFO, "server started successfully on TCP port %d", port);
|
|
||||||
|
|
||||||
connect(_wsServer, SIGNAL(newConnection()),
|
_serverPort = port;
|
||||||
this, SLOT(onNewConnection()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
QString errorString = _wsServer->errorString();
|
|
||||||
blog(LOG_ERROR,
|
|
||||||
"error: failed to start server on TCP port %d: %s",
|
|
||||||
port, errorString.toUtf8().constData());
|
|
||||||
|
|
||||||
QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window();
|
websocketpp::lib::error_code errorCode;
|
||||||
|
_server.listen(_serverPort, errorCode);
|
||||||
|
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
if (errorCode) {
|
||||||
QString title = tr("OBSWebsocket.Server.StartFailed.Title");
|
std::string errorCodeMessage = errorCode.message();
|
||||||
QString msg = tr("OBSWebsocket.Server.StartFailed.Message").arg(port);
|
blog(LOG_INFO, "server: listen failed: %s", errorCodeMessage.c_str());
|
||||||
obs_frontend_pop_ui_translation();
|
|
||||||
|
|
||||||
QMessageBox::warning(mainWindow, title, msg);
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
}
|
QString errorTitle = tr("OBSWebsocket.Server.StartFailed.Title");
|
||||||
|
QString errorMessage = tr("OBSWebsocket.Server.StartFailed.Message").arg(_serverPort);
|
||||||
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
|
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());
|
||||||
|
QMessageBox::warning(mainWindow, errorTitle, errorMessage);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_server.start_accept();
|
||||||
|
|
||||||
|
QtConcurrent::run([=]() {
|
||||||
|
blog(LOG_INFO, "io thread started");
|
||||||
|
_server.run();
|
||||||
|
blog(LOG_INFO, "io thread exited");
|
||||||
|
});
|
||||||
|
|
||||||
|
blog(LOG_INFO, "server started successfully on port %d", _serverPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::Stop() {
|
void WSServer::stop()
|
||||||
QMutexLocker locker(&_clMutex);
|
{
|
||||||
for(QWebSocket* pClient : _clients) {
|
if (!_server.is_listening()) {
|
||||||
pClient->close();
|
return;
|
||||||
}
|
}
|
||||||
locker.unlock();
|
|
||||||
|
|
||||||
_wsServer->close();
|
_server.stop_listening();
|
||||||
|
for (connection_hdl hdl : _connections) {
|
||||||
|
_server.close(hdl, websocketpp::close::status::going_away, "Server stopping");
|
||||||
|
}
|
||||||
|
_connections.clear();
|
||||||
|
_connectionProperties.clear();
|
||||||
|
|
||||||
blog(LOG_INFO, "server stopped successfully");
|
_threadPool.waitForDone();
|
||||||
|
|
||||||
|
while (!_server.stopped()) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
blog(LOG_INFO, "server stopped successfully");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::broadcast(QString message) {
|
void WSServer::broadcast(std::string message)
|
||||||
QMutexLocker locker(&_clMutex);
|
{
|
||||||
for(QWebSocket* pClient : _clients) {
|
QMutexLocker locker(&_clMutex);
|
||||||
if (Config::Current()->AuthRequired
|
for (connection_hdl hdl : _connections) {
|
||||||
&& (pClient->property(PROP_AUTHENTICATED).toBool() == false)) {
|
if (GetConfig()->AuthRequired) {
|
||||||
// Skip this client if unauthenticated
|
bool authenticated = _connectionProperties[hdl].isAuthenticated();
|
||||||
continue;
|
if (!authenticated) {
|
||||||
}
|
continue;
|
||||||
pClient->sendTextMessage(message);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
websocketpp::lib::error_code errorCode;
|
||||||
|
_server.send(hdl, message, websocketpp::frame::opcode::text, errorCode);
|
||||||
|
|
||||||
|
if (errorCode) {
|
||||||
|
std::string errorCodeMessage = errorCode.message();
|
||||||
|
blog(LOG_INFO, "server(broadcast): send failed: %s",
|
||||||
|
errorCodeMessage.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::onNewConnection() {
|
void WSServer::onOpen(connection_hdl hdl)
|
||||||
QWebSocket* pSocket = _wsServer->nextPendingConnection();
|
{
|
||||||
if (pSocket) {
|
QMutexLocker locker(&_clMutex);
|
||||||
connect(pSocket, SIGNAL(textMessageReceived(const QString&)),
|
_connections.insert(hdl);
|
||||||
this, SLOT(onTextMessageReceived(QString)));
|
locker.unlock();
|
||||||
connect(pSocket, SIGNAL(disconnected()),
|
|
||||||
this, SLOT(onSocketDisconnected()));
|
|
||||||
|
|
||||||
pSocket->setProperty(PROP_AUTHENTICATED, false);
|
QString clientIp = getRemoteEndpoint(hdl);
|
||||||
|
notifyConnection(clientIp);
|
||||||
QMutexLocker locker(&_clMutex);
|
blog(LOG_INFO, "new client connection from %s", clientIp.toUtf8().constData());
|
||||||
_clients << pSocket;
|
|
||||||
locker.unlock();
|
|
||||||
|
|
||||||
QHostAddress clientAddr = pSocket->peerAddress();
|
|
||||||
QString clientIp = Utils::FormatIPAddress(clientAddr);
|
|
||||||
|
|
||||||
blog(LOG_INFO, "new client connection from %s:%d",
|
|
||||||
clientIp.toUtf8().constData(), pSocket->peerPort());
|
|
||||||
|
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
|
||||||
QString title = tr("OBSWebsocket.NotifyConnect.Title");
|
|
||||||
QString msg = tr("OBSWebsocket.NotifyConnect.Message")
|
|
||||||
.arg(Utils::FormatIPAddress(clientAddr));
|
|
||||||
obs_frontend_pop_ui_translation();
|
|
||||||
|
|
||||||
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::onTextMessageReceived(QString message) {
|
void WSServer::onMessage(connection_hdl hdl, server::message_ptr message)
|
||||||
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
|
{
|
||||||
if (pSocket) {
|
auto opcode = message->get_opcode();
|
||||||
WSRequestHandler handler(pSocket);
|
if (opcode != websocketpp::frame::opcode::text) {
|
||||||
handler.processIncomingMessage(message);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QtConcurrent::run(&_threadPool, [=]() {
|
||||||
|
std::string payload = message->get_payload();
|
||||||
|
|
||||||
|
QMutexLocker locker(&_clMutex);
|
||||||
|
ConnectionProperties& connProperties = _connectionProperties[hdl];
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
|
WSRequestHandler handler(connProperties);
|
||||||
|
std::string response = handler.processIncomingMessage(payload);
|
||||||
|
|
||||||
|
websocketpp::lib::error_code errorCode;
|
||||||
|
_server.send(hdl, response, websocketpp::frame::opcode::text, errorCode);
|
||||||
|
|
||||||
|
if (errorCode) {
|
||||||
|
std::string errorCodeMessage = errorCode.message();
|
||||||
|
blog(LOG_INFO, "server(response): send failed: %s",
|
||||||
|
errorCodeMessage.c_str());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::onSocketDisconnected() {
|
void WSServer::onClose(connection_hdl hdl)
|
||||||
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
|
{
|
||||||
if (pSocket) {
|
QMutexLocker locker(&_clMutex);
|
||||||
pSocket->setProperty(PROP_AUTHENTICATED, false);
|
_connections.erase(hdl);
|
||||||
|
_connectionProperties.erase(hdl);
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
QMutexLocker locker(&_clMutex);
|
auto conn = _server.get_con_from_hdl(hdl);
|
||||||
_clients.removeAll(pSocket);
|
auto localCloseCode = conn->get_local_close_code();
|
||||||
locker.unlock();
|
|
||||||
|
|
||||||
pSocket->deleteLater();
|
if (localCloseCode != websocketpp::close::status::going_away) {
|
||||||
|
QString clientIp = getRemoteEndpoint(hdl);
|
||||||
QHostAddress clientAddr = pSocket->peerAddress();
|
notifyDisconnection(clientIp);
|
||||||
QString clientIp = Utils::FormatIPAddress(clientAddr);
|
blog(LOG_INFO, "client %s disconnected", clientIp.toUtf8().constData());
|
||||||
|
}
|
||||||
blog(LOG_INFO, "client %s:%d disconnected",
|
}
|
||||||
clientIp.toUtf8().constData(), pSocket->peerPort());
|
|
||||||
|
QString WSServer::getRemoteEndpoint(connection_hdl hdl)
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
{
|
||||||
QString title = tr("OBSWebsocket.NotifyDisconnect.Title");
|
auto conn = _server.get_con_from_hdl(hdl);
|
||||||
QString msg = tr("OBSWebsocket.NotifyDisconnect.Message")
|
return QString::fromStdString(conn->get_remote_endpoint());
|
||||||
.arg(Utils::FormatIPAddress(clientAddr));
|
}
|
||||||
obs_frontend_pop_ui_translation();
|
|
||||||
|
void WSServer::notifyConnection(QString clientIp)
|
||||||
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
|
{
|
||||||
}
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
|
QString title = tr("OBSWebsocket.NotifyConnect.Title");
|
||||||
|
QString msg = tr("OBSWebsocket.NotifyConnect.Message").arg(clientIp);
|
||||||
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
|
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSServer::notifyDisconnection(QString clientIp)
|
||||||
|
{
|
||||||
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
|
QString title = tr("OBSWebsocket.NotifyDisconnect.Title");
|
||||||
|
QString msg = tr("OBSWebsocket.NotifyDisconnect.Message").arg(clientIp);
|
||||||
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
|
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,37 +16,54 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef WSSERVER_H
|
#pragma once
|
||||||
#define WSSERVER_H
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <map>
|
||||||
#include <QList>
|
#include <set>
|
||||||
#include <QMutex>
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QMutex>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
#include <QtCore/QVariantHash>
|
||||||
|
#include <QtCore/QThreadPool>
|
||||||
|
|
||||||
|
#include <websocketpp/config/asio_no_tls.hpp>
|
||||||
|
#include <websocketpp/server.hpp>
|
||||||
|
|
||||||
|
#include "ConnectionProperties.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
|
using websocketpp::connection_hdl;
|
||||||
QT_FORWARD_DECLARE_CLASS(QWebSocket)
|
|
||||||
|
|
||||||
class WSServer : public QObject {
|
typedef websocketpp::server<websocketpp::config::asio> server;
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit WSServer(QObject* parent = Q_NULLPTR);
|
|
||||||
virtual ~WSServer();
|
|
||||||
void Start(quint16 port);
|
|
||||||
void Stop();
|
|
||||||
void broadcast(QString message);
|
|
||||||
static WSServer* Instance;
|
|
||||||
|
|
||||||
private slots:
|
class WSServer : public QObject
|
||||||
void onNewConnection();
|
{
|
||||||
void onTextMessageReceived(QString message);
|
Q_OBJECT
|
||||||
void onSocketDisconnected();
|
|
||||||
|
|
||||||
private:
|
public:
|
||||||
QWebSocketServer* _wsServer;
|
explicit WSServer();
|
||||||
QList<QWebSocket*> _clients;
|
virtual ~WSServer();
|
||||||
QMutex _clMutex;
|
void start(quint16 port);
|
||||||
|
void stop();
|
||||||
|
void broadcast(std::string message);
|
||||||
|
QThreadPool* threadPool() {
|
||||||
|
return &_threadPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onOpen(connection_hdl hdl);
|
||||||
|
void onMessage(connection_hdl hdl, server::message_ptr message);
|
||||||
|
void onClose(connection_hdl hdl);
|
||||||
|
|
||||||
|
QString getRemoteEndpoint(connection_hdl hdl);
|
||||||
|
void notifyConnection(QString clientIp);
|
||||||
|
void notifyDisconnection(QString clientIp);
|
||||||
|
|
||||||
|
server _server;
|
||||||
|
quint16 _serverPort;
|
||||||
|
std::set<connection_hdl, std::owner_less<connection_hdl>> _connections;
|
||||||
|
std::map<connection_hdl, ConnectionProperties, std::owner_less<connection_hdl>> _connectionProperties;
|
||||||
|
QMutex _clMutex;
|
||||||
|
QThreadPool _threadPool;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WSSERVER_H
|
|
@ -22,84 +22,85 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "../Config.h"
|
#include "../Config.h"
|
||||||
#include "../WSServer.h"
|
#include "../WSServer.h"
|
||||||
#include "settings-dialog.h"
|
#include "settings-dialog.h"
|
||||||
#include "ui_settings-dialog.h"
|
|
||||||
|
|
||||||
#define CHANGE_ME "changeme"
|
#define CHANGE_ME "changeme"
|
||||||
|
|
||||||
SettingsDialog::SettingsDialog(QWidget* parent) :
|
SettingsDialog::SettingsDialog(QWidget* parent) :
|
||||||
QDialog(parent, Qt::Dialog),
|
QDialog(parent, Qt::Dialog),
|
||||||
ui(new Ui::SettingsDialog)
|
ui(new Ui::SettingsDialog)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
connect(ui->authRequired, &QCheckBox::stateChanged,
|
connect(ui->authRequired, &QCheckBox::stateChanged,
|
||||||
this, &SettingsDialog::AuthCheckboxChanged);
|
this, &SettingsDialog::AuthCheckboxChanged);
|
||||||
connect(ui->buttonBox, &QDialogButtonBox::accepted,
|
connect(ui->buttonBox, &QDialogButtonBox::accepted,
|
||||||
this, &SettingsDialog::FormAccepted);
|
this, &SettingsDialog::FormAccepted);
|
||||||
|
|
||||||
|
|
||||||
AuthCheckboxChanged();
|
AuthCheckboxChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::showEvent(QShowEvent* event) {
|
void SettingsDialog::showEvent(QShowEvent* event) {
|
||||||
Config* conf = Config::Current();
|
auto conf = GetConfig();
|
||||||
|
|
||||||
ui->serverEnabled->setChecked(conf->ServerEnabled);
|
ui->serverEnabled->setChecked(conf->ServerEnabled);
|
||||||
ui->serverPort->setValue(conf->ServerPort);
|
ui->serverPort->setValue(conf->ServerPort);
|
||||||
|
|
||||||
ui->debugEnabled->setChecked(conf->DebugEnabled);
|
ui->debugEnabled->setChecked(conf->DebugEnabled);
|
||||||
ui->alertsEnabled->setChecked(conf->AlertsEnabled);
|
ui->alertsEnabled->setChecked(conf->AlertsEnabled);
|
||||||
|
|
||||||
ui->authRequired->setChecked(conf->AuthRequired);
|
ui->authRequired->setChecked(conf->AuthRequired);
|
||||||
ui->password->setText(CHANGE_ME);
|
ui->password->setText(CHANGE_ME);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::ToggleShowHide() {
|
void SettingsDialog::ToggleShowHide() {
|
||||||
if (!isVisible())
|
if (!isVisible())
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
else
|
else
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::AuthCheckboxChanged() {
|
void SettingsDialog::AuthCheckboxChanged() {
|
||||||
if (ui->authRequired->isChecked())
|
if (ui->authRequired->isChecked())
|
||||||
ui->password->setEnabled(true);
|
ui->password->setEnabled(true);
|
||||||
else
|
else
|
||||||
ui->password->setEnabled(false);
|
ui->password->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::FormAccepted() {
|
void SettingsDialog::FormAccepted() {
|
||||||
Config* conf = Config::Current();
|
auto conf = GetConfig();
|
||||||
|
|
||||||
conf->ServerEnabled = ui->serverEnabled->isChecked();
|
conf->ServerEnabled = ui->serverEnabled->isChecked();
|
||||||
conf->ServerPort = ui->serverPort->value();
|
conf->ServerPort = ui->serverPort->value();
|
||||||
|
|
||||||
conf->DebugEnabled = ui->debugEnabled->isChecked();
|
conf->DebugEnabled = ui->debugEnabled->isChecked();
|
||||||
conf->AlertsEnabled = ui->alertsEnabled->isChecked();
|
conf->AlertsEnabled = ui->alertsEnabled->isChecked();
|
||||||
|
|
||||||
if (ui->authRequired->isChecked()) {
|
if (ui->authRequired->isChecked()) {
|
||||||
if (ui->password->text() != CHANGE_ME) {
|
if (ui->password->text() != CHANGE_ME) {
|
||||||
conf->SetPassword(ui->password->text());
|
conf->SetPassword(ui->password->text());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Config::Current()->Secret.isEmpty())
|
if (!GetConfig()->Secret.isEmpty())
|
||||||
conf->AuthRequired = true;
|
conf->AuthRequired = true;
|
||||||
else
|
else
|
||||||
conf->AuthRequired = false;
|
conf->AuthRequired = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
conf->AuthRequired = false;
|
conf->AuthRequired = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
conf->Save();
|
conf->Save();
|
||||||
|
|
||||||
if (conf->ServerEnabled)
|
auto server = GetServer();
|
||||||
WSServer::Instance->Start(conf->ServerPort);
|
if (conf->ServerEnabled) {
|
||||||
else
|
server->start(conf->ServerPort);
|
||||||
WSServer::Instance->Stop();
|
} else {
|
||||||
|
server->stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::~SettingsDialog() {
|
SettingsDialog::~SettingsDialog() {
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,31 +16,26 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SETTINGSDIALOG_H
|
#pragma once
|
||||||
#define SETTINGSDIALOG_H
|
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QtWidgets/QDialog>
|
||||||
|
|
||||||
namespace Ui {
|
#include "ui_settings-dialog.h"
|
||||||
class SettingsDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
class SettingsDialog : public QDialog
|
class SettingsDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SettingsDialog(QWidget* parent = 0);
|
explicit SettingsDialog(QWidget* parent = 0);
|
||||||
~SettingsDialog();
|
~SettingsDialog();
|
||||||
void showEvent(QShowEvent* event);
|
void showEvent(QShowEvent* event);
|
||||||
void ToggleShowHide();
|
void ToggleShowHide();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void AuthCheckboxChanged();
|
void AuthCheckboxChanged();
|
||||||
void FormAccepted();
|
void FormAccepted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::SettingsDialog* ui;
|
Ui::SettingsDialog* ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SETTINGSDIALOG_H
|
|
||||||
|
@ -2,150 +2,150 @@
|
|||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>SettingsDialog</class>
|
<class>SettingsDialog</class>
|
||||||
<widget class="QDialog" name="SettingsDialog">
|
<widget class="QDialog" name="SettingsDialog">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>407</width>
|
<width>407</width>
|
||||||
<height>195</height>
|
<height>195</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>OBSWebsocket.Settings.DialogTitle</string>
|
<string>OBSWebsocket.Settings.DialogTitle</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeGripEnabled">
|
<property name="sizeGripEnabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<property name="sizeConstraint">
|
<property name="sizeConstraint">
|
||||||
<enum>QLayout::SetDefaultConstraint</enum>
|
<enum>QLayout::SetDefaultConstraint</enum>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QCheckBox" name="authRequired">
|
<widget class="QCheckBox" name="authRequired">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>OBSWebsocket.Settings.AuthRequired</string>
|
<string>OBSWebsocket.Settings.AuthRequired</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="lbl_password">
|
<widget class="QLabel" name="lbl_password">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>OBSWebsocket.Settings.Password</string>
|
<string>OBSWebsocket.Settings.Password</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QLineEdit" name="password">
|
<widget class="QLineEdit" name="password">
|
||||||
<property name="echoMode">
|
<property name="echoMode">
|
||||||
<enum>QLineEdit::Password</enum>
|
<enum>QLineEdit::Password</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="serverEnabled">
|
<widget class="QCheckBox" name="serverEnabled">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>OBSWebsocket.Settings.ServerEnable</string>
|
<string>OBSWebsocket.Settings.ServerEnable</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="lbl_serverPort">
|
<widget class="QLabel" name="lbl_serverPort">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>OBSWebsocket.Settings.ServerPort</string>
|
<string>OBSWebsocket.Settings.ServerPort</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QSpinBox" name="serverPort">
|
<widget class="QSpinBox" name="serverPort">
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>1024</number>
|
<number>1024</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>65535</number>
|
<number>65535</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>4444</number>
|
<number>4444</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QCheckBox" name="alertsEnabled">
|
<widget class="QCheckBox" name="alertsEnabled">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>OBSWebsocket.Settings.AlertsEnable</string>
|
<string>OBSWebsocket.Settings.AlertsEnable</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QCheckBox" name="debugEnabled">
|
<widget class="QCheckBox" name="debugEnabled">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>OBSWebsocket.Settings.DebugEnable</string>
|
<string>OBSWebsocket.Settings.DebugEnable</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
<sender>buttonBox</sender>
|
<sender>buttonBox</sender>
|
||||||
<signal>accepted()</signal>
|
<signal>accepted()</signal>
|
||||||
<receiver>SettingsDialog</receiver>
|
<receiver>SettingsDialog</receiver>
|
||||||
<slot>accept()</slot>
|
<slot>accept()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
||||||
<x>248</x>
|
<x>248</x>
|
||||||
<y>294</y>
|
<y>294</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel">
|
<hint type="destinationlabel">
|
||||||
<x>157</x>
|
<x>157</x>
|
||||||
<y>314</y>
|
<y>314</y>
|
||||||
</hint>
|
</hint>
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
<connection>
|
<connection>
|
||||||
<sender>buttonBox</sender>
|
<sender>buttonBox</sender>
|
||||||
<signal>rejected()</signal>
|
<signal>rejected()</signal>
|
||||||
<receiver>SettingsDialog</receiver>
|
<receiver>SettingsDialog</receiver>
|
||||||
<slot>reject()</slot>
|
<slot>reject()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
||||||
<x>316</x>
|
<x>316</x>
|
||||||
<y>300</y>
|
<y>300</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel">
|
<hint type="destinationlabel">
|
||||||
<x>286</x>
|
<x>286</x>
|
||||||
<y>314</y>
|
<y>314</y>
|
||||||
</hint>
|
</hint>
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
</connections>
|
</connections>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -18,9 +18,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <QAction>
|
#include <obs-data.h>
|
||||||
#include <QMainWindow>
|
|
||||||
#include <QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtWidgets/QAction>
|
||||||
|
#include <QtWidgets/QMainWindow>
|
||||||
|
|
||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
#include "WSServer.h"
|
#include "WSServer.h"
|
||||||
@ -34,47 +36,82 @@ void ___data_dummy_addref(obs_data_t*) {}
|
|||||||
void ___data_array_dummy_addref(obs_data_array_t*) {}
|
void ___data_array_dummy_addref(obs_data_array_t*) {}
|
||||||
void ___output_dummy_addref(obs_output_t*) {}
|
void ___output_dummy_addref(obs_output_t*) {}
|
||||||
|
|
||||||
|
void ___data_item_dummy_addref(obs_data_item_t*) {}
|
||||||
|
void ___data_item_release(obs_data_item_t* dataItem) {
|
||||||
|
obs_data_item_release(&dataItem);
|
||||||
|
}
|
||||||
|
|
||||||
OBS_DECLARE_MODULE()
|
OBS_DECLARE_MODULE()
|
||||||
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
||||||
|
|
||||||
SettingsDialog* settings_dialog;
|
ConfigPtr _config;
|
||||||
|
WSServerPtr _server;
|
||||||
|
WSEventsPtr _eventsSystem;
|
||||||
|
|
||||||
bool obs_module_load(void) {
|
bool obs_module_load(void) {
|
||||||
blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION);
|
blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION);
|
||||||
blog(LOG_INFO, "qt version (compile-time): %s ; qt version (run-time): %s",
|
blog(LOG_INFO, "qt version (compile-time): %s ; qt version (run-time): %s",
|
||||||
QT_VERSION_STR, qVersion());
|
QT_VERSION_STR, qVersion());
|
||||||
|
|
||||||
// Core setup
|
// Core setup
|
||||||
Config* config = Config::Current();
|
_config = ConfigPtr(new Config());
|
||||||
config->Load();
|
_config->MigrateFromGlobalSettings(); // TODO remove this on the next minor jump
|
||||||
|
_config->Load();
|
||||||
|
|
||||||
WSServer::Instance = new WSServer();
|
_server = WSServerPtr(new WSServer());
|
||||||
WSEvents::Instance = new WSEvents(WSServer::Instance);
|
_eventsSystem = WSEventsPtr(new WSEvents(_server));
|
||||||
|
|
||||||
if (config->ServerEnabled)
|
// UI setup
|
||||||
WSServer::Instance->Start(config->ServerPort);
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
|
QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
SettingsDialog* settingsDialog = new SettingsDialog(mainWindow);
|
||||||
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
// UI setup
|
const char* menuActionText =
|
||||||
QAction* menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(
|
obs_module_text("OBSWebsocket.Settings.DialogTitle");
|
||||||
obs_module_text("OBSWebsocket.Menu.SettingsItem"));
|
QAction* menuAction =
|
||||||
|
(QAction*)obs_frontend_add_tools_menu_qaction(menuActionText);
|
||||||
|
QObject::connect(menuAction, &QAction::triggered, [settingsDialog] {
|
||||||
|
// 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();
|
||||||
|
});
|
||||||
|
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
// Setup event handler to start the server once OBS is ready
|
||||||
QMainWindow* main_window = (QMainWindow*)obs_frontend_get_main_window();
|
auto eventCallback = [](enum obs_frontend_event event, void *param) {
|
||||||
settings_dialog = new SettingsDialog(main_window);
|
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
|
||||||
obs_frontend_pop_ui_translation();
|
if (_config->ServerEnabled) {
|
||||||
|
_server->start(_config->ServerPort);
|
||||||
|
}
|
||||||
|
obs_frontend_remove_event_callback((obs_frontend_event_cb)param, nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
obs_frontend_add_event_callback(eventCallback, (void*)(obs_frontend_event_cb)eventCallback);
|
||||||
|
|
||||||
auto menu_cb = [] {
|
// Loading finished
|
||||||
settings_dialog->ToggleShowHide();
|
blog(LOG_INFO, "module loaded!");
|
||||||
};
|
|
||||||
menu_action->connect(menu_action, &QAction::triggered, menu_cb);
|
|
||||||
|
|
||||||
// Loading finished
|
return true;
|
||||||
blog(LOG_INFO, "module loaded!");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void obs_module_unload() {
|
void obs_module_unload() {
|
||||||
blog(LOG_INFO, "goodbye!");
|
_server->stop();
|
||||||
|
|
||||||
|
_eventsSystem.reset();
|
||||||
|
_server.reset();
|
||||||
|
_config.reset();
|
||||||
|
|
||||||
|
blog(LOG_INFO, "goodbye!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfigPtr GetConfig() {
|
||||||
|
return _config;
|
||||||
|
}
|
||||||
|
|
||||||
|
WSServerPtr GetServer() {
|
||||||
|
return _server;
|
||||||
|
}
|
||||||
|
|
||||||
|
WSEventsPtr GetEventsSystem() {
|
||||||
|
return _eventsSystem;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,10 +16,10 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef OBSWEBSOCKET_H
|
#pragma once
|
||||||
#define OBSWEBSOCKET_H
|
|
||||||
|
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
void ___source_dummy_addref(obs_source_t*);
|
void ___source_dummy_addref(obs_source_t*);
|
||||||
void ___sceneitem_dummy_addref(obs_sceneitem_t*);
|
void ___sceneitem_dummy_addref(obs_sceneitem_t*);
|
||||||
@ -38,9 +38,24 @@ using OBSDataArrayAutoRelease =
|
|||||||
using OBSOutputAutoRelease =
|
using OBSOutputAutoRelease =
|
||||||
OBSRef<obs_output_t*, ___output_dummy_addref, obs_output_release>;
|
OBSRef<obs_output_t*, ___output_dummy_addref, obs_output_release>;
|
||||||
|
|
||||||
#define PROP_AUTHENTICATED "wsclient_authenticated"
|
void ___data_item_dummy_addref(obs_data_item_t*);
|
||||||
#define OBS_WEBSOCKET_VERSION "4.3.0"
|
void ___data_item_release(obs_data_item_t*);
|
||||||
|
using OBSDataItemAutoRelease =
|
||||||
|
OBSRef<obs_data_item_t*, ___data_item_dummy_addref, ___data_item_release>;
|
||||||
|
|
||||||
|
class Config;
|
||||||
|
typedef std::shared_ptr<Config> ConfigPtr;
|
||||||
|
|
||||||
|
class WSServer;
|
||||||
|
typedef std::shared_ptr<WSServer> WSServerPtr;
|
||||||
|
|
||||||
|
class WSEvents;
|
||||||
|
typedef std::shared_ptr<WSEvents> WSEventsPtr;
|
||||||
|
|
||||||
|
ConfigPtr GetConfig();
|
||||||
|
WSServerPtr GetServer();
|
||||||
|
WSEventsPtr GetEventsSystem();
|
||||||
|
|
||||||
|
#define OBS_WEBSOCKET_VERSION "4.7.0"
|
||||||
|
|
||||||
#define blog(level, msg, ...) blog(level, "[obs-websocket] " msg, ##__VA_ARGS__)
|
#define blog(level, msg, ...) blog(level, "[obs-websocket] " msg, ##__VA_ARGS__)
|
||||||
|
|
||||||
#endif // OBSWEBSOCKET_H
|
|
Reference in New Issue
Block a user