mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
ef5480dd7b | |||
de13707f8b | |||
7b81b81a3e | |||
2854c4e1b5 | |||
1848442e91 | |||
7a19b168b7 | |||
9adea8bab4 | |||
1db18e12b3 | |||
3be4c8ae0e | |||
437f4df84c |
@ -3,7 +3,6 @@ insert_final_newline = true
|
|||||||
|
|
||||||
[*.{c,cpp,h,hpp}]
|
[*.{c,cpp,h,hpp}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[*.{yml,yaml}]
|
[*.{yml,yaml}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
22
.github/CONTRIBUTING.md
vendored
Normal file
22
.github/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
## Contributing to obs-websocket
|
||||||
|
|
||||||
|
### Translating obs-websocket to your language
|
||||||
|
Localization happens on Crowdin: https://crowdin.com/project/obs-websocket
|
||||||
|
|
||||||
|
### Writing code for obs-websocket
|
||||||
|
#### Coding Guidelines
|
||||||
|
- Function and variable names: snake_case for C names, CamelCase for C++ names
|
||||||
|
- Tabs are 8 columns wide
|
||||||
|
- 80 columns max.
|
||||||
|
|
||||||
|
#### Commit Guidelines
|
||||||
|
- Commits follow the 50/72 standard:
|
||||||
|
- 50 characters max for the title
|
||||||
|
- One empty line after the title
|
||||||
|
- Description wrapped to 72 columns max per line.
|
||||||
|
- Commit titles:
|
||||||
|
- Use present tense
|
||||||
|
- Prefix the title with a "scope" name
|
||||||
|
- e.g: "CI: fix wrong behaviour when packaging for OS X"
|
||||||
|
- Typical scopes: CI, General, Request, Event, Server
|
||||||
|
- Look at existing commits for more examples
|
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@ -1,3 +0,0 @@
|
|||||||
open_collective: obs-websocket
|
|
||||||
github: Palakis
|
|
||||||
custom: https://www.paypal.me/stephanelepin
|
|
14
.github/ISSUE_TEMPLATE.md
vendored
14
.github/ISSUE_TEMPLATE.md
vendored
@ -1,20 +1,16 @@
|
|||||||
##### Issue type
|
##### Issue type
|
||||||
<!--- Uncomment one of the two options below. -->
|
Bug report? Feature request? Other?
|
||||||
|
|
||||||
<!--- - Bug report -->
|
|
||||||
<!--- - Feature request -->
|
|
||||||
|
|
||||||
##### Description
|
##### Description
|
||||||
<!--- Describe the bug encountered or feature requested. -->
|
*Replace this with a description of the bug encountered or feature requested.*
|
||||||
|
|
||||||
##### Steps to reproduce and other useful info
|
##### Steps to reproduce and other useful info
|
||||||
<!--- If it's a bug, please describe the steps to reproduce it and PLEASE include an OBS log file. Otherwise, remove this section. -->
|
*If it's a bug, please describe the steps to reproduce it and PLEASE include an OBS log file. Otherwise, remove this section.*
|
||||||
|
|
||||||
##### Technical information
|
##### Technical information
|
||||||
- **Operating System** :
|
- **Operating System** :
|
||||||
- **OBS Studio version** :
|
- **OBS Studio version** :
|
||||||
- **obs-websocket version** :
|
|
||||||
|
|
||||||
##### Development Environment
|
##### Development Environment
|
||||||
<!--- If you're trying to compile obs-websocket, please describe your compiler type and version (e.g: GCC 4.7, VC2013, ...), and the CMake settings used. -->
|
*If you're trying to compile obs-websocket, please describe your compiler type and version (e.g: GCC 4.7, VC2013, ...), and the CMake settings used.
|
||||||
<!--- Remove this section if it does not apply. -->
|
Remove this section if it doesn't apply to your case.*
|
||||||
|
BIN
.github/images/obsws_logo.png
vendored
BIN
.github/images/obsws_logo.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 57 KiB |
35
.github/pull_request_template.md
vendored
35
.github/pull_request_template.md
vendored
@ -1,35 +0,0 @@
|
|||||||
<!--- Please fill out the following template, which will help other contributors review your Pull Request. -->
|
|
||||||
|
|
||||||
<!--- Make sure you’ve read the contribution guidelines here: https://github.com/Palakis/obs-websocket/blob/4.x-current/CONTRIBUTING.md -->
|
|
||||||
|
|
||||||
### Description
|
|
||||||
<!--- Describe your changes. -->
|
|
||||||
|
|
||||||
### Motivation and Context
|
|
||||||
<!--- Why is this change required? What problem does it solve? -->
|
|
||||||
<!--- If it fixes/closes an open issue or implements feature request, -->
|
|
||||||
<!--- please link to the issue here. -->
|
|
||||||
|
|
||||||
### How Has This Been Tested?
|
|
||||||
<!--- Please describe in detail how you tested your changes, along with the OS(s) you tested with. -->
|
|
||||||
Tested OS(s):
|
|
||||||
|
|
||||||
### Types of changes
|
|
||||||
<!--- What types of changes does your PR introduce? Uncomment all that apply -->
|
|
||||||
|
|
||||||
<!--- - Bug fix (non-breaking change which fixes an issue) -->
|
|
||||||
<!--- - New request/event (non-breaking) -->
|
|
||||||
<!--- - Documentation change (a change to documentation pages) -->
|
|
||||||
<!--- - Enhancement (modification to a current event/request which adds functionality) -->
|
|
||||||
<!--- - Performance enhancement (non-breaking change which improves efficiency) -->
|
|
||||||
<!--- - Code cleanup (non-breaking change which makes code smaller or more readable) -->
|
|
||||||
|
|
||||||
### Checklist:
|
|
||||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
|
||||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
|
||||||
- [ ] I have read the [**contributing** document](https://github.com/Palakis/obs-websocket/blob/4.x-current/CONTRIBUTING.md).
|
|
||||||
- [ ] My code is not on the master branch.
|
|
||||||
- [ ] The code has been tested.
|
|
||||||
- [ ] All commit messages are properly formatted and commits squashed where appropriate.
|
|
||||||
- [ ] I have included updates to all appropriate documentation.
|
|
||||||
|
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -4,7 +4,6 @@
|
|||||||
/build32/
|
/build32/
|
||||||
/build64/
|
/build64/
|
||||||
/release/
|
/release/
|
||||||
/package/
|
|
||||||
/installer/Output/
|
/installer/Output/
|
||||||
.idea
|
|
||||||
.vscode
|
.vscode
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,6 +0,0 @@
|
|||||||
[submodule "deps/websocketpp"]
|
|
||||||
path = deps/websocketpp
|
|
||||||
url = https://github.com/zaphoyd/websocketpp.git
|
|
||||||
[submodule "deps/asio"]
|
|
||||||
path = deps/asio
|
|
||||||
url = https://github.com/chriskohlhoff/asio.git
|
|
49
.travis.yml
Normal file
49
.travis.yml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
language: cpp
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
# AWS key ID
|
||||||
|
- secure: pAiNUGVbjP12BfnWPk0FFTkbnk4Tocvv88XiT3rzRqkQaD7/iyEogLBfHM4nOEgFiIMHbC41aE83w5JgRNPwn6mTgoQBOglzqq1tGuXfqPyV2VStk8beji1evubGoVjjPaoPTFyIdQc5GGxdHyogI/ed9Hb3ccyykYvjyolj9XoCiW42QHx60AHGwl+So+dEa8xydj9SLRPlZ/AitmI/cPVN3YotA7s37BLFiab54enxk7T4rwpR1nU0HVfoCpn5F4wZYxRq+LlSVFzC8vVE9cpDSLS5kjrZIZaT18tYG1/untCj+wqMIZbghaJXLtPSRW2YPHcJTz8q1YSXnJ19+0uiAIMAqaVv0kD5BAM97byYDBW+b9H6SYFkb/Pw/qcK9amMzMBjDPFpYFkl9Q2kzhsNs3HsZf/flSZjtrkQJiP3SOi/KvKzVK9X4Wym6hYZWHgmMTTYFrvr6BYnf2GkpfKNjm1d2kc0NNrq4d5H4NOEQB8MP+QH+o+BPeM6d9dthrUc1Pw+BXzOAr85CN4qtpPGoAl/Dbfgd6eu/88E2LpUufW2VFAOPWjykSOqzSN3orh7AaWuE34VFEnQ+2y3uIE8AKoyXzJv6zYkyNnNewKZeGe2kKYNwLn5UxQA9JEj7a+tvVevk4xBSkkjFAvjSG2z8/F1FXNbEfoLX1Hz/bU=
|
||||||
|
# AWS key secret
|
||||||
|
- secure: bGwljoP3E1OVBXLXox0O6p8kwQXLcNQ8YDKVa4H8u9Y+Ic7uqE4iV3rYS3ynNWSBMVRWY3ZbyClnhrCNwRhBAlcd8qWSJdpjVzs6HdQyzhuKa1P3V4FJPb7upGP/5R/DECGwex8Mun9dmXpYDak75LxfKIJUidPis5VDCYqul7k/xVVCou6Ctjpj7vQhWXDj2G/py+mdB8DERhymnQCtyK1Ziu8c4QlFKByZmnD72GFm/h3JPI1Pq1V2mz3x6x6GaYjb9Rdbd0UNwqjGQX4q2M/c3GEJa6B2JBCoTncawNZBNnPUF9qtv+zh0TNaNHMRWX13AJ/qYB+nVDub0C9b/6Mc48mt0Tv4ze15MproVrylZdV6qHYEG8yGPBqpTVbRP6gv6Y2TXIHWoTzqA+F/Gv2IDChyHXsld/MQQS2MSo5iaYktIrZKtX8Z0qAmTzPwIVBromaSI3vrE7UH0fRSQ6fAM8+Tn+MRthOBdqu23kS1dnG+X2CPbUhBfsJp0OSwVQD5jQtA51/sREVeGFiJvzQIkvwQDjb5MYilsRnwmoBXemkLmqaviXVY4rz1o5AIvz2pgZS2YggK1xHZCuI5tSjcNEkb77VwZTfsqrdDo9EJh6VgfdnGlHQhR2/A5hUJ4ANpJ/LgZlgfVp71Xg2GWQW6M4Znc5uj6A6xLBkO6FA=
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- node_modules
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: linux
|
||||||
|
env: _generate_docs
|
||||||
|
script: "./CI/generate-docs.sh"
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
env: _linux_build
|
||||||
|
dist: trusty
|
||||||
|
sudo: required
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
before_install:
|
||||||
|
- docker run -d --name xenial -v $(dirname $(pwd)):/root -v /home/travis/package:/package
|
||||||
|
-e TRAVIS_BRANCH="$TRAVIS_BRANCH" -e TRAVIS_TAG="$TRAVIS_TAG" -w /root nimmis/ubuntu:16.04
|
||||||
|
- docker exec -it xenial /root/obs-websocket/CI/install-dependencies-xenial.sh
|
||||||
|
script:
|
||||||
|
- docker exec -it xenial /root/obs-websocket/CI/build-xenial.sh
|
||||||
|
after_success:
|
||||||
|
- docker exec -it xenial /root/obs-websocket/CI/package-xenial.sh
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
- provider: s3
|
||||||
|
region: eu-central-1
|
||||||
|
bucket: obs-websocket-linux-builds
|
||||||
|
access_key_id: "$AWS_ID"
|
||||||
|
secret_access_key: "$AWS_SECRET"
|
||||||
|
local_dir: /home/travis/package
|
||||||
|
skip_cleanup: true
|
||||||
|
acl: public_read
|
||||||
|
on:
|
||||||
|
repo: Palakis/obs-websocket
|
||||||
|
condition:
|
||||||
|
- "$TRAVIS_OS_NAME = linux"
|
||||||
|
- "-d /home/travis/package"
|
||||||
|
all_branches: true
|
45
BUILDING.md
45
BUILDING.md
@ -1,46 +1,34 @@
|
|||||||
# Compiling obs-websocket
|
# Compiling obs-websocket
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
You'll need [Qt 5.10.x](https://download.qt.io/official_releases/qt/5.10/),
|
||||||
You'll need [Qt 5.15.2](https://download.qt.io/official_releases/qt/5.15/5.15.2/),
|
[CMake](https://cmake.org/download/), and a working [development environment for
|
||||||
[CMake](https://cmake.org/download/) and a working [OBS Studio development environment](https://obsproject.com/wiki/install-instructions) installed on your
|
OBS Studio](https://obsproject.com/wiki/install-instructions) installed on your
|
||||||
computer.
|
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
|
||||||
cmake -DLIBOBS_INCLUDE_DIR="<path to the libobs sub-folder in obs-studio's source code>" -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=true ..
|
cmake -DLIBOBS_INCLUDE_DIR="<path to the libobs sub-folder in obs-studio's source code>" -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
make -j4
|
make -j4
|
||||||
sudo make install
|
sudo make install
|
||||||
```
|
```
|
||||||
|
|
||||||
On other linux OS's, use this cmake command instead:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cmake -DLIBOBS_INCLUDE_DIR="<path to the libobs sub-folder in obs-studio's source code>" -DCMAKE_INSTALL_PREFIX=/usr ..
|
|
||||||
```
|
|
||||||
|
|
||||||
## OS X
|
## OS X
|
||||||
|
As a prerequisite, you will need Xcode for your current OSX version, the command line tools, and [Homebrew](https://brew.sh/).
|
||||||
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.
|
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 macOS CI scripts is recommended. Please note that these
|
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
|
scripts install new software and can change several settings on your system. An
|
||||||
existing obs-studio development environment is not required, as
|
existing obs-studio development environment is not required, as
|
||||||
`install-build-obs-macos.sh` will install it for you. If you already have a
|
`install-build-obs-macos.sh` will install it for you. If you already have a
|
||||||
@ -50,17 +38,18 @@ skip that script.
|
|||||||
Of course, you're encouraged to dig through the contents of these scripts to
|
Of course, you're encouraged to dig through the contents of these scripts to
|
||||||
look for issues or specificities.
|
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/macos/install-dependencies-macos.sh
|
./CI/install-dependencies-macos.sh
|
||||||
./CI/macos/install-build-obs-macos.sh
|
./CI/install-build-obs-macos.sh
|
||||||
./CI/macos/build-plugin-macos.sh
|
./CI/build-macos.sh
|
||||||
./CI/macos/package-plugin-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
|
||||||
|
|
||||||
[](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current)
|
- 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)
|
||||||
|
@ -3,21 +3,23 @@
|
|||||||
OSTYPE=$(uname)
|
OSTYPE=$(uname)
|
||||||
|
|
||||||
if [ "${OSTYPE}" != "Darwin" ]; then
|
if [ "${OSTYPE}" != "Darwin" ]; then
|
||||||
echo "[obs-websocket - Error] macOS build script can be run on Darwin-type OS only."
|
echo "[obs-websocket - Error] macOS build script can be run on Darwin-type OS only."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
HAS_CMAKE=$(type cmake 2>/dev/null)
|
HAS_CMAKE=$(type cmake 2>/dev/null)
|
||||||
|
|
||||||
if [ "${HAS_CMAKE}" = "" ]; then
|
if [ "${HAS_CMAKE}" = "" ]; then
|
||||||
echo "[obs-websocket - Error] CMake not installed - please run 'install-dependencies-macos.sh' first."
|
echo "[obs-websocket - Error] CMake not installed - please run 'install-dependencies-macos.sh' first."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#export QT_PREFIX="$(find /usr/local/Cellar/qt5 -d 1 | tail -n 1)"
|
||||||
|
|
||||||
echo "[obs-websocket] Building 'obs-websocket' for macOS."
|
echo "[obs-websocket] Building 'obs-websocket' for macOS."
|
||||||
mkdir -p build && cd build
|
mkdir -p build && cd build
|
||||||
cmake .. \
|
cmake .. \
|
||||||
-DQTDIR=/tmp/obsdeps \
|
-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" \
|
8
CI/build-xenial.sh
Executable file
8
CI/build-xenial.sh
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
cd /root/obs-websocket
|
||||||
|
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
make -j4
|
@ -4,9 +4,6 @@ echo "-- Generating documentation."
|
|||||||
echo "-- Node version: $(node -v)"
|
echo "-- Node version: $(node -v)"
|
||||||
echo "-- NPM version: $(npm -v)"
|
echo "-- NPM version: $(npm -v)"
|
||||||
|
|
||||||
git fetch origin
|
|
||||||
git checkout ${CHECKOUT_REF/refs\/heads\//}
|
|
||||||
|
|
||||||
cd docs
|
cd docs
|
||||||
npm install
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
@ -18,14 +15,19 @@ if git diff --quiet; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
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."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
REMOTE_URL="$(git config remote.origin.url)"
|
REMOTE_URL="$(git config remote.origin.url)"
|
||||||
TARGET_REPO=${REMOTE_URL/https:\/\/github.com\//github.com/}
|
TARGET_REPO=${REMOTE_URL/https:\/\/github.com\//github.com/}
|
||||||
GITHUB_REPO=https://${GH_TOKEN:-git}@${TARGET_REPO}
|
GITHUB_REPO=https://${GH_TOKEN:-git}@${TARGET_REPO}
|
||||||
|
|
||||||
git config user.name "Azure CI"
|
git config user.name "Travis CI"
|
||||||
git config user.email "$COMMIT_AUTHOR_EMAIL"
|
git config user.email "$COMMIT_AUTHOR_EMAIL"
|
||||||
|
|
||||||
git add ./generated
|
git add ./generated
|
||||||
git pull
|
git pull
|
||||||
git commit -m "docs(ci): Update protocol.md - $(git rev-parse --short HEAD) [skip ci]"
|
git commit -m "docs(travis): Update protocol.md - $(git rev-parse --short HEAD) [skip ci]"
|
||||||
git push -q $GITHUB_REPO
|
git push -q $GITHUB_REPO HEAD:$TRAVIS_BRANCH
|
||||||
|
41
CI/install-build-obs-macos.sh
Executable file
41
CI/install-build-obs-macos.sh
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
#!/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 .. \
|
||||||
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \
|
||||||
|
-DDISABLE_PLUGINS=true \
|
||||||
|
-DDepsPath=/tmp/obsdeps \
|
||||||
|
-DCMAKE_PREFIX_PATH=/usr/local/opt/qt/lib/cmake \
|
||||||
|
&& make -j4
|
129
CI/install-build-obs.cmd
Normal file
129
CI/install-build-obs.cmd
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
@echo off
|
||||||
|
SETLOCAL EnableDelayedExpansion
|
||||||
|
|
||||||
|
REM Check if obs-studio build exists.
|
||||||
|
REM If the obs-studio directory does exist, check if the last OBS tag built
|
||||||
|
REM matches the latest OBS tag.
|
||||||
|
REM If the tags match, do not build obs-studio.
|
||||||
|
REM If the tags do not match, build obs-studio.
|
||||||
|
REM If the obs-studio directory doesn't exist, build obs-studio.
|
||||||
|
echo Checking for obs-studio build...
|
||||||
|
|
||||||
|
set OBSLatestTagPrePull=0
|
||||||
|
set OBSLatestTagPostPull=0
|
||||||
|
echo Latest tag pre-pull: %OBSLatestTagPrePull%
|
||||||
|
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
||||||
|
|
||||||
|
REM Set up the build flag as undefined.
|
||||||
|
set "BuildOBS="
|
||||||
|
|
||||||
|
REM Check the last tag successfully built by CI.
|
||||||
|
if exist C:\projects\obs-studio-last-tag-built.txt (
|
||||||
|
set /p OBSLastTagBuilt=<C:\projects\obs-studio-last-tag-built.txt
|
||||||
|
) else (
|
||||||
|
set OBSLastTagBuilt=0
|
||||||
|
)
|
||||||
|
|
||||||
|
REM If obs-studio directory exists, run git pull and get the latest tag number.
|
||||||
|
if exist C:\projects\obs-studio\ (
|
||||||
|
echo obs-studio directory exists
|
||||||
|
echo Updating tag info
|
||||||
|
cd C:\projects\obs-studio\
|
||||||
|
git describe --tags --abbrev=0 > 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 pull
|
||||||
|
git describe --tags --abbrev=0 > 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
|
||||||
|
echo %OBSLatestTagPostPull%> C:\projects\latest-obs-studio-tag.txt
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Check the obs-studio tags for mismatches.
|
||||||
|
REM If a new tag was pulled, set the build flag.
|
||||||
|
if not %OBSLatestTagPrePull%==%OBSLatestTagPostPull% (
|
||||||
|
echo Latest tag pre-pull: %OBSLatestTagPrePull%
|
||||||
|
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
||||||
|
echo Tags do not match. Need to rebuild OBS.
|
||||||
|
set BuildOBS=true
|
||||||
|
)
|
||||||
|
|
||||||
|
REM If the latest git tag doesn't match the last built tag, set the build flag.
|
||||||
|
if not %OBSLatestTagPostPull%==%OBSLastTagBuilt% (
|
||||||
|
echo Last built OBS tag: %OBSLastTagBuilt%
|
||||||
|
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
||||||
|
echo Tags do not match. Need to rebuild OBS.
|
||||||
|
set BuildOBS=true
|
||||||
|
)
|
||||||
|
|
||||||
|
REM If obs-studio directory does not exist, clone the git repo, get the latest
|
||||||
|
REM tag number, and set the build flag.
|
||||||
|
if not exist C:\projects\obs-studio (
|
||||||
|
echo obs-studio directory does not exist
|
||||||
|
git clone https://github.com/obsproject/obs-studio
|
||||||
|
cd C:\projects\obs-studio\
|
||||||
|
git describe --tags --abbrev=0 > C:\projects\obs-studio-latest-tag.txt
|
||||||
|
set /p OBSLatestTag=<C:\projects\obs-studio-latest-tag.txt
|
||||||
|
set BuildOBS=true
|
||||||
|
)
|
||||||
|
|
||||||
|
REM If the needed obs-studio libs for this build_config do not exist,
|
||||||
|
REM set the build flag.
|
||||||
|
if not exist 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
|
||||||
|
echo:
|
||||||
|
echo Latest tag pre-pull: %OBSLatestTagPrePull%
|
||||||
|
echo Latest tag post-pull: %OBSLatestTagPostPull%
|
||||||
|
echo Latest tag: %OBSLatestTag%
|
||||||
|
echo Last built OBS tag: %OBSLastTagBuilt%
|
||||||
|
|
||||||
|
if defined BuildOBS (
|
||||||
|
echo BuildOBS: true
|
||||||
|
) else (
|
||||||
|
echo BuildOBS: false
|
||||||
|
)
|
||||||
|
echo:
|
||||||
|
|
||||||
|
REM If the build flag is set, build obs-studio.
|
||||||
|
if defined BuildOBS (
|
||||||
|
echo Building obs-studio...
|
||||||
|
echo git checkout %OBSLatestTag%
|
||||||
|
git checkout %OBSLatestTag%
|
||||||
|
echo:
|
||||||
|
echo Removing previous build dirs...
|
||||||
|
if exist build rmdir /s /q C:\projects\obs-studio\build
|
||||||
|
if exist build32 rmdir /s /q C:\projects\obs-studio\build32
|
||||||
|
if exist build64 rmdir /s /q C:\projects\obs-studio\build64
|
||||||
|
echo Making new build dirs...
|
||||||
|
mkdir build
|
||||||
|
mkdir build32
|
||||||
|
mkdir build64
|
||||||
|
echo Running cmake for obs-studio %OBSLatestTag% 32-bit...
|
||||||
|
cd ./build32
|
||||||
|
cmake -G "Visual Studio 14 2015" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
||||||
|
echo:
|
||||||
|
echo:
|
||||||
|
echo Running cmake for obs-studio %OBSLatestTag% 64-bit...
|
||||||
|
cd ../build64
|
||||||
|
cmake -G "Visual Studio 14 2015 Win64" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
||||||
|
echo:
|
||||||
|
echo:
|
||||||
|
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"
|
||||||
|
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"
|
||||||
|
cd ..
|
||||||
|
git describe --tags --abbrev=0 > C:\projects\obs-studio-last-tag-built.txt
|
||||||
|
set /p OBSLastTagBuilt=<C:\projects\obs-studio-last-tag-built.txt
|
||||||
|
) else (
|
||||||
|
echo Last OBS tag built is: %OBSLastTagBuilt%
|
||||||
|
echo No need to rebuild OBS.
|
||||||
|
)
|
61
CI/install-dependencies-macos.sh
Executable file
61
CI/install-dependencies-macos.sh
Executable file
@ -0,0 +1,61 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
echo "[obs-websocket] Updating Homebrew.."
|
||||||
|
brew update >/dev/null
|
||||||
|
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
|
||||||
|
echo "[obs-websocket] Installing obs-websocket dependency 'QT 5.10.1'.."
|
||||||
|
# =!= NOTICE =!=
|
||||||
|
# When building QT5 from sources on macOS 10.13+, use local qt5 formula:
|
||||||
|
# brew install ./CI/macos/qt.rb
|
||||||
|
# Pouring from the bottle is much quicker though, so use bottle for now.
|
||||||
|
# =!= NOTICE =!=
|
||||||
|
|
||||||
|
brew install https://gist.githubusercontent.com/DDRBoxman/b3956fab6073335a4bf151db0dcbd4ad/raw/ed1342a8a86793ea8c10d8b4d712a654da121ace/qt.rb
|
||||||
|
|
||||||
|
# Pin this version of QT5 to avoid `brew upgrade`
|
||||||
|
# upgrading it to incompatible version
|
||||||
|
brew pin qt
|
||||||
|
|
||||||
|
# Fetch and install Packages app
|
||||||
|
# =!= NOTICE =!=
|
||||||
|
# Installs a LaunchDaemon under /Library/LaunchDaemons/fr.whitebox.packages.build.dispatcher.plist
|
||||||
|
# =!= NOTICE =!=
|
||||||
|
|
||||||
|
HAS_PACKAGES=$(type packagesbuild 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "${HAS_PACKAGES}" = "" ]; then
|
||||||
|
echo "[obs-websocket] Installing Packaging app (might require password due to 'sudo').."
|
||||||
|
curl -o './Packages.pkg' --retry-connrefused -s --retry-delay 1 'https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg'
|
||||||
|
sudo installer -pkg ./Packages.pkg -target /
|
||||||
|
fi
|
19
CI/install-dependencies-xenial.sh
Executable file
19
CI/install-dependencies-xenial.sh
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
add-apt-repository -y ppa:obsproject/obs-studio
|
||||||
|
apt-get -qq update
|
||||||
|
|
||||||
|
apt-get install -y \
|
||||||
|
libc-dev-bin \
|
||||||
|
libc6-dev git \
|
||||||
|
build-essential \
|
||||||
|
checkinstall \
|
||||||
|
cmake \
|
||||||
|
obs-studio \
|
||||||
|
libqt5websockets5-dev
|
||||||
|
|
||||||
|
# Dirty hack
|
||||||
|
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
|
||||||
|
|
||||||
|
ldconfig
|
6
CI/install-setup-qt.cmd
Normal file
6
CI/install-setup-qt.cmd
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
REM Set default values to use AppVeyor's built-in Qt.
|
||||||
|
set QTDIR32=C:\Qt\5.10.1\msvc2015
|
||||||
|
set QTDIR64=C:\Qt\5.10.1\msvc2015_64
|
||||||
|
set QTCompileVersion=5.10.1
|
@ -1,9 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
echo "[obs-websocket] Running CMake.."
|
|
||||||
mkdir build && cd build
|
|
||||||
cmake -DCMAKE_INSTALL_PREFIX=/usr -DUSE_UBUNTU_FIX=true ..
|
|
||||||
|
|
||||||
echo "[obs-websocket] Building plugin.."
|
|
||||||
make -j4
|
|
@ -1,26 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
echo "[obs-websocket] Installing obs-studio PPA and updates.."
|
|
||||||
sudo add-apt-repository -y ppa:obsproject/obs-studio
|
|
||||||
sudo apt-get -qq update
|
|
||||||
|
|
||||||
echo "[obs-websocket] Installing obs-studio and dependencies.."
|
|
||||||
sudo apt-get install -y \
|
|
||||||
libc-dev-bin \
|
|
||||||
libc6-dev git \
|
|
||||||
build-essential \
|
|
||||||
checkinstall \
|
|
||||||
cmake \
|
|
||||||
obs-studio \
|
|
||||||
qtbase5-dev
|
|
||||||
|
|
||||||
echo "[obs-websocket] Installed OBS Version: $(obs --version)"
|
|
||||||
|
|
||||||
ls /usr/include/
|
|
||||||
ls /usr/include/obs/
|
|
||||||
|
|
||||||
# Dirty hack
|
|
||||||
sudo wget -O /usr/include/obs/obs-frontend-api.h https://raw.githubusercontent.com/obsproject/obs-studio/27.0.0/UI/obs-frontend-api/obs-frontend-api.h
|
|
||||||
|
|
||||||
sudo ldconfig
|
|
@ -1,24 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
export GIT_HASH=$(git rev-parse --short HEAD)
|
|
||||||
export PKG_VERSION="1-$GIT_HASH-$BRANCH_SHORT_NAME-git"
|
|
||||||
|
|
||||||
if [[ $BRANCH_FULL_NAME =~ ^refs/tags/ ]]; then
|
|
||||||
export PKG_VERSION="$BRANCH_SHORT_NAME"
|
|
||||||
echo "[obs-websocket] Branch is a tag. Setting version to $PKG_VERSION."
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd ./build
|
|
||||||
|
|
||||||
PAGER="cat" sudo checkinstall -y --type=debian --fstrans=no --nodoc \
|
|
||||||
--backup=no --deldoc=yes --install=no \
|
|
||||||
--pkgname=obs-websocket --pkgversion="$PKG_VERSION" \
|
|
||||||
--pkglicense="GPLv2.0" --maintainer="stephane.lepin@gmail.com" \
|
|
||||||
--pkggroup="video" \
|
|
||||||
--pkgsource="https://github.com/Palakis/obs-websocket" \
|
|
||||||
--requires="obs-studio \(\>= 26.1.0\), libqt5core5a, libqt5widgets5, qt5-image-formats-plugins" \
|
|
||||||
--pakdir="../package"
|
|
||||||
|
|
||||||
sudo chmod ao+r ../package/*
|
|
@ -1,5 +0,0 @@
|
|||||||
brew "jack"
|
|
||||||
brew "speexdsp"
|
|
||||||
brew "cmake"
|
|
||||||
brew "freetype"
|
|
||||||
brew "fdk-aac"
|
|
@ -1,39 +0,0 @@
|
|||||||
#!/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
|
|
||||||
|
|
||||||
# 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 .. \
|
|
||||||
-DQTDIR=/tmp/obsdeps \
|
|
||||||
-DDepsPath=/tmp/obsdeps \
|
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
|
|
||||||
-DDISABLE_PLUGINS=true \
|
|
||||||
-DENABLE_SCRIPTING=0 \
|
|
||||||
-DCMAKE_PREFIX_PATH=/tmp/obsdeps/lib/cmake \
|
|
||||||
&& make -j4
|
|
@ -1,57 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
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 Brew Deps
|
|
||||||
echo "[obs-websocket] Updating Homebrew.."
|
|
||||||
brew update >/dev/null
|
|
||||||
echo "[obs-websocket] Checking installed Homebrew formulas.."
|
|
||||||
|
|
||||||
if [ -d /usr/local/opt/openssl@1.0.2t ]; then
|
|
||||||
brew uninstall openssl@1.0.2t
|
|
||||||
brew untap local/openssl
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d /usr/local/opt/python@2.7.17 ]; then
|
|
||||||
brew uninstall python@2.7.17
|
|
||||||
brew untap local/python2
|
|
||||||
fi
|
|
||||||
|
|
||||||
brew bundle --file ./CI/macos/Brewfile
|
|
||||||
|
|
||||||
# Fetch and install Packages app
|
|
||||||
# =!= NOTICE =!=
|
|
||||||
# Installs a LaunchDaemon under /Library/LaunchDaemons/fr.whitebox.packages.build.dispatcher.plist
|
|
||||||
# =!= NOTICE =!=
|
|
||||||
|
|
||||||
HAS_PACKAGES=$(type packagesbuild 2>/dev/null)
|
|
||||||
|
|
||||||
if [ "${HAS_PACKAGES}" = "" ]; then
|
|
||||||
echo "[obs-websocket] Installing Packaging app (might require password due to 'sudo').."
|
|
||||||
curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg
|
|
||||||
sudo hdiutil attach ./Packages.dmg
|
|
||||||
sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target /
|
|
||||||
fi
|
|
||||||
|
|
||||||
# OBS Deps
|
|
||||||
echo "[obs-websocket] Installing obs-websocket dependency 'OBS Deps ${OBS_DEPS_VERSION}'.."
|
|
||||||
wget --quiet --retry-connrefused --waitretry=1 https://github.com/obsproject/obs-deps/releases/download/${OBS_DEPS_VERSION}/macos-deps-${OBS_DEPS_VERSION}.tar.gz
|
|
||||||
tar -xf ./macos-deps-${OBS_DEPS_VERSION}.tar.gz -C /tmp
|
|
||||||
|
|
||||||
# Qt deps
|
|
||||||
echo "[obs-websocket] Installing obs-websocket dependency 'Qt ${QT_VERSION}'.."
|
|
||||||
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${OBS_DEPS_VERSION}/macos-qt-${QT_VERSION}-${OBS_DEPS_VERSION}.tar.gz
|
|
||||||
tar -xf ./macos-qt-${QT_VERSION}-${OBS_DEPS_VERSION}.tar.gz -C "/tmp"
|
|
||||||
xattr -r -d com.apple.quarantine /tmp/obsdeps
|
|
@ -12,6 +12,123 @@
|
|||||||
<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>
|
||||||
@ -514,11 +631,11 @@
|
|||||||
<key>CONCLUSION_ACTION</key>
|
<key>CONCLUSION_ACTION</key>
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
<key>IDENTIFIER</key>
|
<key>IDENTIFIER</key>
|
||||||
<string>fr.palakis.obs-websocket</string>
|
<string>fr.palakis.obswebsocket</string>
|
||||||
<key>OVERWRITE_PERMISSIONS</key>
|
<key>OVERWRITE_PERMISSIONS</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>VERSION</key>
|
<key>VERSION</key>
|
||||||
<string>4.9.1</string>
|
<string>4.5.1</string>
|
||||||
</dict>
|
</dict>
|
||||||
<key>PROJECT_COMMENTS</key>
|
<key>PROJECT_COMMENTS</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
OSTYPE=$(uname)
|
|
||||||
|
|
||||||
if [ "${OSTYPE}" != "Darwin" ]; then
|
|
||||||
echo "[obs-websocket - Error] macOS package script can be run on Darwin-type OS only."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[obs-websocket] Preparing package build"
|
|
||||||
|
|
||||||
GIT_HASH=$(git rev-parse --short HEAD)
|
|
||||||
GIT_BRANCH_OR_TAG=$(git name-rev --name-only HEAD | awk -F/ '{print $NF}')
|
|
||||||
|
|
||||||
VERSION="$GIT_HASH-$GIT_BRANCH_OR_TAG"
|
|
||||||
|
|
||||||
FILENAME_UNSIGNED="obs-websocket-$VERSION-Unsigned.pkg"
|
|
||||||
FILENAME="obs-websocket-$VERSION.pkg"
|
|
||||||
|
|
||||||
echo "[obs-websocket] Modifying obs-websocket.so linking"
|
|
||||||
install_name_tool \
|
|
||||||
-change /tmp/obsdeps/lib/QtWidgets.framework/Versions/5/QtWidgets \
|
|
||||||
@executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets \
|
|
||||||
-change /tmp/obsdeps/lib/QtGui.framework/Versions/5/QtGui \
|
|
||||||
@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui \
|
|
||||||
-change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore \
|
|
||||||
@executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore \
|
|
||||||
./build/obs-websocket.so
|
|
||||||
|
|
||||||
# Check if replacement worked
|
|
||||||
echo "[obs-websocket] Dependencies for obs-websocket"
|
|
||||||
otool -L ./build/obs-websocket.so
|
|
||||||
|
|
||||||
if [[ "$RELEASE_MODE" == "True" ]]; then
|
|
||||||
echo "[obs-websocket] Signing plugin binary: obs-websocket.so"
|
|
||||||
codesign --sign "$CODE_SIGNING_IDENTITY" ./build/obs-websocket.so
|
|
||||||
else
|
|
||||||
echo "[obs-websocket] Skipped plugin codesigning"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[obs-websocket] Actual package build"
|
|
||||||
packagesbuild ./CI/macos/obs-websocket.pkgproj
|
|
||||||
|
|
||||||
echo "[obs-websocket] Renaming obs-websocket.pkg to $FILENAME"
|
|
||||||
mv ./release/obs-websocket.pkg ./release/$FILENAME_UNSIGNED
|
|
||||||
|
|
||||||
if [[ "$RELEASE_MODE" == "True" ]]; then
|
|
||||||
echo "[obs-websocket] Signing installer: $FILENAME"
|
|
||||||
productsign \
|
|
||||||
--sign "$INSTALLER_SIGNING_IDENTITY" \
|
|
||||||
./release/$FILENAME_UNSIGNED \
|
|
||||||
./release/$FILENAME
|
|
||||||
rm ./release/$FILENAME_UNSIGNED
|
|
||||||
|
|
||||||
echo "[obs-websocket] Submitting installer $FILENAME for notarization"
|
|
||||||
zip -r ./release/$FILENAME.zip ./release/$FILENAME
|
|
||||||
UPLOAD_RESULT=$(xcrun altool \
|
|
||||||
--notarize-app \
|
|
||||||
--primary-bundle-id "fr.palakis.obs-websocket" \
|
|
||||||
--username "$AC_USERNAME" \
|
|
||||||
--password "$AC_PASSWORD" \
|
|
||||||
--asc-provider "$AC_PROVIDER_SHORTNAME" \
|
|
||||||
--file "./release/$FILENAME.zip")
|
|
||||||
rm ./release/$FILENAME.zip
|
|
||||||
|
|
||||||
REQUEST_UUID=$(echo $UPLOAD_RESULT | awk -F ' = ' '/RequestUUID/ {print $2}')
|
|
||||||
echo "Request UUID: $REQUEST_UUID"
|
|
||||||
|
|
||||||
echo "[obs-websocket] Wait for notarization result"
|
|
||||||
# Pieces of code borrowed from rednoah/notarized-app
|
|
||||||
while sleep 30 && date; do
|
|
||||||
CHECK_RESULT=$(xcrun altool \
|
|
||||||
--notarization-info "$REQUEST_UUID" \
|
|
||||||
--username "$AC_USERNAME" \
|
|
||||||
--password "$AC_PASSWORD" \
|
|
||||||
--asc-provider "$AC_PROVIDER_SHORTNAME")
|
|
||||||
echo $CHECK_RESULT
|
|
||||||
|
|
||||||
if ! grep -q "Status: in progress" <<< "$CHECK_RESULT"; then
|
|
||||||
echo "[obs-websocket] Staple ticket to installer: $FILENAME"
|
|
||||||
xcrun stapler staple ./release/$FILENAME
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo "[obs-websocket] Skipped installer codesigning and notarization"
|
|
||||||
fi
|
|
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
|
72
CI/package-macos.sh
Executable file
72
CI/package-macos.sh
Executable file
@ -0,0 +1,72 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
OSTYPE=$(uname)
|
||||||
|
|
||||||
|
if [ "${OSTYPE}" != "Darwin" ]; then
|
||||||
|
echo "[obs-websocket - Error] macOS package script can be run on Darwin-type OS only."
|
||||||
|
exit 1
|
||||||
|
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 WS_LIB="/usr/local/opt/qt/lib/QtWebSockets.framework/QtWebSockets"
|
||||||
|
export NET_LIB="/usr/local/opt/qt/lib/QtNetwork.framework/QtNetwork"
|
||||||
|
|
||||||
|
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-$GIT_BRANCH_OR_TAG"
|
||||||
|
export LATEST_VERSION="$GIT_BRANCH_OR_TAG"
|
||||||
|
|
||||||
|
export FILENAME="obs-websocket-$VERSION.pkg"
|
||||||
|
export LATEST_FILENAME="obs-websocket-latest-$LATEST_VERSION.pkg"
|
||||||
|
|
||||||
|
echo "[obs-websocket] Copying Qt dependencies"
|
||||||
|
if [ ! -f ./build/$(basename $WS_LIB) ]; then cp $WS_LIB ./build; fi
|
||||||
|
if [ ! -f ./build/$(basename $NET_LIB) ]; then cp $NET_LIB ./build; fi
|
||||||
|
|
||||||
|
chmod +rw ./build/QtWebSockets ./build/QtNetwork
|
||||||
|
|
||||||
|
echo "[obs-websocket] Modifying QtNetwork"
|
||||||
|
install_name_tool \
|
||||||
|
-id @rpath/QtNetwork \
|
||||||
|
-change /usr/local/opt/qt/lib/QtNetwork.framework/Versions/5/QtNetwork @rpath/QtNetwork \
|
||||||
|
-change $QT_CELLAR_PREFIX/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
|
||||||
|
./build/QtNetwork
|
||||||
|
|
||||||
|
echo "[obs-websocket] Modifying QtWebSockets"
|
||||||
|
install_name_tool \
|
||||||
|
-id @rpath/QtWebSockets \
|
||||||
|
-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 "[obs-websocket] 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/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/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
|
||||||
|
./build/obs-websocket.so
|
||||||
|
|
||||||
|
# Check if replacement worked
|
||||||
|
echo "[obs-websocket] Dependencies for QtNetwork"
|
||||||
|
otool -L ./build/QtNetwork
|
||||||
|
echo "[obs-websocket] Dependencies for QtWebSockets"
|
||||||
|
otool -L ./build/QtWebSockets
|
||||||
|
echo "[obs-websocket] Dependencies for obs-websocket"
|
||||||
|
otool -L ./build/obs-websocket.so
|
||||||
|
|
||||||
|
chmod -w ./build/QtWebSockets ./build/QtNetwork
|
||||||
|
|
||||||
|
echo "[obs-websocket] Actual package build"
|
||||||
|
packagesbuild ./CI/macos/obs-websocket.pkgproj
|
||||||
|
|
||||||
|
echo "[obs-websocket] Renaming obs-websocket.pkg to $FILENAME"
|
||||||
|
mv ./release/obs-websocket.pkg ./release/$FILENAME
|
||||||
|
cp ./release/$FILENAME ./release/$LATEST_FILENAME
|
24
CI/package-xenial.sh
Executable file
24
CI/package-xenial.sh
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd /root/obs-websocket
|
||||||
|
|
||||||
|
export GIT_HASH=$(git rev-parse --short HEAD)
|
||||||
|
export PKG_VERSION="1-$GIT_HASH-$TRAVIS_BRANCH-git"
|
||||||
|
|
||||||
|
if [ -n "${TRAVIS_TAG}" ]; then
|
||||||
|
export PKG_VERSION="$TRAVIS_TAG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd /root/obs-websocket/build
|
||||||
|
|
||||||
|
PAGER=cat checkinstall -y --type=debian --fstrans=no --nodoc \
|
||||||
|
--backup=no --deldoc=yes --install=no \
|
||||||
|
--pkgname=obs-websocket --pkgversion="$PKG_VERSION" \
|
||||||
|
--pkglicense="GPLv2.0" --maintainer="contact@slepin.fr" \
|
||||||
|
--requires="libqt5websockets5" --pkggroup="video" \
|
||||||
|
--pkgsource="https://github.com/Palakis/obs-websocket" \
|
||||||
|
--pakdir="/package"
|
||||||
|
|
||||||
|
chmod ao+r /package/*
|
@ -1,7 +0,0 @@
|
|||||||
if exist %DEPS_BASE_PATH% (
|
|
||||||
echo "OBS dependencies found. Download skipped."
|
|
||||||
) else (
|
|
||||||
echo "OBS dependencies not found. Downloading..."
|
|
||||||
curl -o %DEPS_BASE_PATH%.zip -kLO https://cdn-fastly.obsproject.com/downloads/dependencies2019.zip -f --retry 5 -C -
|
|
||||||
7z x %DEPS_BASE_PATH%.zip -o%DEPS_BASE_PATH%
|
|
||||||
)
|
|
@ -1,8 +0,0 @@
|
|||||||
if exist %QT_BASE_DIR% (
|
|
||||||
echo "Qt directory found. Download skipped."
|
|
||||||
) else (
|
|
||||||
echo "Qt directory not found. Downloading..."
|
|
||||||
curl -kLO https://tt2468.net/dl/Qt_5.15.2.7z -f --retry 5 -C -
|
|
||||||
7z x Qt_5.15.2.7z -o%QT_BASE_DIR%
|
|
||||||
)
|
|
||||||
dir %QT_BASE_DIR%
|
|
@ -1,12 +0,0 @@
|
|||||||
mkdir package
|
|
||||||
cd package
|
|
||||||
|
|
||||||
git rev-parse --short HEAD > package-version.txt
|
|
||||||
set /p PackageVersion=<package-version.txt
|
|
||||||
del package-version.txt
|
|
||||||
|
|
||||||
REM Package ZIP archive
|
|
||||||
7z a "obs-websocket-%PackageVersion%-Windows.zip" "..\release\*"
|
|
||||||
|
|
||||||
REM Build installer
|
|
||||||
iscc ..\installer\installer.iss /O. /F"obs-websocket-%PackageVersion%-Windows-Installer"
|
|
@ -1,37 +0,0 @@
|
|||||||
|
|
||||||
@echo off
|
|
||||||
SETLOCAL EnableDelayedExpansion
|
|
||||||
|
|
||||||
REM If obs-studio directory does not exist, clone the git repo
|
|
||||||
if not exist %OBS_PATH% (
|
|
||||||
echo obs-studio directory does not exist
|
|
||||||
git clone https://github.com/obsproject/obs-studio %OBS_PATH%
|
|
||||||
cd /D %OBS_PATH%\
|
|
||||||
git describe --tags --abbrev=0 > "%OBS_PATH%\obs-studio-latest-tag.txt"
|
|
||||||
set /p OBS_LATEST_TAG=<"%OBS_PATH%\obs-studio-latest-tag.txt"
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Prepare OBS Studio builds
|
|
||||||
|
|
||||||
echo Running CMake...
|
|
||||||
cd /D %OBS_PATH%
|
|
||||||
echo git checkout %OBS_LATEST_TAG%
|
|
||||||
git checkout %OBS_LATEST_TAG%
|
|
||||||
echo:
|
|
||||||
|
|
||||||
if not exist build32 mkdir build32
|
|
||||||
if not exist build64 mkdir build64
|
|
||||||
|
|
||||||
echo Running cmake for obs-studio %OBS_LATEST_TAG% 32-bit...
|
|
||||||
cd build32
|
|
||||||
cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DDepsPath="%DEPS_PATH_32%" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
|
||||||
echo:
|
|
||||||
echo:
|
|
||||||
|
|
||||||
echo Running cmake for obs-studio %OBS_LATEST_TAG% 64-bit...
|
|
||||||
cd ..\build64
|
|
||||||
cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DDepsPath="%DEPS_PATH_64%" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
|
||||||
echo:
|
|
||||||
echo:
|
|
||||||
|
|
||||||
dir "%OBS_PATH%\libobs"
|
|
@ -1,7 +0,0 @@
|
|||||||
mkdir build32
|
|
||||||
mkdir build64
|
|
||||||
|
|
||||||
cd build32
|
|
||||||
cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR32%" -DLibObs_DIR="%OBS_PATH%\build32\libobs" -DLIBOBS_INCLUDE_DIR="%OBS_PATH%\libobs" -DLIBOBS_LIB="%OBS_PATH%\build32\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="%OBS_PATH%\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
|
|
||||||
cd ..\build64
|
|
||||||
cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_SYSTEM_VERSION=10.0 -DQTDIR="%QTDIR64%" -DLibObs_DIR="%OBS_PATH%\build64\libobs" -DLIBOBS_INCLUDE_DIR="%OBS_PATH%\libobs" -DLIBOBS_LIB="%OBS_PATH%\build64\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="%OBS_PATH%\build64\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
|
|
161
CMakeLists.txt
161
CMakeLists.txt
@ -1,31 +1,23 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.2)
|
||||||
project(obs-websocket VERSION 4.9.1)
|
project(obs-websocket)
|
||||||
|
|
||||||
set(CMAKE_PREFIX_PATH "${QTDIR}")
|
set(CMAKE_PREFIX_PATH "${QTDIR}")
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
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 17)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
add_definitions(-DASIO_STANDALONE)
|
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
|
|
||||||
set(CMAKE_CXX_FLAGS "-mfpu=neon")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (WIN32 OR APPLE)
|
if (WIN32 OR APPLE)
|
||||||
include(external/FindLibObs.cmake)
|
include(external/FindLibObs.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(LibObs REQUIRED)
|
find_package(LibObs REQUIRED)
|
||||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets)
|
find_package(Qt5Core REQUIRED)
|
||||||
|
find_package(Qt5WebSockets REQUIRED)
|
||||||
|
find_package(Qt5Widgets REQUIRED)
|
||||||
|
|
||||||
set(obs-websocket_SOURCES
|
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
|
||||||
@ -36,32 +28,20 @@ set(obs-websocket_SOURCES
|
|||||||
src/WSRequestHandler_SceneItems.cpp
|
src/WSRequestHandler_SceneItems.cpp
|
||||||
src/WSRequestHandler_Sources.cpp
|
src/WSRequestHandler_Sources.cpp
|
||||||
src/WSRequestHandler_Streaming.cpp
|
src/WSRequestHandler_Streaming.cpp
|
||||||
src/WSRequestHandler_VirtualCam.cpp
|
|
||||||
src/WSRequestHandler_StudioMode.cpp
|
src/WSRequestHandler_StudioMode.cpp
|
||||||
src/WSRequestHandler_Transitions.cpp
|
src/WSRequestHandler_Transitions.cpp
|
||||||
src/WSRequestHandler_Outputs.cpp
|
|
||||||
src/WSRequestHandler_MediaControl.cpp
|
|
||||||
src/WSEvents.cpp
|
src/WSEvents.cpp
|
||||||
src/Config.cpp
|
src/Config.cpp
|
||||||
src/Utils.cpp
|
src/Utils.cpp
|
||||||
src/rpc/RpcRequest.cpp
|
|
||||||
src/rpc/RpcResponse.cpp
|
|
||||||
src/rpc/RpcEvent.cpp
|
|
||||||
src/protocol/OBSRemoteProtocol.cpp
|
|
||||||
src/forms/settings-dialog.cpp)
|
src/forms/settings-dialog.cpp)
|
||||||
|
|
||||||
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
|
||||||
src/Utils.h
|
src/Utils.h
|
||||||
src/rpc/RpcRequest.h
|
|
||||||
src/rpc/RpcResponse.h
|
|
||||||
src/rpc/RpcEvent.h
|
|
||||||
src/protocol/OBSRemoteProtocol.h
|
|
||||||
src/forms/settings-dialog.h)
|
src/forms/settings-dialog.h)
|
||||||
|
|
||||||
# --- Platform-independent build settings ---
|
# --- Platform-independent build settings ---
|
||||||
@ -72,31 +52,24 @@ add_library(obs-websocket MODULE
|
|||||||
include_directories(
|
include_directories(
|
||||||
"${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api"
|
"${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api"
|
||||||
${Qt5Core_INCLUDES}
|
${Qt5Core_INCLUDES}
|
||||||
${Qt5Widgets_INCLUDES}
|
${Qt5WebSockets_INCLUDES}
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include"
|
${Qt5Widgets_INCLUDES})
|
||||||
"${CMAKE_CURRENT_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)
|
||||||
|
|
||||||
# --- End of section ---
|
# --- End of section ---
|
||||||
|
|
||||||
# --- Windows-specific build settings and tasks ---
|
# --- Windows-specific build settings and tasks ---
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(OBS_FRONTEND_LIB "NOTFOUND" CACHE FILEPATH "OBS frontend library")
|
if(NOT DEFINED OBS_FRONTEND_LIB)
|
||||||
if(OBS_FRONTEND_LIB STREQUAL "NOTFOUND")
|
set(OBS_FRONTEND_LIB "OBS_FRONTEND_LIB-NOTFOUND" CACHE FILEPATH "OBS frontend library")
|
||||||
message(FATAL_ERROR "OBS Frontend API library not found!")
|
message(FATAL_ERROR "Could not find OBS Frontend API's library !")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
# Enable Multicore Builds and disable FH4 (to not depend on VCRUNTIME140_1.DLL)
|
|
||||||
add_definitions(/MP /d2FH4-)
|
|
||||||
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")
|
||||||
@ -117,70 +90,63 @@ if(WIN32)
|
|||||||
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 or RelWithDebInfo, package release files
|
# If config is Release, package release files
|
||||||
COMMAND if $<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>==1 (
|
COMMAND if $<CONFIG:Release>==1 (
|
||||||
"${CMAKE_COMMAND}" -E make_directory
|
"${CMAKE_COMMAND}" -E make_directory
|
||||||
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}"
|
|
||||||
)
|
|
||||||
|
|
||||||
COMMAND if $<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>==1 (
|
|
||||||
"${CMAKE_COMMAND}" -E copy_directory
|
|
||||||
"${PROJECT_SOURCE_DIR}/data"
|
|
||||||
"${RELEASE_DIR}/data/obs-plugins/obs-websocket"
|
"${RELEASE_DIR}/data/obs-plugins/obs-websocket"
|
||||||
)
|
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
||||||
|
|
||||||
COMMAND if $<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>==1 (
|
COMMAND if $<CONFIG:Release>==1 ("${CMAKE_COMMAND}" -E copy_directory
|
||||||
"${CMAKE_COMMAND}" -E copy
|
"${PROJECT_SOURCE_DIR}/data"
|
||||||
|
"${RELEASE_DIR}/data/obs-plugins/obs-websocket")
|
||||||
|
|
||||||
|
COMMAND if $<CONFIG:Release>==1 ("${CMAKE_COMMAND}" -E copy
|
||||||
"$<TARGET_FILE:obs-websocket>"
|
"$<TARGET_FILE:obs-websocket>"
|
||||||
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}"
|
"${QTDIR}/bin/Qt5WebSockets.dll"
|
||||||
)
|
"${QTDIR}/bin/Qt5Network.dll"
|
||||||
|
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
||||||
|
|
||||||
# In Release or RelWithDebInfo mode, copy Qt image format plugins
|
# If config is RelWithDebInfo, package release files
|
||||||
COMMAND if $<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>==1 (
|
|
||||||
"${CMAKE_COMMAND}" -E make_directory
|
|
||||||
"${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats"
|
|
||||||
)
|
|
||||||
COMMAND if $<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>==1 (
|
|
||||||
"${CMAKE_COMMAND}" -E copy
|
|
||||||
"${QTDIR}/plugins/imageformats/qicns.dll"
|
|
||||||
"${QTDIR}/plugins/imageformats/qico.dll"
|
|
||||||
"${QTDIR}/plugins/imageformats/qjpeg.dll"
|
|
||||||
"${QTDIR}/plugins/imageformats/qtiff.dll"
|
|
||||||
"${QTDIR}/plugins/imageformats/qwbmp.dll"
|
|
||||||
"${QTDIR}/plugins/imageformats/qwebp.dll"
|
|
||||||
"${RELEASE_DIR}/bin/${ARCH_NAME}/imageformats"
|
|
||||||
)
|
|
||||||
|
|
||||||
# If config is RelWithDebInfo, package PDB file for target
|
|
||||||
COMMAND if $<CONFIG:RelWithDebInfo>==1 (
|
COMMAND if $<CONFIG:RelWithDebInfo>==1 (
|
||||||
"${CMAKE_COMMAND}" -E copy
|
"${CMAKE_COMMAND}" -E make_directory
|
||||||
"$<TARGET_PDB_FILE:obs-websocket>"
|
"${RELEASE_DIR}/data/obs-plugins/obs-websocket"
|
||||||
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}"
|
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
||||||
)
|
|
||||||
|
|
||||||
# In the Debug configuration, copy to obs-studio dev environment for immediate testing
|
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>"
|
||||||
|
"${QTDIR}/bin/Qt5WebSockets.dll"
|
||||||
|
"${QTDIR}/bin/Qt5Network.dll"
|
||||||
|
"${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}")
|
||||||
|
|
||||||
|
# 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>"
|
||||||
"${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$<CONFIG>/obs-plugins/${ARCH_NAME}"
|
"${QTDIR}/bin/Qt5WebSocketsd.dll"
|
||||||
)
|
"${QTDIR}/bin/Qt5Networkd.dll"
|
||||||
|
"${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$<CONFIG>/obs-plugins/${ARCH_NAME}")
|
||||||
|
|
||||||
COMMAND if $<CONFIG:Debug>==1 (
|
COMMAND if $<CONFIG:Debug>==1 (
|
||||||
"${CMAKE_COMMAND}" -E copy
|
"${CMAKE_COMMAND}" -E copy
|
||||||
"$<TARGET_PDB_FILE:obs-websocket>"
|
"$<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 (
|
||||||
"${CMAKE_COMMAND}" -E make_directory
|
"${CMAKE_COMMAND}" -E make_directory
|
||||||
"${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$<CONFIG>/data/obs-plugins/obs-websocket"
|
"${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$<CONFIG>/data/obs-plugins/obs-websocket")
|
||||||
)
|
|
||||||
|
|
||||||
COMMAND if $<CONFIG:Debug>==1 (
|
COMMAND if $<CONFIG:Debug>==1 (
|
||||||
"${CMAKE_COMMAND}" -E copy_directory
|
"${CMAKE_COMMAND}" -E copy_directory
|
||||||
"${PROJECT_SOURCE_DIR}/data"
|
"${PROJECT_SOURCE_DIR}/data"
|
||||||
"${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$<CONFIG>/data/obs-plugins/obs-websocket"
|
"${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$<CONFIG>/data/obs-plugins/obs-websocket")
|
||||||
)
|
|
||||||
)
|
)
|
||||||
# --- End of sub-section ---
|
# --- End of sub-section ---
|
||||||
|
|
||||||
@ -189,30 +155,23 @@ endif()
|
|||||||
|
|
||||||
# --- Linux-specific build settings and tasks ---
|
# --- Linux-specific build settings and tasks ---
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
include(GNUInstallDirs)
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||||
|
|
||||||
set_target_properties(obs-websocket PROPERTIES PREFIX "")
|
set_target_properties(obs-websocket PROPERTIES PREFIX "")
|
||||||
target_link_libraries(obs-websocket obs-frontend-api)
|
target_link_libraries(obs-websocket
|
||||||
|
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)
|
||||||
|
|
||||||
set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
|
install(TARGETS obs-websocket
|
||||||
OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/obs-plugins")
|
||||||
GROUP_READ GROUP_WRITE GROUP_EXECUTE
|
# Dirty fix for Ubuntu
|
||||||
WORLD_READ WORLD_EXECUTE
|
install(TARGETS obs-websocket
|
||||||
)
|
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/${UNAME_MACHINE}-linux-gnu/obs-plugins")
|
||||||
|
|
||||||
if(${USE_UBUNTU_FIX})
|
|
||||||
install(TARGETS obs-websocket LIBRARY
|
|
||||||
DESTINATION "/usr/lib/obs-plugins"
|
|
||||||
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
|
|
||||||
endif()
|
|
||||||
install(TARGETS obs-websocket LIBRARY
|
|
||||||
DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins"
|
|
||||||
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
|
|
||||||
|
|
||||||
install(FILES ${locale_files}
|
install(FILES ${locale_files}
|
||||||
DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/obs/obs-plugins/obs-websocket/locale")
|
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/obs-websocket/locale")
|
||||||
endif()
|
endif()
|
||||||
# --- End of section ---
|
# --- End of section ---
|
||||||
|
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
# Contributing to obs-websocket
|
|
||||||
|
|
||||||
## Translating obs-websocket to your language
|
|
||||||
|
|
||||||
Localization happens on [Crowdin](https://crowdin.com/project/obs-websocket)
|
|
||||||
|
|
||||||
## Branches
|
|
||||||
|
|
||||||
**Development happens on `4.x-current`**
|
|
||||||
|
|
||||||
## Writing code for obs-websocket
|
|
||||||
|
|
||||||
### Code Formatting Guidelines
|
|
||||||
|
|
||||||
* Function and variable names: snake_case for C names, camelCase for C++ method names
|
|
||||||
|
|
||||||
* Request and Event names should use MixedCaps names
|
|
||||||
|
|
||||||
* Request and Event json properties should use camelCase. For more detailed info on property naming, see [Google's JSON Style Guide](https://google.github.io/styleguide/jsoncstyleguide.xml)
|
|
||||||
|
|
||||||
* Code is indented with Tabs. Assume they are 8 columns wide
|
|
||||||
|
|
||||||
* 80 columns max code width. (Docs can be larger)
|
|
||||||
|
|
||||||
* New and updated requests/events must always come with accompanying documentation comments (see existing protocol elements for examples).
|
|
||||||
These are required to automatically generate the [protocol specification document](docs/generated/protocol.md).
|
|
||||||
|
|
||||||
### Code Best-Practices
|
|
||||||
|
|
||||||
* Favor return-early code and avoid wrapping huge portions of code in conditionals. As an example, this:
|
|
||||||
```cpp
|
|
||||||
if (success) {
|
|
||||||
return req->SendOKResponse();
|
|
||||||
} else {
|
|
||||||
return req->SendErrorResponse("something went wrong");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
is better like this:
|
|
||||||
```cpp
|
|
||||||
if (!success) {
|
|
||||||
return req->SendErrorResponse("something went wrong");
|
|
||||||
}
|
|
||||||
return req->SendOKResponse();
|
|
||||||
```
|
|
||||||
|
|
||||||
* Some example common response/request property names are:
|
|
||||||
* `sceneName` - The name of a scene
|
|
||||||
* `sourceName` - The name of a source
|
|
||||||
* `fromScene` - From a scene - scene name
|
|
||||||
|
|
||||||
### Commit Guidelines
|
|
||||||
|
|
||||||
* Commits follow the 50/72 standard:
|
|
||||||
* 50 characters max for the commit title (excluding scope name)
|
|
||||||
* One empty line after the title
|
|
||||||
* Description wrapped to 72 columns max width per line.
|
|
||||||
|
|
||||||
* Commit titles:
|
|
||||||
* Use present tense
|
|
||||||
* Prefix the title with a "scope" name
|
|
||||||
* e.g: "CI: fix wrong behaviour when packaging for OS X"
|
|
||||||
* Typical scopes: CI, General, Requests, Events, Server
|
|
||||||
|
|
||||||
**Example commit:**
|
|
||||||
|
|
||||||
```
|
|
||||||
Requests: Add GetTransitionPosition
|
|
||||||
|
|
||||||
Adds a new request called `GetTransitionPosition` which gets the current
|
|
||||||
transition's state from 0.0f to 1.0f. Works with both auto and manual
|
|
||||||
transitions.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pull Requests
|
|
||||||
|
|
||||||
* Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`).
|
|
||||||
* Start your work in a newly named branch based on the upstream main one (e.g.: `feature/cool-new-feature`, `bugfix/fix-palakis-mistakes`, ...)
|
|
||||||
|
|
||||||
* Only open a pull request if you are ready to show off your work.
|
|
||||||
|
|
||||||
* If your work is not done yet, but for any reason you need to PR it (like collecting discussions, testing with CI, getting testers),
|
|
||||||
create it as a Draft Pull Request (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request").
|
|
124
README.md
124
README.md
@ -1,133 +1,75 @@
|
|||||||
# obs-websocket
|
obs-websocket
|
||||||
|
==============
|
||||||
|
Remote control of OBS Studio made easy.
|
||||||
|
|
||||||
<p align="center">
|
Follow the project on Twitter for news & updates : [@obswebsocket](https://twitter.com/obswebsocket)
|
||||||
<img src="/.github/images/obsws_logo.png" width=150 align="center">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
[](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current)
|
|
||||||
[](https://www.codefactor.io/repository/github/palakis/obs-websocket)
|
|
||||||
[](https://twitter.com/LePalakis)
|
|
||||||
[](https://discord.gg/WBaSQ3A)
|
|
||||||
[](https://opencollective.com/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, MacOS, and Linux are available in the [Releases](https://github.com/Palakis/obs-websocket/releases) section.
|
|
||||||
|
|
||||||
### Homebrew
|
|
||||||
|
|
||||||
If you're using MacOS you can use Homebrew for installation as well:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
brew install obs-websocket
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using obs-websocket
|
## Using obs-websocket
|
||||||
|
A web client and frontend made by [t2t2](https://github.com/t2t2/obs-tablet-remote) (compatible with tablets and other touch interfaces) is available here : http://t2t2.github.io/obs-tablet-remote/
|
||||||
Here is a list of available web clients: (compatible with tablets and other touch interfaces)
|
|
||||||
|
|
||||||
- [Niek/obs-web](https://github.com/Niek/obs-web)
|
|
||||||
- [t2t2/obs-tablet-remote](https://github.com/t2t2/obs-tablet-remote)
|
|
||||||
|
|
||||||
It is **highly recommended** to protect obs-websocket with a password against unauthorized control. To do this, open the "Websocket server settings" dialog under OBS' "Tools" menu. In the settings dialogs, you can enable or disable authentication and set a password for it.
|
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
|
- 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 :
|
||||||
- Javascript (browser & nodejs): [obs-websocket-js](https://github.com/haganbmj/obs-websocket-js) by Brendan Hagan
|
- Javascript (browser & nodejs): [obs-websocket-js](https://github.com/haganbmj/obs-websocket-js) by Brendan Hagan
|
||||||
- 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
|
||||||
- Python 3.6+ with asyncio: [simpleobsws](https://github.com/IRLToolkit/simpleobsws) by tt2468
|
|
||||||
- Java 8+: [obs-websocket-java](https://github.com/Twasi/websocket-obs-java) by TwasiNET
|
|
||||||
- Java 11+: [obs-java-client](https://github.com/harm27/obs-java-client) by harm27
|
|
||||||
- Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf
|
|
||||||
- Rust: [obws](https://github.com/dnaka91/obws) by dnaka91
|
|
||||||
- Dart: [obs_websocket](https://pub.dev/packages/obs_websocket) by faithoflifedev
|
|
||||||
- HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468
|
|
||||||
- CLI: [obs-cli](https://github.com/leafac/obs-cli) by leafac
|
|
||||||
|
|
||||||
I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop a message in `#project-showoff` in the [discord server!](https://discord.gg/WBaSQ3A)
|
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` !
|
||||||
|
|
||||||
### Securing obs-websocket (via TLS/SSL)
|
|
||||||
|
|
||||||
If you are intending to use obs-websocket outside of a LAN environment, it is highly recommended to secure the connection using a tunneling service.
|
|
||||||
|
|
||||||
See the SSL [tunnelling guide](SSL-TUNNELLING.md) for easy instructions on how to encrypt your websocket connection.
|
|
||||||
|
|
||||||
## Compiling obs-websocket
|
## Compiling obs-websocket
|
||||||
|
|
||||||
See the [build instructions](BUILDING.md).
|
See the [build instructions](BUILDING.md).
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
See [the contributing document](/CONTRIBUTING.md)
|
|
||||||
|
|
||||||
## 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:
|
||||||
Thank you so much to all of the contibutors [(here)](https://github.com/Palakis/obs-websocket/graphs/contributors) for your amazing help.
|
- [Brendan H.](https://github.com/haganbmj) : Code contributions and gooder English in the Protocol specification
|
||||||
|
- [Mikhail Swift](https://github.com/mikhailswift) : Code contributions
|
||||||
|
- [Tobias Frahmer](https://github.com/Frahmer) : German localization
|
||||||
|
- [Genture](https://github.com/Genteure) : Simplified Chinese and Traditional Chinese localizations
|
||||||
|
- [Larissa Gabilan](https://github.com/laris151) : Portuguese localization
|
||||||
|
- [Andy Asquelt](https://github.com/asquelt) : Polish localization
|
||||||
|
- [Marcel Haazen](https://github.com/inpothet) : Dutch localization
|
||||||
|
- [Peter Antonvich](https://github.com/pantonvich) : Code contributions
|
||||||
|
- [yinzara](https://github.com/yinzara) : Code contributions
|
||||||
|
- [Chris Angelico](https://github.com/Rosuav) : Code contributions
|
||||||
|
- [Guillaume "Elektordi" Genty](https://github.com/Elektordi) : Code contributions
|
||||||
|
- [Marwin M](https://github.com/dragonbane0) : Code contributions
|
||||||
|
- [Logan S.](https://github.com/lsdaniel) : Code contributions
|
||||||
|
- [RainbowEK](https://github.com/RainbowEK) : Code contributions
|
||||||
|
- [RytoEX](https://github.com/RytoEX) : CI script and code contributions
|
||||||
|
- [Theodore Stoddard](https://github.com/TStod) : Code contributions
|
||||||
|
- [Philip Loche](https://github.com/PicoCentauri) : Code contributions
|
||||||
|
|
||||||
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!
|
||||||
These supporters have contributed financially to the project and made possible the addition of several features into obs-websocket. Many thanks to them!
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[Support Class](http://supportclass.net) designs and develops professional livestreams, with services ranging from broadcast graphics design and integration to event organization, along many other skills.
|
[Support Class](http://supportclass.net) designs and develops professional livestreams, with services ranging from broadcast graphics design and integration to event organization, along many other skills.
|
||||||
|
|
||||||
[](http://supportclass.net)
|
[](http://supportclass.net)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[MediaUnit](http://www.mediaunit.no) is a Norwegian media company developing products and services for the media industry, primarly focused on web and events.
|
[MediaUnit](http://www.mediaunit.no) is a Norwegian media company developing products and services for the media industry, primarly focused on web and events.
|
||||||
|
|
||||||
[](http://www.mediaunit.no/)
|
[](http://www.mediaunit.no/)
|
||||||
|
|
||||||
## Contributors
|
|
||||||
|
|
||||||
### Code Contributors
|
|
||||||
|
|
||||||
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
|
|
||||||
<a href="https://github.com/Palakis/obs-websocket/graphs/contributors"><img src="https://opencollective.com/obs-websocket/contributors.svg?width=890&button=false" /></a>
|
|
||||||
|
|
||||||
### Financial Contributors
|
|
||||||
|
|
||||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/obs-websocket/contribute)]
|
|
||||||
|
|
||||||
#### Individuals
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/obs-websocket"><img src="https://opencollective.com/obs-websocket/individuals.svg?width=890"></a>
|
|
||||||
|
|
||||||
#### Organizations
|
|
||||||
|
|
||||||
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/obs-websocket/contribute)]
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/obs-websocket/organization/0/website"><img src="https://opencollective.com/obs-websocket/organization/0/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/obs-websocket/organization/1/website"><img src="https://opencollective.com/obs-websocket/organization/1/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/obs-websocket/organization/2/website"><img src="https://opencollective.com/obs-websocket/organization/2/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/obs-websocket/organization/3/website"><img src="https://opencollective.com/obs-websocket/organization/3/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/obs-websocket/organization/4/website"><img src="https://opencollective.com/obs-websocket/organization/4/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/obs-websocket/organization/5/website"><img src="https://opencollective.com/obs-websocket/organization/5/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/obs-websocket/organization/6/website"><img src="https://opencollective.com/obs-websocket/organization/6/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/obs-websocket/organization/7/website"><img src="https://opencollective.com/obs-websocket/organization/7/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/obs-websocket/organization/8/website"><img src="https://opencollective.com/obs-websocket/organization/8/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/obs-websocket/organization/9/website"><img src="https://opencollective.com/obs-websocket/organization/9/avatar.svg"></a>
|
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
# Connecting over a TLS/secure connection (or remotely)
|
|
||||||
|
|
||||||
If you want to expose the WebSocket server of obs-websocket over a secure TLS connection (or to connect remotely), the easiest approach is to use a localhost tunneling service like [ngrok](https://ngrok.com/) or [pagekite](https://pagekite.net/).
|
|
||||||
|
|
||||||
**Before doing this, secure the WebSocket server first by enabling authentication with a strong password!**
|
|
||||||
|
|
||||||
**Please bear in mind that doing this will expose your OBS instance to the open Internet and the security risks it implies. *You've been warned!***
|
|
||||||
|
|
||||||
|
|
||||||
## ngrok
|
|
||||||
|
|
||||||
[Install the ngrok CLI tool](https://ngrok.com/download) on a linux OS, then start ngrok bound to port 4444 like this:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ngrok http 4444
|
|
||||||
```
|
|
||||||
|
|
||||||
The ngrok command will output something like this:
|
|
||||||
|
|
||||||
```text
|
|
||||||
ngrok by @inconshreveable
|
|
||||||
|
|
||||||
Tunnel Status online
|
|
||||||
Version 2.0/2.0
|
|
||||||
Web Interface http://127.0.0.1:4040
|
|
||||||
Forwarding http://TUNNEL_ID.ngrok.io -> localhost:4444
|
|
||||||
Forwarding https://TUNNEL_ID.ngrok.io -> localhost:4444
|
|
||||||
```
|
|
||||||
|
|
||||||
Where `TUNNEL_ID` is, as the name implies, the unique name of your ngrok tunnel. You'll get a new one every time you start ngrok.
|
|
||||||
|
|
||||||
Then, use `wss://TUNNEL_ID.ngrok.io` to connect to obs-websocket over TLS.
|
|
||||||
|
|
||||||
See the [ngrok documentation](https://ngrok.com/docs) for more tunneling options and settings.
|
|
||||||
|
|
||||||
|
|
||||||
## PageKite
|
|
||||||
|
|
||||||
[Install the PageKite CLI tool](http://pagekite.net/downloads), then start PageKite bound to port 4444 like this (replace NAME with one of your choosing):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python pagekite.py 4444 NAME.pagekite.me
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, use `wss://NAME.pagekite.me` to connect to obs-websocket over TLS.
|
|
40
appveyor.yml
Normal file
40
appveyor.yml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
environment:
|
||||||
|
CURL_VERSION: 7.39.0
|
||||||
|
|
||||||
|
install:
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
- cd C:\projects\
|
||||||
|
- if not exist dependencies2015.zip curl -kLO https://obsproject.com/downloads/dependencies2015.zip -f --retry 5 -C -
|
||||||
|
- 7z x dependencies2015.zip -odependencies2015
|
||||||
|
- set DepsPath32=%CD%\dependencies2015\win32
|
||||||
|
- set DepsPath64=%CD%\dependencies2015\win64
|
||||||
|
- call C:\projects\obs-websocket\CI\install-setup-qt.cmd
|
||||||
|
- set build_config=RelWithDebInfo
|
||||||
|
- call C:\projects\obs-websocket\CI\install-build-obs.cmd
|
||||||
|
- cd C:\projects\obs-websocket\
|
||||||
|
- mkdir build32
|
||||||
|
- mkdir build64
|
||||||
|
- cd ./build32
|
||||||
|
- 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
|
||||||
|
- 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:
|
||||||
|
- 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\build64\obs-websocket.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
|
|
||||||
|
before_deploy:
|
||||||
|
- 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:
|
||||||
|
- 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
|
||||||
|
|
||||||
|
cache:
|
||||||
|
- C:\projects\dependencies2015.zip
|
||||||
|
- C:\projects\obs-studio-last-tag-built.txt
|
||||||
|
- C:\projects\obs-studio\
|
@ -1,199 +1,20 @@
|
|||||||
variables:
|
pool:
|
||||||
isReleaseMode: ${{ startsWith(variables['Build.SourceBranch'], 'refs/tags/') }}
|
vmImage: 'macOS-10.13'
|
||||||
|
|
||||||
trigger:
|
steps:
|
||||||
branches:
|
- script: ./CI/install-dependencies-macos.sh
|
||||||
include:
|
displayName: 'Install Dependencies'
|
||||||
- '4.x-current'
|
|
||||||
tags:
|
|
||||||
include:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
jobs:
|
- script: ./CI/install-build-obs-macos.sh
|
||||||
- job: 'GenerateDocs'
|
displayName: 'Build OBS'
|
||||||
condition: |
|
|
||||||
or(
|
|
||||||
eq(variables['Build.SourceBranch'], 'refs/heads/4.x-current'),
|
|
||||||
eq(variables['Build.SourceBranch'], 'refs/heads/master')
|
|
||||||
)
|
|
||||||
pool:
|
|
||||||
vmImage: 'ubuntu-18.04'
|
|
||||||
steps:
|
|
||||||
- checkout: self
|
|
||||||
submodules: false
|
|
||||||
|
|
||||||
- script: ./CI/generate-docs.sh
|
- script: ./CI/build-macos.sh
|
||||||
displayName: 'Generate docs'
|
displayName: 'Build obs-websocket'
|
||||||
env:
|
|
||||||
CHECKOUT_REF: $(Build.SourceBranch)
|
|
||||||
GH_TOKEN: $(GithubToken)
|
|
||||||
|
|
||||||
- job: 'Build_Windows'
|
- script: ./CI/package-macos.sh
|
||||||
pool:
|
displayName: 'Package'
|
||||||
vmImage: 'windows-2019'
|
|
||||||
variables:
|
|
||||||
build_config: RelWithDebInfo
|
|
||||||
DEPS_CACHE_VERSION: '1' # Change whenever updating OBS dependencies URL, in order to force a cache reset
|
|
||||||
DEPS_BASE_PATH: 'D:\obsdependencies'
|
|
||||||
DEPS_PATH_32: '$(DEPS_BASE_PATH)\win32'
|
|
||||||
DEPS_PATH_64: '$(DEPS_BASE_PATH)\win64'
|
|
||||||
QT_CACHE_VERSION: '1' # Change whenever updating Qt dependency URL, in order to force a cache reset
|
|
||||||
QT_BASE_DIR: 'D:\QtDep'
|
|
||||||
QTDIR32: '$(QT_BASE_DIR)\5.15.2\msvc2019'
|
|
||||||
QTDIR64: '$(QT_BASE_DIR)\5.15.2\msvc2019_64'
|
|
||||||
OBS_CACHE_VERSION: '1'
|
|
||||||
OBS_PATH: 'D:\obs-studio'
|
|
||||||
steps:
|
|
||||||
- checkout: self
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- task: Cache@2
|
- task: PublishBuildArtifacts@1
|
||||||
displayName: Restore cached Qt archive file
|
inputs:
|
||||||
inputs:
|
pathtoPublish: './release'
|
||||||
key: 'qtdep-"$(QT_CACHE_VERSION)" | "$(Agent.OS)"'
|
artifactName: 'build'
|
||||||
restoreKeys: |
|
|
||||||
qtdep-"$(QT_CACHE_VERSION)" | "$(Agent.OS)"
|
|
||||||
path: $(QT_BASE_DIR)
|
|
||||||
|
|
||||||
- script: ./CI/windows/install-qt-win.cmd
|
|
||||||
displayName: 'Install Qt'
|
|
||||||
env:
|
|
||||||
QT_BASE_DIR: $(QT_BASE_DIR)
|
|
||||||
|
|
||||||
- task: Cache@2
|
|
||||||
displayName: Restore cached OBS Studio dependencies
|
|
||||||
inputs:
|
|
||||||
key: 'obsdeps-"$(DEPS_CACHE_VERSION)" | "$(Agent.OS)"'
|
|
||||||
restoreKeys: |
|
|
||||||
obsdeps-"$(DEPS_CACHE_VERSION)" | "$(Agent.OS)"
|
|
||||||
path: $(DEPS_BASE_PATH)
|
|
||||||
|
|
||||||
- script: ./CI/windows/download-obs-deps.cmd
|
|
||||||
displayName: 'Download OBS Studio dependencies'
|
|
||||||
|
|
||||||
- task: Cache@2
|
|
||||||
displayName: Restore cached OBS Studio builds
|
|
||||||
inputs:
|
|
||||||
key: 'obs-"$(OBS_CACHE_VERSION)" | "$(Agent.OS)"'
|
|
||||||
restoreKeys: |
|
|
||||||
obs-"$(OBS_CACHE_VERSION)" | "$(Agent.OS)"
|
|
||||||
path: $(OBS_PATH)
|
|
||||||
|
|
||||||
- script: ./CI/windows/prepare-obs-windows.cmd
|
|
||||||
displayName: 'Checkout & CMake OBS Studio'
|
|
||||||
env:
|
|
||||||
build_config: $(build_config)
|
|
||||||
DEPS_PATH_32: $(DEPS_PATH_32)
|
|
||||||
DEPS_PATH_64: $(DEPS_PATH_64)
|
|
||||||
QTDIR32: $(QTDIR32)
|
|
||||||
QTDIR64: $(QTDIR64)
|
|
||||||
OBS_PATH: $(OBS_PATH)
|
|
||||||
|
|
||||||
- task: MSBuild@1
|
|
||||||
displayName: 'Build OBS Studio 32-bit'
|
|
||||||
inputs:
|
|
||||||
msbuildArguments: '/m /p:Configuration=$(build_config)'
|
|
||||||
solution: '$(OBS_PATH)\build32\obs-studio.sln'
|
|
||||||
|
|
||||||
- task: MSBuild@1
|
|
||||||
displayName: 'Build OBS Studio 64-bit'
|
|
||||||
inputs:
|
|
||||||
msbuildArguments: '/m /p:Configuration=$(build_config)'
|
|
||||||
solution: '$(OBS_PATH)\build64\obs-studio.sln'
|
|
||||||
|
|
||||||
- script: ./CI/windows/prepare-plugin-windows.cmd
|
|
||||||
displayName: 'CMake obs-websocket'
|
|
||||||
env:
|
|
||||||
build_config: $(build_config)
|
|
||||||
QTDIR32: $(QTDIR32)
|
|
||||||
QTDIR64: $(QTDIR64)
|
|
||||||
OBS_PATH: $(OBS_PATH)
|
|
||||||
|
|
||||||
- task: MSBuild@1
|
|
||||||
displayName: 'Build obs-websocket 32-bit'
|
|
||||||
inputs:
|
|
||||||
msbuildArguments: '/m /p:Configuration=$(build_config)'
|
|
||||||
solution: '.\build32\obs-websocket.sln'
|
|
||||||
|
|
||||||
- task: MSBuild@1
|
|
||||||
displayName: 'Build obs-websocket 64-bit'
|
|
||||||
inputs:
|
|
||||||
msbuildArguments: '/m /p:Configuration=$(build_config)'
|
|
||||||
solution: '.\build64\obs-websocket.sln'
|
|
||||||
|
|
||||||
- script: ./CI/windows/package-plugin-windows.cmd
|
|
||||||
displayName: 'Package obs-websocket'
|
|
||||||
|
|
||||||
- task: PublishBuildArtifacts@1
|
|
||||||
displayName: 'Upload package artifacts'
|
|
||||||
inputs:
|
|
||||||
pathtoPublish: './package'
|
|
||||||
artifactName: 'windows_build'
|
|
||||||
|
|
||||||
- job: 'Build_Linux'
|
|
||||||
pool:
|
|
||||||
vmImage: 'ubuntu-20.04'
|
|
||||||
variables:
|
|
||||||
BRANCH_SHORT_NAME: $(Build.SourceBranchName)
|
|
||||||
BRANCH_FULL_NAME: $(Build.SourceBranch)
|
|
||||||
steps:
|
|
||||||
- checkout: self
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- script: ./CI/linux/install-dependencies-ubuntu.sh
|
|
||||||
displayName: 'Install dependencies'
|
|
||||||
|
|
||||||
- script: ./CI/linux/build-plugin-ubuntu.sh
|
|
||||||
displayName: 'Build obs-websocket'
|
|
||||||
|
|
||||||
- script: ./CI/linux/package-plugin-ubuntu.sh
|
|
||||||
displayName: 'Package obs-websocket'
|
|
||||||
|
|
||||||
- task: PublishBuildArtifacts@1
|
|
||||||
inputs:
|
|
||||||
pathtoPublish: './package'
|
|
||||||
artifactName: 'deb_build'
|
|
||||||
|
|
||||||
- job: 'Build_macOS'
|
|
||||||
pool:
|
|
||||||
vmImage: 'macOS-10.15'
|
|
||||||
variables:
|
|
||||||
OBS_DEPS_VERSION: '2020-12-22'
|
|
||||||
QT_VERSION: '5.15.2'
|
|
||||||
steps:
|
|
||||||
- checkout: self
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- script: ./CI/macos/install-dependencies-macos.sh
|
|
||||||
displayName: 'Install dependencies'
|
|
||||||
env:
|
|
||||||
OBS_DEPS_VERSION: $(OBS_DEPS_VERSION)
|
|
||||||
QT_VERSION: $(QT_VERSION)
|
|
||||||
|
|
||||||
- script: ./CI/macos/install-build-obs-macos.sh
|
|
||||||
displayName: 'Build OBS'
|
|
||||||
|
|
||||||
- script: ./CI/macos/build-plugin-macos.sh
|
|
||||||
displayName: 'Build obs-websocket'
|
|
||||||
|
|
||||||
- task: InstallAppleCertificate@2
|
|
||||||
displayName: 'Install release signing certificates'
|
|
||||||
condition: eq(variables['isReleaseMode'], true)
|
|
||||||
inputs:
|
|
||||||
certSecureFile: 'Certificates.p12'
|
|
||||||
certPwd: $(secrets.macOS.certificatesImportPassword)
|
|
||||||
|
|
||||||
- script: ./CI/macos/package-plugin-macos.sh
|
|
||||||
displayName: 'Package obs-websocket'
|
|
||||||
env:
|
|
||||||
RELEASE_MODE: $(isReleaseMode)
|
|
||||||
CODE_SIGNING_IDENTITY: $(secrets.macOS.codeSigningIdentity)
|
|
||||||
INSTALLER_SIGNING_IDENTITY: $(secrets.macOS.installerSigningIdentity)
|
|
||||||
AC_USERNAME: $(secrets.macOS.notarization.username)
|
|
||||||
AC_PASSWORD: $(secrets.macOS.notarization.password)
|
|
||||||
AC_PROVIDER_SHORTNAME: $(secrets.macOS.notarization.providerShortName)
|
|
||||||
|
|
||||||
- task: PublishBuildArtifacts@1
|
|
||||||
inputs:
|
|
||||||
pathtoPublish: './release'
|
|
||||||
artifactName: 'macos_build'
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,18 +1,14 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="WebSockets-Servereinstellungen"
|
OBSWebsocket.Menu.SettingsItem="Websocket-Server Einstellungen"
|
||||||
OBSWebsocket.Settings.ServerEnable="WebSockets-Server aktivieren"
|
OBSWebsocket.Settings.DialogTitle="Websocket-Server Einstellungen"
|
||||||
OBSWebsocket.Settings.ServerPort="Server-Port"
|
OBSWebsocket.Settings.ServerEnable="Websocket-Server aktivieren"
|
||||||
OBSWebsocket.Settings.AuthRequired="Authentifizierung aktivieren"
|
OBSWebsocket.Settings.ServerPort="Server Port"
|
||||||
|
OBSWebsocket.Settings.AuthRequired="Authentifizierung erforderlich"
|
||||||
OBSWebsocket.Settings.Password="Passwort"
|
OBSWebsocket.Settings.Password="Passwort"
|
||||||
OBSWebsocket.Settings.LockToIPv4="Nur IPv4 verwenden (deaktiviert IPv6)"
|
|
||||||
OBSWebsocket.Settings.DebugEnable="Debug-Protokollierung aktivieren"
|
OBSWebsocket.Settings.DebugEnable="Debug-Protokollierung aktivieren"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Infobereichbenachrichtigungen aktivieren"
|
OBSWebsocket.Settings.AlertsEnable="Infobereich-Benachrichtigungen 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-Serverfehler"
|
OBSWebsocket.Server.StartFailed.Title="WebSocket-Server Fehler"
|
||||||
OBSWebsocket.Server.StartFailed.Message="Der WebSocket-Server konnte nicht gestartet werden, mögliche Gründe:\n - Der TCP-Port %1 wird möglicherweise gerade von einem anderen Programm verwendet. Versuchen Sie einen anderen Port in den Websocket-Servereinstellungen zu setzen oder alle Programme zu beenden, die den Port möglicherweise verwenden.\n - Fehler: %2"
|
OBSWebsocket.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.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,21 +1,14 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="WebSockets Server Settings"
|
OBSWebsocket.Menu.SettingsItem="Websocket server settings"
|
||||||
OBSWebsocket.Settings.ServerEnable="Enable WebSockets server"
|
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
||||||
|
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"
|
||||||
OBSWebsocket.Settings.LockToIPv4="Lock server to only using IPv4"
|
|
||||||
OBSWebsocket.Settings.DebugEnable="Enable debug logging"
|
OBSWebsocket.Settings.DebugEnable="Enable debug logging"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Enable System Tray Alerts"
|
OBSWebsocket.Settings.AlertsEnable="Enable System Tray Alerts"
|
||||||
OBSWebsocket.Settings.AuthDisabledWarning="Running obs-websocket with authentication disabled is not recommended, as it allows attackers to easily collect sensetive data. Are you sure you want to proceed?"
|
|
||||||
OBSWebsocket.NotifyConnect.Title="New WebSocket connection"
|
OBSWebsocket.NotifyConnect.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="WebSockets Server failure"
|
OBSWebsocket.Server.StartFailed.Title="WebSocket Server failure"
|
||||||
OBSWebsocket.Server.StartFailed.Message="The WebSockets server failed to start, maybe because:\n - TCP port %1 may currently be in use elsewhere on this system, possibly by another application. Try setting a different TCP port in the WebSocket server settings, or stop any application that could be using this port.\n - Error message: %2"
|
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.ProfileChanged.Started="WebSockets server enabled in this profile. Server started."
|
|
||||||
OBSWebsocket.ProfileChanged.Stopped="WebSockets server disabled in this profile. Server stopped."
|
|
||||||
OBSWebsocket.ProfileChanged.Restarted="WebSockets server port changed in this profile. Server restarted."
|
|
||||||
OBSWebsocket.InitialPasswordSetup.Title="obs-websocket - Server Password Configuration"
|
|
||||||
OBSWebsocket.InitialPasswordSetup.Text="It looks like you are running obs-websocket for the first time. Do you want to configure a password now for the WebSockets server? Setting a password is highly recommended."
|
|
||||||
OBSWebsocket.InitialPasswordSetup.DismissedText="You can configure a server password later in the WebSockets Server Settings. (Under the Tools menu of OBS Studio)"
|
|
@ -1,16 +1,14 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="Configuración del servidor WebSocket"
|
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 en la bandeja de sistema"
|
OBSWebsocket.Settings.AlertsEnable="Habilitar alertas de 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="Falla en el servidor WebSockets"
|
OBSWebsocket.Server.StartFailed.Title="Fallo del servidor WebSocket"
|
||||||
OBSWebsocket.ProfileChanged.Started="El servidor WebSocket esta habilitado en este perfil. El servidor ha iniciado."
|
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.ProfileChanged.Stopped="Servidor WebSockets deshabilitado en este perfil. Servidor detenido."
|
|
||||||
OBSWebsocket.ProfileChanged.Restarted="Puerto del servidor WebSockets cambiado en este perfil. Servidor reiniciado."
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="Paramètres du serveur WebSockets"
|
OBSWebsocket.Menu.SettingsItem="Paramètres du serveur Websocket"
|
||||||
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,8 +9,5 @@ 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 WebSockets"
|
OBSWebsocket.Server.StartFailed.Title="Impossible de démarrer le serveur WebSocket"
|
||||||
OBSWebsocket.ProfileChanged.Started="Serveur WebSockets actif dans ce profil."
|
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.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é."
|
|
||||||
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,9 +1,9 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="Impostazioni del server di WebSocket"
|
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"
|
||||||
OBSWebsocket.Settings.LockToIPv4="Blocca il server per usare solo IPv4"
|
|
||||||
OBSWebsocket.Settings.DebugEnable="Attivare la registrazione di debug"
|
OBSWebsocket.Settings.DebugEnable="Attivare la registrazione di debug"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Attivare gli avvisi di vassoio di sistema"
|
OBSWebsocket.Settings.AlertsEnable="Attivare gli avvisi di vassoio di sistema"
|
||||||
OBSWebsocket.NotifyConnect.Title="Nuova connessione WebSocket"
|
OBSWebsocket.NotifyConnect.Title="Nuova connessione WebSocket"
|
||||||
@ -11,8 +11,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="L'avvio del server WebSockets è fallito, forse perché:\n - La porta TCP %1 può attualmente essere in uso altrove su questo sistema, forse da un'altra applicazione. Provare a impostare una porta TCP diversa nelle impostazioni del server WebSocket o interrompere qualsiasi applicazione che potrebbe utilizzare questa porta.\n - Messaggio di errore: %2"
|
OBSWebsocket.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.ProfileChanged.Started="Server WebSockets abilitato in questo profilo. Il server è avviato."
|
|
||||||
OBSWebsocket.ProfileChanged.Stopped="Server WebSocket disabilitato in questo profilo. Server interrotto."
|
|
||||||
OBSWebsocket.ProfileChanged.Restarted="La porta del server WebSocket è stata modificata in questo profilo. Il server è stato riavviato."
|
|
||||||
|
|
||||||
|
@ -1,16 +1,8 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="Websocket サーバー設定"
|
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.Settings.AlertsEnable="システムトレイ通知を有効にする"
|
OBSWebsocket.NotifyConnect.Title="新しいWebSocket接続"
|
||||||
OBSWebsocket.NotifyConnect.Title="新しい WebSocket 接続"
|
|
||||||
OBSWebsocket.NotifyConnect.Message="接続されているクライアント %1"
|
|
||||||
OBSWebsocket.NotifyDisconnect.Title="WebSocket クライアントが切断されました"
|
|
||||||
OBSWebsocket.NotifyDisconnect.Message="切断されたクライアント %1"
|
|
||||||
OBSWebsocket.Server.StartFailed.Title="WebSockets サーバー障害"
|
|
||||||
OBSWebsocket.ProfileChanged.Started="このプロファイルでWebSocketサーバが有効になりました。サーバを起動しました。"
|
|
||||||
OBSWebsocket.ProfileChanged.Stopped="このプロファイルでWebSocketサーバが無効になりました。サーバを停止しました。"
|
|
||||||
OBSWebsocket.ProfileChanged.Restarted="このプロファイルでWebSocketサーバの通信ポートが変更されました。サーバを再起動しました。"
|
|
||||||
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="웹소켓 서버 설정"
|
|
||||||
OBSWebsocket.Settings.ServerEnable="웹소켓 서버 활성화"
|
|
||||||
OBSWebsocket.Settings.ServerPort="서버 포트"
|
|
||||||
OBSWebsocket.Settings.AuthRequired="인증 활성화"
|
|
||||||
OBSWebsocket.Settings.Password="비밀번호"
|
|
||||||
OBSWebsocket.Settings.LockToIPv4="IPv4만 이용하여 서버를 연결"
|
|
||||||
OBSWebsocket.Settings.DebugEnable="디버그 로깅 활성화"
|
|
||||||
OBSWebsocket.Settings.AlertsEnable="시스템 트레이 알림 활성화"
|
|
||||||
OBSWebsocket.NotifyConnect.Title="새로운 웹소켓 연결"
|
|
||||||
OBSWebsocket.NotifyConnect.Message="클라이언트 %1 가 연결되었습니다"
|
|
||||||
OBSWebsocket.NotifyDisconnect.Title="웹소켓 클라이언트가 연결 해제되었습니다"
|
|
||||||
OBSWebsocket.NotifyDisconnect.Message="클라이언트 %1 가 연결이 해제되었습니다"
|
|
||||||
OBSWebsocket.Server.StartFailed.Title="웹소켓 서버 시작이 실패하였습니다"
|
|
||||||
OBSWebsocket.Server.StartFailed.Message="웹소켓 서버가 시작되지 못했습니다. \n 시스템 상의 다른 프로그램이 TCP 포트 %1을 사용 중인 것으로 보입니다. 다른 TCP 포트를 사용하거나, 해당 포트를 사용 중인 프로그램을 종료하고 다시 시도해주세요. \n 에러 메시지: %2"
|
|
||||||
OBSWebsocket.ProfileChanged.Started="프로파일 설정에 따라 웹소켓 서버가 활성화되었습니다. 서버가 시작되었습니다."
|
|
||||||
OBSWebsocket.ProfileChanged.Stopped="프로파일 설정에 따라 웹소켓 서버가 비활성화되었습니다. 서버가 중지되었습니다."
|
|
||||||
OBSWebsocket.ProfileChanged.Restarted="프로파일 설정에 따라 웹소켓 포트가 변경되었습니다. 서버가 재시작되었습니다."
|
|
||||||
|
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="WebSockets Server Instellingen"
|
OBSWebsocket.Menu.SettingsItem="Websocket server instellingen"
|
||||||
OBSWebsocket.Settings.ServerEnable="WebSockets server inschakelen"
|
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
||||||
|
OBSWebsocket.Settings.ServerEnable="Activeer Websocket server"
|
||||||
OBSWebsocket.Settings.ServerPort="Serverpoort"
|
OBSWebsocket.Settings.ServerPort="Serverpoort"
|
||||||
OBSWebsocket.Settings.AuthRequired="Verificatie inschakelen"
|
OBSWebsocket.Settings.AuthRequired="Activeer authenticatie"
|
||||||
OBSWebsocket.Settings.Password="Wachtwoord"
|
OBSWebsocket.Settings.Password="Wachtwoord"
|
||||||
OBSWebsocket.Settings.LockToIPv4="Server vergrendelen om alleen IPv4 te gebruiken"
|
|
||||||
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="Fout in WebSocket server"
|
OBSWebsocket.Server.StartFailed.Title="WebSocket Server mislukt"
|
||||||
OBSWebsocket.Server.StartFailed.Message="De obs-websocket server kan niet worden gestart, misschien omdat: \n - TCP-poort %1 momenteel elders wordt gebruikt op dit systeem, eventueel door een andere toepassing. Probeer een andere TCP-poort in te stellen in de WebSocket Server-instellingen of stop elke toepassing die deze poort zou kunnen gebruiken.\n Een onbekende Netwerkfout op uw systeem. Probeer het opnieuw door de instellingen te wijzigen, OBS te herstarten of uw systeem te herstarten."
|
OBSWebsocket.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.ProfileChanged.Started="WebSockets server ingeschakeld in dit profiel. Server gestart."
|
|
||||||
OBSWebsocket.ProfileChanged.Stopped="WebSockets server uitgeschakeld in dit profiel. Server is gestopt."
|
|
||||||
OBSWebsocket.ProfileChanged.Restarted="WebSockets server poort is veranderd in dit profiel. Server is herstart."
|
|
||||||
|
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="Ustawienia serwera WebSockets"
|
OBSWebsocket.Menu.SettingsItem="Ustawienia serwera zdalnego sterowania"
|
||||||
OBSWebsocket.Settings.ServerEnable="Włącz serwer WebSockets"
|
OBSWebsocket.Settings.DialogTitle="Serwer zdalnego sterowania"
|
||||||
|
OBSWebsocket.Settings.ServerEnable="Włącz serwer zdalnego sterowania (Websocket)"
|
||||||
OBSWebsocket.Settings.ServerPort="Port serwera"
|
OBSWebsocket.Settings.ServerPort="Port serwera"
|
||||||
OBSWebsocket.Settings.AuthRequired="Wymagaj uwierzytelniania"
|
OBSWebsocket.Settings.AuthRequired="Wymagaj hasła"
|
||||||
OBSWebsocket.Settings.Password="Hasło"
|
OBSWebsocket.Settings.Password="Hasło"
|
||||||
OBSWebsocket.Settings.LockToIPv4="Zablokuj serwer tylko za pomocą IPv4"
|
|
||||||
OBSWebsocket.Settings.DebugEnable="Włącz rejestrowanie debugowania"
|
OBSWebsocket.Settings.DebugEnable="Włącz rejestrowanie debugowania"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Włącz powiadomienia w zasobniku systemowym"
|
OBSWebsocket.Settings.AlertsEnable="Włącz powiadomienia o 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 rozłączony"
|
OBSWebsocket.NotifyDisconnect.Message="Klient %1 połączony"
|
||||||
OBSWebsocket.Server.StartFailed.Title="Awaria serwera WebSockets"
|
OBSWebsocket.Server.StartFailed.Title="Awaria serwera WebSocket"
|
||||||
OBSWebsocket.ProfileChanged.Started="Serwer WebSockets włączony w tym profilu. Serwer uruchomiony."
|
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."
|
||||||
|
|
||||||
|
5
data/locale/pt-BR.ini
Normal file
5
data/locale/pt-BR.ini
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
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,9 +1,9 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="Configurações do servidor de WebSockets"
|
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"
|
||||||
OBSWebsocket.Settings.Password="Palavra passe"
|
OBSWebsocket.Settings.Password="Palavra passe"
|
||||||
OBSWebsocket.Settings.LockToIPv4="Forçar apenas o uso de IPv4 (desabilitar IPv6)"
|
|
||||||
OBSWebsocket.Settings.DebugEnable="Habilitar registro de debug"
|
OBSWebsocket.Settings.DebugEnable="Habilitar registro de debug"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Ativar Alertas da bandeja do sistema"
|
OBSWebsocket.Settings.AlertsEnable="Ativar Alertas da bandeja do sistema"
|
||||||
OBSWebsocket.NotifyConnect.Title="Nova conexão WebSocket"
|
OBSWebsocket.NotifyConnect.Title="Nova conexão WebSocket"
|
||||||
@ -11,8 +11,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 de WebSockets falhou ao iniciar, possivelmente porque:\n - A porta TCP %1 pode já estar em uso em algum outro lugar neste sistema, possivelmente por outro aplicativo. Tente definir uma porta TCP diferente nas configurações do servidor de WebSockets, ou pare qualquer aplicativo que possa estar usando essa porta.\n - Mensagem de erro: %2"
|
OBSWebsocket.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.ProfileChanged.Started="Servidor de WebSockets habilitado nesse perfil. Servidor iniciado."
|
|
||||||
OBSWebsocket.ProfileChanged.Stopped="Servidor de WebSockets desabilitado nesse perfil. Servidor parado."
|
|
||||||
OBSWebsocket.ProfileChanged.Restarted="Porta do servidor de WebSockets foi alterada neste perfil. Servidor reiniciado."
|
|
||||||
|
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="Настройки сервера WebSockets"
|
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.LockToIPv4="Блокировка сервера только с использованием IPv4"
|
|
||||||
OBSWebsocket.Settings.DebugEnable="Включить ведение журнала отладки"
|
OBSWebsocket.Settings.DebugEnable="Включить ведение журнала отладки"
|
||||||
OBSWebsocket.Settings.AlertsEnable="Включить оповещения в системном трее"
|
OBSWebsocket.Settings.AlertsEnable="Включить оповещения в системном трее"
|
||||||
OBSWebsocket.NotifyConnect.Title="Новое соединение WebSocket"
|
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="Сбой сервера WebSockets"
|
OBSWebsocket.Server.StartFailed.Title="Сбой сервера WebSocket"
|
||||||
OBSWebsocket.Server.StartFailed.Message="Сервер WebSockets не запустился, возможно, потому, что:\n-TCP-порт %1 в настоящее время может использоваться в другом месте этой системы, возможно, другим приложением. Попробуйте установить другой TCP-порт в настройках сервера WebSocket или остановить любое приложение, которое может использовать этот порт.\n-сообщение об ошибке: %2"
|
OBSWebsocket.Server.StartFailed.Message="Сбой запуска сервера obs-websocket. Вероятные причины:\n - Возможно, TCP-порт %1 занят другим приложением в системе. Попробуйте задать другой TCP-порт в настройках сервера WebSocket или закройте приложение, которое может использовать данный порт.\n - Произошла неизвестная сетевая ошибка в системе. Попробуйте снова после изменения настроек, перезапуска OBS или перезапуска системы."
|
||||||
OBSWebsocket.ProfileChanged.Started="Сервер WebSockets включен в этом профиле. Сервер запущен."
|
|
||||||
OBSWebsocket.ProfileChanged.Stopped="Сервер WebSockets отключен в этом профиле. Сервер остановлен."
|
|
||||||
OBSWebsocket.ProfileChanged.Restarted="Порт сервера WebSockets изменен в этом профиле. Сервер перезапущен."
|
|
||||||
|
|
||||||
|
@ -1,16 +1,6 @@
|
|||||||
OBSWebsocket.Settings.DialogTitle="WebSockets 服务器设置"
|
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.ProfileChanged.Started="此配置文件中启用了 WebSockets 服务器。服务器已启动。"
|
|
||||||
OBSWebsocket.ProfileChanged.Stopped="此配置文件中禁用了 WebSockets 服务器。服务器已停止。"
|
|
||||||
OBSWebsocket.ProfileChanged.Restarted="此配置文件中的 WebSockets 服务器端口已更改。服务器已重新启动。"
|
|
||||||
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
OBSWebsocket.Settings.ServerPort="伺服器連接埠"
|
OBSWebsocket.Menu.SettingsItem="Websocket 伺服器設定"
|
||||||
OBSWebsocket.Settings.DebugEnable="啟用除錯日誌"
|
OBSWebsocket.Settings.DialogTitle="obs-websocket 設定"
|
||||||
OBSWebsocket.Settings.AlertsEnable="啟用系統列通知"
|
OBSWebsocket.Settings.ServerEnable="啟用 Websocket 伺服器"
|
||||||
OBSWebsocket.NotifyConnect.Title="新的 WebSocket 連線"
|
OBSWebsocket.Settings.ServerPort="伺服器端口"
|
||||||
OBSWebsocket.NotifyConnect.Message="客戶端 %1 已連線"
|
OBSWebsocket.Settings.AuthRequired="啟用密碼認證"
|
||||||
OBSWebsocket.NotifyDisconnect.Title="WebSocket 客戶端已離線"
|
OBSWebsocket.Settings.Password="密碼"
|
||||||
OBSWebsocket.NotifyDisconnect.Message="客戶端 %1 已離線"
|
|
||||||
OBSWebsocket.Server.StartFailed.Title="WebSocket 伺服器錯誤"
|
|
||||||
|
|
||||||
|
1
deps/asio
vendored
1
deps/asio
vendored
Submodule deps/asio deleted from b73dc1d2c0
1
deps/websocketpp
vendored
1
deps/websocketpp
vendored
Submodule deps/websocketpp deleted from c6d7e295bf
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,10 @@
|
|||||||
# obs-websocket 4.9.1 protocol reference
|
# obs-websocket 4.5.0 protocol reference
|
||||||
|
|
||||||
# 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. As of v5.0.0, backwards compatability with the protocol will not be kept.
|
This protocol is based on the original OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio.
|
||||||
|
|
||||||
# Authentication
|
# Authentication
|
||||||
**Starting with obs-websocket 4.9, authentication is enabled by default and users are encouraged to configure a password on first run.**
|
|
||||||
|
|
||||||
`obs-websocket` uses SHA256 to transmit credentials.
|
`obs-websocket` uses SHA256 to transmit credentials.
|
||||||
|
|
||||||
A request for [`GetAuthRequired`](#getauthrequired) returns two elements:
|
A request for [`GetAuthRequired`](#getauthrequired) returns two elements:
|
||||||
@ -34,5 +32,3 @@ auth_response_string = secret + challenge
|
|||||||
auth_response_hash = binary_sha256(auth_response_string)
|
auth_response_hash = binary_sha256(auth_response_string)
|
||||||
auth_response = base64_encode(auth_response_hash)
|
auth_response = base64_encode(auth_response_hash)
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also refer to any of the [client libraries](https://github.com/Palakis/obs-websocket#for-developers) listed on the README for examples of how to authenticate.
|
|
||||||
|
@ -6,6 +6,6 @@ Requests are sent by the client and require at least the following two fields:
|
|||||||
Once a request is sent, the server will return a JSON response with at least the following fields:
|
Once a request is sent, the server will return a JSON response with at least the following fields:
|
||||||
- `message-id` _String_: The client defined identifier specified in the request.
|
- `message-id` _String_: The client defined identifier specified in the request.
|
||||||
- `status` _String_: Response status, will be one of the following: `ok`, `error`
|
- `status` _String_: Response status, will be one of the following: `ok`, `error`
|
||||||
- `error` _String (Optional)_: An error message accompanying an `error` status.
|
- `error` _String_: An error message accompanying an `error` status.
|
||||||
|
|
||||||
Additional information may be required/returned depending on the request type. See below for more information.
|
Additional information may be required/returned depending on the request type. See below for more information.
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
; 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.9.1"
|
#define MyAppVersion "4.5.1"
|
||||||
#define MyAppPublisher "Stephane Lepin"
|
#define MyAppPublisher "Stephane Lepin"
|
||||||
#define MyAppURL "http://github.com/Palakis/obs-websocket"
|
#define MyAppURL "http://github.com/Palakis/obs-websocket"
|
||||||
|
|
||||||
@ -23,14 +23,14 @@ DefaultGroupName={#MyAppName}
|
|||||||
OutputBaseFilename=obs-websocket-Windows-Installer
|
OutputBaseFilename=obs-websocket-Windows-Installer
|
||||||
Compression=lzma
|
Compression=lzma
|
||||||
SolidCompression=yes
|
SolidCompression=yes
|
||||||
DirExistsWarning=no
|
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,21 +38,6 @@ 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;
|
||||||
|
238
src/Config.cpp
238
src/Config.cpp
@ -17,105 +17,46 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
#include <util/config-file.h>
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QCryptographicHash>
|
||||||
#include <QtCore/QCryptographicHash>
|
#include <QTime>
|
||||||
#include <QtCore/QTime>
|
|
||||||
#include <QtWidgets/QSystemTrayIcon>
|
|
||||||
#include <QtWidgets/QMainWindow>
|
|
||||||
#include <QtWidgets/QInputDialog>
|
|
||||||
#include <QtWidgets/QMessageBox>
|
|
||||||
|
|
||||||
#define SECTION_NAME "WebsocketAPI"
|
#define SECTION_NAME "WebsocketAPI"
|
||||||
#define PARAM_ENABLE "ServerEnabled"
|
#define PARAM_ENABLE "ServerEnabled"
|
||||||
#define PARAM_PORT "ServerPort"
|
#define PARAM_PORT "ServerPort"
|
||||||
#define PARAM_LOCKTOIPV4 "LockToIPv4"
|
|
||||||
#define PARAM_DEBUG "DebugEnabled"
|
#define PARAM_DEBUG "DebugEnabled"
|
||||||
#define PARAM_ALERT "AlertsEnabled"
|
#define PARAM_ALERT "AlertsEnabled"
|
||||||
#define PARAM_AUTHREQUIRED "AuthRequired"
|
#define PARAM_AUTHREQUIRED "AuthRequired"
|
||||||
#define PARAM_SECRET "AuthSecret"
|
#define PARAM_SECRET "AuthSecret"
|
||||||
#define PARAM_SALT "AuthSalt"
|
#define PARAM_SALT "AuthSalt"
|
||||||
|
|
||||||
#define GLOBAL_AUTH_SETUP_PROMPTED "AuthSetupPrompted"
|
|
||||||
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "WSServer.h"
|
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
#define QT_TO_UTF8(str) str.toUtf8().constData()
|
#define QT_TO_UTF8(str) str.toUtf8().constData()
|
||||||
|
|
||||||
|
Config* Config::_instance = new Config();
|
||||||
|
|
||||||
Config::Config() :
|
Config::Config() :
|
||||||
ServerEnabled(true),
|
ServerEnabled(true),
|
||||||
ServerPort(4444),
|
ServerPort(4444),
|
||||||
LockToIPv4(false),
|
|
||||||
DebugEnabled(false),
|
DebugEnabled(false),
|
||||||
AlertsEnabled(true),
|
AlertsEnabled(true),
|
||||||
AuthRequired(true),
|
AuthRequired(false),
|
||||||
Secret(""),
|
Secret(""),
|
||||||
Salt(""),
|
Salt(""),
|
||||||
SettingsLoaded(false)
|
SettingsLoaded(false)
|
||||||
{
|
{
|
||||||
qsrand(QTime::currentTime().msec());
|
qsrand(QTime::currentTime().msec());
|
||||||
|
|
||||||
SetDefaults();
|
|
||||||
SessionChallenge = GenerateSalt();
|
|
||||||
|
|
||||||
obs_frontend_add_event_callback(OnFrontendEvent, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Config::~Config()
|
|
||||||
{
|
|
||||||
obs_frontend_remove_event_callback(OnFrontendEvent, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Config::Load()
|
|
||||||
{
|
|
||||||
config_t* obsConfig = GetConfigStore();
|
|
||||||
|
|
||||||
ServerEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ENABLE);
|
|
||||||
ServerPort = config_get_uint(obsConfig, SECTION_NAME, PARAM_PORT);
|
|
||||||
LockToIPv4 = config_get_bool(obsConfig, SECTION_NAME, PARAM_LOCKTOIPV4);
|
|
||||||
|
|
||||||
DebugEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_DEBUG);
|
|
||||||
AlertsEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ALERT);
|
|
||||||
|
|
||||||
AuthRequired = config_get_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED);
|
|
||||||
Secret = config_get_string(obsConfig, SECTION_NAME, PARAM_SECRET);
|
|
||||||
Salt = config_get_string(obsConfig, SECTION_NAME, PARAM_SALT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Config::Save()
|
|
||||||
{
|
|
||||||
config_t* obsConfig = GetConfigStore();
|
|
||||||
|
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
|
||||||
config_set_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort);
|
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_LOCKTOIPV4, LockToIPv4);
|
|
||||||
|
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_ALERT, AlertsEnabled);
|
|
||||||
|
|
||||||
config_set_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
|
||||||
config_set_string(obsConfig, SECTION_NAME, PARAM_SECRET,
|
|
||||||
QT_TO_UTF8(Secret));
|
|
||||||
config_set_string(obsConfig, SECTION_NAME, PARAM_SALT,
|
|
||||||
QT_TO_UTF8(Salt));
|
|
||||||
|
|
||||||
config_save(obsConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Config::SetDefaults()
|
|
||||||
{
|
|
||||||
// OBS Config defaults
|
// OBS Config defaults
|
||||||
config_t* obsConfig = GetConfigStore();
|
config_t* obsConfig = obs_frontend_get_global_config();
|
||||||
if (obsConfig) {
|
if (obsConfig) {
|
||||||
config_set_default_bool(obsConfig,
|
config_set_default_bool(obsConfig,
|
||||||
SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
||||||
config_set_default_uint(obsConfig,
|
config_set_default_uint(obsConfig,
|
||||||
SECTION_NAME, PARAM_PORT, ServerPort);
|
SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
config_set_default_bool(obsConfig,
|
|
||||||
SECTION_NAME, PARAM_LOCKTOIPV4, LockToIPv4);
|
|
||||||
|
|
||||||
config_set_default_bool(obsConfig,
|
config_set_default_bool(obsConfig,
|
||||||
SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
||||||
@ -129,75 +70,46 @@ void Config::SetDefaults()
|
|||||||
config_set_default_string(obsConfig,
|
config_set_default_string(obsConfig,
|
||||||
SECTION_NAME, PARAM_SALT, QT_TO_UTF8(Salt));
|
SECTION_NAME, PARAM_SALT, QT_TO_UTF8(Salt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SessionChallenge = GenerateSalt();
|
||||||
}
|
}
|
||||||
|
|
||||||
config_t* Config::GetConfigStore()
|
Config::~Config()
|
||||||
{
|
{
|
||||||
return obs_frontend_get_profile_config();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::MigrateFromGlobalSettings()
|
void Config::Load()
|
||||||
{
|
{
|
||||||
config_t* source = obs_frontend_get_global_config();
|
config_t* obsConfig = obs_frontend_get_global_config();
|
||||||
config_t* destination = obs_frontend_get_profile_config();
|
|
||||||
|
|
||||||
if(config_has_user_value(source, SECTION_NAME, PARAM_ENABLE)) {
|
ServerEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ENABLE);
|
||||||
bool value = config_get_bool(source, SECTION_NAME, PARAM_ENABLE);
|
ServerPort = config_get_uint(obsConfig, SECTION_NAME, PARAM_PORT);
|
||||||
config_set_bool(destination, SECTION_NAME, PARAM_ENABLE, value);
|
|
||||||
|
|
||||||
config_remove_value(source, SECTION_NAME, PARAM_ENABLE);
|
DebugEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_DEBUG);
|
||||||
}
|
AlertsEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ALERT);
|
||||||
|
|
||||||
if(config_has_user_value(source, SECTION_NAME, PARAM_PORT)) {
|
AuthRequired = config_get_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED);
|
||||||
uint64_t value = config_get_uint(source, SECTION_NAME, PARAM_PORT);
|
Secret = config_get_string(obsConfig, SECTION_NAME, PARAM_SECRET);
|
||||||
config_set_uint(destination, SECTION_NAME, PARAM_PORT, value);
|
Salt = config_get_string(obsConfig, SECTION_NAME, PARAM_SALT);
|
||||||
|
}
|
||||||
|
|
||||||
config_remove_value(source, SECTION_NAME, PARAM_PORT);
|
void Config::Save()
|
||||||
}
|
{
|
||||||
|
config_t* obsConfig = obs_frontend_get_global_config();
|
||||||
if(config_has_user_value(source, SECTION_NAME, PARAM_LOCKTOIPV4)) {
|
|
||||||
bool value = config_get_bool(source, SECTION_NAME, PARAM_LOCKTOIPV4);
|
|
||||||
config_set_bool(destination, SECTION_NAME, PARAM_LOCKTOIPV4, value);
|
|
||||||
|
|
||||||
config_remove_value(source, SECTION_NAME, PARAM_LOCKTOIPV4);
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
||||||
}
|
config_set_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
|
|
||||||
if(config_has_user_value(source, SECTION_NAME, PARAM_DEBUG)) {
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
||||||
bool value = config_get_bool(source, SECTION_NAME, PARAM_DEBUG);
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_ALERT, AlertsEnabled);
|
||||||
config_set_bool(destination, SECTION_NAME, PARAM_DEBUG, value);
|
|
||||||
|
|
||||||
config_remove_value(source, SECTION_NAME, PARAM_DEBUG);
|
config_set_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
||||||
}
|
config_set_string(obsConfig, SECTION_NAME, PARAM_SECRET,
|
||||||
|
QT_TO_UTF8(Secret));
|
||||||
|
config_set_string(obsConfig, SECTION_NAME, PARAM_SALT,
|
||||||
|
QT_TO_UTF8(Salt));
|
||||||
|
|
||||||
if(config_has_user_value(source, SECTION_NAME, PARAM_ALERT)) {
|
config_save(obsConfig);
|
||||||
bool value = config_get_bool(source, SECTION_NAME, PARAM_ALERT);
|
|
||||||
config_set_bool(destination, SECTION_NAME, PARAM_ALERT, value);
|
|
||||||
|
|
||||||
config_remove_value(source, SECTION_NAME, PARAM_ALERT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config_has_user_value(source, SECTION_NAME, PARAM_AUTHREQUIRED)) {
|
|
||||||
bool value = config_get_bool(source, SECTION_NAME, PARAM_AUTHREQUIRED);
|
|
||||||
config_set_bool(destination, SECTION_NAME, PARAM_AUTHREQUIRED, value);
|
|
||||||
|
|
||||||
config_remove_value(source, SECTION_NAME, PARAM_AUTHREQUIRED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config_has_user_value(source, SECTION_NAME, PARAM_SECRET)) {
|
|
||||||
const char* value = config_get_string(source, SECTION_NAME, PARAM_SECRET);
|
|
||||||
config_set_string(destination, SECTION_NAME, PARAM_SECRET, value);
|
|
||||||
|
|
||||||
config_remove_value(source, SECTION_NAME, PARAM_SECRET);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config_has_user_value(source, SECTION_NAME, PARAM_SALT)) {
|
|
||||||
const char* value = config_get_string(source, SECTION_NAME, PARAM_SALT);
|
|
||||||
config_set_string(destination, SECTION_NAME, PARAM_SALT, value);
|
|
||||||
|
|
||||||
config_remove_value(source, SECTION_NAME, PARAM_SALT);
|
|
||||||
}
|
|
||||||
|
|
||||||
config_save(destination);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Config::GenerateSalt()
|
QString Config::GenerateSalt()
|
||||||
@ -268,85 +180,7 @@ bool Config::CheckAuth(QString response)
|
|||||||
return authSuccess;
|
return authSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::OnFrontendEvent(enum obs_frontend_event event, void* param)
|
Config* Config::Current()
|
||||||
{
|
{
|
||||||
auto config = reinterpret_cast<Config*>(param);
|
return _instance;
|
||||||
|
|
||||||
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;
|
|
||||||
bool previousLock = config->LockToIPv4;
|
|
||||||
|
|
||||||
config->SetDefaults();
|
|
||||||
config->Load();
|
|
||||||
|
|
||||||
if (config->ServerEnabled != previousEnabled || config->ServerPort != previousPort || config->LockToIPv4 != previousLock) {
|
|
||||||
auto server = GetServer();
|
|
||||||
server->stop();
|
|
||||||
|
|
||||||
if (config->ServerEnabled) {
|
|
||||||
server->start(config->ServerPort, config->LockToIPv4);
|
|
||||||
|
|
||||||
if (previousEnabled != config->ServerEnabled) {
|
|
||||||
Utils::SysTrayNotify(startMessage, QSystemTrayIcon::MessageIcon::Information);
|
|
||||||
} else {
|
|
||||||
Utils::SysTrayNotify(restartMessage, QSystemTrayIcon::MessageIcon::Information);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Utils::SysTrayNotify(stopMessage, QSystemTrayIcon::MessageIcon::Information);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
|
|
||||||
FirstRunPasswordSetup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Config::FirstRunPasswordSetup()
|
|
||||||
{
|
|
||||||
// check if we already showed the auth setup prompt to the user, independently of the current settings (tied to the current profile)
|
|
||||||
config_t* globalConfig = obs_frontend_get_global_config();
|
|
||||||
bool alreadyPrompted = config_get_bool(globalConfig, SECTION_NAME, GLOBAL_AUTH_SETUP_PROMPTED);
|
|
||||||
if (alreadyPrompted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// lift the flag up and save it
|
|
||||||
config_set_bool(globalConfig, SECTION_NAME, GLOBAL_AUTH_SETUP_PROMPTED, true);
|
|
||||||
config_save(globalConfig);
|
|
||||||
|
|
||||||
// check if the password is already set
|
|
||||||
auto config = GetConfig();
|
|
||||||
if (!config) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(config->Secret.isEmpty()) && !(config->Salt.isEmpty())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
|
||||||
QString dialogTitle = QObject::tr("OBSWebsocket.InitialPasswordSetup.Title");
|
|
||||||
QString dialogText = QObject::tr("OBSWebsocket.InitialPasswordSetup.Text");
|
|
||||||
QString dismissedText = QObject::tr("OBSWebsocket.InitialPasswordSetup.DismissedText");
|
|
||||||
obs_frontend_pop_ui_translation();
|
|
||||||
|
|
||||||
auto mainWindow = reinterpret_cast<QMainWindow*>(
|
|
||||||
obs_frontend_get_main_window()
|
|
||||||
);
|
|
||||||
|
|
||||||
QMessageBox::StandardButton response = QMessageBox::question(mainWindow, dialogTitle, dialogText);
|
|
||||||
if (response == QMessageBox::Yes) {
|
|
||||||
ShowPasswordSetting();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// tell the user they still can set the password later in our settings dialog
|
|
||||||
QMessageBox::information(mainWindow, dialogTitle, dismissedText);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
22
src/Config.h
22
src/Config.h
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 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,12 +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/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
#include <obs-frontend-api.h>
|
#include <QString>
|
||||||
#include <util/config-file.h>
|
|
||||||
#include <QtCore/QString>
|
|
||||||
#include <QtCore/QSharedPointer>
|
|
||||||
|
|
||||||
class Config {
|
class Config {
|
||||||
public:
|
public:
|
||||||
@ -29,10 +27,6 @@ class Config {
|
|||||||
~Config();
|
~Config();
|
||||||
void Load();
|
void Load();
|
||||||
void Save();
|
void Save();
|
||||||
void SetDefaults();
|
|
||||||
config_t* GetConfigStore();
|
|
||||||
|
|
||||||
void MigrateFromGlobalSettings();
|
|
||||||
|
|
||||||
void SetPassword(QString password);
|
void SetPassword(QString password);
|
||||||
bool CheckAuth(QString userChallenge);
|
bool CheckAuth(QString userChallenge);
|
||||||
@ -42,7 +36,6 @@ class Config {
|
|||||||
|
|
||||||
bool ServerEnabled;
|
bool ServerEnabled;
|
||||||
uint64_t ServerPort;
|
uint64_t ServerPort;
|
||||||
bool LockToIPv4;
|
|
||||||
|
|
||||||
bool DebugEnabled;
|
bool DebugEnabled;
|
||||||
bool AlertsEnabled;
|
bool AlertsEnabled;
|
||||||
@ -53,7 +46,10 @@ class Config {
|
|||||||
QString SessionChallenge;
|
QString SessionChallenge;
|
||||||
bool SettingsLoaded;
|
bool SettingsLoaded;
|
||||||
|
|
||||||
|
static Config* Current();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void OnFrontendEvent(enum obs_frontend_event event, void* param);
|
static Config* _instance;
|
||||||
static void FirstRunPasswordSetup();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif // CONFIG_H
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
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);
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
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;
|
|
||||||
};
|
|
678
src/Utils.cpp
678
src/Utils.cpp
@ -16,16 +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 <inttypes.h>
|
#include <QMainWindow>
|
||||||
#include <QtWidgets/QMainWindow>
|
#include <QDir>
|
||||||
#include <QtCore/QDir>
|
#include <QUrl>
|
||||||
#include <QtCore/QUrl>
|
|
||||||
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
#include <util/platform.h>
|
|
||||||
#include <obs-data.h>
|
|
||||||
|
|
||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
@ -33,84 +28,22 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
Q_DECLARE_METATYPE(OBSScene);
|
Q_DECLARE_METATYPE(OBSScene);
|
||||||
|
|
||||||
const QHash<obs_bounds_type, QString> boundTypeNames = {
|
obs_data_array_t* Utils::StringListToArray(char** strings, char* key) {
|
||||||
{ OBS_BOUNDS_STRETCH, "OBS_BOUNDS_STRETCH" },
|
if (!strings)
|
||||||
{ OBS_BOUNDS_SCALE_INNER, "OBS_BOUNDS_SCALE_INNER" },
|
return obs_data_array_create();
|
||||||
{ OBS_BOUNDS_SCALE_OUTER, "OBS_BOUNDS_SCALE_OUTER" },
|
|
||||||
{ OBS_BOUNDS_SCALE_TO_WIDTH, "OBS_BOUNDS_SCALE_TO_WIDTH" },
|
|
||||||
{ OBS_BOUNDS_SCALE_TO_HEIGHT, "OBS_BOUNDS_SCALE_TO_HEIGHT" },
|
|
||||||
{ OBS_BOUNDS_MAX_ONLY, "OBS_BOUNDS_MAX_ONLY" },
|
|
||||||
{ OBS_BOUNDS_NONE, "OBS_BOUNDS_NONE" },
|
|
||||||
};
|
|
||||||
|
|
||||||
QString getBoundsNameFromType(obs_bounds_type type) {
|
|
||||||
QString fallback = boundTypeNames.value(OBS_BOUNDS_NONE);
|
|
||||||
return boundTypeNames.value(type, fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_bounds_type getBoundsTypeFromName(QString name) {
|
|
||||||
return boundTypeNames.key(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QHash<obs_scale_type, QString> scaleTypeNames = {
|
|
||||||
{ OBS_SCALE_DISABLE, "OBS_SCALE_DISABLE" },
|
|
||||||
{ OBS_SCALE_POINT, "OBS_SCALE_POINT" },
|
|
||||||
{ OBS_SCALE_BICUBIC, "OBS_SCALE_BICUBIC" },
|
|
||||||
{ OBS_SCALE_BILINEAR, "OBS_SCALE_BILINEAR" },
|
|
||||||
{ OBS_SCALE_LANCZOS, "OBS_SCALE_LANCZOS" },
|
|
||||||
{ OBS_SCALE_AREA, "OBS_SCALE_AREA" },
|
|
||||||
};
|
|
||||||
|
|
||||||
QString getScaleNameFromType(obs_scale_type type) {
|
|
||||||
QString fallback = scaleTypeNames.value(OBS_SCALE_DISABLE);
|
|
||||||
return scaleTypeNames.value(type, fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_scale_type getFilterTypeFromName(QString name) {
|
|
||||||
return scaleTypeNames.key(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Utils::StringInStringList(char** strings, const char* string) {
|
|
||||||
if (!strings) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = 0;
|
|
||||||
while (strings[index] != NULL) {
|
|
||||||
char* value = strings[index];
|
|
||||||
|
|
||||||
if (strcmp(value, string) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_array_t* Utils::StringListToArray(char** strings, const char* key) {
|
|
||||||
obs_data_array_t* list = obs_data_array_create();
|
obs_data_array_t* list = obs_data_array_create();
|
||||||
|
|
||||||
if (!strings || !key) {
|
char* value = "";
|
||||||
return list; // empty list
|
for (int i = 0; value != nullptr; i++) {
|
||||||
}
|
value = strings[i];
|
||||||
|
|
||||||
size_t index = 0;
|
|
||||||
char* value = nullptr;
|
|
||||||
|
|
||||||
do {
|
|
||||||
value = strings[index];
|
|
||||||
|
|
||||||
OBSDataAutoRelease item = obs_data_create();
|
OBSDataAutoRelease item = obs_data_create();
|
||||||
obs_data_set_string(item, key, value);
|
obs_data_set_string(item, key, value);
|
||||||
|
|
||||||
if (value) {
|
if (value)
|
||||||
obs_data_array_push_back(list, item);
|
obs_data_array_push_back(list, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
index++;
|
|
||||||
} while (value != nullptr);
|
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@ -119,16 +52,15 @@ obs_data_array_t* Utils::GetSceneItems(obs_source_t* source) {
|
|||||||
obs_data_array_t* items = obs_data_array_create();
|
obs_data_array_t* items = obs_data_array_create();
|
||||||
OBSScene scene = obs_scene_from_source(source);
|
OBSScene scene = obs_scene_from_source(source);
|
||||||
|
|
||||||
if (!scene) {
|
if (!scene)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
obs_scene_enum_items(scene, [](
|
obs_scene_enum_items(scene, [](
|
||||||
obs_scene_t* scene,
|
obs_scene_t* scene,
|
||||||
obs_sceneitem_t* currentItem,
|
obs_sceneitem_t* currentItem,
|
||||||
void* param)
|
void* param)
|
||||||
{
|
{
|
||||||
obs_data_array_t* data = reinterpret_cast<obs_data_array_t*>(param);
|
obs_data_array_t* data = static_cast<obs_data_array_t*>(param);
|
||||||
|
|
||||||
OBSDataAutoRelease itemData = GetSceneItemData(currentItem);
|
OBSDataAutoRelease itemData = GetSceneItemData(currentItem);
|
||||||
obs_data_array_insert(data, 0, itemData);
|
obs_data_array_insert(data, 0, itemData);
|
||||||
@ -138,29 +70,9 @@ obs_data_array_t* Utils::GetSceneItems(obs_source_t* source) {
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} `SceneItem` An OBS Scene Item.
|
|
||||||
* @property {Number} `cy`
|
|
||||||
* @property {Number} `cx`
|
|
||||||
* @property {Number} `alignment` The point on the source that the item is manipulated from. The sum of 1=Left or 2=Right, and 4=Top or 8=Bottom, or omit to center on that axis.
|
|
||||||
* @property {String} `name` The name of this Scene Item.
|
|
||||||
* @property {int} `id` Scene item ID
|
|
||||||
* @property {Boolean} `render` Whether or not this Scene Item is set to "visible".
|
|
||||||
* @property {Boolean} `muted` Whether or not this Scene Item is muted.
|
|
||||||
* @property {Boolean} `locked` Whether or not this Scene Item is locked and can't be moved around
|
|
||||||
* @property {Number} `source_cx`
|
|
||||||
* @property {Number} `source_cy`
|
|
||||||
* @property {String} `type` Source type. Value is one of the following: "input", "filter", "transition", "scene" or "unknown"
|
|
||||||
* @property {Number} `volume`
|
|
||||||
* @property {Number} `x`
|
|
||||||
* @property {Number} `y`
|
|
||||||
* @property {String (optional)} `parentGroupName` Name of the item's parent (if this item belongs to a group)
|
|
||||||
* @property {Array<SceneItem> (optional)} `groupChildren` List of children (if this item is a group)
|
|
||||||
*/
|
|
||||||
obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
|
obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
|
||||||
if (!item) {
|
if (!item)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
vec2 pos;
|
vec2 pos;
|
||||||
obs_sceneitem_get_pos(item, &pos);
|
obs_sceneitem_get_pos(item, &pos);
|
||||||
@ -175,77 +87,58 @@ obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
|
|||||||
|
|
||||||
obs_data_t* data = obs_data_create();
|
obs_data_t* data = obs_data_create();
|
||||||
obs_data_set_string(data, "name",
|
obs_data_set_string(data, "name",
|
||||||
obs_source_get_name(itemSource));
|
obs_source_get_name(obs_sceneitem_get_source(item)));
|
||||||
obs_data_set_int(data, "id",
|
|
||||||
obs_sceneitem_get_id(item));
|
|
||||||
obs_data_set_string(data, "type",
|
obs_data_set_string(data, "type",
|
||||||
obs_source_get_id(itemSource));
|
obs_source_get_id(obs_sceneitem_get_source(item)));
|
||||||
obs_data_set_double(data, "volume",
|
obs_data_set_double(data, "volume",
|
||||||
obs_source_get_volume(itemSource));
|
obs_source_get_volume(obs_sceneitem_get_source(item)));
|
||||||
obs_data_set_double(data, "x", pos.x);
|
obs_data_set_double(data, "x", pos.x);
|
||||||
obs_data_set_double(data, "y", pos.y);
|
obs_data_set_double(data, "y", pos.y);
|
||||||
obs_data_set_int(data, "source_cx", (int)item_width);
|
obs_data_set_int(data, "source_cx", (int)item_width);
|
||||||
obs_data_set_int(data, "source_cy", (int)item_height);
|
obs_data_set_int(data, "source_cy", (int)item_height);
|
||||||
obs_data_set_bool(data, "muted", obs_source_muted(itemSource));
|
obs_data_set_double(data, "cx", item_width* scale.x);
|
||||||
obs_data_set_int(data, "alignment", (int)obs_sceneitem_get_alignment(item));
|
obs_data_set_double(data, "cy", item_height* scale.y);
|
||||||
obs_data_set_double(data, "cx", item_width * scale.x);
|
|
||||||
obs_data_set_double(data, "cy", item_height * scale.y);
|
|
||||||
obs_data_set_bool(data, "render", obs_sceneitem_visible(item));
|
obs_data_set_bool(data, "render", obs_sceneitem_visible(item));
|
||||||
obs_data_set_bool(data, "locked", obs_sceneitem_locked(item));
|
|
||||||
|
|
||||||
obs_scene_t* parent = obs_sceneitem_get_scene(item);
|
|
||||||
if (parent) {
|
|
||||||
OBSSource parentSource = obs_scene_get_source(parent);
|
|
||||||
QString parentKind = obs_source_get_id(parentSource);
|
|
||||||
if (parentKind == "group") {
|
|
||||||
obs_data_set_string(data, "parentGroupName", obs_source_get_name(parentSource));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obs_sceneitem_is_group(item)) {
|
|
||||||
OBSDataArrayAutoRelease children = obs_data_array_create();
|
|
||||||
obs_sceneitem_group_enum_items(item, [](obs_scene_t*, obs_sceneitem_t* currentItem, void* param) {
|
|
||||||
obs_data_array_t* items = reinterpret_cast<obs_data_array_t*>(param);
|
|
||||||
|
|
||||||
OBSDataAutoRelease itemData = GetSceneItemData(currentItem);
|
|
||||||
obs_data_array_push_back(items, itemData);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}, children);
|
|
||||||
obs_data_set_array(data, "groupChildren", children);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_t* Utils::GetSceneItemFromName(obs_scene_t* scene, QString name) {
|
obs_sceneitem_t* Utils::GetSceneItemFromItem(obs_source_t* source, obs_data_t* item) {
|
||||||
if (!scene) {
|
OBSSceneItem sceneItem;
|
||||||
return nullptr;
|
if (obs_data_has_user_value(item, "id")) {
|
||||||
}
|
sceneItem = GetSceneItemFromId(source, obs_data_get_int(item, "id"));
|
||||||
|
if (obs_data_has_user_value(item, "name") &&
|
||||||
|
(QString)obs_source_get_name(obs_sceneitem_get_source(sceneItem)) !=
|
||||||
|
(QString)obs_data_get_string(item, "name")) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (obs_data_has_user_value(item, "name")) {
|
||||||
|
sceneItem = GetSceneItemFromName(source, obs_data_get_string(item, "name"));
|
||||||
|
}
|
||||||
|
return sceneItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, QString name) {
|
||||||
struct current_search {
|
struct current_search {
|
||||||
QString query;
|
QString query;
|
||||||
obs_sceneitem_t* result;
|
obs_sceneitem_t* result;
|
||||||
bool (*enumCallback)(obs_scene_t*, obs_sceneitem_t*, void*);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
current_search search;
|
current_search search;
|
||||||
search.query = name;
|
search.query = name;
|
||||||
search.result = nullptr;
|
search.result = nullptr;
|
||||||
|
|
||||||
search.enumCallback = [](
|
OBSScene scene = obs_scene_from_source(source);
|
||||||
|
if (!scene)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
obs_scene_enum_items(scene, [](
|
||||||
obs_scene_t* scene,
|
obs_scene_t* scene,
|
||||||
obs_sceneitem_t* currentItem,
|
obs_sceneitem_t* currentItem,
|
||||||
void* param)
|
void* param)
|
||||||
{
|
{
|
||||||
current_search* search = reinterpret_cast<current_search*>(param);
|
current_search* search = static_cast<current_search*>(param);
|
||||||
|
|
||||||
if (obs_sceneitem_is_group(currentItem)) {
|
|
||||||
obs_sceneitem_group_enum_items(currentItem, search->enumCallback, search);
|
|
||||||
if (search->result) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString currentItemName =
|
QString currentItemName =
|
||||||
obs_source_get_name(obs_sceneitem_get_source(currentItem));
|
obs_source_get_name(obs_sceneitem_get_source(currentItem));
|
||||||
@ -257,41 +150,31 @@ obs_sceneitem_t* Utils::GetSceneItemFromName(obs_scene_t* scene, QString name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
}, &search);
|
||||||
|
|
||||||
obs_scene_enum_items(scene, search.enumCallback, &search);
|
|
||||||
|
|
||||||
return search.result;
|
return search.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_t* Utils::GetSceneItemFromId(obs_scene_t* scene, int64_t id) {
|
obs_sceneitem_t* Utils::GetSceneItemFromId(obs_source_t* source, size_t id) {
|
||||||
if (!scene) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct current_search {
|
struct current_search {
|
||||||
int query;
|
size_t query;
|
||||||
obs_sceneitem_t* result;
|
obs_sceneitem_t* result;
|
||||||
bool (*enumCallback)(obs_scene_t*, obs_sceneitem_t*, void*);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
current_search search;
|
current_search search;
|
||||||
search.query = id;
|
search.query = id;
|
||||||
search.result = nullptr;
|
search.result = nullptr;
|
||||||
|
|
||||||
search.enumCallback = [](
|
OBSScene scene = obs_scene_from_source(source);
|
||||||
obs_scene_t* scene,
|
if (!scene)
|
||||||
obs_sceneitem_t* currentItem,
|
return nullptr;
|
||||||
void* param)
|
|
||||||
{
|
|
||||||
current_search* search = reinterpret_cast<current_search*>(param);
|
|
||||||
|
|
||||||
if (obs_sceneitem_is_group(currentItem)) {
|
obs_scene_enum_items(scene, [](
|
||||||
obs_sceneitem_group_enum_items(currentItem, search->enumCallback, search);
|
obs_scene_t* scene,
|
||||||
if (search->result) {
|
obs_sceneitem_t* currentItem,
|
||||||
return false;
|
void* param)
|
||||||
}
|
{
|
||||||
}
|
current_search* search = static_cast<current_search*>(param);
|
||||||
|
|
||||||
if (obs_sceneitem_get_id(currentItem) == search->query) {
|
if (obs_sceneitem_get_id(currentItem) == search->query) {
|
||||||
search->result = currentItem;
|
search->result = currentItem;
|
||||||
@ -300,56 +183,11 @@ obs_sceneitem_t* Utils::GetSceneItemFromId(obs_scene_t* scene, int64_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
}, &search);
|
||||||
|
|
||||||
obs_scene_enum_items(scene, search.enumCallback, &search);
|
|
||||||
|
|
||||||
return search.result;
|
return search.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_sceneitem_t* Utils::GetSceneItemFromItem(obs_scene_t* scene, obs_data_t* itemInfo) {
|
|
||||||
if (!scene) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataItemAutoRelease idInfoItem = obs_data_item_byname(itemInfo, "id");
|
|
||||||
int id = obs_data_item_get_int(idInfoItem);
|
|
||||||
|
|
||||||
OBSDataItemAutoRelease nameInfoItem = obs_data_item_byname(itemInfo, "name");
|
|
||||||
const char* name = obs_data_item_get_string(nameInfoItem);
|
|
||||||
|
|
||||||
if (idInfoItem) {
|
|
||||||
obs_sceneitem_t* sceneItem = GetSceneItemFromId(scene, id);
|
|
||||||
obs_source_t* sceneItemSource = obs_sceneitem_get_source(sceneItem);
|
|
||||||
|
|
||||||
QString sceneItemName = obs_source_get_name(sceneItemSource);
|
|
||||||
if (nameInfoItem && (QString(name) != sceneItemName)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sceneItem;
|
|
||||||
} else if (nameInfoItem) {
|
|
||||||
return GetSceneItemFromName(scene, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_sceneitem_t* Utils::GetSceneItemFromRequestField(obs_scene_t* scene, obs_data_item_t* dataItem)
|
|
||||||
{
|
|
||||||
enum obs_data_type dataType = obs_data_item_gettype(dataItem);
|
|
||||||
|
|
||||||
if (dataType == OBS_DATA_OBJECT) {
|
|
||||||
OBSDataAutoRelease itemData = obs_data_item_get_obj(dataItem);
|
|
||||||
return GetSceneItemFromItem(scene, itemData);
|
|
||||||
} else if (dataType == OBS_DATA_STRING) {
|
|
||||||
QString name = obs_data_item_get_string(dataItem);
|
|
||||||
return GetSceneItemFromName(scene, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Utils::IsValidAlignment(const uint32_t alignment) {
|
bool Utils::IsValidAlignment(const uint32_t alignment) {
|
||||||
switch (alignment) {
|
switch (alignment) {
|
||||||
case OBS_ALIGN_CENTER:
|
case OBS_ALIGN_CENTER:
|
||||||
@ -388,19 +226,17 @@ obs_source_t* Utils::GetTransitionFromName(QString searchName) {
|
|||||||
return foundTransition;
|
return foundTransition;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_scene_t* Utils::GetSceneFromNameOrCurrent(QString sceneName) {
|
obs_source_t* Utils::GetSceneFromNameOrCurrent(QString sceneName) {
|
||||||
// Both obs_frontend_get_current_scene() and obs_get_source_by_name()
|
// Both obs_frontend_get_current_scene() and obs_get_source_by_name()
|
||||||
// increase the returned source's refcount
|
// do addref on the return source, so no need to use an OBSSource helper
|
||||||
OBSSourceAutoRelease sceneSource = nullptr;
|
obs_source_t* scene = nullptr;
|
||||||
|
|
||||||
if (sceneName.isEmpty() || sceneName.isNull()) {
|
if (sceneName.isEmpty() || sceneName.isNull())
|
||||||
sceneSource = obs_frontend_get_current_scene();
|
scene = obs_frontend_get_current_scene();
|
||||||
}
|
else
|
||||||
else {
|
scene = obs_get_source_by_name(sceneName.toUtf8());
|
||||||
sceneSource = obs_get_source_by_name(sceneName.toUtf8());
|
|
||||||
}
|
|
||||||
|
|
||||||
return obs_scene_from_source(sceneSource);
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_array_t* Utils::GetScenes() {
|
obs_data_array_t* Utils::GetScenes() {
|
||||||
@ -433,37 +269,18 @@ QSpinBox* Utils::GetTransitionDurationControl() {
|
|||||||
return window->findChild<QSpinBox*>("transitionDuration");
|
return window->findChild<QSpinBox*>("transitionDuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
int Utils::GetTransitionDuration(obs_source_t* transition) {
|
int Utils::GetTransitionDuration() {
|
||||||
if (!transition || obs_source_get_type(transition) != OBS_SOURCE_TYPE_TRANSITION) {
|
QSpinBox* control = GetTransitionDurationControl();
|
||||||
|
if (control)
|
||||||
|
return control->value();
|
||||||
|
else
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString transitionKind = obs_source_get_id(transition);
|
void Utils::SetTransitionDuration(int ms) {
|
||||||
if (transitionKind == "cut_transition") {
|
QSpinBox* control = GetTransitionDurationControl();
|
||||||
// If this is a Cut transition, return 0
|
if (control && ms >= 0)
|
||||||
return 0;
|
control->setValue(ms);
|
||||||
}
|
|
||||||
|
|
||||||
if (obs_transition_fixed(transition)) {
|
|
||||||
// If this transition has a fixed duration (such as a Stinger),
|
|
||||||
// we don't currently have a way of retrieving that number.
|
|
||||||
// For now, return -1 to indicate that we don't know the actual duration.
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease destinationScene = obs_transition_get_active_source(transition);
|
|
||||||
OBSDataAutoRelease destinationSettings = obs_source_get_private_settings(destinationScene);
|
|
||||||
|
|
||||||
// Detect if transition is the global transition or a transition override.
|
|
||||||
// Fetching the duration is different depending on the case.
|
|
||||||
obs_data_item_t* transitionDurationItem = obs_data_item_byname(destinationSettings, "transition_duration");
|
|
||||||
int duration = (
|
|
||||||
transitionDurationItem
|
|
||||||
? obs_data_item_get_int(transitionDurationItem)
|
|
||||||
: obs_frontend_get_transition_duration()
|
|
||||||
);
|
|
||||||
|
|
||||||
return duration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::SetTransitionByName(QString transitionName) {
|
bool Utils::SetTransitionByName(QString transitionName) {
|
||||||
@ -477,35 +294,51 @@ bool Utils::SetTransitionByName(QString transitionName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_t* Utils::GetTransitionData(obs_source_t* transition) {
|
QPushButton* Utils::GetPreviewModeButtonControl() {
|
||||||
int duration = Utils::GetTransitionDuration(transition);
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
if (duration < 0) {
|
return main->findChild<QPushButton*>("modeSwitch");
|
||||||
blog(LOG_WARNING, "GetTransitionData: duration is negative !");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease sourceScene = obs_transition_get_source(transition, OBS_TRANSITION_SOURCE_A);
|
QListWidget* Utils::GetSceneListControl() {
|
||||||
OBSSourceAutoRelease destinationScene = obs_transition_get_active_source(transition);
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
return main->findChild<QListWidget*>("scenes");
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_t* transitionData = obs_data_create();
|
obs_scene_t* Utils::SceneListItemToScene(QListWidgetItem* item) {
|
||||||
obs_data_set_string(transitionData, "name", obs_source_get_name(transition));
|
if (!item)
|
||||||
obs_data_set_string(transitionData, "type", obs_source_get_id(transition));
|
return nullptr;
|
||||||
obs_data_set_int(transitionData, "duration", duration);
|
|
||||||
|
|
||||||
// When a transition starts and while it is running, SOURCE_A is the source scene
|
QVariant itemData = item->data(static_cast<int>(Qt::UserRole));
|
||||||
// and SOURCE_B is the destination scene.
|
return itemData.value<OBSScene>();
|
||||||
// Before the transition_end event is triggered on a transition, the destination scene
|
}
|
||||||
// goes into SOURCE_A and SOURCE_B becomes null. This means that, in transition_stop
|
|
||||||
// we don't know what was the source scene
|
|
||||||
// TODO fix this in libobs
|
|
||||||
|
|
||||||
bool isTransitionEndEvent = (sourceScene == destinationScene);
|
QLayout* Utils::GetPreviewLayout() {
|
||||||
if (!isTransitionEndEvent) {
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
obs_data_set_string(transitionData, "from-scene", obs_source_get_name(sourceScene));
|
return main->findChild<QLayout*>("previewLayout");
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_set_string(transitionData, "to-scene", obs_source_get_name(destinationScene));
|
|
||||||
|
|
||||||
return transitionData;
|
void Utils::TransitionToProgram() {
|
||||||
|
if (!obs_frontend_preview_program_mode_active())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// WARNING : if the layout created in OBS' CreateProgramOptions() changes
|
||||||
|
// then this won't work as expected
|
||||||
|
|
||||||
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
|
||||||
|
// The program options widget is the second item in the left-to-right layout
|
||||||
|
QWidget* programOptions = GetPreviewLayout()->itemAt(1)->widget();
|
||||||
|
|
||||||
|
// The "Transition" button lies in the mainButtonLayout
|
||||||
|
// which is the first itemin the program options' layout
|
||||||
|
QLayout* mainButtonLayout = programOptions->layout()->itemAt(1)->layout();
|
||||||
|
QWidget* transitionBtnWidget = mainButtonLayout->itemAt(0)->widget();
|
||||||
|
|
||||||
|
// Try to cast that widget into a button
|
||||||
|
QPushButton* transitionBtn = qobject_cast<QPushButton*>(transitionBtnWidget);
|
||||||
|
|
||||||
|
// Perform a click on that button
|
||||||
|
transitionBtn->click();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Utils::OBSVersionString() {
|
QString Utils::OBSVersionString() {
|
||||||
@ -523,13 +356,15 @@ QString Utils::OBSVersionString() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QSystemTrayIcon* Utils::GetTrayIcon() {
|
QSystemTrayIcon* Utils::GetTrayIcon() {
|
||||||
void* systemTray = obs_frontend_get_system_tray();
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
return reinterpret_cast<QSystemTrayIcon*>(systemTray);
|
if (!main) return nullptr;
|
||||||
|
|
||||||
|
return main->findChildren<QSystemTrayIcon*>().first();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::SysTrayNotify(QString text, QSystemTrayIcon::MessageIcon icon, QString title) {
|
void Utils::SysTrayNotify(QString &text,
|
||||||
auto config = GetConfig();
|
QSystemTrayIcon::MessageIcon icon, QString title) {
|
||||||
if ((config && !config->AlertsEnabled) ||
|
if (!Config::Current()->AlertsEnabled ||
|
||||||
!QSystemTrayIcon::isSystemTrayAvailable() ||
|
!QSystemTrayIcon::isSystemTrayAvailable() ||
|
||||||
!QSystemTrayIcon::supportsMessages())
|
!QSystemTrayIcon::supportsMessages())
|
||||||
{
|
{
|
||||||
@ -541,6 +376,15 @@ void Utils::SysTrayNotify(QString text, QSystemTrayIcon::MessageIcon icon, QStri
|
|||||||
trayIcon->showMessage(title, text, icon);
|
trayIcon->showMessage(title, text, icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Utils::FormatIPAddress(QHostAddress &addr) {
|
||||||
|
QRegExp v4regex("(::ffff:)(((\\d).){3})", Qt::CaseInsensitive);
|
||||||
|
QString addrString = addr.toString();
|
||||||
|
if (addrString.contains(v4regex)) {
|
||||||
|
addrString = QHostAddress(addr.toIPv4Address()).toString();
|
||||||
|
}
|
||||||
|
return addrString;
|
||||||
|
}
|
||||||
|
|
||||||
const char* Utils::GetRecordingFolder() {
|
const char* Utils::GetRecordingFolder() {
|
||||||
config_t* profile = obs_frontend_get_profile_config();
|
config_t* profile = obs_frontend_get_profile_config();
|
||||||
QString outputMode = config_get_string(profile, "Output", "Mode");
|
QString outputMode = config_get_string(profile, "Output", "Mode");
|
||||||
@ -556,13 +400,17 @@ const char* Utils::GetRecordingFolder() {
|
|||||||
|
|
||||||
bool Utils::SetRecordingFolder(const char* path) {
|
bool Utils::SetRecordingFolder(const char* path) {
|
||||||
QDir dir(path);
|
QDir dir(path);
|
||||||
if (!dir.exists()) {
|
if (!dir.exists())
|
||||||
dir.mkpath(".");
|
dir.mkpath(".");
|
||||||
}
|
|
||||||
|
|
||||||
config_t* profile = obs_frontend_get_profile_config();
|
config_t* profile = obs_frontend_get_profile_config();
|
||||||
config_set_string(profile, "AdvOut", "RecFilePath", path);
|
QString outputMode = config_get_string(profile, "Output", "Mode");
|
||||||
config_set_string(profile, "SimpleOutput", "FilePath", path);
|
|
||||||
|
if (outputMode == "Advanced") {
|
||||||
|
config_set_string(profile, "AdvOut", "RecFilePath", path);
|
||||||
|
} else {
|
||||||
|
config_set_string(profile, "SimpleOutput", "FilePath", path);
|
||||||
|
}
|
||||||
|
|
||||||
config_save(profile);
|
config_save(profile);
|
||||||
return true;
|
return true;
|
||||||
@ -636,7 +484,7 @@ obs_hotkey_t* Utils::FindHotkeyByName(QString name) {
|
|||||||
search.result = nullptr;
|
search.result = nullptr;
|
||||||
|
|
||||||
obs_enum_hotkeys([](void* data, obs_hotkey_id id, obs_hotkey_t* hotkey) {
|
obs_enum_hotkeys([](void* data, obs_hotkey_id id, obs_hotkey_t* hotkey) {
|
||||||
current_search* search = reinterpret_cast<current_search*>(data);
|
current_search* search = static_cast<current_search*>(data);
|
||||||
|
|
||||||
const char* hk_name = obs_hotkey_get_name(hotkey);
|
const char* hk_name = obs_hotkey_get_name(hotkey);
|
||||||
if (hk_name == search->query) {
|
if (hk_name == search->query) {
|
||||||
@ -672,7 +520,7 @@ void Utils::StartReplayBuffer() {
|
|||||||
obs_output_t* rpOutput = obs_frontend_get_replay_buffer_output();
|
obs_output_t* rpOutput = obs_frontend_get_replay_buffer_output();
|
||||||
OBSData outputHotkeys = obs_hotkeys_save_output(rpOutput);
|
OBSData outputHotkeys = obs_hotkeys_save_output(rpOutput);
|
||||||
|
|
||||||
OBSDataAutoRelease dummyBinding = obs_data_create();
|
OBSData dummyBinding = obs_data_create();
|
||||||
obs_data_set_bool(dummyBinding, "control", true);
|
obs_data_set_bool(dummyBinding, "control", true);
|
||||||
obs_data_set_bool(dummyBinding, "alt", true);
|
obs_data_set_bool(dummyBinding, "alt", true);
|
||||||
obs_data_set_bool(dummyBinding, "shift", true);
|
obs_data_set_bool(dummyBinding, "shift", true);
|
||||||
@ -714,253 +562,3 @@ bool Utils::SetFilenameFormatting(const char* filenameFormatting) {
|
|||||||
config_save(profile);
|
config_save(profile);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Utils::GetCurrentRecordingFilename()
|
|
||||||
{
|
|
||||||
OBSOutputAutoRelease recordingOutput = obs_frontend_get_recording_output();
|
|
||||||
if (!recordingOutput) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease settings = obs_output_get_settings(recordingOutput);
|
|
||||||
|
|
||||||
// mimicks the behavior of BasicOutputHandler::GetRecordingFilename :
|
|
||||||
// try to fetch the path from the "url" property, then try "path" if the first one
|
|
||||||
// didn't yield any result
|
|
||||||
OBSDataItemAutoRelease item = obs_data_item_byname(settings, "url");
|
|
||||||
if (!item) {
|
|
||||||
item = obs_data_item_byname(settings, "path");
|
|
||||||
if (!item) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return obs_data_item_get_string(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform properties copy-pasted from WSRequestHandler_SceneItems.cpp because typedefs can't be extended yet
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} `SceneItemTransform`
|
|
||||||
* @property {double} `position.x` The x position of the scene item from the left.
|
|
||||||
* @property {double} `position.y` The y position of the scene item from the top.
|
|
||||||
* @property {int} `position.alignment` The point on the scene item that the item is manipulated from.
|
|
||||||
* @property {double} `rotation` The clockwise rotation of the scene item in degrees around the point of alignment.
|
|
||||||
* @property {double} `scale.x` The x-scale factor of the scene item.
|
|
||||||
* @property {double} `scale.y` The y-scale factor of the scene item.
|
|
||||||
* @property {String} `scale.filter` The scale filter of the source. Can be "OBS_SCALE_DISABLE", "OBS_SCALE_POINT", "OBS_SCALE_BICUBIC", "OBS_SCALE_BILINEAR", "OBS_SCALE_LANCZOS" or "OBS_SCALE_AREA".
|
|
||||||
* @property {int} `crop.top` The number of pixels cropped off the top of the scene item before scaling.
|
|
||||||
* @property {int} `crop.right` The number of pixels cropped off the right of the scene item before scaling.
|
|
||||||
* @property {int} `crop.bottom` The number of pixels cropped off the bottom of the scene item before scaling.
|
|
||||||
* @property {int} `crop.left` The number of pixels cropped off the left of the scene item before scaling.
|
|
||||||
* @property {bool} `visible` If the scene item is visible.
|
|
||||||
* @property {bool} `locked` If the scene item is locked in position.
|
|
||||||
* @property {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".
|
|
||||||
* @property {int} `bounds.alignment` Alignment of the bounding box.
|
|
||||||
* @property {double} `bounds.x` Width of the bounding box.
|
|
||||||
* @property {double} `bounds.y` Height of the bounding box.
|
|
||||||
* @property {int} `sourceWidth` Base width (without scaling) of the source
|
|
||||||
* @property {int} `sourceHeight` Base source (without scaling) of the source
|
|
||||||
* @property {double} `width` Scene item width (base source width multiplied by the horizontal scaling factor)
|
|
||||||
* @property {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)
|
|
||||||
*/
|
|
||||||
obs_data_t* Utils::GetSceneItemPropertiesData(obs_sceneitem_t* sceneItem) {
|
|
||||||
if (!sceneItem) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSource source = obs_sceneitem_get_source(sceneItem);
|
|
||||||
uint32_t baseSourceWidth = obs_source_get_width(source);
|
|
||||||
uint32_t baseSourceHeight = obs_source_get_height(source);
|
|
||||||
|
|
||||||
vec2 pos, scale, bounds;
|
|
||||||
obs_sceneitem_crop crop;
|
|
||||||
|
|
||||||
obs_sceneitem_get_pos(sceneItem, &pos);
|
|
||||||
obs_sceneitem_get_scale(sceneItem, &scale);
|
|
||||||
obs_sceneitem_get_crop(sceneItem, &crop);
|
|
||||||
obs_sceneitem_get_bounds(sceneItem, &bounds);
|
|
||||||
|
|
||||||
uint32_t alignment = obs_sceneitem_get_alignment(sceneItem);
|
|
||||||
float rotation = obs_sceneitem_get_rot(sceneItem);
|
|
||||||
bool isVisible = obs_sceneitem_visible(sceneItem);
|
|
||||||
bool isLocked = obs_sceneitem_locked(sceneItem);
|
|
||||||
|
|
||||||
obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(sceneItem);
|
|
||||||
uint32_t boundsAlignment = obs_sceneitem_get_bounds_alignment(sceneItem);
|
|
||||||
QString boundsTypeName = getBoundsNameFromType(boundsType);
|
|
||||||
|
|
||||||
obs_scale_type scaleFilter = obs_sceneitem_get_scale_filter(sceneItem);
|
|
||||||
QString scaleFilterName = getScaleNameFromType(scaleFilter);
|
|
||||||
|
|
||||||
OBSDataAutoRelease posData = obs_data_create();
|
|
||||||
obs_data_set_double(posData, "x", pos.x);
|
|
||||||
obs_data_set_double(posData, "y", pos.y);
|
|
||||||
obs_data_set_int(posData, "alignment", alignment);
|
|
||||||
|
|
||||||
OBSDataAutoRelease scaleData = obs_data_create();
|
|
||||||
obs_data_set_string(scaleData, "filter", scaleFilterName.toUtf8());
|
|
||||||
obs_data_set_double(scaleData, "x", scale.x);
|
|
||||||
obs_data_set_double(scaleData, "y", scale.y);
|
|
||||||
|
|
||||||
OBSDataAutoRelease cropData = obs_data_create();
|
|
||||||
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);
|
|
||||||
|
|
||||||
OBSDataAutoRelease boundsData = obs_data_create();
|
|
||||||
obs_data_set_string(boundsData, "type", boundsTypeName.toUtf8());
|
|
||||||
obs_data_set_int(boundsData, "alignment", boundsAlignment);
|
|
||||||
obs_data_set_double(boundsData, "x", bounds.x);
|
|
||||||
obs_data_set_double(boundsData, "y", bounds.y);
|
|
||||||
|
|
||||||
obs_data_t* data = obs_data_create();
|
|
||||||
obs_data_set_obj(data, "position", posData);
|
|
||||||
obs_data_set_double(data, "rotation", rotation);
|
|
||||||
obs_data_set_obj(data, "scale", scaleData);
|
|
||||||
obs_data_set_obj(data, "crop", cropData);
|
|
||||||
obs_data_set_bool(data, "visible", isVisible);
|
|
||||||
obs_data_set_bool(data, "locked", isLocked);
|
|
||||||
obs_data_set_obj(data, "bounds", boundsData);
|
|
||||||
|
|
||||||
obs_data_set_int(data, "sourceWidth", baseSourceWidth);
|
|
||||||
obs_data_set_int(data, "sourceHeight", baseSourceHeight);
|
|
||||||
obs_data_set_double(data, "width", baseSourceWidth * scale.x);
|
|
||||||
obs_data_set_double(data, "height", baseSourceHeight * scale.y);
|
|
||||||
|
|
||||||
obs_scene_t* parent = obs_sceneitem_get_scene(sceneItem);
|
|
||||||
if (parent) {
|
|
||||||
OBSSource parentSource = obs_scene_get_source(parent);
|
|
||||||
QString parentKind = obs_source_get_id(parentSource);
|
|
||||||
if (parentKind == "group") {
|
|
||||||
obs_data_set_string(data, "parentGroupName", obs_source_get_name(parentSource));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obs_sceneitem_is_group(sceneItem)) {
|
|
||||||
OBSDataArrayAutoRelease children = obs_data_array_create();
|
|
||||||
obs_sceneitem_group_enum_items(sceneItem, [](obs_scene_t*, obs_sceneitem_t* subItem, void* param) {
|
|
||||||
obs_data_array_t* items = reinterpret_cast<obs_data_array_t*>(param);
|
|
||||||
|
|
||||||
OBSDataAutoRelease itemData = GetSceneItemPropertiesData(subItem);
|
|
||||||
obs_data_array_push_back(items, itemData);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}, children);
|
|
||||||
obs_data_set_array(data, "groupChildren", children);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t* Utils::GetSourceFilterInfo(obs_source_t* filter, bool includeSettings)
|
|
||||||
{
|
|
||||||
obs_data_t* data = obs_data_create();
|
|
||||||
obs_data_set_bool(data, "enabled", obs_source_enabled(filter));
|
|
||||||
obs_data_set_string(data, "type", obs_source_get_id(filter));
|
|
||||||
obs_data_set_string(data, "name", obs_source_get_name(filter));
|
|
||||||
if (includeSettings) {
|
|
||||||
OBSDataAutoRelease settings = obs_source_get_settings(filter);
|
|
||||||
obs_data_set_obj(data, "settings", settings);
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_array_t* Utils::GetSourceFiltersList(obs_source_t* source, bool includeSettings)
|
|
||||||
{
|
|
||||||
struct enum_params {
|
|
||||||
obs_data_array_t* filters;
|
|
||||||
bool includeSettings;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!source) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct enum_params enumParams;
|
|
||||||
|
|
||||||
enumParams.filters = obs_data_array_create();
|
|
||||||
enumParams.includeSettings = includeSettings;
|
|
||||||
|
|
||||||
obs_source_enum_filters(source, [](obs_source_t* parent, obs_source_t* child, void* param)
|
|
||||||
{
|
|
||||||
auto enumParams = reinterpret_cast<struct enum_params*>(param);
|
|
||||||
|
|
||||||
OBSDataAutoRelease filterData = Utils::GetSourceFilterInfo(child, enumParams->includeSettings);
|
|
||||||
obs_data_array_push_back(enumParams->filters, filterData);
|
|
||||||
}, &enumParams);
|
|
||||||
|
|
||||||
return enumParams.filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
void getPauseRecordingFunctions(RecordingPausedFunction* recPausedFuncPtr, PauseRecordingFunction* pauseRecFuncPtr)
|
|
||||||
{
|
|
||||||
void* frontendApi = os_dlopen("obs-frontend-api");
|
|
||||||
|
|
||||||
if (recPausedFuncPtr) {
|
|
||||||
*recPausedFuncPtr = (RecordingPausedFunction)os_dlsym(frontendApi, "obs_frontend_recording_paused");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pauseRecFuncPtr) {
|
|
||||||
*pauseRecFuncPtr = (PauseRecordingFunction)os_dlsym(frontendApi, "obs_frontend_recording_pause");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Utils::nsToTimestamp(uint64_t ns)
|
|
||||||
{
|
|
||||||
uint64_t ms = ns / 1000000ULL;
|
|
||||||
uint64_t secs = ms / 1000ULL;
|
|
||||||
uint64_t minutes = secs / 60ULL;
|
|
||||||
|
|
||||||
uint64_t hoursPart = minutes / 60ULL;
|
|
||||||
uint64_t minutesPart = minutes % 60ULL;
|
|
||||||
uint64_t secsPart = secs % 60ULL;
|
|
||||||
uint64_t msPart = ms % 1000ULL;
|
|
||||||
|
|
||||||
return QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Utils::AddSourceHelper(void *_data, obs_scene_t *scene)
|
|
||||||
{
|
|
||||||
auto *data = reinterpret_cast<AddSourceData*>(_data);
|
|
||||||
data->sceneItem = obs_scene_add(scene, data->source);
|
|
||||||
obs_sceneitem_set_visible(data->sceneItem, data->setVisible);
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t *Utils::OBSDataGetDefaults(obs_data_t *data)
|
|
||||||
{
|
|
||||||
obs_data_t *returnData = obs_data_create();
|
|
||||||
obs_data_item_t *item = NULL;
|
|
||||||
|
|
||||||
for (item = obs_data_first(data); item; obs_data_item_next(&item)) {
|
|
||||||
enum obs_data_type type = obs_data_item_gettype(item);
|
|
||||||
const char *name = obs_data_item_get_name(item);
|
|
||||||
|
|
||||||
if (type == OBS_DATA_STRING) {
|
|
||||||
const char *val = obs_data_item_get_string(item);
|
|
||||||
obs_data_set_string(returnData, name, val);
|
|
||||||
} else if (type == OBS_DATA_NUMBER) {
|
|
||||||
enum obs_data_number_type type = obs_data_item_numtype(item);
|
|
||||||
if (type == OBS_DATA_NUM_INT) {
|
|
||||||
long long val = obs_data_item_get_int(item);
|
|
||||||
obs_data_set_int(returnData, name, val);
|
|
||||||
} else {
|
|
||||||
double val = obs_data_item_get_double(item);
|
|
||||||
obs_data_set_double(returnData, name, val);
|
|
||||||
}
|
|
||||||
} else if (type == OBS_DATA_BOOLEAN) {
|
|
||||||
bool val = obs_data_item_get_bool(item);
|
|
||||||
obs_data_set_bool(returnData, name, val);
|
|
||||||
} else if (type == OBS_DATA_OBJECT) {
|
|
||||||
OBSDataAutoRelease obj = obs_data_item_get_obj(item);
|
|
||||||
obs_data_set_obj(returnData, name, obj);
|
|
||||||
} else if (type == OBS_DATA_ARRAY) {
|
|
||||||
OBSDataArrayAutoRelease array = obs_data_item_get_array(item);
|
|
||||||
obs_data_set_array(returnData, name, array);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return returnData;
|
|
||||||
}
|
|
||||||
|
108
src/Utils.h
108
src/Utils.h
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 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,84 +16,72 @@ 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/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QSpinBox>
|
||||||
#include <QtWidgets/QSpinBox>
|
#include <QPushButton>
|
||||||
#include <QtWidgets/QPushButton>
|
#include <QLayout>
|
||||||
#include <QtWidgets/QLayout>
|
#include <QListWidget>
|
||||||
#include <QtWidgets/QListWidget>
|
#include <QSystemTrayIcon>
|
||||||
#include <QtWidgets/QSystemTrayIcon>
|
#include <QHostAddress>
|
||||||
|
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
#include <util/config-file.h>
|
#include <util/config-file.h>
|
||||||
|
|
||||||
typedef void(*PauseRecordingFunction)(bool);
|
class Utils {
|
||||||
typedef bool(*RecordingPausedFunction)();
|
public:
|
||||||
|
static obs_data_array_t* StringListToArray(char** strings, char* key);
|
||||||
|
static obs_data_array_t* GetSceneItems(obs_source_t* source);
|
||||||
|
static obs_data_t* GetSceneItemData(obs_sceneitem_t* item);
|
||||||
|
static obs_sceneitem_t* GetSceneItemFromName(
|
||||||
|
obs_source_t* source, QString name);
|
||||||
|
static obs_sceneitem_t* GetSceneItemFromId(obs_source_t* source, size_t id);
|
||||||
|
static obs_sceneitem_t* GetSceneItemFromItem(obs_source_t* source, obs_data_t* item);
|
||||||
|
static obs_source_t* GetTransitionFromName(QString transitionName);
|
||||||
|
static obs_source_t* GetSceneFromNameOrCurrent(QString sceneName);
|
||||||
|
|
||||||
namespace Utils {
|
static bool IsValidAlignment(const uint32_t alignment);
|
||||||
bool StringInStringList(char** strings, const char* string);
|
|
||||||
obs_data_array_t* StringListToArray(char** strings, const char* key);
|
|
||||||
obs_data_array_t* GetSceneItems(obs_source_t* source);
|
|
||||||
obs_data_t* GetSceneItemData(obs_sceneitem_t* item);
|
|
||||||
|
|
||||||
// These functions support nested lookup into groups
|
static obs_data_array_t* GetScenes();
|
||||||
obs_sceneitem_t* GetSceneItemFromName(obs_scene_t* scene, QString name);
|
static obs_data_t* GetSceneData(obs_source_t* source);
|
||||||
obs_sceneitem_t* GetSceneItemFromId(obs_scene_t* scene, int64_t id);
|
|
||||||
obs_sceneitem_t* GetSceneItemFromItem(obs_scene_t* scene, obs_data_t* item);
|
|
||||||
obs_sceneitem_t* GetSceneItemFromRequestField(obs_scene_t* scene, obs_data_item_t* dataItem);
|
|
||||||
|
|
||||||
obs_scene_t* GetSceneFromNameOrCurrent(QString sceneName);
|
static QSpinBox* GetTransitionDurationControl();
|
||||||
obs_data_t* GetSceneItemPropertiesData(obs_sceneitem_t* item);
|
static int GetTransitionDuration();
|
||||||
|
static void SetTransitionDuration(int ms);
|
||||||
|
|
||||||
obs_data_t* GetSourceFilterInfo(obs_source_t* filter, bool includeSettings);
|
static bool SetTransitionByName(QString transitionName);
|
||||||
obs_data_array_t* GetSourceFiltersList(obs_source_t* source, bool includeSettings);
|
|
||||||
|
|
||||||
bool IsValidAlignment(const uint32_t alignment);
|
static QPushButton* GetPreviewModeButtonControl();
|
||||||
|
static QLayout* GetPreviewLayout();
|
||||||
|
static QListWidget* GetSceneListControl();
|
||||||
|
static obs_scene_t* SceneListItemToScene(QListWidgetItem* item);
|
||||||
|
|
||||||
obs_data_array_t* GetScenes();
|
static void TransitionToProgram();
|
||||||
obs_data_t* GetSceneData(obs_source_t* source);
|
|
||||||
|
|
||||||
// TODO contribute a proper frontend API method for this to OBS and remove this hack
|
static QString OBSVersionString();
|
||||||
QSpinBox* GetTransitionDurationControl();
|
|
||||||
int GetTransitionDuration(obs_source_t* transition);
|
|
||||||
obs_source_t* GetTransitionFromName(QString transitionName);
|
|
||||||
bool SetTransitionByName(QString transitionName);
|
|
||||||
obs_data_t* GetTransitionData(obs_source_t* transition);
|
|
||||||
|
|
||||||
QString OBSVersionString();
|
static QSystemTrayIcon* GetTrayIcon();
|
||||||
|
static void SysTrayNotify(
|
||||||
QSystemTrayIcon* GetTrayIcon();
|
QString &text,
|
||||||
void SysTrayNotify(
|
|
||||||
QString text,
|
|
||||||
QSystemTrayIcon::MessageIcon n,
|
QSystemTrayIcon::MessageIcon n,
|
||||||
QString title = QString("obs-websocket"));
|
QString title = QString("obs-websocket"));
|
||||||
|
|
||||||
const char* GetRecordingFolder();
|
static QString FormatIPAddress(QHostAddress &addr);
|
||||||
bool SetRecordingFolder(const char* path);
|
|
||||||
|
|
||||||
QString ParseDataToQueryString(obs_data_t* data);
|
static const char* GetRecordingFolder();
|
||||||
obs_hotkey_t* FindHotkeyByName(QString name);
|
static bool SetRecordingFolder(const char* path);
|
||||||
|
|
||||||
bool ReplayBufferEnabled();
|
static QString ParseDataToQueryString(obs_data_t* data);
|
||||||
void StartReplayBuffer();
|
static obs_hotkey_t* FindHotkeyByName(QString name);
|
||||||
bool IsRPHotkeySet();
|
static bool ReplayBufferEnabled();
|
||||||
|
static void StartReplayBuffer();
|
||||||
const char* GetFilenameFormatting();
|
static bool IsRPHotkeySet();
|
||||||
bool SetFilenameFormatting(const char* filenameFormatting);
|
static const char* GetFilenameFormatting();
|
||||||
|
static bool SetFilenameFormatting(const char* filenameFormatting);
|
||||||
const char* GetCurrentRecordingFilename();
|
|
||||||
|
|
||||||
QString nsToTimestamp(uint64_t ns);
|
|
||||||
struct AddSourceData {
|
|
||||||
obs_source_t *source;
|
|
||||||
obs_sceneitem_t *sceneItem;
|
|
||||||
bool setVisible;
|
|
||||||
};
|
|
||||||
void AddSourceHelper(void *_data, obs_scene_t *scene);
|
|
||||||
|
|
||||||
obs_data_t *OBSDataGetDefaults(obs_data_t *data);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif // UTILS_H
|
||||||
|
1705
src/WSEvents.cpp
1705
src/WSEvents.cpp
File diff suppressed because it is too large
Load Diff
107
src/WSEvents.h
107
src/WSEvents.h
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 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,63 +17,50 @@ 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/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#ifndef WSEVENTS_H
|
||||||
|
#define WSEVENTS_H
|
||||||
|
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <util/platform.h>
|
#include <QListWidgetItem>
|
||||||
|
|
||||||
#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
|
||||||
Q_OBJECT
|
public:
|
||||||
|
explicit WSEvents(WSServer* srv);
|
||||||
public:
|
|
||||||
explicit WSEvents(WSServerPtr srv);
|
|
||||||
~WSEvents();
|
~WSEvents();
|
||||||
|
static void FrontendEventHandler(
|
||||||
|
enum obs_frontend_event event, void* privateData);
|
||||||
|
static WSEvents* Instance;
|
||||||
|
void connectSceneSignals(obs_source_t* scene);
|
||||||
|
|
||||||
void connectSourceSignals(obs_source_t* source);
|
void hookTransitionBeginEvent();
|
||||||
void disconnectSourceSignals(obs_source_t* source);
|
|
||||||
|
|
||||||
void connectFilterSignals(obs_source_t* filter);
|
uint64_t GetStreamingTime();
|
||||||
void disconnectFilterSignals(obs_source_t* filter);
|
const char* GetStreamingTimecode();
|
||||||
|
uint64_t GetRecordingTime();
|
||||||
void hookTransitionPlaybackEvents();
|
const char* GetRecordingTimecode();
|
||||||
void unhookTransitionPlaybackEvents();
|
|
||||||
|
|
||||||
uint64_t getStreamingTime();
|
|
||||||
uint64_t getRecordingTime();
|
|
||||||
uint64_t getVirtualCamTime();
|
|
||||||
|
|
||||||
QString getStreamingTimecode();
|
|
||||||
QString getRecordingTimecode();
|
|
||||||
QString getVirtualCamTimecode();
|
|
||||||
|
|
||||||
obs_data_t* GetStats();
|
|
||||||
|
|
||||||
void OnBroadcastCustomMessage(QString realm, obs_data_t* data);
|
|
||||||
|
|
||||||
bool HeartbeatIsActive;
|
bool HeartbeatIsActive;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void deferredInitOperations();
|
||||||
void StreamStatus();
|
void StreamStatus();
|
||||||
void Heartbeat();
|
void Heartbeat();
|
||||||
void TransitionDurationChanged(int ms);
|
void TransitionDurationChanged(int ms);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WSServerPtr _srv;
|
WSServer* _srv;
|
||||||
QTimer streamStatusTimer;
|
OBSSource currentScene;
|
||||||
QTimer heartbeatTimer;
|
|
||||||
os_cpu_usage_info_t* cpuUsageInfo;
|
|
||||||
|
|
||||||
bool pulse;
|
bool pulse;
|
||||||
|
|
||||||
|
bool _streamingActive;
|
||||||
|
bool _recordingActive;
|
||||||
|
|
||||||
uint64_t _streamStarttime;
|
uint64_t _streamStarttime;
|
||||||
|
uint64_t _recStarttime;
|
||||||
|
|
||||||
uint64_t _lastBytesSent;
|
uint64_t _lastBytesSent;
|
||||||
uint64_t _lastBytesSentTime;
|
uint64_t _lastBytesSentTime;
|
||||||
@ -101,11 +88,6 @@ private:
|
|||||||
void OnRecordingStarted();
|
void OnRecordingStarted();
|
||||||
void OnRecordingStopping();
|
void OnRecordingStopping();
|
||||||
void OnRecordingStopped();
|
void OnRecordingStopped();
|
||||||
void OnRecordingPaused();
|
|
||||||
void OnRecordingResumed();
|
|
||||||
|
|
||||||
void OnVirtualCamStarted();
|
|
||||||
void OnVirtualCamStopped();
|
|
||||||
|
|
||||||
void OnReplayStarting();
|
void OnReplayStarting();
|
||||||
void OnReplayStarted();
|
void OnReplayStarted();
|
||||||
@ -117,45 +99,12 @@ private:
|
|||||||
|
|
||||||
void OnExit();
|
void OnExit();
|
||||||
|
|
||||||
static void FrontendEventHandler(
|
|
||||||
enum obs_frontend_event event, void* privateData);
|
|
||||||
|
|
||||||
static void OnTransitionBegin(void* param, calldata_t* data);
|
static void OnTransitionBegin(void* param, calldata_t* data);
|
||||||
static void OnTransitionEnd(void* param, calldata_t* data);
|
|
||||||
static void OnTransitionVideoEnd(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 OnSourceAudioActivated(void* param, calldata_t* data);
|
|
||||||
static void OnSourceAudioDeactivated(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 OnMediaPlaying(void* param, calldata_t* data);
|
|
||||||
static void OnMediaPaused(void* param, calldata_t* data);
|
|
||||||
static void OnMediaRestarted(void* param, calldata_t* data);
|
|
||||||
static void OnMediaStopped(void* param, calldata_t* data);
|
|
||||||
static void OnMediaNext(void* param, calldata_t* data);
|
|
||||||
static void OnMediaPrevious(void* param, calldata_t* data);
|
|
||||||
static void OnMediaStarted(void* param, calldata_t* data);
|
|
||||||
static void OnMediaEnded(void* param, calldata_t* data);
|
|
||||||
|
|
||||||
static void OnSceneReordered(void* param, calldata_t* data);
|
static void OnSceneReordered(void* param, calldata_t* data);
|
||||||
static void OnSceneItemAdd(void* param, calldata_t* data);
|
static void OnSceneItemAdd(void* param, calldata_t* data);
|
||||||
static void OnSceneItemDelete(void* param, calldata_t* data);
|
static void OnSceneItemDelete(void* param, calldata_t* data);
|
||||||
static void OnSceneItemVisibilityChanged(void* param, calldata_t* data);
|
static void OnSceneItemVisibilityChanged(void* param, calldata_t* data);
|
||||||
static void OnSceneItemLockChanged(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
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
* with this program. If not, see <https://www.gnu.org/licenses/>
|
* with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <obs-data.h>
|
#include <obs-data.h>
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
@ -26,196 +24,207 @@
|
|||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
QHash<QString, void(*)(WSRequestHandler*)> WSRequestHandler::messageMap {
|
||||||
|
{ "GetVersion", WSRequestHandler::HandleGetVersion },
|
||||||
|
{ "GetAuthRequired", WSRequestHandler::HandleGetAuthRequired },
|
||||||
|
{ "Authenticate", WSRequestHandler::HandleAuthenticate },
|
||||||
|
|
||||||
const QHash<QString, RpcMethodHandler> WSRequestHandler::messageMap{
|
{ "SetHeartbeat", WSRequestHandler::HandleSetHeartbeat },
|
||||||
// Category: General
|
|
||||||
{ "GetVersion", &WSRequestHandler::GetVersion },
|
|
||||||
{ "GetAuthRequired", &WSRequestHandler::GetAuthRequired },
|
|
||||||
{ "Authenticate", &WSRequestHandler::Authenticate },
|
|
||||||
{ "SetHeartbeat", &WSRequestHandler::SetHeartbeat },
|
|
||||||
{ "SetFilenameFormatting", &WSRequestHandler::SetFilenameFormatting },
|
|
||||||
{ "GetFilenameFormatting", &WSRequestHandler::GetFilenameFormatting },
|
|
||||||
{ "GetStats", &WSRequestHandler::GetStats },
|
|
||||||
{ "BroadcastCustomMessage", &WSRequestHandler::BroadcastCustomMessage },
|
|
||||||
{ "GetVideoInfo", &WSRequestHandler::GetVideoInfo },
|
|
||||||
{ "OpenProjector", &WSRequestHandler::OpenProjector },
|
|
||||||
{ "TriggerHotkeyByName", &WSRequestHandler::TriggerHotkeyByName },
|
|
||||||
{ "TriggerHotkeyBySequence", &WSRequestHandler::TriggerHotkeyBySequence },
|
|
||||||
{ "ExecuteBatch", &WSRequestHandler::ExecuteBatch },
|
|
||||||
{ "Sleep", &WSRequestHandler::Sleep },
|
|
||||||
|
|
||||||
// Category: Media Control
|
{ "SetFilenameFormatting", WSRequestHandler::HandleSetFilenameFormatting },
|
||||||
{ "PlayPauseMedia", &WSRequestHandler::PlayPauseMedia },
|
{ "GetFilenameFormatting", WSRequestHandler::HandleGetFilenameFormatting },
|
||||||
{ "RestartMedia", &WSRequestHandler::RestartMedia },
|
|
||||||
{ "StopMedia", &WSRequestHandler::StopMedia },
|
|
||||||
{ "NextMedia", &WSRequestHandler::NextMedia },
|
|
||||||
{ "PreviousMedia", &WSRequestHandler::PreviousMedia },
|
|
||||||
{ "GetMediaDuration", &WSRequestHandler::GetMediaDuration },
|
|
||||||
{ "GetMediaTime", &WSRequestHandler::GetMediaTime },
|
|
||||||
{ "SetMediaTime", &WSRequestHandler::SetMediaTime },
|
|
||||||
{ "ScrubMedia", &WSRequestHandler::ScrubMedia },
|
|
||||||
{ "GetMediaState", &WSRequestHandler::GetMediaState },
|
|
||||||
{ "GetMediaSourcesList", &WSRequestHandler::GetMediaSourcesList },
|
|
||||||
|
|
||||||
// Category: Outputs
|
{ "SetCurrentScene", WSRequestHandler::HandleSetCurrentScene },
|
||||||
{ "ListOutputs", &WSRequestHandler::ListOutputs },
|
{ "GetCurrentScene", WSRequestHandler::HandleGetCurrentScene },
|
||||||
{ "GetOutputInfo", &WSRequestHandler::GetOutputInfo },
|
{ "GetSceneList", WSRequestHandler::HandleGetSceneList },
|
||||||
{ "StartOutput", &WSRequestHandler::StartOutput },
|
|
||||||
{ "StopOutput", &WSRequestHandler::StopOutput },
|
|
||||||
|
|
||||||
// Category: Profiles
|
{ "SetSourceRender", WSRequestHandler::HandleSetSceneItemRender }, // Retrocompat
|
||||||
{ "SetCurrentProfile", &WSRequestHandler::SetCurrentProfile },
|
{ "SetSceneItemRender", WSRequestHandler::HandleSetSceneItemRender },
|
||||||
{ "GetCurrentProfile", &WSRequestHandler::GetCurrentProfile },
|
{ "SetSceneItemPosition", WSRequestHandler::HandleSetSceneItemPosition },
|
||||||
{ "ListProfiles", &WSRequestHandler::ListProfiles },
|
{ "SetSceneItemTransform", WSRequestHandler::HandleSetSceneItemTransform },
|
||||||
|
{ "SetSceneItemCrop", WSRequestHandler::HandleSetSceneItemCrop },
|
||||||
|
{ "GetSceneItemProperties", WSRequestHandler::HandleGetSceneItemProperties },
|
||||||
|
{ "SetSceneItemProperties", WSRequestHandler::HandleSetSceneItemProperties },
|
||||||
|
{ "ResetSceneItem", WSRequestHandler::HandleResetSceneItem },
|
||||||
|
{ "DeleteSceneItem", WSRequestHandler::HandleDeleteSceneItem },
|
||||||
|
{ "DuplicateSceneItem", WSRequestHandler::HandleDuplicateSceneItem },
|
||||||
|
{ "ReorderSceneItems", WSRequestHandler::HandleReorderSceneItems },
|
||||||
|
|
||||||
// Category: Recording
|
{ "GetStreamingStatus", WSRequestHandler::HandleGetStreamingStatus },
|
||||||
{ "GetRecordingStatus", &WSRequestHandler::GetRecordingStatus },
|
{ "StartStopStreaming", WSRequestHandler::HandleStartStopStreaming },
|
||||||
{ "StartStopRecording", &WSRequestHandler::StartStopRecording },
|
{ "StartStopRecording", WSRequestHandler::HandleStartStopRecording },
|
||||||
{ "StartRecording", &WSRequestHandler::StartRecording },
|
{ "StartStreaming", WSRequestHandler::HandleStartStreaming },
|
||||||
{ "StopRecording", &WSRequestHandler::StopRecording },
|
{ "StopStreaming", WSRequestHandler::HandleStopStreaming },
|
||||||
{ "PauseRecording", &WSRequestHandler::PauseRecording },
|
{ "StartRecording", WSRequestHandler::HandleStartRecording },
|
||||||
{ "ResumeRecording", &WSRequestHandler::ResumeRecording },
|
{ "StopRecording", WSRequestHandler::HandleStopRecording },
|
||||||
{ "SetRecordingFolder", &WSRequestHandler::SetRecordingFolder },
|
|
||||||
{ "GetRecordingFolder", &WSRequestHandler::GetRecordingFolder },
|
|
||||||
|
|
||||||
// Category: Replay Buffer
|
{ "StartStopReplayBuffer", WSRequestHandler::HandleStartStopReplayBuffer },
|
||||||
{ "GetReplayBufferStatus", &WSRequestHandler::GetReplayBufferStatus },
|
{ "StartReplayBuffer", WSRequestHandler::HandleStartReplayBuffer },
|
||||||
{ "StartStopReplayBuffer", &WSRequestHandler::StartStopReplayBuffer },
|
{ "StopReplayBuffer", WSRequestHandler::HandleStopReplayBuffer },
|
||||||
{ "StartReplayBuffer", &WSRequestHandler::StartReplayBuffer },
|
{ "SaveReplayBuffer", WSRequestHandler::HandleSaveReplayBuffer },
|
||||||
{ "StopReplayBuffer", &WSRequestHandler::StopReplayBuffer },
|
|
||||||
{ "SaveReplayBuffer", &WSRequestHandler::SaveReplayBuffer },
|
|
||||||
|
|
||||||
// Category: Scene Collections
|
{ "SetRecordingFolder", WSRequestHandler::HandleSetRecordingFolder },
|
||||||
{ "SetCurrentSceneCollection", &WSRequestHandler::SetCurrentSceneCollection },
|
{ "GetRecordingFolder", WSRequestHandler::HandleGetRecordingFolder },
|
||||||
{ "GetCurrentSceneCollection", &WSRequestHandler::GetCurrentSceneCollection },
|
|
||||||
{ "ListSceneCollections", &WSRequestHandler::ListSceneCollections },
|
|
||||||
|
|
||||||
// Category: Scene Items
|
{ "GetTransitionList", WSRequestHandler::HandleGetTransitionList },
|
||||||
{ "GetSceneItemList", &WSRequestHandler::GetSceneItemList },
|
{ "GetCurrentTransition", WSRequestHandler::HandleGetCurrentTransition },
|
||||||
{ "GetSceneItemProperties", &WSRequestHandler::GetSceneItemProperties },
|
{ "SetCurrentTransition", WSRequestHandler::HandleSetCurrentTransition },
|
||||||
{ "SetSceneItemProperties", &WSRequestHandler::SetSceneItemProperties },
|
{ "SetTransitionDuration", WSRequestHandler::HandleSetTransitionDuration },
|
||||||
{ "ResetSceneItem", &WSRequestHandler::ResetSceneItem },
|
{ "GetTransitionDuration", WSRequestHandler::HandleGetTransitionDuration },
|
||||||
{ "SetSceneItemRender", &WSRequestHandler::SetSceneItemRender },
|
|
||||||
{ "SetSceneItemPosition", &WSRequestHandler::SetSceneItemPosition },
|
|
||||||
{ "SetSceneItemTransform", &WSRequestHandler::SetSceneItemTransform },
|
|
||||||
{ "SetSceneItemCrop", &WSRequestHandler::SetSceneItemCrop },
|
|
||||||
{ "SetSourceRender", &WSRequestHandler::SetSceneItemRender }, // Retrocompat TODO: Remove in 5.0.0
|
|
||||||
{ "DeleteSceneItem", &WSRequestHandler::DeleteSceneItem },
|
|
||||||
{ "AddSceneItem", &WSRequestHandler::AddSceneItem },
|
|
||||||
{ "DuplicateSceneItem", &WSRequestHandler::DuplicateSceneItem },
|
|
||||||
|
|
||||||
// Category: Scenes
|
{ "SetVolume", WSRequestHandler::HandleSetVolume },
|
||||||
{ "SetCurrentScene", &WSRequestHandler::SetCurrentScene },
|
{ "GetVolume", WSRequestHandler::HandleGetVolume },
|
||||||
{ "GetCurrentScene", &WSRequestHandler::GetCurrentScene },
|
{ "ToggleMute", WSRequestHandler::HandleToggleMute },
|
||||||
{ "GetSceneList", &WSRequestHandler::GetSceneList },
|
{ "SetMute", WSRequestHandler::HandleSetMute },
|
||||||
{ "CreateScene", &WSRequestHandler::CreateScene },
|
{ "GetMute", WSRequestHandler::HandleGetMute },
|
||||||
{ "ReorderSceneItems", &WSRequestHandler::ReorderSceneItems },
|
{ "SetSyncOffset", WSRequestHandler::HandleSetSyncOffset },
|
||||||
{ "SetSceneTransitionOverride", &WSRequestHandler::SetSceneTransitionOverride },
|
{ "GetSyncOffset", WSRequestHandler::HandleGetSyncOffset },
|
||||||
{ "RemoveSceneTransitionOverride", &WSRequestHandler::RemoveSceneTransitionOverride },
|
{ "GetSpecialSources", WSRequestHandler::HandleGetSpecialSources },
|
||||||
{ "GetSceneTransitionOverride", &WSRequestHandler::GetSceneTransitionOverride },
|
{ "GetSourcesList", WSRequestHandler::HandleGetSourcesList },
|
||||||
|
{ "GetSourceTypesList", WSRequestHandler::HandleGetSourceTypesList },
|
||||||
|
{ "GetSourceSettings", WSRequestHandler::HandleGetSourceSettings },
|
||||||
|
{ "SetSourceSettings", WSRequestHandler::HandleSetSourceSettings },
|
||||||
|
|
||||||
// Category: Sources
|
{ "GetSourceFilters", WSRequestHandler::HandleGetSourceFilters },
|
||||||
{ "CreateSource", &WSRequestHandler::CreateSource },
|
{ "AddFilterToSource", WSRequestHandler::HandleAddFilterToSource },
|
||||||
{ "GetSourcesList", &WSRequestHandler::GetSourcesList },
|
{ "RemoveFilterFromSource", WSRequestHandler::HandleRemoveFilterFromSource },
|
||||||
{ "GetSourceTypesList", &WSRequestHandler::GetSourceTypesList },
|
{ "ReorderSourceFilter", WSRequestHandler::HandleReorderSourceFilter },
|
||||||
{ "GetVolume", &WSRequestHandler::GetVolume },
|
{ "MoveSourceFilter", WSRequestHandler::HandleMoveSourceFilter },
|
||||||
{ "SetVolume", &WSRequestHandler::SetVolume },
|
{ "SetSourceFilterSettings", WSRequestHandler::HandleSetSourceFilterSettings },
|
||||||
{ "SetAudioTracks", &WSRequestHandler::SetAudioTracks },
|
|
||||||
{ "GetAudioTracks", &WSRequestHandler::GetAudioTracks },
|
|
||||||
{ "GetMute", &WSRequestHandler::GetMute },
|
|
||||||
{ "SetMute", &WSRequestHandler::SetMute },
|
|
||||||
{ "ToggleMute", &WSRequestHandler::ToggleMute },
|
|
||||||
{ "GetSourceActive", &WSRequestHandler::GetSourceActive },
|
|
||||||
{ "GetAudioActive", &WSRequestHandler::GetAudioActive },
|
|
||||||
{ "SetSourceName", &WSRequestHandler::SetSourceName },
|
|
||||||
{ "SetSyncOffset", &WSRequestHandler::SetSyncOffset },
|
|
||||||
{ "GetSyncOffset", &WSRequestHandler::GetSyncOffset },
|
|
||||||
{ "GetSourceSettings", &WSRequestHandler::GetSourceSettings },
|
|
||||||
{ "SetSourceSettings", &WSRequestHandler::SetSourceSettings },
|
|
||||||
{ "GetTextGDIPlusProperties", &WSRequestHandler::GetTextGDIPlusProperties },
|
|
||||||
{ "SetTextGDIPlusProperties", &WSRequestHandler::SetTextGDIPlusProperties },
|
|
||||||
{ "GetTextFreetype2Properties", &WSRequestHandler::GetTextFreetype2Properties },
|
|
||||||
{ "SetTextFreetype2Properties", &WSRequestHandler::SetTextFreetype2Properties },
|
|
||||||
{ "GetBrowserSourceProperties", &WSRequestHandler::GetBrowserSourceProperties },
|
|
||||||
{ "SetBrowserSourceProperties", &WSRequestHandler::SetBrowserSourceProperties },
|
|
||||||
{ "GetSpecialSources", &WSRequestHandler::GetSpecialSources },
|
|
||||||
{ "GetSourceFilters", &WSRequestHandler::GetSourceFilters },
|
|
||||||
{ "GetSourceFilterInfo", &WSRequestHandler::GetSourceFilterInfo },
|
|
||||||
{ "AddFilterToSource", &WSRequestHandler::AddFilterToSource },
|
|
||||||
{ "RemoveFilterFromSource", &WSRequestHandler::RemoveFilterFromSource },
|
|
||||||
{ "ReorderSourceFilter", &WSRequestHandler::ReorderSourceFilter },
|
|
||||||
{ "MoveSourceFilter", &WSRequestHandler::MoveSourceFilter },
|
|
||||||
{ "SetSourceFilterSettings", &WSRequestHandler::SetSourceFilterSettings },
|
|
||||||
{ "SetSourceFilterVisibility", &WSRequestHandler::SetSourceFilterVisibility },
|
|
||||||
{ "GetAudioMonitorType", &WSRequestHandler::GetAudioMonitorType },
|
|
||||||
{ "SetAudioMonitorType", &WSRequestHandler::SetAudioMonitorType },
|
|
||||||
{ "GetSourceDefaultSettings", &WSRequestHandler::GetSourceDefaultSettings },
|
|
||||||
{ "TakeSourceScreenshot", &WSRequestHandler::TakeSourceScreenshot },
|
|
||||||
{ "RefreshBrowserSource", &WSRequestHandler::RefreshBrowserSource },
|
|
||||||
|
|
||||||
// Category: Streaming
|
{ "SetCurrentSceneCollection", WSRequestHandler::HandleSetCurrentSceneCollection },
|
||||||
{ "GetStreamingStatus", &WSRequestHandler::GetStreamingStatus },
|
{ "GetCurrentSceneCollection", WSRequestHandler::HandleGetCurrentSceneCollection },
|
||||||
{ "StartStopStreaming", &WSRequestHandler::StartStopStreaming },
|
{ "ListSceneCollections", WSRequestHandler::HandleListSceneCollections },
|
||||||
{ "StartStreaming", &WSRequestHandler::StartStreaming },
|
|
||||||
{ "StopStreaming", &WSRequestHandler::StopStreaming },
|
|
||||||
{ "SetStreamSettings", &WSRequestHandler::SetStreamSettings },
|
|
||||||
{ "GetStreamSettings", &WSRequestHandler::GetStreamSettings },
|
|
||||||
{ "SaveStreamSettings", &WSRequestHandler::SaveStreamSettings },
|
|
||||||
{ "SendCaptions", &WSRequestHandler::SendCaptions },
|
|
||||||
|
|
||||||
// Category: VirtualCam
|
|
||||||
{ "GetVirtualCamStatus", &WSRequestHandler::GetVirtualCamStatus },
|
|
||||||
{ "StartStopVirtualCam", &WSRequestHandler::StartStopVirtualCam },
|
|
||||||
{ "StartVirtualCam", &WSRequestHandler::StartVirtualCam },
|
|
||||||
{ "StopVirtualCam", &WSRequestHandler::StopVirtualCam },
|
|
||||||
|
|
||||||
// Category: Studio Mode
|
{ "SetCurrentProfile", WSRequestHandler::HandleSetCurrentProfile },
|
||||||
{ "GetStudioModeStatus", &WSRequestHandler::GetStudioModeStatus },
|
{ "GetCurrentProfile", WSRequestHandler::HandleGetCurrentProfile },
|
||||||
{ "GetPreviewScene", &WSRequestHandler::GetPreviewScene },
|
{ "ListProfiles", WSRequestHandler::HandleListProfiles },
|
||||||
{ "SetPreviewScene", &WSRequestHandler::SetPreviewScene },
|
|
||||||
{ "TransitionToProgram", &WSRequestHandler::TransitionToProgram },
|
|
||||||
{ "EnableStudioMode", &WSRequestHandler::EnableStudioMode },
|
|
||||||
{ "DisableStudioMode", &WSRequestHandler::DisableStudioMode },
|
|
||||||
{ "ToggleStudioMode", &WSRequestHandler::ToggleStudioMode },
|
|
||||||
|
|
||||||
// Category: Transitions
|
{ "SetStreamSettings", WSRequestHandler::HandleSetStreamSettings },
|
||||||
{ "GetTransitionList", &WSRequestHandler::GetTransitionList },
|
{ "GetStreamSettings", WSRequestHandler::HandleGetStreamSettings },
|
||||||
{ "GetCurrentTransition", &WSRequestHandler::GetCurrentTransition },
|
{ "SaveStreamSettings", WSRequestHandler::HandleSaveStreamSettings },
|
||||||
{ "SetCurrentTransition", &WSRequestHandler::SetCurrentTransition },
|
|
||||||
{ "SetTransitionDuration", &WSRequestHandler::SetTransitionDuration },
|
{ "GetStudioModeStatus", WSRequestHandler::HandleGetStudioModeStatus },
|
||||||
{ "GetTransitionDuration", &WSRequestHandler::GetTransitionDuration },
|
{ "GetPreviewScene", WSRequestHandler::HandleGetPreviewScene },
|
||||||
{ "GetTransitionPosition", &WSRequestHandler::GetTransitionPosition },
|
{ "SetPreviewScene", WSRequestHandler::HandleSetPreviewScene },
|
||||||
{ "GetTransitionSettings", &WSRequestHandler::GetTransitionSettings },
|
{ "TransitionToProgram", WSRequestHandler::HandleTransitionToProgram },
|
||||||
{ "SetTransitionSettings", &WSRequestHandler::SetTransitionSettings },
|
{ "EnableStudioMode", WSRequestHandler::HandleEnableStudioMode },
|
||||||
{ "ReleaseTBar", &WSRequestHandler::ReleaseTBar },
|
{ "DisableStudioMode", WSRequestHandler::HandleDisableStudioMode },
|
||||||
{ "SetTBarPosition", &WSRequestHandler::SetTBarPosition }
|
{ "ToggleStudioMode", WSRequestHandler::HandleToggleStudioMode },
|
||||||
|
|
||||||
|
{ "SetTextGDIPlusProperties", WSRequestHandler::HandleSetTextGDIPlusProperties },
|
||||||
|
{ "GetTextGDIPlusProperties", WSRequestHandler::HandleGetTextGDIPlusProperties },
|
||||||
|
|
||||||
|
{ "SetTextFreetype2Properties", WSRequestHandler::HandleSetTextFreetype2Properties },
|
||||||
|
{ "GetTextFreetype2Properties", WSRequestHandler::HandleGetTextFreetype2Properties },
|
||||||
|
|
||||||
|
{ "GetBrowserSourceProperties", WSRequestHandler::HandleGetBrowserSourceProperties },
|
||||||
|
{ "SetBrowserSourceProperties", WSRequestHandler::HandleSetBrowserSourceProperties }
|
||||||
};
|
};
|
||||||
|
|
||||||
const QSet<QString> WSRequestHandler::authNotRequired {
|
QSet<QString> WSRequestHandler::authNotRequired {
|
||||||
"GetVersion",
|
"GetVersion",
|
||||||
"GetAuthRequired",
|
"GetAuthRequired",
|
||||||
"Authenticate"
|
"Authenticate"
|
||||||
};
|
};
|
||||||
|
|
||||||
WSRequestHandler::WSRequestHandler(ConnectionProperties& connProperties) :
|
WSRequestHandler::WSRequestHandler(QWebSocket* client) :
|
||||||
_connProperties(connProperties)
|
_messageId(0),
|
||||||
|
_requestType(""),
|
||||||
|
data(nullptr),
|
||||||
|
_client(client)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
RpcResponse WSRequestHandler::processRequest(const RpcRequest& request) {
|
void WSRequestHandler::processIncomingMessage(QString textMessage) {
|
||||||
auto config = GetConfig();
|
QByteArray msgData = textMessage.toUtf8();
|
||||||
if ((config && config->AuthRequired)
|
const char* msg = msgData.constData();
|
||||||
&& (!authNotRequired.contains(request.methodName()))
|
|
||||||
&& (!_connProperties.isAuthenticated()))
|
data = obs_data_create_from_json(msg);
|
||||||
|
if (!data) {
|
||||||
|
if (!msg)
|
||||||
|
msg = "<null pointer>";
|
||||||
|
|
||||||
|
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
|
||||||
|
SendErrorResponse("invalid JSON payload");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config::Current()->DebugEnabled) {
|
||||||
|
blog(LOG_DEBUG, "Request >> '%s'", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasField("request-type")
|
||||||
|
|| !hasField("message-id"))
|
||||||
{
|
{
|
||||||
return RpcResponse::fail(request, "Not Authenticated");
|
SendErrorResponse("missing request parameters");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RpcMethodHandler handlerFunc = messageMap[request.methodName()];
|
_requestType = obs_data_get_string(data, "request-type");
|
||||||
if (!handlerFunc) {
|
_messageId = obs_data_get_string(data, "message-id");
|
||||||
return RpcResponse::fail(request, "invalid request type");
|
|
||||||
|
if (Config::Current()->AuthRequired
|
||||||
|
&& (_client->property(PROP_AUTHENTICATED).toBool() == false)
|
||||||
|
&& (authNotRequired.find(_requestType) == authNotRequired.end()))
|
||||||
|
{
|
||||||
|
SendErrorResponse("Not Authenticated");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::bind(handlerFunc, this, _1)(request);
|
void (*handlerFunc)(WSRequestHandler*) = (messageMap[_requestType]);
|
||||||
|
|
||||||
|
if (handlerFunc != nullptr)
|
||||||
|
handlerFunc(this);
|
||||||
|
else
|
||||||
|
SendErrorResponse("invalid request type");
|
||||||
|
}
|
||||||
|
|
||||||
|
WSRequestHandler::~WSRequestHandler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSRequestHandler::SendOKResponse(obs_data_t* additionalFields) {
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
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) {
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_string(response, "status", "error");
|
||||||
|
obs_data_set_string(response, "error", errorMessage);
|
||||||
|
obs_data_set_string(response, "message-id", _messageId);
|
||||||
|
|
||||||
|
SendResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSRequestHandler::SendErrorResponse(obs_data_t* additionalFields) {
|
||||||
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
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) {
|
||||||
|
QString json = obs_data_get_json(response);
|
||||||
|
_client->sendTextMessage(json);
|
||||||
|
|
||||||
|
if (Config::Current()->DebugEnabled)
|
||||||
|
blog(LOG_DEBUG, "Response << '%s'", json.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WSRequestHandler::hasField(QString name) {
|
||||||
|
if (!data || name.isEmpty() || name.isNull())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return obs_data_has_user_value(data, name.toUtf8());
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 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,194 +17,137 @@ 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/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#ifndef WSREQUESTHANDLER_H
|
||||||
|
#define WSREQUESTHANDLER_H
|
||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QHash>
|
||||||
#include <QtCore/QHash>
|
#include <QSet>
|
||||||
#include <QtCore/QSet>
|
#include <QWebSocket>
|
||||||
|
#include <QWebSocketServer>
|
||||||
|
|
||||||
#include <obs.hpp>
|
#include <obs.hpp>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
#include "ConnectionProperties.h"
|
|
||||||
|
|
||||||
#include "rpc/RpcRequest.h"
|
|
||||||
#include "rpc/RpcResponse.h"
|
|
||||||
|
|
||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
|
|
||||||
class WSRequestHandler;
|
class WSRequestHandler : public QObject {
|
||||||
typedef RpcResponse(WSRequestHandler::*RpcMethodHandler)(const RpcRequest&);
|
Q_OBJECT
|
||||||
|
|
||||||
class WSRequestHandler {
|
|
||||||
public:
|
public:
|
||||||
explicit WSRequestHandler(ConnectionProperties& connProperties);
|
explicit WSRequestHandler(QWebSocket* client);
|
||||||
RpcResponse processRequest(const RpcRequest& textMessage);
|
~WSRequestHandler();
|
||||||
|
void processIncomingMessage(QString textMessage);
|
||||||
|
bool hasField(QString name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ConnectionProperties& _connProperties;
|
QWebSocket* _client;
|
||||||
|
const char* _messageId;
|
||||||
|
const char* _requestType;
|
||||||
|
OBSDataAutoRelease data;
|
||||||
|
|
||||||
static const QHash<QString, RpcMethodHandler> messageMap;
|
void SendOKResponse(obs_data_t* additionalFields = NULL);
|
||||||
static const QSet<QString> authNotRequired;
|
void SendErrorResponse(const char* errorMessage);
|
||||||
|
void SendErrorResponse(obs_data_t* additionalFields = NULL);
|
||||||
|
void SendResponse(obs_data_t* response);
|
||||||
|
|
||||||
// Category: General
|
static QHash<QString, void(*)(WSRequestHandler*)> messageMap;
|
||||||
RpcResponse GetVersion(const RpcRequest&);
|
static QSet<QString> authNotRequired;
|
||||||
RpcResponse GetAuthRequired(const RpcRequest&);
|
|
||||||
RpcResponse Authenticate(const RpcRequest&);
|
|
||||||
RpcResponse SetHeartbeat(const RpcRequest&);
|
|
||||||
RpcResponse SetFilenameFormatting(const RpcRequest&);
|
|
||||||
RpcResponse GetFilenameFormatting(const RpcRequest&);
|
|
||||||
RpcResponse GetStats(const RpcRequest&);
|
|
||||||
RpcResponse BroadcastCustomMessage(const RpcRequest&);
|
|
||||||
RpcResponse GetVideoInfo(const RpcRequest&);
|
|
||||||
RpcResponse OpenProjector(const RpcRequest&);
|
|
||||||
RpcResponse TriggerHotkeyByName(const RpcRequest&);
|
|
||||||
RpcResponse TriggerHotkeyBySequence(const RpcRequest&);
|
|
||||||
RpcResponse ExecuteBatch(const RpcRequest&);
|
|
||||||
RpcResponse Sleep(const RpcRequest&);
|
|
||||||
|
|
||||||
// Category: Media Control
|
static void HandleGetVersion(WSRequestHandler* req);
|
||||||
RpcResponse PlayPauseMedia(const RpcRequest&);
|
static void HandleGetAuthRequired(WSRequestHandler* req);
|
||||||
RpcResponse RestartMedia(const RpcRequest&);
|
static void HandleAuthenticate(WSRequestHandler* req);
|
||||||
RpcResponse StopMedia(const RpcRequest&);
|
|
||||||
RpcResponse NextMedia(const RpcRequest&);
|
|
||||||
RpcResponse PreviousMedia(const RpcRequest&);
|
|
||||||
RpcResponse GetMediaDuration(const RpcRequest&);
|
|
||||||
RpcResponse GetMediaTime(const RpcRequest&);
|
|
||||||
RpcResponse SetMediaTime(const RpcRequest&);
|
|
||||||
RpcResponse ScrubMedia(const RpcRequest&);
|
|
||||||
RpcResponse GetMediaState(const RpcRequest&);
|
|
||||||
RpcResponse GetMediaSourcesList(const RpcRequest&);
|
|
||||||
|
|
||||||
// Category: Outputs
|
static void HandleSetHeartbeat(WSRequestHandler* req);
|
||||||
RpcResponse ListOutputs(const RpcRequest&);
|
|
||||||
RpcResponse GetOutputInfo(const RpcRequest&);
|
|
||||||
RpcResponse StartOutput(const RpcRequest&);
|
|
||||||
RpcResponse StopOutput(const RpcRequest&);
|
|
||||||
|
|
||||||
// Category: Profiles
|
static void HandleSetFilenameFormatting(WSRequestHandler* req);
|
||||||
RpcResponse SetCurrentProfile(const RpcRequest&);
|
static void HandleGetFilenameFormatting(WSRequestHandler* req);
|
||||||
RpcResponse GetCurrentProfile(const RpcRequest&);
|
|
||||||
RpcResponse ListProfiles(const RpcRequest&);
|
|
||||||
|
|
||||||
// Category: Recording
|
static void HandleSetCurrentScene(WSRequestHandler* req);
|
||||||
RpcResponse GetRecordingStatus(const RpcRequest&);
|
static void HandleGetCurrentScene(WSRequestHandler* req);
|
||||||
RpcResponse StartStopRecording(const RpcRequest&);
|
static void HandleGetSceneList(WSRequestHandler* req);
|
||||||
RpcResponse StartRecording(const RpcRequest&);
|
|
||||||
RpcResponse StopRecording(const RpcRequest&);
|
|
||||||
RpcResponse PauseRecording(const RpcRequest&);
|
|
||||||
RpcResponse ResumeRecording(const RpcRequest&);
|
|
||||||
RpcResponse SetRecordingFolder(const RpcRequest&);
|
|
||||||
RpcResponse GetRecordingFolder(const RpcRequest&);
|
|
||||||
|
|
||||||
// Category: Replay Buffer
|
static void HandleSetSceneItemRender(WSRequestHandler* req);
|
||||||
RpcResponse GetReplayBufferStatus(const RpcRequest&);
|
static void HandleSetSceneItemPosition(WSRequestHandler* req);
|
||||||
RpcResponse StartStopReplayBuffer(const RpcRequest&);
|
static void HandleSetSceneItemTransform(WSRequestHandler* req);
|
||||||
RpcResponse StartReplayBuffer(const RpcRequest&);
|
static void HandleSetSceneItemCrop(WSRequestHandler* req);
|
||||||
RpcResponse StopReplayBuffer(const RpcRequest&);
|
static void HandleGetSceneItemProperties(WSRequestHandler* req);
|
||||||
RpcResponse SaveReplayBuffer(const RpcRequest&);
|
static void HandleSetSceneItemProperties(WSRequestHandler* req);
|
||||||
|
static void HandleResetSceneItem(WSRequestHandler* req);
|
||||||
|
static void HandleDuplicateSceneItem(WSRequestHandler* req);
|
||||||
|
static void HandleDeleteSceneItem(WSRequestHandler* req);
|
||||||
|
static void HandleReorderSceneItems(WSRequestHandler* req);
|
||||||
|
|
||||||
// Category: Scene Collections
|
static void HandleGetStreamingStatus(WSRequestHandler* req);
|
||||||
RpcResponse SetCurrentSceneCollection(const RpcRequest&);
|
static void HandleStartStopStreaming(WSRequestHandler* req);
|
||||||
RpcResponse GetCurrentSceneCollection(const RpcRequest&);
|
static void HandleStartStopRecording(WSRequestHandler* req);
|
||||||
RpcResponse ListSceneCollections(const RpcRequest&);
|
static void HandleStartStreaming(WSRequestHandler* req);
|
||||||
|
static void HandleStopStreaming(WSRequestHandler* req);
|
||||||
|
static void HandleStartRecording(WSRequestHandler* req);
|
||||||
|
static void HandleStopRecording(WSRequestHandler* req);
|
||||||
|
|
||||||
// Category: Scene Items
|
static void HandleStartStopReplayBuffer(WSRequestHandler* req);
|
||||||
RpcResponse GetSceneItemList(const RpcRequest&);
|
static void HandleStartReplayBuffer(WSRequestHandler* req);
|
||||||
RpcResponse GetSceneItemProperties(const RpcRequest&);
|
static void HandleStopReplayBuffer(WSRequestHandler* req);
|
||||||
RpcResponse SetSceneItemProperties(const RpcRequest&);
|
static void HandleSaveReplayBuffer(WSRequestHandler* req);
|
||||||
RpcResponse ResetSceneItem(const RpcRequest&);
|
|
||||||
RpcResponse SetSceneItemRender(const RpcRequest&);
|
|
||||||
RpcResponse SetSceneItemPosition(const RpcRequest&);
|
|
||||||
RpcResponse SetSceneItemTransform(const RpcRequest&);
|
|
||||||
RpcResponse SetSceneItemCrop(const RpcRequest&);
|
|
||||||
RpcResponse DeleteSceneItem(const RpcRequest&);
|
|
||||||
RpcResponse AddSceneItem(const RpcRequest&);
|
|
||||||
RpcResponse DuplicateSceneItem(const RpcRequest&);
|
|
||||||
|
|
||||||
// Category: Scenes
|
static void HandleSetRecordingFolder(WSRequestHandler* req);
|
||||||
RpcResponse SetCurrentScene(const RpcRequest&);
|
static void HandleGetRecordingFolder(WSRequestHandler* req);
|
||||||
RpcResponse GetCurrentScene(const RpcRequest&);
|
|
||||||
RpcResponse GetSceneList(const RpcRequest&);
|
|
||||||
RpcResponse CreateScene(const RpcRequest&);
|
|
||||||
RpcResponse ReorderSceneItems(const RpcRequest&);
|
|
||||||
RpcResponse SetSceneTransitionOverride(const RpcRequest&);
|
|
||||||
RpcResponse RemoveSceneTransitionOverride(const RpcRequest&);
|
|
||||||
RpcResponse GetSceneTransitionOverride(const RpcRequest&);
|
|
||||||
|
|
||||||
// Category: Sources
|
static void HandleGetTransitionList(WSRequestHandler* req);
|
||||||
RpcResponse CreateSource(const RpcRequest&);
|
static void HandleGetCurrentTransition(WSRequestHandler* req);
|
||||||
RpcResponse GetSourcesList(const RpcRequest&);
|
static void HandleSetCurrentTransition(WSRequestHandler* req);
|
||||||
RpcResponse GetSourceTypesList(const RpcRequest&);
|
|
||||||
RpcResponse GetVolume(const RpcRequest&);
|
|
||||||
RpcResponse SetVolume(const RpcRequest&);
|
|
||||||
RpcResponse SetAudioTracks(const RpcRequest&);
|
|
||||||
RpcResponse GetAudioTracks(const RpcRequest&);
|
|
||||||
RpcResponse GetMute(const RpcRequest&);
|
|
||||||
RpcResponse SetMute(const RpcRequest&);
|
|
||||||
RpcResponse ToggleMute(const RpcRequest&);
|
|
||||||
RpcResponse GetSourceActive(const RpcRequest&);
|
|
||||||
RpcResponse GetAudioActive(const RpcRequest&);
|
|
||||||
RpcResponse SetSourceName(const RpcRequest&);
|
|
||||||
RpcResponse SetSyncOffset(const RpcRequest&);
|
|
||||||
RpcResponse GetSyncOffset(const RpcRequest&);
|
|
||||||
RpcResponse GetSourceSettings(const RpcRequest&);
|
|
||||||
RpcResponse SetSourceSettings(const RpcRequest&);
|
|
||||||
RpcResponse GetTextGDIPlusProperties(const RpcRequest&);
|
|
||||||
RpcResponse SetTextGDIPlusProperties(const RpcRequest&);
|
|
||||||
RpcResponse GetTextFreetype2Properties(const RpcRequest&);
|
|
||||||
RpcResponse SetTextFreetype2Properties(const RpcRequest&);
|
|
||||||
RpcResponse GetBrowserSourceProperties(const RpcRequest&);
|
|
||||||
RpcResponse SetBrowserSourceProperties(const RpcRequest&);
|
|
||||||
RpcResponse GetSpecialSources(const RpcRequest&);
|
|
||||||
RpcResponse GetSourceFilters(const RpcRequest&);
|
|
||||||
RpcResponse GetSourceFilterInfo(const RpcRequest&);
|
|
||||||
RpcResponse AddFilterToSource(const RpcRequest&);
|
|
||||||
RpcResponse RemoveFilterFromSource(const RpcRequest&);
|
|
||||||
RpcResponse ReorderSourceFilter(const RpcRequest&);
|
|
||||||
RpcResponse MoveSourceFilter(const RpcRequest&);
|
|
||||||
RpcResponse SetSourceFilterSettings(const RpcRequest&);
|
|
||||||
RpcResponse SetSourceFilterVisibility(const RpcRequest&);
|
|
||||||
RpcResponse GetAudioMonitorType(const RpcRequest&);
|
|
||||||
RpcResponse SetAudioMonitorType(const RpcRequest&);
|
|
||||||
RpcResponse GetSourceDefaultSettings(const RpcRequest&);
|
|
||||||
RpcResponse TakeSourceScreenshot(const RpcRequest&);
|
|
||||||
RpcResponse RefreshBrowserSource(const RpcRequest&);
|
|
||||||
|
|
||||||
// Category: Streaming
|
static void HandleSetVolume(WSRequestHandler* req);
|
||||||
RpcResponse GetStreamingStatus(const RpcRequest&);
|
static void HandleGetVolume(WSRequestHandler* req);
|
||||||
RpcResponse StartStopStreaming(const RpcRequest&);
|
static void HandleToggleMute(WSRequestHandler* req);
|
||||||
RpcResponse StartStreaming(const RpcRequest&);
|
static void HandleSetMute(WSRequestHandler* req);
|
||||||
RpcResponse StopStreaming(const RpcRequest&);
|
static void HandleGetMute(WSRequestHandler* req);
|
||||||
RpcResponse SetStreamSettings(const RpcRequest&);
|
static void HandleSetSyncOffset(WSRequestHandler* req);
|
||||||
RpcResponse GetStreamSettings(const RpcRequest&);
|
static void HandleGetSyncOffset(WSRequestHandler* req);
|
||||||
RpcResponse SaveStreamSettings(const RpcRequest&);
|
static void HandleGetSpecialSources(WSRequestHandler* req);
|
||||||
RpcResponse SendCaptions(const RpcRequest&);
|
static void HandleGetSourcesList(WSRequestHandler* req);
|
||||||
|
static void HandleGetSourceTypesList(WSRequestHandler* req);
|
||||||
// Category: Virtual Cam
|
static void HandleGetSourceSettings(WSRequestHandler* req);
|
||||||
RpcResponse GetVirtualCamStatus(const RpcRequest&);
|
static void HandleSetSourceSettings(WSRequestHandler* req);
|
||||||
RpcResponse StartStopVirtualCam(const RpcRequest&);
|
|
||||||
RpcResponse StartVirtualCam(const RpcRequest&);
|
|
||||||
RpcResponse StopVirtualCam(const RpcRequest&);
|
|
||||||
|
|
||||||
// Category: Studio Mode
|
static void HandleGetSourceFilters(WSRequestHandler* req);
|
||||||
RpcResponse GetStudioModeStatus(const RpcRequest&);
|
static void HandleAddFilterToSource(WSRequestHandler* req);
|
||||||
RpcResponse GetPreviewScene(const RpcRequest&);
|
static void HandleRemoveFilterFromSource(WSRequestHandler* req);
|
||||||
RpcResponse SetPreviewScene(const RpcRequest&);
|
static void HandleReorderSourceFilter(WSRequestHandler* req);
|
||||||
RpcResponse TransitionToProgram(const RpcRequest&);
|
static void HandleMoveSourceFilter(WSRequestHandler* req);
|
||||||
RpcResponse EnableStudioMode(const RpcRequest&);
|
static void HandleSetSourceFilterSettings(WSRequestHandler* req);
|
||||||
RpcResponse DisableStudioMode(const RpcRequest&);
|
|
||||||
RpcResponse ToggleStudioMode(const RpcRequest&);
|
|
||||||
|
|
||||||
// Category: Transitions
|
static void HandleSetCurrentSceneCollection(WSRequestHandler* req);
|
||||||
RpcResponse GetTransitionList(const RpcRequest&);
|
static void HandleGetCurrentSceneCollection(WSRequestHandler* req);
|
||||||
RpcResponse GetCurrentTransition(const RpcRequest&);
|
static void HandleListSceneCollections(WSRequestHandler* req);
|
||||||
RpcResponse SetCurrentTransition(const RpcRequest&);
|
|
||||||
RpcResponse SetTransitionDuration(const RpcRequest&);
|
static void HandleSetCurrentProfile(WSRequestHandler* req);
|
||||||
RpcResponse GetTransitionDuration(const RpcRequest&);
|
static void HandleGetCurrentProfile(WSRequestHandler* req);
|
||||||
RpcResponse GetTransitionPosition(const RpcRequest&);
|
static void HandleListProfiles(WSRequestHandler* req);
|
||||||
RpcResponse GetTransitionSettings(const RpcRequest&);
|
|
||||||
RpcResponse SetTransitionSettings(const RpcRequest&);
|
static void HandleSetStreamSettings(WSRequestHandler* req);
|
||||||
RpcResponse ReleaseTBar(const RpcRequest&);
|
static void HandleGetStreamSettings(WSRequestHandler* req);
|
||||||
RpcResponse SetTBarPosition(const RpcRequest&);
|
static void HandleSaveStreamSettings(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleSetTransitionDuration(WSRequestHandler* req);
|
||||||
|
static void HandleGetTransitionDuration(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleGetStudioModeStatus(WSRequestHandler* req);
|
||||||
|
static void HandleGetPreviewScene(WSRequestHandler* req);
|
||||||
|
static void HandleSetPreviewScene(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 void HandleGetTextGDIPlusProperties(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleSetTextFreetype2Properties(WSRequestHandler* req);
|
||||||
|
static void HandleGetTextFreetype2Properties(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleSetBrowserSourceProperties(WSRequestHandler* req);
|
||||||
|
static void HandleGetBrowserSourceProperties(WSRequestHandler* req);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif // WSPROTOCOL_H
|
||||||
|
@ -1,61 +1,10 @@
|
|||||||
#include "WSRequestHandler.h"
|
#include <QString>
|
||||||
|
|
||||||
#include <QtCore/QByteArray>
|
|
||||||
#include <QtGui/QImageWriter>
|
|
||||||
|
|
||||||
#include "obs-websocket.h"
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "WSEvents.h"
|
#include "WSEvents.h"
|
||||||
#include "protocol/OBSRemoteProtocol.h"
|
|
||||||
|
|
||||||
#define CASE(x) case x: return #x;
|
#include "WSRequestHandler.h"
|
||||||
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.
|
||||||
@ -64,41 +13,31 @@ const char *describe_scale_type(int scale) {
|
|||||||
* @return {String} `obs-websocket-version` obs-websocket plugin version.
|
* @return {String} `obs-websocket-version` obs-websocket plugin version.
|
||||||
* @return {String} `obs-studio-version` OBS Studio program version.
|
* @return {String} `obs-studio-version` OBS Studio program version.
|
||||||
* @return {String} `available-requests` List of available request types, formatted as a comma-separated list string (e.g. : "Method1,Method2,Method3").
|
* @return {String} `available-requests` List of available request types, formatted as a comma-separated list string (e.g. : "Method1,Method2,Method3").
|
||||||
* @return {String} `supported-image-export-formats` List of supported formats for features that use image export (like the TakeSourceScreenshot request type) formatted as a comma-separated list string
|
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name GetVersion
|
* @name GetVersion
|
||||||
* @category general
|
* @category general
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetVersion(const RpcRequest& request) {
|
void WSRequestHandler::HandleGetVersion(WSRequestHandler* req) {
|
||||||
QString obsVersion = Utils::OBSVersionString();
|
QString obsVersion = Utils::OBSVersionString();
|
||||||
|
|
||||||
QList<QString> names = messageMap.keys();
|
QList<QString> names = req->messageMap.keys();
|
||||||
QList<QByteArray> imageWriterFormats = QImageWriter::supportedImageFormats();
|
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;
|
||||||
names.sort(Qt::CaseInsensitive);
|
|
||||||
requests += names.takeFirst();
|
requests += names.takeFirst();
|
||||||
for (const QString& reqName : names) {
|
for (QString reqName : names) {
|
||||||
requests += ("," + reqName);
|
requests += ("," + reqName);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString supportedImageExportFormats;
|
|
||||||
supportedImageExportFormats += QString::fromUtf8(imageWriterFormats.takeFirst());
|
|
||||||
for (const QByteArray& format : imageWriterFormats) {
|
|
||||||
supportedImageExportFormats += ("," + QString::fromUtf8(format));
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
obs_data_set_double(data, "version", 1.1);
|
|
||||||
obs_data_set_string(data, "obs-websocket-version", OBS_WEBSOCKET_VERSION);
|
obs_data_set_string(data, "obs-websocket-version", OBS_WEBSOCKET_VERSION);
|
||||||
obs_data_set_string(data, "obs-studio-version", obsVersion.toUtf8());
|
obs_data_set_string(data, "obs-studio-version", obsVersion.toUtf8());
|
||||||
obs_data_set_string(data, "available-requests", requests.toUtf8());
|
obs_data_set_string(data, "available-requests", requests.toUtf8());
|
||||||
obs_data_set_string(data, "supported-image-export-formats", supportedImageExportFormats.toUtf8());
|
|
||||||
|
|
||||||
return request.success(data);
|
req->SendOKResponse(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,21 +53,20 @@ RpcResponse WSRequestHandler::GetVersion(const RpcRequest& request) {
|
|||||||
* @category general
|
* @category general
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetAuthRequired(const RpcRequest& request) {
|
void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler* req) {
|
||||||
auto config = GetConfig();
|
bool authRequired = Config::Current()->AuthRequired;
|
||||||
bool authRequired = (config && config->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",
|
obs_data_set_string(data, "challenge",
|
||||||
config->SessionChallenge.toUtf8());
|
Config::Current()->SessionChallenge.toUtf8());
|
||||||
obs_data_set_string(data, "salt",
|
obs_data_set_string(data, "salt",
|
||||||
config->Salt.toUtf8());
|
Config::Current()->Salt.toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
return request.success(data);
|
req->SendOKResponse(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,27 +79,26 @@ RpcResponse WSRequestHandler::GetAuthRequired(const RpcRequest& request) {
|
|||||||
* @category general
|
* @category general
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::Authenticate(const RpcRequest& request) {
|
void WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) {
|
||||||
if (!request.hasField("auth")) {
|
if (!req->hasField("auth")) {
|
||||||
return request.failed("missing request parameters");
|
req->SendErrorResponse("missing request parameters");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_connProperties.isAuthenticated()) {
|
QString auth = obs_data_get_string(req->data, "auth");
|
||||||
return request.failed("already authenticated");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString auth = obs_data_get_string(request.parameters(), "auth");
|
|
||||||
if (auth.isEmpty()) {
|
if (auth.isEmpty()) {
|
||||||
return request.failed("auth not specified!");
|
req->SendErrorResponse("auth not specified!");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto config = GetConfig();
|
if ((req->_client->property(PROP_AUTHENTICATED).toBool() == false)
|
||||||
if (!config || (config->CheckAuth(auth) == false)) {
|
&& Config::Current()->CheckAuth(auth))
|
||||||
return request.failed("Authentication Failed.");
|
{
|
||||||
|
req->_client->setProperty(PROP_AUTHENTICATED, true);
|
||||||
|
req->SendOKResponse();
|
||||||
|
} else {
|
||||||
|
req->SendErrorResponse("Authentication Failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_connProperties.setAuthenticated(true);
|
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,20 +110,20 @@ RpcResponse WSRequestHandler::Authenticate(const RpcRequest& request) {
|
|||||||
* @name SetHeartbeat
|
* @name SetHeartbeat
|
||||||
* @category general
|
* @category general
|
||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
* @deprecated Since 4.9.0. Please poll the appropriate data using requests. Will be removed in v5.0.0.
|
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetHeartbeat(const RpcRequest& request) {
|
void WSRequestHandler::HandleSetHeartbeat(WSRequestHandler* req) {
|
||||||
if (!request.hasField("enable")) {
|
if (!req->hasField("enable")) {
|
||||||
return request.failed("Heartbeat <enable> parameter missing");
|
req->SendErrorResponse("Heartbeat <enable> parameter missing");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto events = GetEventsSystem();
|
WSEvents::Instance->HeartbeatIsActive =
|
||||||
events->HeartbeatIsActive = obs_data_get_bool(request.parameters(), "enable");
|
obs_data_get_bool(req->data, "enable");
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_bool(response, "enable", events->HeartbeatIsActive);
|
obs_data_set_bool(response, "enable",
|
||||||
|
WSEvents::Instance->HeartbeatIsActive);
|
||||||
return request.success(response);
|
req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -199,19 +136,19 @@ RpcResponse WSRequestHandler::SetHeartbeat(const RpcRequest& request) {
|
|||||||
* @category general
|
* @category general
|
||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetFilenameFormatting(const RpcRequest& request) {
|
void WSRequestHandler::HandleSetFilenameFormatting(WSRequestHandler* req) {
|
||||||
if (!request.hasField("filename-formatting")) {
|
if (!req->hasField("filename-formatting")) {
|
||||||
return request.failed("<filename-formatting> parameter missing");
|
req->SendErrorResponse("<filename-formatting> parameter missing");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString filenameFormatting = obs_data_get_string(request.parameters(), "filename-formatting");
|
QString filenameFormatting = obs_data_get_string(req->data, "filename-formatting");
|
||||||
if (filenameFormatting.isEmpty()) {
|
if (!filenameFormatting.isEmpty()) {
|
||||||
return request.failed("invalid request parameters");
|
Utils::SetFilenameFormatting(filenameFormatting.toUtf8());
|
||||||
|
req->SendOKResponse();
|
||||||
|
} else {
|
||||||
|
req->SendErrorResponse("invalid request parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::SetFilenameFormatting(filenameFormatting.toUtf8());
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -224,274 +161,8 @@ RpcResponse WSRequestHandler::SetFilenameFormatting(const RpcRequest& request) {
|
|||||||
* @category general
|
* @category general
|
||||||
* @since 4.3.0
|
* @since 4.3.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetFilenameFormatting(const RpcRequest& request) {
|
void 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 request.success(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get OBS stats (almost the same info as provided in OBS' stats window)
|
|
||||||
*
|
|
||||||
* @return {OBSStats} `stats` [OBS stats](#obsstats)
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name GetStats
|
|
||||||
* @category general
|
|
||||||
* @since 4.6.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetStats(const RpcRequest& request) {
|
|
||||||
OBSDataAutoRelease stats = GetEventsSystem()->GetStats();
|
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
|
||||||
obs_data_set_obj(response, "stats", stats);
|
|
||||||
|
|
||||||
return request.success(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
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::BroadcastCustomMessage(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("realm") || !request.hasField("data")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString realm = obs_data_get_string(request.parameters(), "realm");
|
|
||||||
OBSDataAutoRelease data = obs_data_get_obj(request.parameters(), "data");
|
|
||||||
|
|
||||||
if (realm.isEmpty()) {
|
|
||||||
return request.failed("realm not specified!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
return request.failed("data not specified!");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto events = GetEventsSystem();
|
|
||||||
events->OnBroadcastCustomMessage(realm, data);
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetVideoInfo(const RpcRequest& request) {
|
|
||||||
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 request.success(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a projector window or create a projector on a monitor. Requires OBS v24.0.4 or newer.
|
|
||||||
*
|
|
||||||
* @param {String (Optional)} `type` Type of projector: `Preview` (default), `Source`, `Scene`, `StudioProgram`, or `Multiview` (case insensitive).
|
|
||||||
* @param {int (Optional)} `monitor` Monitor to open the projector on. If -1 or omitted, opens a window.
|
|
||||||
* @param {String (Optional)} `geometry` Size and position of the projector window (only if monitor is -1). Encoded in Base64 using [Qt's geometry encoding](https://doc.qt.io/qt-5/qwidget.html#saveGeometry). Corresponds to OBS's saved projectors.
|
|
||||||
* @param {String (Optional)} `name` Name of the source or scene to be displayed (ignored for other projector types).
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name OpenProjector
|
|
||||||
* @category general
|
|
||||||
* @since 4.8.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::OpenProjector(const RpcRequest& request) {
|
|
||||||
const char* type = obs_data_get_string(request.parameters(), "type");
|
|
||||||
|
|
||||||
int monitor = -1;
|
|
||||||
if (request.hasField("monitor")) {
|
|
||||||
monitor = obs_data_get_int(request.parameters(), "monitor");
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* geometry = obs_data_get_string(request.parameters(), "geometry");
|
|
||||||
const char* name = obs_data_get_string(request.parameters(), "name");
|
|
||||||
|
|
||||||
obs_frontend_open_projector(type, monitor, geometry, name);
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes hotkey routine, identified by hotkey unique name
|
|
||||||
*
|
|
||||||
* @param {String} `hotkeyName` Unique name of the hotkey, as defined when registering the hotkey (e.g. "ReplayBuffer.Save")
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name TriggerHotkeyByName
|
|
||||||
* @category general
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::TriggerHotkeyByName(const RpcRequest& request) {
|
|
||||||
const char* name = obs_data_get_string(request.parameters(), "hotkeyName");
|
|
||||||
|
|
||||||
obs_hotkey_t* hk = Utils::FindHotkeyByName(name);
|
|
||||||
if (!hk) {
|
|
||||||
return request.failed("hotkey not found");
|
|
||||||
}
|
|
||||||
obs_hotkey_trigger_routed_callback(obs_hotkey_get_id(hk), true);
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes hotkey routine, identified by bound combination of keys. A single key combination might trigger multiple hotkey routines depending on user settings
|
|
||||||
*
|
|
||||||
* @param {String} `keyId` Main key identifier (e.g. `OBS_KEY_A` for key "A"). Available identifiers [here](https://github.com/obsproject/obs-studio/blob/master/libobs/obs-hotkeys.h)
|
|
||||||
* @param {Object (Optional)} `keyModifiers` Optional key modifiers object. False entries can be ommitted
|
|
||||||
* @param {boolean} `keyModifiers.shift` Trigger Shift Key
|
|
||||||
* @param {boolean} `keyModifiers.alt` Trigger Alt Key
|
|
||||||
* @param {boolean} `keyModifiers.control` Trigger Control (Ctrl) Key
|
|
||||||
* @param {boolean} `keyModifiers.command` Trigger Command Key (Mac)
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name TriggerHotkeyBySequence
|
|
||||||
* @category general
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::TriggerHotkeyBySequence(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("keyId")) {
|
|
||||||
return request.failed("missing request keyId parameter");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_get_obj(request.parameters(), "keyModifiers");
|
|
||||||
|
|
||||||
obs_key_combination_t combo = {0};
|
|
||||||
uint32_t modifiers = 0;
|
|
||||||
if (obs_data_get_bool(data, "shift"))
|
|
||||||
modifiers |= INTERACT_SHIFT_KEY;
|
|
||||||
if (obs_data_get_bool(data, "control"))
|
|
||||||
modifiers |= INTERACT_CONTROL_KEY;
|
|
||||||
if (obs_data_get_bool(data, "alt"))
|
|
||||||
modifiers |= INTERACT_ALT_KEY;
|
|
||||||
if (obs_data_get_bool(data, "command"))
|
|
||||||
modifiers |= INTERACT_COMMAND_KEY;
|
|
||||||
|
|
||||||
combo.modifiers = modifiers;
|
|
||||||
combo.key = obs_key_from_name(obs_data_get_string(request.parameters(), "keyId"));
|
|
||||||
|
|
||||||
if (!modifiers
|
|
||||||
&& (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) {
|
|
||||||
return request.failed("invalid key-modifier combination");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inject hotkey press-release sequence
|
|
||||||
obs_hotkey_inject_event(combo, false);
|
|
||||||
obs_hotkey_inject_event(combo, true);
|
|
||||||
obs_hotkey_inject_event(combo, false);
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes a list of requests sequentially (one-by-one on the same thread).
|
|
||||||
*
|
|
||||||
* @param {Array<Object>} `requests` Array of requests to perform. Executed in order.
|
|
||||||
* @param {String} `requests.*.request-type` Request type. Eg. `GetVersion`.
|
|
||||||
* @param {String (Optional)} `requests.*.message-id` ID of the individual request. Can be any string and not required to be unique. Defaults to empty string if not specified.
|
|
||||||
* @param {boolean (Optional)} `abortOnFail` Stop processing batch requests if one returns a failure.
|
|
||||||
*
|
|
||||||
* @return {Array<Object>} `results` Batch requests results, ordered sequentially.
|
|
||||||
* @return {String} `results.*.message-id` ID of the individual request which was originally provided by the client.
|
|
||||||
* @return {String} `results.*.status` Status response as string. Either `ok` or `error`.
|
|
||||||
* @return {String (Optional)} `results.*.error` Error message accompanying an `error` status.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name ExecuteBatch
|
|
||||||
* @category general
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::ExecuteBatch(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("requests")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool abortOnFail = obs_data_get_bool(request.parameters(), "abortOnFail");
|
|
||||||
|
|
||||||
OBSDataArrayAutoRelease results = obs_data_array_create();
|
|
||||||
|
|
||||||
OBSDataArrayAutoRelease requests = obs_data_get_array(request.parameters(), "requests");
|
|
||||||
size_t requestsCount = obs_data_array_count(requests);
|
|
||||||
for (size_t i = 0; i < requestsCount; i++) {
|
|
||||||
OBSDataAutoRelease requestData = obs_data_array_item(requests, i);
|
|
||||||
QString messageId = obs_data_get_string(requestData, "message-id");
|
|
||||||
QString methodName = obs_data_get_string(requestData, "request-type");
|
|
||||||
obs_data_unset_user_value(requestData, "request-type");
|
|
||||||
obs_data_unset_user_value(requestData, "message-id");
|
|
||||||
|
|
||||||
// build RpcRequest from json data object
|
|
||||||
RpcRequest subRequest(messageId, methodName, requestData);
|
|
||||||
|
|
||||||
// execute the request
|
|
||||||
RpcResponse subResponse = processRequest(subRequest);
|
|
||||||
|
|
||||||
// transform response into json data
|
|
||||||
OBSDataAutoRelease subResponseData = OBSRemoteProtocol::rpcResponseToJsonData(subResponse);
|
|
||||||
|
|
||||||
obs_data_array_push_back(results, subResponseData);
|
|
||||||
|
|
||||||
// if told to abort on fail and a failure occurs, stop request processing and return the progress
|
|
||||||
if (abortOnFail && (subResponse.status() == RpcResponse::Status::Error))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
|
||||||
obs_data_set_array(response, "results", results);
|
|
||||||
return request.success(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits for the specified duration. Designed to be used in `ExecuteBatch` operations.
|
|
||||||
*
|
|
||||||
* @param {int} `sleepMillis` Delay in milliseconds to wait before continuing.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name Sleep
|
|
||||||
* @category general
|
|
||||||
* @since 4.9.1
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::Sleep(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sleepMillis")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
long long sleepMillis = obs_data_get_int(request.parameters(), "sleepMillis");
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(sleepMillis));
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
@ -1,405 +0,0 @@
|
|||||||
#include "Utils.h"
|
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
|
||||||
|
|
||||||
bool isMediaSource(const QString& sourceKind)
|
|
||||||
{
|
|
||||||
return (sourceKind == "vlc_source" || sourceKind == "ffmpeg_source");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString getSourceMediaState(obs_source_t *source)
|
|
||||||
{
|
|
||||||
QString mediaState;
|
|
||||||
enum obs_media_state mstate = obs_source_media_get_state(source);
|
|
||||||
switch (mstate) {
|
|
||||||
case OBS_MEDIA_STATE_NONE:
|
|
||||||
mediaState = "none";
|
|
||||||
break;
|
|
||||||
case OBS_MEDIA_STATE_PLAYING:
|
|
||||||
mediaState = "playing";
|
|
||||||
break;
|
|
||||||
case OBS_MEDIA_STATE_OPENING:
|
|
||||||
mediaState = "opening";
|
|
||||||
break;
|
|
||||||
case OBS_MEDIA_STATE_BUFFERING:
|
|
||||||
mediaState = "buffering";
|
|
||||||
break;
|
|
||||||
case OBS_MEDIA_STATE_PAUSED:
|
|
||||||
mediaState = "paused";
|
|
||||||
break;
|
|
||||||
case OBS_MEDIA_STATE_STOPPED:
|
|
||||||
mediaState = "stopped";
|
|
||||||
break;
|
|
||||||
case OBS_MEDIA_STATE_ENDED:
|
|
||||||
mediaState = "ended";
|
|
||||||
break;
|
|
||||||
case OBS_MEDIA_STATE_ERROR:
|
|
||||||
mediaState = "error";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
mediaState = "unknown";
|
|
||||||
}
|
|
||||||
return mediaState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pause or play a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
|
||||||
* Note :Leaving out `playPause` toggles the current pause state
|
|
||||||
*
|
|
||||||
* @param {String} `sourceName` Source name.
|
|
||||||
* @param {boolean} `playPause` (optional) Whether to pause or play the source. `false` for play, `true` for pause.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name PlayPauseMedia
|
|
||||||
* @category media control
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::PlayPauseMedia(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sourceName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
|
||||||
bool playPause = obs_data_get_bool(request.parameters(), "playPause");
|
|
||||||
if (sourceName.isEmpty()) {
|
|
||||||
return request.failed("invalid request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("specified source doesn't exist");
|
|
||||||
}
|
|
||||||
if (!request.hasField("playPause")) {
|
|
||||||
if (obs_source_media_get_state(source) == obs_media_state::OBS_MEDIA_STATE_PLAYING) {
|
|
||||||
obs_source_media_play_pause(source, true);
|
|
||||||
} else {
|
|
||||||
obs_source_media_play_pause(source, false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bool playPause = obs_data_get_bool(request.parameters(), "playPause");
|
|
||||||
obs_source_media_play_pause(source, playPause);
|
|
||||||
}
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restart a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
|
||||||
*
|
|
||||||
* @param {String} `sourceName` Source name.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name RestartMedia
|
|
||||||
* @category media control
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::RestartMedia(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sourceName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
|
||||||
if (sourceName.isEmpty()) {
|
|
||||||
return request.failed("invalid request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("specified source doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_source_media_restart(source);
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
|
||||||
*
|
|
||||||
* @param {String} `sourceName` Source name.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name StopMedia
|
|
||||||
* @category media control
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::StopMedia(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sourceName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
|
||||||
if (sourceName.isEmpty()) {
|
|
||||||
return request.failed("invalid request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("specified source doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_source_media_stop(source);
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skip to the next media item in the playlist. Supports only vlc media source (as of OBS v25.0.8)
|
|
||||||
*
|
|
||||||
* @param {String} `sourceName` Source name.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name NextMedia
|
|
||||||
* @category media control
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::NextMedia(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sourceName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
|
||||||
if (sourceName.isEmpty()) {
|
|
||||||
return request.failed("invalid request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("specified source doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_source_media_next(source);
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Go to the previous media item in the playlist. Supports only vlc media source (as of OBS v25.0.8)
|
|
||||||
*
|
|
||||||
* @param {String} `sourceName` Source name.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name PreviousMedia
|
|
||||||
* @category media control
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::PreviousMedia(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sourceName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
|
||||||
if (sourceName.isEmpty()) {
|
|
||||||
return request.failed("invalid request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("specified source doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_source_media_previous(source);
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the length of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
|
||||||
* Note: For some reason, for the first 5 or so seconds that the media is playing, the total duration can be off by upwards of 50ms.
|
|
||||||
*
|
|
||||||
* @param {String} `sourceName` Source name.
|
|
||||||
*
|
|
||||||
* @return {int} `mediaDuration` The total length of media in milliseconds..
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name GetMediaDuration
|
|
||||||
* @category media control
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetMediaDuration(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sourceName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
|
||||||
if (sourceName.isEmpty()) {
|
|
||||||
return request.failed("invalid request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("specified source doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
|
||||||
obs_data_set_int(response, "mediaDuration", obs_source_media_get_duration(source));
|
|
||||||
return request.success(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current timestamp of media in milliseconds. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
|
||||||
*
|
|
||||||
* @param {String} `sourceName` Source name.
|
|
||||||
*
|
|
||||||
* @return {int} `timestamp` The time in milliseconds since the start of the media.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name GetMediaTime
|
|
||||||
* @category media control
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetMediaTime(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sourceName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
|
||||||
if (sourceName.isEmpty()) {
|
|
||||||
return request.failed("invalid request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("specified source doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
|
||||||
obs_data_set_int(response, "timestamp", obs_source_media_get_time(source));
|
|
||||||
return request.success(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the timestamp of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
|
||||||
*
|
|
||||||
* @param {String} `sourceName` Source name.
|
|
||||||
* @param {int} `timestamp` Milliseconds to set the timestamp to.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name SetMediaTime
|
|
||||||
* @category media control
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::SetMediaTime(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sourceName") || !request.hasField("timestamp")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
|
||||||
int64_t timestamp = (int64_t)obs_data_get_int(request.parameters(), "timestamp");
|
|
||||||
if (sourceName.isEmpty()) {
|
|
||||||
return request.failed("invalid request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("specified source doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_source_media_set_time(source, timestamp);
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scrub media using a supplied offset. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
|
||||||
* Note: Due to processing/network delays, this request is not perfect. The processing rate of this request has also not been tested.
|
|
||||||
*
|
|
||||||
* @param {String} `sourceName` Source name.
|
|
||||||
* @param {int} `timeOffset` Millisecond offset (positive or negative) to offset the current media position.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name ScrubMedia
|
|
||||||
* @category media control
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::ScrubMedia(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sourceName") || !request.hasField("timeOffset")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
|
||||||
int64_t timeOffset = (int64_t)obs_data_get_int(request.parameters(), "timeOffset");
|
|
||||||
if (sourceName.isEmpty()) {
|
|
||||||
return request.failed("invalid request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("specified source doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t newTime = obs_source_media_get_time(source) + timeOffset;
|
|
||||||
if (newTime < 0) {
|
|
||||||
newTime = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_source_media_set_time(source, newTime);
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current playing state of a media source. Supports ffmpeg and vlc media sources (as of OBS v25.0.8)
|
|
||||||
*
|
|
||||||
* @param {String} `sourceName` Source name.
|
|
||||||
*
|
|
||||||
* @return {String} `mediaState` The media state of the provided source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown`
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name GetMediaState
|
|
||||||
* @category media control
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetMediaState(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sourceName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sourceName = obs_data_get_string(request.parameters(), "sourceName");
|
|
||||||
if (sourceName.isEmpty()) {
|
|
||||||
return request.failed("invalid request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sourceName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("specified source doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
|
||||||
obs_data_set_string(response, "mediaState", getSourceMediaState(source).toUtf8());
|
|
||||||
|
|
||||||
return request.success(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List the media state of all media sources (vlc and media source)
|
|
||||||
*
|
|
||||||
* @return {Array<Object>} `mediaSources` Array of sources
|
|
||||||
* @return {String} `mediaSources.*.sourceName` Unique source name
|
|
||||||
* @return {String} `mediaSources.*.sourceKind` Unique source internal type (a.k.a `ffmpeg_source` or `vlc_source`)
|
|
||||||
* @return {String} `mediaSources.*.mediaState` The current state of media for that source. States: `none`, `playing`, `opening`, `buffering`, `paused`, `stopped`, `ended`, `error`, `unknown`
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name GetMediaSourcesList
|
|
||||||
* @category sources
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetMediaSourcesList(const RpcRequest& request)
|
|
||||||
{
|
|
||||||
OBSDataArrayAutoRelease sourcesArray = obs_data_array_create();
|
|
||||||
|
|
||||||
auto sourceEnumProc = [](void* privateData, obs_source_t* source) -> bool {
|
|
||||||
obs_data_array_t* sourcesArray = (obs_data_array_t*)privateData;
|
|
||||||
|
|
||||||
QString sourceKind = obs_source_get_id(source);
|
|
||||||
if (isMediaSource(sourceKind)) {
|
|
||||||
OBSDataAutoRelease sourceData = obs_data_create();
|
|
||||||
obs_data_set_string(sourceData, "sourceName", obs_source_get_name(source));
|
|
||||||
obs_data_set_string(sourceData, "sourceKind", sourceKind.toUtf8());
|
|
||||||
|
|
||||||
QString mediaState = getSourceMediaState(source);
|
|
||||||
obs_data_set_string(sourceData, "mediaState", mediaState.toUtf8());
|
|
||||||
|
|
||||||
obs_data_array_push_back(sourcesArray, sourceData);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
obs_enum_sources(sourceEnumProc, sourcesArray);
|
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
|
||||||
obs_data_set_array(response, "mediaSources", sourcesArray);
|
|
||||||
return request.success(response);
|
|
||||||
}
|
|
@ -1,188 +0,0 @@
|
|||||||
#include <functional>
|
|
||||||
|
|
||||||
#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 settings
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
RpcResponse findOutputOrFail(const RpcRequest& request, std::function<RpcResponse (obs_output_t*, const RpcRequest&)> callback)
|
|
||||||
{
|
|
||||||
if (!request.hasField("outputName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* outputName = obs_data_get_string(request.parameters(), "outputName");
|
|
||||||
OBSOutputAutoRelease output = obs_get_output_by_name(outputName);
|
|
||||||
if (!output) {
|
|
||||||
return request.failed("specified output doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(output, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List existing outputs
|
|
||||||
*
|
|
||||||
* @return {Array<Output>} `outputs` Outputs list
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name ListOutputs
|
|
||||||
* @category outputs
|
|
||||||
* @since 4.7.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::ListOutputs(const RpcRequest& request)
|
|
||||||
{
|
|
||||||
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 request.success(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
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetOutputInfo(const RpcRequest& request)
|
|
||||||
{
|
|
||||||
return findOutputOrFail(request, [](obs_output_t* output, const RpcRequest& request) {
|
|
||||||
OBSDataAutoRelease outputInfo = getOutputInfo(output);
|
|
||||||
|
|
||||||
OBSDataAutoRelease fields = obs_data_create();
|
|
||||||
obs_data_set_obj(fields, "outputInfo", outputInfo);
|
|
||||||
return request.success(fields);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start an output
|
|
||||||
*
|
|
||||||
* Note: Controlling outputs is an experimental feature of obs-websocket. Some plugins which add outputs to OBS may not function properly when they are controlled in this way.
|
|
||||||
*
|
|
||||||
* @param {String} `outputName` Output name
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name StartOutput
|
|
||||||
* @category outputs
|
|
||||||
* @since 4.7.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::StartOutput(const RpcRequest& request)
|
|
||||||
{
|
|
||||||
return findOutputOrFail(request, [](obs_output_t* output, const RpcRequest& request) {
|
|
||||||
if (obs_output_active(output)) {
|
|
||||||
return request.failed("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 request.failed(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop an output
|
|
||||||
*
|
|
||||||
* Note: Controlling outputs is an experimental feature of obs-websocket. Some plugins which add outputs to OBS may not function properly when they are controlled in this way.
|
|
||||||
*
|
|
||||||
* @param {String} `outputName` Output name
|
|
||||||
* @param {boolean (optional)} `force` Force stop (default: false)
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name StopOutput
|
|
||||||
* @category outputs
|
|
||||||
* @since 4.7.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::StopOutput(const RpcRequest& request)
|
|
||||||
{
|
|
||||||
return findOutputOrFail(request, [](obs_output_t* output, const RpcRequest& request) {
|
|
||||||
if (!obs_output_active(output)) {
|
|
||||||
return request.failed("output not active");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool forceStop = obs_data_get_bool(request.parameters(), "force");
|
|
||||||
if (forceStop) {
|
|
||||||
obs_output_force_stop(output);
|
|
||||||
} else {
|
|
||||||
obs_output_stop(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,10 +1,11 @@
|
|||||||
|
#include <QString>
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the currently active profile.
|
* Set the currently active profile.
|
||||||
*
|
*
|
||||||
* @param {String} `profile-name` Name of the desired profile.
|
* @param {String} `profile-name` Name of the desired profile.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
@ -12,33 +13,25 @@
|
|||||||
* @category profiles
|
* @category profiles
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetCurrentProfile(const RpcRequest& request) {
|
void WSRequestHandler::HandleSetCurrentProfile(WSRequestHandler* req) {
|
||||||
if (!request.hasField("profile-name")) {
|
if (!req->hasField("profile-name")) {
|
||||||
return request.failed("missing request parameters");
|
req->SendErrorResponse("missing request parameters");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* profileName = obs_data_get_string(request.parameters(), "profile-name");
|
QString profileName = obs_data_get_string(req->data, "profile-name");
|
||||||
if (!profileName) {
|
if (!profileName.isEmpty()) {
|
||||||
return request.failed("invalid request parameters");
|
// TODO : check if profile exists
|
||||||
|
obs_frontend_set_current_profile(profileName.toUtf8());
|
||||||
|
req->SendOKResponse();
|
||||||
|
} else {
|
||||||
|
req->SendErrorResponse("invalid request parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
char** profiles = obs_frontend_get_profiles();
|
|
||||||
bool profileExists = Utils::StringInStringList(profiles, profileName);
|
|
||||||
bfree(profiles);
|
|
||||||
if (!profileExists) {
|
|
||||||
return request.failed("profile does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
|
||||||
obs_frontend_set_current_profile(reinterpret_cast<const char*>(param));
|
|
||||||
}, (void*)profileName, true);
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of the current profile.
|
* Get the name of the current profile.
|
||||||
*
|
*
|
||||||
* @return {String} `profile-name` Name of the currently active profile.
|
* @return {String} `profile-name` Name of the currently active profile.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
@ -46,32 +39,32 @@ RpcResponse WSRequestHandler::SetCurrentProfile(const RpcRequest& request) {
|
|||||||
* @category profiles
|
* @category profiles
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetCurrentProfile(const RpcRequest& request) {
|
void WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler* req) {
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
char* currentProfile = obs_frontend_get_current_profile();
|
obs_data_set_string(response, "profile-name",
|
||||||
obs_data_set_string(response, "profile-name", currentProfile);
|
obs_frontend_get_current_profile());
|
||||||
bfree(currentProfile);
|
|
||||||
return request.success(response);
|
req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of available profiles.
|
* Get a list of available profiles.
|
||||||
*
|
*
|
||||||
* @return {Array<Object>} `profiles` List of available profiles.
|
* @return {Array<Object>} `profiles` List of available profiles.
|
||||||
* @return {String} `profiles.*.profile-name` Filter name
|
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name ListProfiles
|
* @name ListProfiles
|
||||||
* @category profiles
|
* @category profiles
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::ListProfiles(const RpcRequest& request) {
|
void WSRequestHandler::HandleListProfiles(WSRequestHandler* req) {
|
||||||
char** profiles = obs_frontend_get_profiles();
|
char** profiles = obs_frontend_get_profiles();
|
||||||
OBSDataArrayAutoRelease list = Utils::StringListToArray(profiles, "profile-name");
|
OBSDataArrayAutoRelease list =
|
||||||
|
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);
|
||||||
|
|
||||||
return request.success(response);
|
req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
@ -1,60 +1,23 @@
|
|||||||
#include "obs-websocket.h"
|
#include <QString>
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <util/platform.h>
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "WSEvents.h"
|
|
||||||
|
|
||||||
RpcResponse ifCanPause(const RpcRequest& request, std::function<RpcResponse()> callback)
|
|
||||||
{
|
|
||||||
if (!obs_frontend_recording_active()) {
|
|
||||||
return request.failed("recording is not active");
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current recording status.
|
|
||||||
*
|
|
||||||
* @return {boolean} `isRecording` Current recording status.
|
|
||||||
* @return {boolean} `isRecordingPaused` Whether the recording is paused or not.
|
|
||||||
* @return {String (optional)} `recordTimecode` Time elapsed since recording started (only present if currently recording).
|
|
||||||
* @return {String (optional)} `recordingFilename` Absolute path to the recording file (only present if currently recording).
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name GetRecordingStatus
|
|
||||||
* @category recording
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetRecordingStatus(const RpcRequest& request) {
|
|
||||||
auto events = GetEventsSystem();
|
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
|
||||||
obs_data_set_bool(data, "isRecording", obs_frontend_recording_active());
|
|
||||||
obs_data_set_bool(data, "isRecordingPaused", obs_frontend_recording_paused());
|
|
||||||
|
|
||||||
if (obs_frontend_recording_active()) {
|
|
||||||
QString recordingTimecode = events->getRecordingTimecode();
|
|
||||||
obs_data_set_string(data, "recordTimecode", recordingTimecode.toUtf8().constData());
|
|
||||||
obs_data_set_string(data, "recordingFilename", Utils::GetCurrentRecordingFilename());
|
|
||||||
}
|
|
||||||
|
|
||||||
return request.success(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle recording on or off (depending on the current recording state).
|
* Toggle recording on or off.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name StartStopRecording
|
* @name StartStopRecording
|
||||||
* @category recording
|
* @category recording
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::StartStopRecording(const RpcRequest& request) {
|
void WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req) {
|
||||||
(obs_frontend_recording_active() ? obs_frontend_recording_stop() : obs_frontend_recording_start());
|
if (obs_frontend_recording_active())
|
||||||
return request.success();
|
obs_frontend_recording_stop();
|
||||||
|
else
|
||||||
|
obs_frontend_recording_start();
|
||||||
|
|
||||||
|
req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,13 +29,13 @@ RpcResponse WSRequestHandler::StartStopRecording(const RpcRequest& request) {
|
|||||||
* @category recording
|
* @category recording
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::StartRecording(const RpcRequest& request) {
|
void WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
|
||||||
if (obs_frontend_recording_active()) {
|
if (obs_frontend_recording_active() == false) {
|
||||||
return request.failed("recording already active");
|
obs_frontend_recording_start();
|
||||||
|
req->SendOKResponse();
|
||||||
|
} else {
|
||||||
|
req->SendErrorResponse("recording already active");
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_frontend_recording_start();
|
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,63 +47,18 @@ RpcResponse WSRequestHandler::StartRecording(const RpcRequest& request) {
|
|||||||
* @category recording
|
* @category recording
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::StopRecording(const RpcRequest& request) {
|
void WSRequestHandler::HandleStopRecording(WSRequestHandler* req) {
|
||||||
if (!obs_frontend_recording_active()) {
|
if (obs_frontend_recording_active() == true) {
|
||||||
return request.failed("recording not active");
|
obs_frontend_recording_stop();
|
||||||
|
req->SendOKResponse();
|
||||||
|
} else {
|
||||||
|
req->SendErrorResponse("recording not active");
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_frontend_recording_stop();
|
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pause the current recording.
|
* Change the current recording folder.
|
||||||
* Returns an error if recording is not active or already paused.
|
*
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name PauseRecording
|
|
||||||
* @category recording
|
|
||||||
* @since 4.7.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::PauseRecording(const RpcRequest& request) {
|
|
||||||
return ifCanPause(request, [request]() {
|
|
||||||
if (obs_frontend_recording_paused()) {
|
|
||||||
return request.failed("recording already paused");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_recording_pause(true);
|
|
||||||
return request.success();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::ResumeRecording(const RpcRequest& request) {
|
|
||||||
return ifCanPause(request, [request]() {
|
|
||||||
if (!obs_frontend_recording_paused()) {
|
|
||||||
return request.failed("recording is not paused");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_recording_pause(false);
|
|
||||||
return request.success();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In the current profile, sets the recording folder of the Simple and Advanced
|
|
||||||
* output modes to the specified value.
|
|
||||||
*
|
|
||||||
* 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
|
||||||
@ -148,18 +66,18 @@ RpcResponse WSRequestHandler::ResumeRecording(const RpcRequest& request) {
|
|||||||
* @category recording
|
* @category recording
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetRecordingFolder(const RpcRequest& request) {
|
void WSRequestHandler::HandleSetRecordingFolder(WSRequestHandler* req) {
|
||||||
if (!request.hasField("rec-folder")) {
|
if (!req->hasField("rec-folder")) {
|
||||||
return request.failed("missing request parameters");
|
req->SendErrorResponse("missing request parameters");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* newRecFolder = obs_data_get_string(request.parameters(), "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)
|
||||||
return request.failed("invalid request parameters");
|
req->SendOKResponse();
|
||||||
}
|
else
|
||||||
|
req->SendErrorResponse("invalid request parameters");
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,11 +90,11 @@ RpcResponse WSRequestHandler::SetRecordingFolder(const RpcRequest& request) {
|
|||||||
* @category recording
|
* @category recording
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetRecordingFolder(const RpcRequest& request) {
|
void 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);
|
||||||
|
|
||||||
return request.success(response);
|
req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
@ -1,42 +1,23 @@
|
|||||||
#include "obs-websocket.h"
|
#include <QString>
|
||||||
#include "WSEvents.h"
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the status of the OBS replay buffer.
|
|
||||||
*
|
|
||||||
* @return {boolean} `isReplayBufferActive` Current recording status.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name GetReplayBufferStatus
|
|
||||||
* @category replay buffer
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetReplayBufferStatus(const RpcRequest& request) {
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
|
||||||
obs_data_set_bool(data, "isReplayBufferActive", obs_frontend_replay_buffer_active());
|
|
||||||
|
|
||||||
return request.success(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle the Replay Buffer on/off (depending on the current state of the replay buffer).
|
* Toggle the Replay Buffer on/off.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name StartStopReplayBuffer
|
* @name StartStopReplayBuffer
|
||||||
* @category replay buffer
|
* @category replay buffer
|
||||||
* @since 4.2.0
|
* @since 4.2.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::StartStopReplayBuffer(const RpcRequest& request) {
|
void 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();
|
||||||
}
|
}
|
||||||
return request.success();
|
req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,17 +32,19 @@ RpcResponse WSRequestHandler::StartStopReplayBuffer(const RpcRequest& request) {
|
|||||||
* @category replay buffer
|
* @category replay buffer
|
||||||
* @since 4.2.0
|
* @since 4.2.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::StartReplayBuffer(const RpcRequest& request) {
|
void WSRequestHandler::HandleStartReplayBuffer(WSRequestHandler* req) {
|
||||||
if (!Utils::ReplayBufferEnabled()) {
|
if (!Utils::ReplayBufferEnabled()) {
|
||||||
return request.failed("replay buffer disabled in settings");
|
req->SendErrorResponse("replay buffer disabled in settings");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obs_frontend_replay_buffer_active() == true) {
|
if (obs_frontend_replay_buffer_active() == true) {
|
||||||
return request.failed("replay buffer already active");
|
req->SendErrorResponse("replay buffer already active");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::StartReplayBuffer();
|
Utils::StartReplayBuffer();
|
||||||
return request.success();
|
req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,12 +56,12 @@ RpcResponse WSRequestHandler::StartReplayBuffer(const RpcRequest& request) {
|
|||||||
* @category replay buffer
|
* @category replay buffer
|
||||||
* @since 4.2.0
|
* @since 4.2.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::StopReplayBuffer(const RpcRequest& request) {
|
void 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();
|
||||||
return request.success();
|
req->SendOKResponse();
|
||||||
} else {
|
} else {
|
||||||
return request.failed("replay buffer not active");
|
req->SendErrorResponse("replay buffer not active");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,9 +75,10 @@ RpcResponse WSRequestHandler::StopReplayBuffer(const RpcRequest& request) {
|
|||||||
* @category replay buffer
|
* @category replay buffer
|
||||||
* @since 4.2.0
|
* @since 4.2.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SaveReplayBuffer(const RpcRequest& request) {
|
void WSRequestHandler::HandleSaveReplayBuffer(WSRequestHandler* req) {
|
||||||
if (!obs_frontend_replay_buffer_active()) {
|
if (!obs_frontend_replay_buffer_active()) {
|
||||||
return request.failed("replay buffer not active");
|
req->SendErrorResponse("replay buffer not active");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSOutputAutoRelease replayOutput = obs_frontend_get_replay_buffer_output();
|
OBSOutputAutoRelease replayOutput = obs_frontend_get_replay_buffer_output();
|
||||||
@ -104,5 +88,5 @@ RpcResponse WSRequestHandler::SaveReplayBuffer(const RpcRequest& request) {
|
|||||||
proc_handler_call(ph, "save", &cd);
|
proc_handler_call(ph, "save", &cd);
|
||||||
calldata_free(&cd);
|
calldata_free(&cd);
|
||||||
|
|
||||||
return request.success();
|
req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
|
#include <QString>
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} `ScenesCollection`
|
|
||||||
* @property {String} `sc-name` Name of the scene collection
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the active scene collection.
|
* Change the active scene collection.
|
||||||
*
|
*
|
||||||
@ -17,28 +13,20 @@
|
|||||||
* @category scene collections
|
* @category scene collections
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetCurrentSceneCollection(const RpcRequest& request) {
|
void WSRequestHandler::HandleSetCurrentSceneCollection(WSRequestHandler* req) {
|
||||||
if (!request.hasField("sc-name")) {
|
if (!req->hasField("sc-name")) {
|
||||||
return request.failed("missing request parameters");
|
req->SendErrorResponse("missing request parameters");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* sceneCollection = obs_data_get_string(request.parameters(), "sc-name");
|
QString sceneCollection = obs_data_get_string(req->data, "sc-name");
|
||||||
if (!sceneCollection) {
|
if (!sceneCollection.isEmpty()) {
|
||||||
return request.failed("invalid request parameters");
|
// TODO : Check if specified profile exists and if changing is allowed
|
||||||
|
obs_frontend_set_current_scene_collection(sceneCollection.toUtf8());
|
||||||
|
req->SendOKResponse();
|
||||||
|
} else {
|
||||||
|
req->SendErrorResponse("invalid request parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
char** collections = obs_frontend_get_scene_collections();
|
|
||||||
bool collectionExists = Utils::StringInStringList(collections, sceneCollection);
|
|
||||||
bfree(collections);
|
|
||||||
if (!collectionExists) {
|
|
||||||
return request.failed("scene collection does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
|
||||||
obs_frontend_set_current_scene_collection(reinterpret_cast<const char*>(param));
|
|
||||||
}, (void*)sceneCollection, true);
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,27 +39,25 @@ RpcResponse WSRequestHandler::SetCurrentSceneCollection(const RpcRequest& reques
|
|||||||
* @category scene collections
|
* @category scene collections
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetCurrentSceneCollection(const RpcRequest& request) {
|
void WSRequestHandler::HandleGetCurrentSceneCollection(WSRequestHandler* req) {
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
|
obs_data_set_string(response, "sc-name",
|
||||||
|
obs_frontend_get_current_scene_collection());
|
||||||
|
|
||||||
char* sceneCollection = obs_frontend_get_current_scene_collection();
|
req->SendOKResponse(response);
|
||||||
obs_data_set_string(response, "sc-name", sceneCollection);
|
|
||||||
bfree(sceneCollection);
|
|
||||||
|
|
||||||
return request.success(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List available scene collections
|
* List available scene collections
|
||||||
*
|
*
|
||||||
* @return {Array<ScenesCollection>} `scene-collections` Scene collections list
|
* @return {Array<String>} `scene-collections` Scene collections list
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name ListSceneCollections
|
* @name ListSceneCollections
|
||||||
* @category scene collections
|
* @category scene collections
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::ListSceneCollections(const RpcRequest& request) {
|
void 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");
|
||||||
@ -80,5 +66,5 @@ RpcResponse WSRequestHandler::ListSceneCollections(const RpcRequest& request) {
|
|||||||
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);
|
||||||
|
|
||||||
return request.success(response);
|
req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,4 @@
|
|||||||
|
#include <QString>
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
@ -5,7 +6,7 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {Object} `Scene`
|
* @typedef {Object} `Scene`
|
||||||
* @property {String} `name` Name of the currently active scene.
|
* @property {String} `name` Name of the currently active scene.
|
||||||
* @property {Array<SceneItem>} `sources` Ordered list of the current scene's source items.
|
* @property {Array<Source>} `sources` Ordered list of the current scene's source items.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,34 +19,35 @@
|
|||||||
* @category scenes
|
* @category scenes
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetCurrentScene(const RpcRequest& request) {
|
void WSRequestHandler::HandleSetCurrentScene(WSRequestHandler* req) {
|
||||||
if (!request.hasField("scene-name")) {
|
if (!req->hasField("scene-name")) {
|
||||||
return request.failed("missing request parameters");
|
req->SendErrorResponse("missing request parameters");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* sceneName = obs_data_get_string(request.parameters(), "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);
|
||||||
return request.success();
|
req->SendOKResponse();
|
||||||
} else {
|
} else {
|
||||||
return request.failed("requested scene does not exist");
|
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 {Array<SceneItem>} `sources` Ordered list of the current scene's source items.
|
* @return {Array<Source>} `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
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetCurrentScene(const RpcRequest& request) {
|
void 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);
|
||||||
|
|
||||||
@ -53,21 +55,21 @@ RpcResponse WSRequestHandler::GetCurrentScene(const RpcRequest& request) {
|
|||||||
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);
|
||||||
|
|
||||||
return request.success(data);
|
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 {Array<Scene>} `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
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetSceneList(const RpcRequest& request) {
|
void 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();
|
||||||
|
|
||||||
@ -76,227 +78,71 @@ RpcResponse WSRequestHandler::GetSceneList(const RpcRequest& request) {
|
|||||||
obs_source_get_name(currentScene));
|
obs_source_get_name(currentScene));
|
||||||
obs_data_set_array(data, "scenes", scenes);
|
obs_data_set_array(data, "scenes", scenes);
|
||||||
|
|
||||||
return request.success(data);
|
req->SendOKResponse(data);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new scene scene.
|
|
||||||
*
|
|
||||||
* @param {String} `sceneName` Name of the scene to create.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name CreateScene
|
|
||||||
* @category scenes
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::CreateScene(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sceneName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* sceneName = obs_data_get_string(request.parameters(), "sceneName");
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName);
|
|
||||||
|
|
||||||
if (source) {
|
|
||||||
return request.failed("scene with this name already exists");
|
|
||||||
}
|
|
||||||
obs_scene_t *createdScene = obs_scene_create(sceneName);
|
|
||||||
obs_scene_release(createdScene);
|
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the order of scene items in the requested scene.
|
* Changes the order of scene items in the requested scene.
|
||||||
*
|
*
|
||||||
* @param {String (optional)} `scene` Name of the scene to reorder (defaults to current).
|
* @param {String (optional)} `scene` Name of the scene to reorder (defaults to current).
|
||||||
* @param {Array<Object>} `items` Ordered list of objects with name and/or id specified. Id preferred due to uniqueness per scene
|
* @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 {int (optional)} `items[].id` Id of a specific scene item. Unique on a scene by scene basis.
|
||||||
* @param {String (optional)} `items.*.name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene.
|
* @param {String (optional)} `items[].name` Name of a scene item. Sufficiently unique if no scene items share sources within the scene.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name ReorderSceneItems
|
* @name ReorderSceneItems
|
||||||
* @category scenes
|
* @category scenes
|
||||||
* @since 4.5.0
|
* @since 4.5.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::ReorderSceneItems(const RpcRequest& request) {
|
void WSRequestHandler::HandleReorderSceneItems(WSRequestHandler* req) {
|
||||||
QString sceneName = obs_data_get_string(request.parameters(), "scene");
|
QString sceneName = obs_data_get_string(req->data, "scene");
|
||||||
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(sceneName);
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
return request.failed("requested scene doesn't exist");
|
req->SendErrorResponse("requested scene doesn't exist");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSDataArrayAutoRelease items = obs_data_get_array(request.parameters(), "items");
|
OBSDataArrayAutoRelease items = obs_data_get_array(req->data, "items");
|
||||||
if (!items) {
|
if (!items) {
|
||||||
return request.failed("sceneItem order not specified");
|
req->SendErrorResponse("sceneItem order not specified");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct reorder_context {
|
size_t count = obs_data_array_count(items);
|
||||||
obs_data_array_t* items;
|
|
||||||
bool success;
|
|
||||||
QString errorMessage;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct reorder_context ctx;
|
std::vector<obs_sceneitem_t*> newOrder;
|
||||||
ctx.success = false;
|
newOrder.reserve(count);
|
||||||
ctx.items = items;
|
|
||||||
|
|
||||||
obs_scene_atomic_update(scene, [](void* param, obs_scene_t* scene) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
auto ctx = reinterpret_cast<struct reorder_context*>(param);
|
OBSDataAutoRelease item = obs_data_array_item(items, i);
|
||||||
|
|
||||||
QVector<struct obs_sceneitem_order_info> orderList;
|
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item);
|
||||||
struct obs_sceneitem_order_info info;
|
obs_sceneitem_release(sceneItem); // ref dec
|
||||||
|
|
||||||
size_t itemCount = obs_data_array_count(ctx->items);
|
if (!sceneItem) {
|
||||||
for (uint i = 0; i < itemCount; i++) {
|
req->SendErrorResponse("Invalid sceneItem id or name specified");
|
||||||
OBSDataAutoRelease item = obs_data_array_item(ctx->items, i);
|
return;
|
||||||
|
}
|
||||||
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item);
|
|
||||||
if (!sceneItem) {
|
for (size_t j = 0; j <= i; ++j) {
|
||||||
ctx->success = false;
|
if (sceneItem == newOrder[j]) {
|
||||||
ctx->errorMessage = "Invalid sceneItem id or name specified";
|
req->SendErrorResponse("Duplicate sceneItem in specified order");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
info.group = nullptr;
|
|
||||||
info.item = sceneItem;
|
|
||||||
orderList.insert(0, info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->success = obs_scene_reorder_items2(scene, orderList.data(), orderList.size());
|
newOrder.push_back(sceneItem);
|
||||||
if (!ctx->success) {
|
|
||||||
ctx->errorMessage = "Invalid sceneItem order";
|
|
||||||
}
|
|
||||||
}, &ctx);
|
|
||||||
|
|
||||||
if (!ctx.success) {
|
|
||||||
return request.failed(ctx.errorMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return request.success();
|
bool success = obs_scene_reorder_items(obs_scene_from_source(scene), newOrder.data(), count);
|
||||||
}
|
if (!success) {
|
||||||
|
req->SendErrorResponse("Invalid sceneItem order");
|
||||||
/**
|
return;
|
||||||
* Set a scene to use a specific transition override.
|
}
|
||||||
*
|
|
||||||
* @param {String} `sceneName` Name of the scene to switch to.
|
for (auto const& item: newOrder) {
|
||||||
* @param {String} `transitionName` Name of the transition to use.
|
obs_sceneitem_release(item);
|
||||||
* @param {int (Optional)} `transitionDuration` Duration in milliseconds of the transition if transition is not fixed. Defaults to the current duration specified in the UI if there is no current override and this value is not given.
|
}
|
||||||
*
|
|
||||||
* @api requests
|
req->SendOKResponse();
|
||||||
* @name SetSceneTransitionOverride
|
|
||||||
* @category scenes
|
|
||||||
* @since 4.8.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::SetSceneTransitionOverride(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sceneName") || !request.hasField("transitionName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sceneName = obs_data_get_string(request.parameters(), "sceneName");
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("requested scene does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
enum obs_source_type sourceType = obs_source_get_type(source);
|
|
||||||
if (sourceType != OBS_SOURCE_TYPE_SCENE) {
|
|
||||||
return request.failed("requested scene is invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString transitionName = obs_data_get_string(request.parameters(), "transitionName");
|
|
||||||
OBSSourceAutoRelease transition = Utils::GetTransitionFromName(transitionName);
|
|
||||||
if (!transition) {
|
|
||||||
return request.failed("requested transition does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease sourceData = obs_source_get_private_settings(source);
|
|
||||||
obs_data_set_string(sourceData, "transition", transitionName.toUtf8().constData());
|
|
||||||
|
|
||||||
if (request.hasField("transitionDuration")) {
|
|
||||||
int transitionOverrideDuration = obs_data_get_int(request.parameters(), "transitionDuration");
|
|
||||||
obs_data_set_int(sourceData, "transition_duration", transitionOverrideDuration);
|
|
||||||
} else if(!obs_data_has_user_value(sourceData, "transition_duration")) {
|
|
||||||
obs_data_set_int(sourceData, "transition_duration",
|
|
||||||
obs_frontend_get_transition_duration()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove any transition override on a scene.
|
|
||||||
*
|
|
||||||
* @param {String} `sceneName` Name of the scene to switch to.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name RemoveSceneTransitionOverride
|
|
||||||
* @category scenes
|
|
||||||
* @since 4.8.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::RemoveSceneTransitionOverride(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sceneName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sceneName = obs_data_get_string(request.parameters(), "sceneName");
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("requested scene does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
enum obs_source_type sourceType = obs_source_get_type(source);
|
|
||||||
if (sourceType != OBS_SOURCE_TYPE_SCENE) {
|
|
||||||
return request.failed("requested scene is invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease sourceData = obs_source_get_private_settings(source);
|
|
||||||
obs_data_erase(sourceData, "transition");
|
|
||||||
obs_data_erase(sourceData, "transition_duration");
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current scene transition override.
|
|
||||||
*
|
|
||||||
* @param {String} `sceneName` Name of the scene to switch to.
|
|
||||||
*
|
|
||||||
* @return {String} `transitionName` Name of the current overriding transition. Empty string if no override is set.
|
|
||||||
* @return {int} `transitionDuration` Transition duration. `-1` if no override is set.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name GetSceneTransitionOverride
|
|
||||||
* @category scenes
|
|
||||||
* @since 4.8.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetSceneTransitionOverride(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("sceneName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString sceneName = obs_data_get_string(request.parameters(), "sceneName");
|
|
||||||
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName.toUtf8());
|
|
||||||
if (!source) {
|
|
||||||
return request.failed("requested scene does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
enum obs_source_type sourceType = obs_source_get_type(source);
|
|
||||||
if (sourceType != OBS_SOURCE_TYPE_SCENE) {
|
|
||||||
return request.failed("requested scene is invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease sourceData = obs_source_get_private_settings(source);
|
|
||||||
const char* transitionOverrideName = obs_data_get_string(sourceData, "transition");
|
|
||||||
|
|
||||||
bool hasDurationOverride = obs_data_has_user_value(sourceData, "transition_duration");
|
|
||||||
int transitionOverrideDuration = obs_data_get_int(sourceData, "transition_duration");
|
|
||||||
|
|
||||||
OBSDataAutoRelease fields = obs_data_create();
|
|
||||||
obs_data_set_string(fields, "transitionName", transitionOverrideName);
|
|
||||||
obs_data_set_int(fields, "transitionDuration",
|
|
||||||
(hasDurationOverride ? transitionOverrideDuration : -1)
|
|
||||||
);
|
|
||||||
|
|
||||||
return request.success(fields);
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
#include "obs-websocket.h"
|
#include <QString>
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "WSEvents.h"
|
#include "WSEvents.h"
|
||||||
|
|
||||||
@ -8,92 +8,83 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 {boolean} `recording-paused` If recording is paused.
|
|
||||||
* @return {boolean} `virtualcam` Current virtual cam status.
|
|
||||||
* @return {boolean} `preview-only` Always false. Retrocompatibility with OBSRemote.
|
|
||||||
* @return {String (optional)} `stream-timecode` Time elapsed since streaming started (only present if currently streaming).
|
* @return {String (optional)} `stream-timecode` Time elapsed since streaming started (only present if currently streaming).
|
||||||
* @return {String (optional)} `rec-timecode` Time elapsed since recording started (only present if currently recording).
|
* @return {String (optional)} `rec-timecode` Time elapsed since recording started (only present if currently recording).
|
||||||
* @return {String (optional)} `virtualcam-timecode` Time elapsed since virtual cam started (only present if virtual cam currently active).
|
* @return {boolean} `preview-only` Always false. Retrocompatibility with OBSRemote.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name GetStreamingStatus
|
* @name GetStreamingStatus
|
||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetStreamingStatus(const RpcRequest& request) {
|
void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req) {
|
||||||
auto events = GetEventsSystem();
|
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
obs_data_set_bool(data, "streaming", obs_frontend_streaming_active());
|
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, "recording", obs_frontend_recording_active());
|
||||||
obs_data_set_bool(data, "recording-paused", obs_frontend_recording_paused());
|
|
||||||
obs_data_set_bool(data, "virtualcam", obs_frontend_virtualcam_active());
|
|
||||||
obs_data_set_bool(data, "preview-only", false);
|
obs_data_set_bool(data, "preview-only", false);
|
||||||
|
|
||||||
|
const char* tc = nullptr;
|
||||||
if (obs_frontend_streaming_active()) {
|
if (obs_frontend_streaming_active()) {
|
||||||
QString streamingTimecode = events->getStreamingTimecode();
|
tc = WSEvents::Instance->GetStreamingTimecode();
|
||||||
obs_data_set_string(data, "stream-timecode", streamingTimecode.toUtf8().constData());
|
obs_data_set_string(data, "stream-timecode", tc);
|
||||||
|
bfree((void*)tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obs_frontend_recording_active()) {
|
if (obs_frontend_recording_active()) {
|
||||||
QString recordingTimecode = events->getRecordingTimecode();
|
tc = WSEvents::Instance->GetRecordingTimecode();
|
||||||
obs_data_set_string(data, "rec-timecode", recordingTimecode.toUtf8().constData());
|
obs_data_set_string(data, "rec-timecode", tc);
|
||||||
|
bfree((void*)tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obs_frontend_virtualcam_active()) {
|
req->SendOKResponse(data);
|
||||||
QString virtualCamTimecode = events->getVirtualCamTimecode();
|
|
||||||
obs_data_set_string(data, "virtualcam-timecode", virtualCamTimecode.toUtf8().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
return request.success(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle streaming on or off (depending on the current stream state).
|
* Toggle streaming on or off.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name StartStopStreaming
|
* @name StartStopStreaming
|
||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::StartStopStreaming(const RpcRequest& request) {
|
void WSRequestHandler::HandleStartStopStreaming(WSRequestHandler* req) {
|
||||||
if (obs_frontend_streaming_active())
|
if (obs_frontend_streaming_active())
|
||||||
return StopStreaming(request);
|
HandleStopStreaming(req);
|
||||||
else
|
else
|
||||||
return StartStreaming(request);
|
HandleStartStreaming(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start streaming.
|
* Start streaming.
|
||||||
* Will return an `error` if streaming is already active.
|
* Will return an `error` if streaming is already active.
|
||||||
*
|
*
|
||||||
* @param {Object (optional)} `stream` Special stream configuration. 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.
|
||||||
* @param {boolean (optional)} `stream.settings.use_auth` Indicates whether authentication should be used when connecting to the streaming server.
|
* @param {boolean (optional)} `stream.settings.use-auth` Indicates whether authentication should be used when connecting to the streaming server.
|
||||||
* @param {String (optional)} `stream.settings.username` If authentication is enabled, the username for the streaming server. Ignored if `use_auth` is not set to `true`.
|
* @param {String (optional)} `stream.settings.username` If authentication is enabled, the username for the streaming server. Ignored if `use-auth` is not set to `true`.
|
||||||
* @param {String (optional)} `stream.settings.password` If authentication is enabled, the password for the streaming server. Ignored if `use_auth` is not set to `true`.
|
* @param {String (optional)} `stream.settings.password` If authentication is enabled, the password for the streaming server. Ignored if `use-auth` is not set to `true`.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name StartStreaming
|
* @name StartStreaming
|
||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::StartStreaming(const RpcRequest& request) {
|
void 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 (request.hasField("stream")) {
|
if (req->hasField("stream")) {
|
||||||
OBSDataAutoRelease streamData = obs_data_get_obj(request.parameters(), "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");
|
||||||
|
|
||||||
@ -112,10 +103,10 @@ RpcResponse WSRequestHandler::StartStreaming(const RpcRequest& request) {
|
|||||||
&& 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");
|
||||||
size_t keylen = strlen(key);
|
int keylen = strlen(key);
|
||||||
|
|
||||||
bool hasQuestionMark = false;
|
bool hasQuestionMark = false;
|
||||||
for (size_t i = 0; i < keylen; i++) {
|
for (int i = 0; i < keylen; i++) {
|
||||||
if (key[i] == '?') {
|
if (key[i] == '?') {
|
||||||
hasQuestionMark = true;
|
hasQuestionMark = true;
|
||||||
break;
|
break;
|
||||||
@ -166,9 +157,9 @@ RpcResponse WSRequestHandler::StartStreaming(const RpcRequest& request) {
|
|||||||
obs_frontend_set_streaming_service(configuredService);
|
obs_frontend_set_streaming_service(configuredService);
|
||||||
}
|
}
|
||||||
|
|
||||||
return request.success();
|
req->SendOKResponse();
|
||||||
} else {
|
} else {
|
||||||
return request.failed("streaming already active");
|
req->SendErrorResponse("streaming already active");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,23 +172,23 @@ RpcResponse WSRequestHandler::StartStreaming(const RpcRequest& request) {
|
|||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::StopStreaming(const RpcRequest& request) {
|
void WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) {
|
||||||
if (obs_frontend_streaming_active() == true) {
|
if (obs_frontend_streaming_active() == true) {
|
||||||
obs_frontend_streaming_stop();
|
obs_frontend_streaming_stop();
|
||||||
return request.success();
|
req->SendOKResponse();
|
||||||
} else {
|
} else {
|
||||||
return request.failed("streaming not active");
|
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.
|
||||||
* @param {String (optional)} `settings.key` The publish key.
|
* @param {String (optional)} `settings.key` The publish key.
|
||||||
* @param {boolean (optional)} `settings.use_auth` Indicates whether authentication should be used when connecting to the streaming server.
|
* @param {boolean (optional)} `settings.use-auth` Indicates whether authentication should be used when connecting to the streaming server.
|
||||||
* @param {String (optional)} `settings.username` The username for the streaming service.
|
* @param {String (optional)} `settings.username` The username for the streaming service.
|
||||||
* @param {String (optional)} `settings.password` The password for the streaming service.
|
* @param {String (optional)} `settings.password` The password for the streaming service.
|
||||||
* @param {boolean} `save` Persist the settings to disk.
|
* @param {boolean} `save` Persist the settings to disk.
|
||||||
@ -207,22 +198,22 @@ RpcResponse WSRequestHandler::StopStreaming(const RpcRequest& request) {
|
|||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetStreamSettings(const RpcRequest& request) {
|
void WSRequestHandler::HandleSetStreamSettings(WSRequestHandler* req) {
|
||||||
OBSService service = obs_frontend_get_streaming_service();
|
OBSService service = obs_frontend_get_streaming_service();
|
||||||
|
|
||||||
OBSDataAutoRelease requestSettings = obs_data_get_obj(request.parameters(), "settings");
|
OBSDataAutoRelease requestSettings = obs_data_get_obj(req->data, "settings");
|
||||||
if (!requestSettings) {
|
if (!requestSettings) {
|
||||||
return request.failed("'settings' are required'");
|
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(request.parameters(), "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);
|
||||||
obs_frontend_set_streaming_service(service);
|
|
||||||
} 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
|
||||||
@ -241,19 +232,17 @@ RpcResponse WSRequestHandler::SetStreamSettings(const RpcRequest& request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//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(request.parameters(), "save")) {
|
if (obs_data_get_bool(req->data, "save")) {
|
||||||
obs_frontend_save_streaming_service();
|
obs_frontend_save_streaming_service();
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSService responseService = obs_frontend_get_streaming_service();
|
OBSDataAutoRelease serviceSettings = obs_service_get_settings(service);
|
||||||
OBSDataAutoRelease serviceSettings = obs_service_get_settings(responseService);
|
|
||||||
const char* responseType = obs_service_get_type(responseService);
|
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_string(response, "type", responseType);
|
obs_data_set_string(response, "type", requestedType.toUtf8());
|
||||||
obs_data_set_obj(response, "settings", serviceSettings);
|
obs_data_set_obj(response, "settings", serviceSettings);
|
||||||
|
|
||||||
return request.success(response);
|
req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -263,16 +252,16 @@ RpcResponse WSRequestHandler::SetStreamSettings(const RpcRequest& request) {
|
|||||||
* @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 authentication 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`.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name GetStreamSettings
|
* @name GetStreamSettings
|
||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetStreamSettings(const RpcRequest& request) {
|
void 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);
|
||||||
@ -282,7 +271,7 @@ RpcResponse WSRequestHandler::GetStreamSettings(const RpcRequest& request) {
|
|||||||
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);
|
||||||
|
|
||||||
return request.success(response);
|
req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -293,34 +282,7 @@ RpcResponse WSRequestHandler::GetStreamSettings(const RpcRequest& request) {
|
|||||||
* @category streaming
|
* @category streaming
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SaveStreamSettings(const RpcRequest& request) {
|
void WSRequestHandler::HandleSaveStreamSettings(WSRequestHandler* req) {
|
||||||
obs_frontend_save_streaming_service();
|
obs_frontend_save_streaming_service();
|
||||||
return request.success();
|
req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send the provided text as embedded CEA-608 caption data.
|
|
||||||
*
|
|
||||||
* @param {String} `text` Captions text
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name SendCaptions
|
|
||||||
* @category streaming
|
|
||||||
* @since 4.6.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::SendCaptions(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("text")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSOutputAutoRelease output = obs_frontend_get_streaming_output();
|
|
||||||
if (output) {
|
|
||||||
const char* caption = obs_data_get_string(request.parameters(), "text");
|
|
||||||
// Send caption text with immediately (0 second delay)
|
|
||||||
obs_output_output_caption_text2(output, caption, 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <QString>
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
@ -12,13 +13,13 @@
|
|||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetStudioModeStatus(const RpcRequest& request) {
|
void 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);
|
||||||
|
|
||||||
return request.success(response);
|
req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,16 +27,17 @@ RpcResponse WSRequestHandler::GetStudioModeStatus(const RpcRequest& request) {
|
|||||||
* 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 {Array<SceneItem>} `sources`
|
* @return {Array<Source>} `sources`
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name GetPreviewScene
|
* @name GetPreviewScene
|
||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetPreviewScene(const RpcRequest& request) {
|
void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler* req) {
|
||||||
if (!obs_frontend_preview_program_mode_active()) {
|
if (!obs_frontend_preview_program_mode_active()) {
|
||||||
return request.failed("studio mode not enabled");
|
req->SendErrorResponse("studio mode not enabled");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSSourceAutoRelease scene = obs_frontend_get_current_preview_scene();
|
OBSSourceAutoRelease scene = obs_frontend_get_current_preview_scene();
|
||||||
@ -45,7 +47,7 @@ RpcResponse WSRequestHandler::GetPreviewScene(const RpcRequest& request) {
|
|||||||
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);
|
||||||
|
|
||||||
return request.success(data);
|
req->SendOKResponse(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,23 +61,26 @@ RpcResponse WSRequestHandler::GetPreviewScene(const RpcRequest& request) {
|
|||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetPreviewScene(const RpcRequest& request) {
|
void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler* req) {
|
||||||
if (!obs_frontend_preview_program_mode_active()) {
|
if (!obs_frontend_preview_program_mode_active()) {
|
||||||
return request.failed("studio mode not enabled");
|
req->SendErrorResponse("studio mode not enabled");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request.hasField("scene-name")) {
|
if (!req->hasField("scene-name")) {
|
||||||
return request.failed("missing request parameters");
|
req->SendErrorResponse("missing request parameters");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* scene_name = obs_data_get_string(request.parameters(), "scene-name");
|
const char* scene_name = obs_data_get_string(req->data, "scene-name");
|
||||||
OBSScene scene = Utils::GetSceneFromNameOrCurrent(scene_name);
|
OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(scene_name);
|
||||||
if (!scene) {
|
|
||||||
return request.failed("specified scene doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_set_current_preview_scene(obs_scene_get_source(scene));
|
if (scene) {
|
||||||
return request.success();
|
obs_frontend_set_current_preview_scene(scene);
|
||||||
|
req->SendOKResponse();
|
||||||
|
} else {
|
||||||
|
req->SendErrorResponse("specified scene doesn't exist");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,37 +96,40 @@ RpcResponse WSRequestHandler::SetPreviewScene(const RpcRequest& request) {
|
|||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::TransitionToProgram(const RpcRequest& request) {
|
void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler* req) {
|
||||||
if (!obs_frontend_preview_program_mode_active()) {
|
if (!obs_frontend_preview_program_mode_active()) {
|
||||||
return request.failed("studio mode not enabled");
|
req->SendErrorResponse("studio mode not enabled");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.hasField("with-transition")) {
|
if (req->hasField("with-transition")) {
|
||||||
OBSDataAutoRelease transitionInfo =
|
OBSDataAutoRelease transitionInfo =
|
||||||
obs_data_get_obj(request.parameters(), "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()) {
|
||||||
return request.failed("invalid request parameters");
|
req->SendErrorResponse("invalid request parameters");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = Utils::SetTransitionByName(transitionName);
|
bool success = Utils::SetTransitionByName(transitionName);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return request.failed("specified transition doesn't exist");
|
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");
|
||||||
obs_frontend_set_transition_duration(transitionDuration);
|
Utils::SetTransitionDuration(transitionDuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_frontend_preview_program_trigger_transition();
|
Utils::TransitionToProgram();
|
||||||
return request.success();
|
req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,16 +140,9 @@ RpcResponse WSRequestHandler::TransitionToProgram(const RpcRequest& request) {
|
|||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::EnableStudioMode(const RpcRequest& request) {
|
void WSRequestHandler::HandleEnableStudioMode(WSRequestHandler* req) {
|
||||||
if (obs_frontend_preview_program_mode_active()) {
|
obs_frontend_set_preview_program_mode(true);
|
||||||
return request.failed("studio mode already active");
|
req->SendOKResponse();
|
||||||
}
|
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
|
||||||
obs_frontend_set_preview_program_mode(true);
|
|
||||||
|
|
||||||
UNUSED_PARAMETER(param);
|
|
||||||
}, nullptr, true);
|
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -152,34 +153,21 @@ RpcResponse WSRequestHandler::EnableStudioMode(const RpcRequest& request) {
|
|||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::DisableStudioMode(const RpcRequest& request) {
|
void WSRequestHandler::HandleDisableStudioMode(WSRequestHandler* req) {
|
||||||
if (!obs_frontend_preview_program_mode_active()) {
|
obs_frontend_set_preview_program_mode(false);
|
||||||
return request.failed("studio mode not active");
|
req->SendOKResponse();
|
||||||
}
|
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
|
||||||
obs_frontend_set_preview_program_mode(false);
|
|
||||||
|
|
||||||
UNUSED_PARAMETER(param);
|
|
||||||
}, nullptr, true);
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles Studio Mode (depending on the current state of studio mode).
|
* Toggles Studio Mode.
|
||||||
*
|
*
|
||||||
* @api requests
|
* @api requests
|
||||||
* @name ToggleStudioMode
|
* @name ToggleStudioMode
|
||||||
* @category studio mode
|
* @category studio mode
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::ToggleStudioMode(const RpcRequest& request) {
|
void WSRequestHandler::HandleToggleStudioMode(WSRequestHandler* req) {
|
||||||
obs_queue_task(OBS_TASK_UI, [](void* param) {
|
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();
|
||||||
|
}
|
||||||
UNUSED_PARAMETER(param);
|
|
||||||
}, nullptr, true);
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <QString>
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
@ -14,7 +15,7 @@
|
|||||||
* @category transitions
|
* @category transitions
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetTransitionList(const RpcRequest& request) {
|
void 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);
|
||||||
@ -34,7 +35,7 @@ RpcResponse WSRequestHandler::GetTransitionList(const RpcRequest& request) {
|
|||||||
obs_source_get_name(currentTransition));
|
obs_source_get_name(currentTransition));
|
||||||
obs_data_set_array(response, "transitions", transitions);
|
obs_data_set_array(response, "transitions", transitions);
|
||||||
|
|
||||||
return request.success(response);
|
req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,7 +49,7 @@ RpcResponse WSRequestHandler::GetTransitionList(const RpcRequest& request) {
|
|||||||
* @category transitions
|
* @category transitions
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetCurrentTransition(const RpcRequest& request) {
|
void 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();
|
||||||
@ -56,9 +57,9 @@ RpcResponse WSRequestHandler::GetCurrentTransition(const RpcRequest& request) {
|
|||||||
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", obs_frontend_get_transition_duration());
|
obs_data_set_int(response, "duration", Utils::GetTransitionDuration());
|
||||||
|
|
||||||
return request.success(response);
|
req->SendOKResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,18 +72,18 @@ RpcResponse WSRequestHandler::GetCurrentTransition(const RpcRequest& request) {
|
|||||||
* @category transitions
|
* @category transitions
|
||||||
* @since 0.3
|
* @since 0.3
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetCurrentTransition(const RpcRequest& request) {
|
void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler* req) {
|
||||||
if (!request.hasField("transition-name")) {
|
if (!req->hasField("transition-name")) {
|
||||||
return request.failed("missing request parameters");
|
req->SendErrorResponse("missing request parameters");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString name = obs_data_get_string(request.parameters(), "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)
|
||||||
return request.failed("requested transition does not exist");
|
req->SendOKResponse();
|
||||||
}
|
else
|
||||||
|
req->SendErrorResponse("requested transition does not exist");
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,14 +96,15 @@ RpcResponse WSRequestHandler::SetCurrentTransition(const RpcRequest& request) {
|
|||||||
* @category transitions
|
* @category transitions
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::SetTransitionDuration(const RpcRequest& request) {
|
void WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler* req) {
|
||||||
if (!request.hasField("duration")) {
|
if (!req->hasField("duration")) {
|
||||||
return request.failed("missing request parameters");
|
req->SendErrorResponse("missing request parameters");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ms = obs_data_get_int(request.parameters(), "duration");
|
int ms = obs_data_get_int(req->data, "duration");
|
||||||
obs_frontend_set_transition_duration(ms);
|
Utils::SetTransitionDuration(ms);
|
||||||
return request.success();
|
req->SendOKResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,162 +117,10 @@ RpcResponse WSRequestHandler::SetTransitionDuration(const RpcRequest& request) {
|
|||||||
* @category transitions
|
* @category transitions
|
||||||
* @since 4.1.0
|
* @since 4.1.0
|
||||||
*/
|
*/
|
||||||
RpcResponse WSRequestHandler::GetTransitionDuration(const RpcRequest& request) {
|
void WSRequestHandler::HandleGetTransitionDuration(WSRequestHandler* req) {
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
OBSDataAutoRelease response = obs_data_create();
|
||||||
obs_data_set_int(response, "transition-duration", obs_frontend_get_transition_duration());
|
obs_data_set_int(response, "transition-duration",
|
||||||
return request.success(response);
|
Utils::GetTransitionDuration());
|
||||||
}
|
|
||||||
|
req->SendOKResponse(response);
|
||||||
/**
|
|
||||||
* Get the position of the current transition.
|
|
||||||
*
|
|
||||||
* @return {double} `position` current transition position. This value will be between 0.0 and 1.0. Note: Transition returns 1.0 when not active.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name GetTransitionPosition
|
|
||||||
* @category transitions
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetTransitionPosition(const RpcRequest& request) {
|
|
||||||
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
|
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
|
||||||
obs_data_set_double(response, "position", obs_transition_get_time(currentTransition));
|
|
||||||
|
|
||||||
return request.success(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current settings of a transition
|
|
||||||
*
|
|
||||||
* @param {String} `transitionName` Transition name
|
|
||||||
*
|
|
||||||
* @return {Object} `transitionSettings` Current transition settings
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name GetTransitionSettings
|
|
||||||
* @category transitions
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetTransitionSettings(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("transitionName")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* transitionName = obs_data_get_string(request.parameters(), "transitionName");
|
|
||||||
OBSSourceAutoRelease transition = Utils::GetTransitionFromName(transitionName);
|
|
||||||
if (!transition) {
|
|
||||||
return request.failed("specified transition doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease transitionSettings = obs_source_get_settings(transition);
|
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
|
||||||
obs_data_set_obj(response, "transitionSettings", transitionSettings);
|
|
||||||
return request.success(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the current settings of a transition
|
|
||||||
*
|
|
||||||
* @param {String} `transitionName` Transition name
|
|
||||||
* @param {Object} `transitionSettings` Transition settings (they can be partial)
|
|
||||||
*
|
|
||||||
* @return {Object} `transitionSettings` Updated transition settings
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name SetTransitionSettings
|
|
||||||
* @category transitions
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::SetTransitionSettings(const RpcRequest& request) {
|
|
||||||
if (!request.hasField("transitionName") || !request.hasField("transitionSettings")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* transitionName = obs_data_get_string(request.parameters(), "transitionName");
|
|
||||||
OBSSourceAutoRelease transition = Utils::GetTransitionFromName(transitionName);
|
|
||||||
if (!transition) {
|
|
||||||
return request.failed("specified transition doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataAutoRelease newSettings = obs_data_get_obj(request.parameters(), "transitionSettings");
|
|
||||||
obs_source_update(transition, newSettings);
|
|
||||||
obs_source_update_properties(transition);
|
|
||||||
|
|
||||||
OBSDataAutoRelease updatedSettings = obs_source_get_settings(transition);
|
|
||||||
|
|
||||||
OBSDataAutoRelease response = obs_data_create();
|
|
||||||
obs_data_set_obj(response, "transitionSettings", updatedSettings);
|
|
||||||
return request.success(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release the T-Bar (like a user releasing their mouse button after moving it).
|
|
||||||
* *YOU MUST CALL THIS if you called `SetTBarPosition` with the `release` parameter set to `false`.*
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name ReleaseTBar
|
|
||||||
* @category transitions
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::ReleaseTBar(const RpcRequest& request) {
|
|
||||||
if (!obs_frontend_preview_program_mode_active()) {
|
|
||||||
return request.failed("studio mode not enabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
|
||||||
if (obs_transition_fixed(transition)) {
|
|
||||||
return request.failed("current transition doesn't support t-bar control");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_release_tbar();
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the manual position of the T-Bar (in Studio Mode) to the specified value. Will return an error if OBS is not in studio mode
|
|
||||||
* or if the current transition doesn't support T-Bar control.
|
|
||||||
*
|
|
||||||
* If your code needs to perform multiple successive T-Bar moves (e.g. : in an animation, or in response to a user moving a T-Bar control in your User Interface), set `release` to false and call `ReleaseTBar` later once the animation/interaction is over.
|
|
||||||
*
|
|
||||||
* @param {double} `position` T-Bar position. This value must be between 0.0 and 1.0.
|
|
||||||
* @param {boolean (optional)} `release` Whether or not the T-Bar gets released automatically after setting its new position (like a user releasing their mouse button after moving the T-Bar). Call `ReleaseTBar` manually if you set `release` to false. Defaults to true.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name SetTBarPosition
|
|
||||||
* @category transitions
|
|
||||||
* @since 4.9.0
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::SetTBarPosition(const RpcRequest& request) {
|
|
||||||
if (!obs_frontend_preview_program_mode_active()) {
|
|
||||||
return request.failed("studio mode not enabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
|
|
||||||
if (obs_transition_fixed(transition)) {
|
|
||||||
return request.failed("current transition doesn't support t-bar control");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!request.hasField("position")) {
|
|
||||||
return request.failed("missing request parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
double position = obs_data_get_double(request.parameters(), "position");
|
|
||||||
if (position < 0.0 || position > 1.0) {
|
|
||||||
return request.failed("position is out of range");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool release = true;
|
|
||||||
if (request.hasField("release")) {
|
|
||||||
release = obs_data_get_bool(request.parameters(), "release");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_set_tbar_position((int)((float)position * 1024.0));
|
|
||||||
if (release) {
|
|
||||||
obs_frontend_release_tbar();
|
|
||||||
}
|
|
||||||
|
|
||||||
return request.success();
|
|
||||||
}
|
}
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
#include "obs-websocket.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "WSEvents.h"
|
|
||||||
|
|
||||||
#include "WSRequestHandler.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current virtual cam status.
|
|
||||||
*
|
|
||||||
* @return {boolean} `isVirtualCam` Current virtual camera status.
|
|
||||||
* @return {String (optional)} `virtualCamTimecode` Time elapsed since virtual cam started (only present if virtual cam currently active).
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name GetVirtualCamStatus
|
|
||||||
* @category virtual cam
|
|
||||||
* @since 4.9.1
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::GetVirtualCamStatus(const RpcRequest& request) {
|
|
||||||
auto events = GetEventsSystem();
|
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create();
|
|
||||||
obs_data_set_bool(data, "isVirtualCam", obs_frontend_virtualcam_active());
|
|
||||||
|
|
||||||
if (obs_frontend_virtualcam_active()) {
|
|
||||||
QString virtualCamTimecode = events->getVirtualCamTimecode();
|
|
||||||
obs_data_set_string(data, "virtualCamTimecode", virtualCamTimecode.toUtf8().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
return request.success(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle virtual cam on or off (depending on the current virtual cam state).
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name StartStopVirtualCam
|
|
||||||
* @category virtual cam
|
|
||||||
* @since 4.9.1
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::StartStopVirtualCam(const RpcRequest& request) {
|
|
||||||
(obs_frontend_virtualcam_active() ? obs_frontend_stop_virtualcam() : obs_frontend_start_virtualcam());
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start virtual cam.
|
|
||||||
* Will return an `error` if virtual cam is already active.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name StartVirtualCam
|
|
||||||
* @category virtual cam
|
|
||||||
* @since 4.9.1
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::StartVirtualCam(const RpcRequest& request) {
|
|
||||||
if (obs_frontend_virtualcam_active()) {
|
|
||||||
return request.failed("virtual cam already active");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_start_virtualcam();
|
|
||||||
return request.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop virtual cam.
|
|
||||||
* Will return an `error` if virtual cam is not active.
|
|
||||||
*
|
|
||||||
* @api requests
|
|
||||||
* @name StopVirtualCam
|
|
||||||
* @category virtual cam
|
|
||||||
* @since 4.9.1
|
|
||||||
*/
|
|
||||||
RpcResponse WSRequestHandler::StopVirtualCam(const RpcRequest& request) {
|
|
||||||
if (!obs_frontend_virtualcam_active()) {
|
|
||||||
return request.failed("virtual cam not active");
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_stop_virtualcam();
|
|
||||||
return request.success();
|
|
||||||
}
|
|
306
src/WSServer.cpp
306
src/WSServer.cpp
@ -16,267 +16,153 @@ 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 <chrono>
|
#include <QtWebSockets/QWebSocket>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
#include <QtWidgets/QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QtWidgets/QMessageBox>
|
#include <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"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "protocol/OBSRemoteProtocol.h"
|
|
||||||
|
|
||||||
QT_USE_NAMESPACE
|
QT_USE_NAMESPACE
|
||||||
|
|
||||||
using websocketpp::lib::placeholders::_1;
|
WSServer* WSServer::Instance = nullptr;
|
||||||
using websocketpp::lib::placeholders::_2;
|
|
||||||
using websocketpp::lib::bind;
|
|
||||||
|
|
||||||
WSServer::WSServer()
|
WSServer::WSServer(QObject* parent)
|
||||||
: QObject(nullptr),
|
: QObject(parent),
|
||||||
_connections(),
|
_wsServer(Q_NULLPTR),
|
||||||
|
_clients(),
|
||||||
_clMutex(QMutex::Recursive)
|
_clMutex(QMutex::Recursive)
|
||||||
{
|
{
|
||||||
_server.get_alog().clear_channels(websocketpp::log::alevel::frame_header | websocketpp::log::alevel::frame_payload | websocketpp::log::alevel::control);
|
_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::serverRunner()
|
void WSServer::Start(quint16 port) {
|
||||||
{
|
if (port == _wsServer->serverPort())
|
||||||
blog(LOG_INFO, "IO thread started.");
|
|
||||||
try {
|
|
||||||
_server.run();
|
|
||||||
} catch (websocketpp::exception const & e) {
|
|
||||||
blog(LOG_ERROR, "websocketpp instance returned an error: %s", e.what());
|
|
||||||
} catch (const std::exception & e) {
|
|
||||||
blog(LOG_ERROR, "websocketpp instance returned an error: %s", e.what());
|
|
||||||
} catch (...) {
|
|
||||||
blog(LOG_ERROR, "websocketpp instance returned an error");
|
|
||||||
}
|
|
||||||
blog(LOG_INFO, "IO thread exited.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void WSServer::start(quint16 port, bool lockToIPv4)
|
|
||||||
{
|
|
||||||
if (_server.is_listening() && (port == _serverPort && _lockToIPv4 == lockToIPv4)) {
|
|
||||||
blog(LOG_INFO, "WSServer::start: server already on this port and protocol mode. no restart needed");
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if(_wsServer->isListening())
|
||||||
|
Stop();
|
||||||
|
|
||||||
|
bool serverStarted = _wsServer->listen(QHostAddress::Any, port);
|
||||||
|
if (serverStarted) {
|
||||||
|
blog(LOG_INFO, "server started successfully on TCP port %d", port);
|
||||||
|
|
||||||
|
connect(_wsServer, SIGNAL(newConnection()),
|
||||||
|
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());
|
||||||
|
|
||||||
if (_server.is_listening()) {
|
QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
_server.reset();
|
|
||||||
|
|
||||||
_serverPort = port;
|
|
||||||
_lockToIPv4 = lockToIPv4;
|
|
||||||
|
|
||||||
websocketpp::lib::error_code errorCode;
|
|
||||||
if (lockToIPv4) {
|
|
||||||
blog(LOG_INFO, "WSServer::start: Locked to IPv4 bindings");
|
|
||||||
_server.listen(websocketpp::lib::asio::ip::tcp::v4(), _serverPort, errorCode);
|
|
||||||
} else {
|
|
||||||
blog(LOG_INFO, "WSServer::start: Not locked to IPv4 bindings");
|
|
||||||
_server.listen(_serverPort, errorCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorCode) {
|
|
||||||
std::string errorCodeMessage = errorCode.message();
|
|
||||||
blog(LOG_INFO, "server: listen failed: %s", errorCodeMessage.c_str());
|
|
||||||
|
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
QString errorTitle = tr("OBSWebsocket.Server.StartFailed.Title");
|
QString title = tr("OBSWebsocket.Server.StartFailed.Title");
|
||||||
QString errorMessage = tr("OBSWebsocket.Server.StartFailed.Message").arg(_serverPort).arg(errorCodeMessage.c_str());
|
QString msg = tr("OBSWebsocket.Server.StartFailed.Message").arg(port);
|
||||||
obs_frontend_pop_ui_translation();
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());
|
QMessageBox::warning(mainWindow, title, msg);
|
||||||
QMessageBox::warning(mainWindow, errorTitle, errorMessage);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_server.start_accept();
|
|
||||||
|
|
||||||
_serverThread = std::thread(&WSServer::serverRunner, this);
|
|
||||||
|
|
||||||
blog(LOG_INFO, "server started successfully on port %d", _serverPort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::stop()
|
void WSServer::Stop() {
|
||||||
{
|
QMutexLocker locker(&_clMutex);
|
||||||
if (!_server.is_listening()) {
|
for(QWebSocket* pClient : _clients) {
|
||||||
return;
|
pClient->close();
|
||||||
}
|
}
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
_server.stop_listening();
|
_wsServer->close();
|
||||||
for (connection_hdl hdl : _connections) {
|
|
||||||
websocketpp::lib::error_code errorCode;
|
|
||||||
_server.pause_reading(hdl, errorCode);
|
|
||||||
if (errorCode) {
|
|
||||||
blog(LOG_ERROR, "Error: %s", errorCode.message().c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_server.close(hdl, websocketpp::close::status::going_away, "Server stopping", errorCode);
|
|
||||||
if (errorCode) {
|
|
||||||
blog(LOG_ERROR, "Error: %s", errorCode.message().c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_threadPool.waitForDone();
|
|
||||||
|
|
||||||
while (_connections.size() > 0) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
_serverThread.join();
|
|
||||||
|
|
||||||
blog(LOG_INFO, "server stopped successfully");
|
blog(LOG_INFO, "server stopped successfully");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::broadcast(const RpcEvent& event)
|
void WSServer::broadcast(QString message) {
|
||||||
{
|
|
||||||
std::string message = OBSRemoteProtocol::encodeEvent(event);
|
|
||||||
|
|
||||||
auto config = GetConfig();
|
|
||||||
if (config && config->DebugEnabled) {
|
|
||||||
blog(LOG_INFO, "Update << '%s'", message.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
QMutexLocker locker(&_clMutex);
|
QMutexLocker locker(&_clMutex);
|
||||||
for (connection_hdl hdl : _connections) {
|
for(QWebSocket* pClient : _clients) {
|
||||||
if (config && config->AuthRequired) {
|
if (Config::Current()->AuthRequired
|
||||||
bool authenticated = _connectionProperties[hdl].isAuthenticated();
|
&& (pClient->property(PROP_AUTHENTICATED).toBool() == false)) {
|
||||||
if (!authenticated) {
|
// Skip this client if unauthenticated
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
pClient->sendTextMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WSServer::isListening()
|
void WSServer::onNewConnection() {
|
||||||
{
|
QWebSocket* pSocket = _wsServer->nextPendingConnection();
|
||||||
return _server.is_listening();
|
if (pSocket) {
|
||||||
}
|
connect(pSocket, SIGNAL(textMessageReceived(const QString&)),
|
||||||
|
this, SLOT(onTextMessageReceived(QString)));
|
||||||
|
connect(pSocket, SIGNAL(disconnected()),
|
||||||
|
this, SLOT(onSocketDisconnected()));
|
||||||
|
|
||||||
void WSServer::onOpen(connection_hdl hdl)
|
pSocket->setProperty(PROP_AUTHENTICATED, false);
|
||||||
{
|
|
||||||
QMutexLocker locker(&_clMutex);
|
|
||||||
_connections.insert(hdl);
|
|
||||||
locker.unlock();
|
|
||||||
|
|
||||||
QString clientIp = getRemoteEndpoint(hdl);
|
|
||||||
notifyConnection(clientIp);
|
|
||||||
blog(LOG_INFO, "new client connection from %s", clientIp.toUtf8().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
void WSServer::onMessage(connection_hdl hdl, server::message_ptr message)
|
|
||||||
{
|
|
||||||
auto opcode = message->get_opcode();
|
|
||||||
if (opcode != websocketpp::frame::opcode::text) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QtConcurrent::run(&_threadPool, [=]() {
|
|
||||||
std::string payload = message->get_payload();
|
|
||||||
|
|
||||||
QMutexLocker locker(&_clMutex);
|
QMutexLocker locker(&_clMutex);
|
||||||
ConnectionProperties& connProperties = _connectionProperties[hdl];
|
_clients << pSocket;
|
||||||
locker.unlock();
|
locker.unlock();
|
||||||
|
|
||||||
auto config = GetConfig();
|
QHostAddress clientAddr = pSocket->peerAddress();
|
||||||
if (config && config->DebugEnabled) {
|
QString clientIp = Utils::FormatIPAddress(clientAddr);
|
||||||
blog(LOG_INFO, "Request >> '%s'", payload.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
WSRequestHandler requestHandler(connProperties);
|
blog(LOG_INFO, "new client connection from %s:%d",
|
||||||
std::string response = OBSRemoteProtocol::processMessage(requestHandler, payload);
|
clientIp.toUtf8().constData(), pSocket->peerPort());
|
||||||
|
|
||||||
if (config && config->DebugEnabled) {
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
blog(LOG_INFO, "Response << '%s'", response.c_str());
|
QString title = tr("OBSWebsocket.NotifyConnect.Title");
|
||||||
}
|
QString msg = tr("OBSWebsocket.NotifyConnect.Message")
|
||||||
|
.arg(Utils::FormatIPAddress(clientAddr));
|
||||||
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
websocketpp::lib::error_code errorCode;
|
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
|
||||||
_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::onClose(connection_hdl hdl)
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&_clMutex);
|
|
||||||
_connections.erase(hdl);
|
|
||||||
_connectionProperties.erase(hdl);
|
|
||||||
locker.unlock();
|
|
||||||
|
|
||||||
auto conn = _server.get_con_from_hdl(hdl);
|
|
||||||
auto localCloseCode = conn->get_local_close_code();
|
|
||||||
auto localCloseReason = conn->get_local_close_reason();
|
|
||||||
QString clientIp = getRemoteEndpoint(hdl);
|
|
||||||
|
|
||||||
blog(LOG_INFO, "Websocket connection with client '%s' closed (disconnected). Code is %d, reason is: '%s'", clientIp.toUtf8().constData(), localCloseCode, localCloseReason.c_str());
|
|
||||||
if (localCloseCode != websocketpp::close::status::going_away && _server.is_listening()) {
|
|
||||||
notifyDisconnection(clientIp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WSServer::getRemoteEndpoint(connection_hdl hdl)
|
void WSServer::onTextMessageReceived(QString message) {
|
||||||
{
|
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
|
||||||
auto conn = _server.get_con_from_hdl(hdl);
|
if (pSocket) {
|
||||||
return QString::fromStdString(conn->get_remote_endpoint());
|
WSRequestHandler handler(pSocket);
|
||||||
|
handler.processIncomingMessage(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::notifyConnection(QString clientIp)
|
void WSServer::onSocketDisconnected() {
|
||||||
{
|
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
if (pSocket) {
|
||||||
QString title = tr("OBSWebsocket.NotifyConnect.Title");
|
pSocket->setProperty(PROP_AUTHENTICATED, false);
|
||||||
QString msg = tr("OBSWebsocket.NotifyConnect.Message").arg(clientIp);
|
|
||||||
obs_frontend_pop_ui_translation();
|
|
||||||
|
|
||||||
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
|
QMutexLocker locker(&_clMutex);
|
||||||
}
|
_clients.removeAll(pSocket);
|
||||||
|
locker.unlock();
|
||||||
void WSServer::notifyDisconnection(QString clientIp)
|
|
||||||
{
|
pSocket->deleteLater();
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
|
||||||
QString title = tr("OBSWebsocket.NotifyDisconnect.Title");
|
QHostAddress clientAddr = pSocket->peerAddress();
|
||||||
QString msg = tr("OBSWebsocket.NotifyDisconnect.Message").arg(clientIp);
|
QString clientIp = Utils::FormatIPAddress(clientAddr);
|
||||||
obs_frontend_pop_ui_translation();
|
|
||||||
|
blog(LOG_INFO, "client %s:%d disconnected",
|
||||||
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
|
clientIp.toUtf8().constData(), pSocket->peerPort());
|
||||||
|
|
||||||
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
|
QString title = tr("OBSWebsocket.NotifyDisconnect.Title");
|
||||||
|
QString msg = tr("OBSWebsocket.NotifyDisconnect.Message")
|
||||||
|
.arg(Utils::FormatIPAddress(clientAddr));
|
||||||
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
|
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 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,59 +16,37 @@ 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/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#ifndef WSSERVER_H
|
||||||
|
#define WSSERVER_H
|
||||||
|
|
||||||
#include <map>
|
#include <QObject>
|
||||||
#include <set>
|
#include <QList>
|
||||||
#include <QtCore/QObject>
|
#include <QMutex>
|
||||||
#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"
|
||||||
#include "rpc/RpcEvent.h"
|
|
||||||
|
|
||||||
using websocketpp::connection_hdl;
|
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
|
||||||
|
QT_FORWARD_DECLARE_CLASS(QWebSocket)
|
||||||
|
|
||||||
typedef websocketpp::server<websocketpp::config::asio> server;
|
class WSServer : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit WSServer(QObject* parent = Q_NULLPTR);
|
||||||
|
virtual ~WSServer();
|
||||||
|
void Start(quint16 port);
|
||||||
|
void Stop();
|
||||||
|
void broadcast(QString message);
|
||||||
|
static WSServer* Instance;
|
||||||
|
|
||||||
class WSServer : public QObject
|
private slots:
|
||||||
{
|
void onNewConnection();
|
||||||
Q_OBJECT
|
void onTextMessageReceived(QString message);
|
||||||
|
void onSocketDisconnected();
|
||||||
|
|
||||||
public:
|
private:
|
||||||
explicit WSServer();
|
QWebSocketServer* _wsServer;
|
||||||
virtual ~WSServer();
|
QList<QWebSocket*> _clients;
|
||||||
void start(quint16 port, bool lockToIPv4);
|
QMutex _clMutex;
|
||||||
void stop();
|
|
||||||
void broadcast(const RpcEvent& event);
|
|
||||||
bool isListening();
|
|
||||||
QThreadPool* threadPool() {
|
|
||||||
return &_threadPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void serverRunner();
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
std::thread _serverThread;
|
|
||||||
server _server;
|
|
||||||
quint16 _serverPort;
|
|
||||||
bool _lockToIPv4;
|
|
||||||
std::set<connection_hdl, std::owner_less<connection_hdl>> _connections;
|
|
||||||
std::map<connection_hdl, ConnectionProperties, std::owner_less<connection_hdl>> _connectionProperties;
|
|
||||||
QMutex _clMutex;
|
|
||||||
QThreadPool _threadPool;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif // WSSERVER_H
|
||||||
|
@ -16,16 +16,12 @@ 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 "settings-dialog.h"
|
|
||||||
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <obs-module.h>
|
|
||||||
#include <QtWidgets/QMessageBox>
|
|
||||||
|
|
||||||
#include "../obs-websocket.h"
|
#include "../obs-websocket.h"
|
||||||
#include "../Config.h"
|
#include "../Config.h"
|
||||||
#include "../WSServer.h"
|
#include "../WSServer.h"
|
||||||
|
#include "settings-dialog.h"
|
||||||
|
|
||||||
#define CHANGE_ME "changeme"
|
#define CHANGE_ME "changeme"
|
||||||
|
|
||||||
@ -39,25 +35,22 @@ SettingsDialog::SettingsDialog(QWidget* parent) :
|
|||||||
this, &SettingsDialog::AuthCheckboxChanged);
|
this, &SettingsDialog::AuthCheckboxChanged);
|
||||||
connect(ui->buttonBox, &QDialogButtonBox::accepted,
|
connect(ui->buttonBox, &QDialogButtonBox::accepted,
|
||||||
this, &SettingsDialog::FormAccepted);
|
this, &SettingsDialog::FormAccepted);
|
||||||
|
|
||||||
|
|
||||||
|
AuthCheckboxChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::showEvent(QShowEvent* event) {
|
void SettingsDialog::showEvent(QShowEvent* event) {
|
||||||
auto conf = GetConfig();
|
Config* conf = Config::Current();
|
||||||
if (conf) {
|
|
||||||
ui->serverEnabled->setChecked(conf->ServerEnabled);
|
|
||||||
ui->serverPort->setValue(conf->ServerPort);
|
|
||||||
ui->lockToIPv4->setChecked(conf->LockToIPv4);
|
|
||||||
|
|
||||||
ui->debugEnabled->setChecked(conf->DebugEnabled);
|
ui->serverEnabled->setChecked(conf->ServerEnabled);
|
||||||
ui->alertsEnabled->setChecked(conf->AlertsEnabled);
|
ui->serverPort->setValue(conf->ServerPort);
|
||||||
|
|
||||||
ui->authRequired->blockSignals(true);
|
ui->debugEnabled->setChecked(conf->DebugEnabled);
|
||||||
ui->authRequired->setChecked(conf->AuthRequired);
|
ui->alertsEnabled->setChecked(conf->AlertsEnabled);
|
||||||
ui->authRequired->blockSignals(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ui->authRequired->setChecked(conf->AuthRequired);
|
||||||
ui->password->setText(CHANGE_ME);
|
ui->password->setText(CHANGE_ME);
|
||||||
ui->password->setEnabled(ui->authRequired->isChecked());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::ToggleShowHide() {
|
void SettingsDialog::ToggleShowHide() {
|
||||||
@ -67,41 +60,18 @@ void SettingsDialog::ToggleShowHide() {
|
|||||||
setVisible(false);
|
setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::PreparePasswordEntry() {
|
|
||||||
ui->authRequired->blockSignals(true);
|
|
||||||
ui->authRequired->setChecked(true);
|
|
||||||
ui->authRequired->blockSignals(false);
|
|
||||||
ui->password->setEnabled(true);
|
|
||||||
ui->password->setFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
|
||||||
QString authDisabledWarning = QObject::tr("OBSWebsocket.Settings.AuthDisabledWarning");
|
|
||||||
obs_frontend_pop_ui_translation();
|
|
||||||
|
|
||||||
QMessageBox::StandardButton response = QMessageBox::question(this, "obs-websocket", authDisabledWarning);
|
|
||||||
if (response == QMessageBox::Yes) {
|
|
||||||
ui->password->setEnabled(false);
|
|
||||||
} else {
|
|
||||||
ui->authRequired->setChecked(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::FormAccepted() {
|
void SettingsDialog::FormAccepted() {
|
||||||
auto conf = GetConfig();
|
Config* conf = Config::Current();
|
||||||
if (!conf) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
conf->ServerEnabled = ui->serverEnabled->isChecked();
|
conf->ServerEnabled = ui->serverEnabled->isChecked();
|
||||||
conf->ServerPort = ui->serverPort->value();
|
conf->ServerPort = ui->serverPort->value();
|
||||||
conf->LockToIPv4 = ui->lockToIPv4->isChecked();
|
|
||||||
|
|
||||||
conf->DebugEnabled = ui->debugEnabled->isChecked();
|
conf->DebugEnabled = ui->debugEnabled->isChecked();
|
||||||
conf->AlertsEnabled = ui->alertsEnabled->isChecked();
|
conf->AlertsEnabled = ui->alertsEnabled->isChecked();
|
||||||
@ -111,7 +81,7 @@ void SettingsDialog::FormAccepted() {
|
|||||||
conf->SetPassword(ui->password->text());
|
conf->SetPassword(ui->password->text());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!conf->Secret.isEmpty())
|
if (!Config::Current()->Secret.isEmpty())
|
||||||
conf->AuthRequired = true;
|
conf->AuthRequired = true;
|
||||||
else
|
else
|
||||||
conf->AuthRequired = false;
|
conf->AuthRequired = false;
|
||||||
@ -123,12 +93,10 @@ void SettingsDialog::FormAccepted() {
|
|||||||
|
|
||||||
conf->Save();
|
conf->Save();
|
||||||
|
|
||||||
auto server = GetServer();
|
if (conf->ServerEnabled)
|
||||||
if (conf->ServerEnabled) {
|
WSServer::Instance->Start(conf->ServerPort);
|
||||||
server->start(conf->ServerPort, conf->LockToIPv4);
|
else
|
||||||
} else {
|
WSServer::Instance->Stop();
|
||||||
server->stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::~SettingsDialog() {
|
SettingsDialog::~SettingsDialog() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 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,9 +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/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#ifndef SETTINGSDIALOG_H
|
||||||
|
#define SETTINGSDIALOG_H
|
||||||
|
|
||||||
#include <QtWidgets/QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
#include "ui_settings-dialog.h"
|
#include "ui_settings-dialog.h"
|
||||||
|
|
||||||
@ -31,7 +32,6 @@ public:
|
|||||||
~SettingsDialog();
|
~SettingsDialog();
|
||||||
void showEvent(QShowEvent* event);
|
void showEvent(QShowEvent* event);
|
||||||
void ToggleShowHide();
|
void ToggleShowHide();
|
||||||
void PreparePasswordEntry();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void AuthCheckboxChanged();
|
void AuthCheckboxChanged();
|
||||||
@ -40,3 +40,5 @@ private Q_SLOTS:
|
|||||||
private:
|
private:
|
||||||
Ui::SettingsDialog* ui;
|
Ui::SettingsDialog* ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif // SETTINGSDIALOG_H
|
||||||
|
@ -2,157 +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>216</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="6" 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="7" 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>
|
||||||
<item row="5" column="1">
|
</layout>
|
||||||
<widget class="QCheckBox" name="lockToIPv4">
|
</item>
|
||||||
<property name="text">
|
<item>
|
||||||
<string>OBSWebsocket.Settings.LockToIPv4</string>
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
</property>
|
<property name="orientation">
|
||||||
</widget>
|
<enum>Qt::Horizontal</enum>
|
||||||
</item>
|
</property>
|
||||||
</layout>
|
<property name="standardButtons">
|
||||||
</item>
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
<item>
|
</property>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
</widget>
|
||||||
<property name="orientation">
|
</item>
|
||||||
<enum>Qt::Horizontal</enum>
|
</layout>
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</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,11 +18,9 @@ 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 <obs-data.h>
|
#include <QAction>
|
||||||
|
#include <QMainWindow>
|
||||||
#include <QtCore/QTimer>
|
#include <QTimer>
|
||||||
#include <QtWidgets/QAction>
|
|
||||||
#include <QtWidgets/QMainWindow>
|
|
||||||
|
|
||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
#include "WSServer.h"
|
#include "WSServer.h"
|
||||||
@ -36,18 +34,10 @@ 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")
|
||||||
|
|
||||||
ConfigPtr _config;
|
SettingsDialog* settings_dialog;
|
||||||
WSServerPtr _server;
|
|
||||||
WSEventsPtr _eventsSystem;
|
|
||||||
SettingsDialog* settingsDialog = nullptr;
|
|
||||||
|
|
||||||
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);
|
||||||
@ -55,39 +45,28 @@ bool obs_module_load(void) {
|
|||||||
QT_VERSION_STR, qVersion());
|
QT_VERSION_STR, qVersion());
|
||||||
|
|
||||||
// Core setup
|
// Core setup
|
||||||
_config = ConfigPtr(new Config());
|
Config* config = Config::Current();
|
||||||
_config->MigrateFromGlobalSettings(); // TODO remove this on the next minor jump
|
config->Load();
|
||||||
_config->Load();
|
|
||||||
|
|
||||||
_server = WSServerPtr(new WSServer());
|
WSServer::Instance = new WSServer();
|
||||||
_eventsSystem = WSEventsPtr(new WSEvents(_server));
|
WSEvents::Instance = new WSEvents(WSServer::Instance);
|
||||||
|
|
||||||
|
if (config->ServerEnabled)
|
||||||
|
WSServer::Instance->Start(config->ServerPort);
|
||||||
|
|
||||||
// UI setup
|
// UI setup
|
||||||
|
QAction* menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(
|
||||||
|
obs_module_text("OBSWebsocket.Menu.SettingsItem"));
|
||||||
|
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window();
|
QMainWindow* main_window = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
settingsDialog = new SettingsDialog(mainWindow);
|
settings_dialog = new SettingsDialog(main_window);
|
||||||
obs_frontend_pop_ui_translation();
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
const char* menuActionText =
|
auto menu_cb = [] {
|
||||||
obs_module_text("OBSWebsocket.Settings.DialogTitle");
|
settings_dialog->ToggleShowHide();
|
||||||
QAction* menuAction =
|
|
||||||
(QAction*)obs_frontend_add_tools_menu_qaction(menuActionText);
|
|
||||||
QObject::connect(menuAction, &QAction::triggered, [] {
|
|
||||||
// The settings dialog belongs to the main window. Should be ok
|
|
||||||
// to pass the pointer to this QAction belonging to the main window
|
|
||||||
settingsDialog->ToggleShowHide();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Setup event handler to start the server once OBS is ready
|
|
||||||
auto eventCallback = [](enum obs_frontend_event event, void *param) {
|
|
||||||
if (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {
|
|
||||||
if (_config->ServerEnabled) {
|
|
||||||
_server->start(_config->ServerPort, _config->LockToIPv4);
|
|
||||||
}
|
|
||||||
obs_frontend_remove_event_callback((obs_frontend_event_cb)param, nullptr);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
obs_frontend_add_event_callback(eventCallback, (void*)(obs_frontend_event_cb)eventCallback);
|
menu_action->connect(menu_action, &QAction::triggered, menu_cb);
|
||||||
|
|
||||||
// Loading finished
|
// Loading finished
|
||||||
blog(LOG_INFO, "module loaded!");
|
blog(LOG_INFO, "module loaded!");
|
||||||
@ -96,30 +75,6 @@ bool obs_module_load(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void obs_module_unload() {
|
void obs_module_unload() {
|
||||||
_server->stop();
|
|
||||||
|
|
||||||
_eventsSystem.reset();
|
|
||||||
_server.reset();
|
|
||||||
_config.reset();
|
|
||||||
|
|
||||||
blog(LOG_INFO, "goodbye!");
|
blog(LOG_INFO, "goodbye!");
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigPtr GetConfig() {
|
|
||||||
return _config;
|
|
||||||
}
|
|
||||||
|
|
||||||
WSServerPtr GetServer() {
|
|
||||||
return _server;
|
|
||||||
}
|
|
||||||
|
|
||||||
WSEventsPtr GetEventsSystem() {
|
|
||||||
return _eventsSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShowPasswordSetting() {
|
|
||||||
if (settingsDialog) {
|
|
||||||
settingsDialog->PreparePasswordEntry();
|
|
||||||
settingsDialog->setVisible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016-2019 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 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/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#ifndef OBSWEBSOCKET_H
|
||||||
|
#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,25 +38,9 @@ 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>;
|
||||||
|
|
||||||
void ___data_item_dummy_addref(obs_data_item_t*);
|
#define PROP_AUTHENTICATED "wsclient_authenticated"
|
||||||
void ___data_item_release(obs_data_item_t*);
|
#define OBS_WEBSOCKET_VERSION "4.5.1"
|
||||||
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();
|
|
||||||
void ShowPasswordSetting();
|
|
||||||
|
|
||||||
#define OBS_WEBSOCKET_VERSION "4.9.1"
|
|
||||||
|
|
||||||
#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
|
||||||
|
@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
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 <inttypes.h>
|
|
||||||
|
|
||||||
#include "OBSRemoteProtocol.h"
|
|
||||||
#include "../WSRequestHandler.h"
|
|
||||||
#include "../rpc/RpcEvent.h"
|
|
||||||
#include "../Utils.h"
|
|
||||||
|
|
||||||
std::string OBSRemoteProtocol::processMessage(WSRequestHandler& requestHandler, std::string message)
|
|
||||||
{
|
|
||||||
std::string msgContainer(message);
|
|
||||||
const char* msg = msgContainer.c_str();
|
|
||||||
|
|
||||||
OBSDataAutoRelease data = obs_data_create_from_json(msg);
|
|
||||||
if (!data) {
|
|
||||||
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
|
|
||||||
return jsonDataToString(
|
|
||||||
errorResponse(nullptr, "invalid JSON payload")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!obs_data_has_user_value(data, "request-type") || !obs_data_has_user_value(data, "message-id")) {
|
|
||||||
return jsonDataToString(
|
|
||||||
errorResponse(nullptr, "missing request parameters")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString methodName = obs_data_get_string(data, "request-type");
|
|
||||||
QString messageId = obs_data_get_string(data, "message-id");
|
|
||||||
|
|
||||||
OBSDataAutoRelease params = obs_data_create();
|
|
||||||
obs_data_apply(params, data);
|
|
||||||
obs_data_unset_user_value(params, "request-type");
|
|
||||||
obs_data_unset_user_value(params, "message-id");
|
|
||||||
|
|
||||||
RpcRequest request(messageId, methodName, params);
|
|
||||||
RpcResponse response = requestHandler.processRequest(request);
|
|
||||||
|
|
||||||
OBSDataAutoRelease responseData = rpcResponseToJsonData(response);
|
|
||||||
return jsonDataToString(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event)
|
|
||||||
{
|
|
||||||
OBSDataAutoRelease eventData = obs_data_create();
|
|
||||||
|
|
||||||
QString updateType = event.updateType();
|
|
||||||
obs_data_set_string(eventData, "update-type", updateType.toUtf8().constData());
|
|
||||||
|
|
||||||
std::optional<uint64_t> streamTime = event.streamTime();
|
|
||||||
if (streamTime.has_value()) {
|
|
||||||
QString streamingTimecode = Utils::nsToTimestamp(streamTime.value());
|
|
||||||
obs_data_set_string(eventData, "stream-timecode", streamingTimecode.toUtf8().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<uint64_t> recordingTime = event.recordingTime();
|
|
||||||
if (recordingTime.has_value()) {
|
|
||||||
QString recordingTimecode = Utils::nsToTimestamp(recordingTime.value());
|
|
||||||
obs_data_set_string(eventData, "rec-timecode", recordingTimecode.toUtf8().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSData additionalFields = event.additionalFields();
|
|
||||||
if (additionalFields) {
|
|
||||||
obs_data_apply(eventData, additionalFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::string(obs_data_get_json(eventData));
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t* OBSRemoteProtocol::rpcResponseToJsonData(const RpcResponse& response)
|
|
||||||
{
|
|
||||||
QByteArray messageIdBytes = response.messageId().toUtf8();
|
|
||||||
const char* messageId = messageIdBytes.constData();
|
|
||||||
|
|
||||||
OBSData additionalFields = response.additionalFields();
|
|
||||||
switch (response.status()) {
|
|
||||||
case RpcResponse::Status::Ok:
|
|
||||||
return successResponse(messageId, additionalFields);
|
|
||||||
case RpcResponse::Status::Error:
|
|
||||||
return errorResponse(messageId, response.errorMessage().toUtf8().constData(), additionalFields);
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t* OBSRemoteProtocol::successResponse(const char* messageId, obs_data_t* fields)
|
|
||||||
{
|
|
||||||
return buildResponse(messageId, "ok", fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t* OBSRemoteProtocol::errorResponse(const char* messageId, const char* errorMessage, obs_data_t* additionalFields)
|
|
||||||
{
|
|
||||||
OBSDataAutoRelease fields = obs_data_create();
|
|
||||||
if (additionalFields) {
|
|
||||||
obs_data_apply(fields, additionalFields);
|
|
||||||
}
|
|
||||||
obs_data_set_string(fields, "error", errorMessage);
|
|
||||||
return buildResponse(messageId, "error", fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_t* OBSRemoteProtocol::buildResponse(const char* messageId, const char* status, obs_data_t* fields)
|
|
||||||
{
|
|
||||||
obs_data_t* response = obs_data_create();
|
|
||||||
if (messageId) {
|
|
||||||
obs_data_set_string(response, "message-id", messageId);
|
|
||||||
}
|
|
||||||
obs_data_set_string(response, "status", status);
|
|
||||||
|
|
||||||
if (fields) {
|
|
||||||
obs_data_apply(response, fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string OBSRemoteProtocol::jsonDataToString(OBSDataAutoRelease data)
|
|
||||||
{
|
|
||||||
std::string responseString = obs_data_get_json(data);
|
|
||||||
return responseString;
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
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 <string>
|
|
||||||
#include <obs-data.h>
|
|
||||||
|
|
||||||
#include "../rpc/RpcResponse.h"
|
|
||||||
|
|
||||||
class WSRequestHandler;
|
|
||||||
class RpcEvent;
|
|
||||||
|
|
||||||
class OBSRemoteProtocol
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static std::string processMessage(WSRequestHandler& requestHandler, std::string message);
|
|
||||||
static std::string encodeEvent(const RpcEvent& event);
|
|
||||||
static obs_data_t* rpcResponseToJsonData(const RpcResponse& response);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static obs_data_t* successResponse(const char* messageId, obs_data_t* fields = nullptr);
|
|
||||||
static obs_data_t* errorResponse(const char* messageId, const char* errorMessage, obs_data_t* additionalFields = nullptr);
|
|
||||||
static obs_data_t* buildResponse(const char* messageId, const char*, obs_data_t* fields = nullptr);
|
|
||||||
static std::string jsonDataToString(OBSDataAutoRelease data);
|
|
||||||
};
|
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
obs-websocket
|
|
||||||
Copyright (C) 2016-2020 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 "RpcEvent.h"
|
|
||||||
|
|
||||||
RpcEvent::RpcEvent(
|
|
||||||
const QString& updateType,
|
|
||||||
std::optional<uint64_t> streamTime, std::optional<uint64_t> recordingTime,
|
|
||||||
obs_data_t* additionalFields
|
|
||||||
) :
|
|
||||||
_updateType(updateType),
|
|
||||||
_streamTime(streamTime),
|
|
||||||
_recordingTime(recordingTime),
|
|
||||||
_additionalFields(nullptr)
|
|
||||||
{
|
|
||||||
if (additionalFields) {
|
|
||||||
_additionalFields = obs_data_create();
|
|
||||||
obs_data_apply(_additionalFields, additionalFields);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
obs-websocket
|
|
||||||
Copyright (C) 2016-2020 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 <optional>
|
|
||||||
#include <obs-data.h>
|
|
||||||
#include <QtCore/QString>
|
|
||||||
|
|
||||||
#include "../obs-websocket.h"
|
|
||||||
|
|
||||||
class RpcEvent
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit RpcEvent(
|
|
||||||
const QString& updateType,
|
|
||||||
std::optional<uint64_t> streamTime, std::optional<uint64_t> recordingTime,
|
|
||||||
obs_data_t* additionalFields = nullptr
|
|
||||||
);
|
|
||||||
|
|
||||||
const QString& updateType() const
|
|
||||||
{
|
|
||||||
return _updateType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::optional<uint64_t> streamTime() const
|
|
||||||
{
|
|
||||||
return _streamTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::optional<uint64_t> recordingTime() const
|
|
||||||
{
|
|
||||||
return _recordingTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
const OBSData additionalFields() const
|
|
||||||
{
|
|
||||||
return OBSData(_additionalFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString _updateType;
|
|
||||||
std::optional<uint64_t> _streamTime;
|
|
||||||
std::optional<uint64_t> _recordingTime;
|
|
||||||
OBSDataAutoRelease _additionalFields;
|
|
||||||
};
|
|
@ -1,104 +0,0 @@
|
|||||||
/*
|
|
||||||
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 "RpcRequest.h"
|
|
||||||
#include "RpcResponse.h"
|
|
||||||
|
|
||||||
RpcRequest::RpcRequest(const QString& messageId, const QString& methodName, obs_data_t* params) :
|
|
||||||
_messageId(messageId),
|
|
||||||
_methodName(methodName),
|
|
||||||
_parameters(nullptr)
|
|
||||||
{
|
|
||||||
if (params) {
|
|
||||||
_parameters = obs_data_create();
|
|
||||||
obs_data_apply(_parameters, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const RpcResponse RpcRequest::success(obs_data_t* additionalFields) const
|
|
||||||
{
|
|
||||||
return RpcResponse::ok(*this, additionalFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
const RpcResponse RpcRequest::failed(const QString& errorMessage, obs_data_t* additionalFields) const
|
|
||||||
{
|
|
||||||
return RpcResponse::fail(*this, errorMessage, additionalFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool RpcRequest::hasField(QString name, obs_data_type expectedFieldType, obs_data_number_type expectedNumberType) const
|
|
||||||
{
|
|
||||||
if (!_parameters || name.isEmpty() || name.isNull()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
OBSDataItemAutoRelease dataItem = obs_data_item_byname(_parameters, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool RpcRequest::hasBool(QString fieldName) const
|
|
||||||
{
|
|
||||||
return this->hasField(fieldName, OBS_DATA_BOOLEAN);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool RpcRequest::hasString(QString fieldName) const
|
|
||||||
{
|
|
||||||
return this->hasField(fieldName, OBS_DATA_STRING);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool RpcRequest::hasNumber(QString fieldName, obs_data_number_type expectedNumberType) const
|
|
||||||
{
|
|
||||||
return this->hasField(fieldName, OBS_DATA_NUMBER, expectedNumberType);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool RpcRequest::hasInteger(QString fieldName) const
|
|
||||||
{
|
|
||||||
return this->hasNumber(fieldName, OBS_DATA_NUM_INT);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool RpcRequest::hasDouble(QString fieldName) const
|
|
||||||
{
|
|
||||||
return this->hasNumber(fieldName, OBS_DATA_NUM_DOUBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool RpcRequest::hasArray(QString fieldName) const
|
|
||||||
{
|
|
||||||
return this->hasField(fieldName, OBS_DATA_ARRAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool RpcRequest::hasObject(QString fieldName) const
|
|
||||||
{
|
|
||||||
return this->hasField(fieldName, OBS_DATA_OBJECT);
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
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 <obs-data.h>
|
|
||||||
#include <QtCore/QString>
|
|
||||||
#include "../obs-websocket.h"
|
|
||||||
|
|
||||||
// forward declarations
|
|
||||||
class RpcResponse;
|
|
||||||
|
|
||||||
class RpcRequest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit RpcRequest(const QString& messageId, const QString& methodName, obs_data_t* params);
|
|
||||||
|
|
||||||
const QString& messageId() const
|
|
||||||
{
|
|
||||||
return _messageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString& methodName() const
|
|
||||||
{
|
|
||||||
return _methodName;
|
|
||||||
}
|
|
||||||
|
|
||||||
const OBSData parameters() const
|
|
||||||
{
|
|
||||||
return OBSData(_parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
const RpcResponse success(obs_data_t* additionalFields = nullptr) const;
|
|
||||||
const RpcResponse failed(const QString& errorMessage, obs_data_t* additionalFields = nullptr) const;
|
|
||||||
|
|
||||||
const bool hasField(QString fieldName, obs_data_type expectedFieldType = OBS_DATA_NULL,
|
|
||||||
obs_data_number_type expectedNumberType = OBS_DATA_NUM_INVALID) const;
|
|
||||||
const bool hasBool(QString fieldName) const;
|
|
||||||
const bool hasString(QString fieldName) const;
|
|
||||||
const bool hasNumber(QString fieldName, obs_data_number_type expectedNumberType = OBS_DATA_NUM_INVALID) const;
|
|
||||||
const bool hasInteger(QString fieldName) const;
|
|
||||||
const bool hasDouble(QString fieldName) const;
|
|
||||||
const bool hasArray(QString fieldName) const;
|
|
||||||
const bool hasObject(QString fieldName) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const QString _messageId;
|
|
||||||
const QString _methodName;
|
|
||||||
OBSDataAutoRelease _parameters;
|
|
||||||
};
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user