Merge branch '4.x-current' into feature/t-bar

This commit is contained in:
tt2468 2020-05-13 20:51:23 -07:00
commit 64062cc879
23 changed files with 457 additions and 263 deletions

@ -34,7 +34,6 @@ git checkout $OBSLatestTag
mkdir build && cd build mkdir build && cd build
echo "[obs-websocket] Building obs-studio.." echo "[obs-websocket] Building obs-studio.."
cmake .. \ cmake .. \
-DBUILD_CAPTIONS=true \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 \
-DDISABLE_PLUGINS=true \ -DDISABLE_PLUGINS=true \
-DENABLE_SCRIPTING=0 \ -DENABLE_SCRIPTING=0 \

@ -514,11 +514,11 @@
<key>CONCLUSION_ACTION</key> <key>CONCLUSION_ACTION</key>
<integer>0</integer> <integer>0</integer>
<key>IDENTIFIER</key> <key>IDENTIFIER</key>
<string>fr.palakis.obswebsocket</string> <string>fr.palakis.obs-websocket</string>
<key>OVERWRITE_PERMISSIONS</key> <key>OVERWRITE_PERMISSIONS</key>
<false/> <false/>
<key>VERSION</key> <key>VERSION</key>
<string>4.7.0</string> <string>4.8.0</string>
</dict> </dict>
<key>PROJECT_COMMENTS</key> <key>PROJECT_COMMENTS</key>
<dict> <dict>

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -e set -e
@ -12,30 +12,79 @@ fi
echo "[obs-websocket] Preparing package build" 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 QT_CELLAR_PREFIX="$(/usr/bin/find /usr/local/Cellar/qt -d 1 | sort -t '.' -k 1,1n -k 2,2n -k 3,3n | tail -n 1)"
export GIT_HASH=$(git rev-parse --short HEAD) GIT_HASH=$(git rev-parse --short HEAD)
export GIT_BRANCH_OR_TAG=$(git name-rev --name-only HEAD | awk -F/ '{print $NF}') GIT_BRANCH_OR_TAG=$(git name-rev --name-only HEAD | awk -F/ '{print $NF}')
export VERSION="$GIT_HASH-$GIT_BRANCH_OR_TAG" VERSION="$GIT_HASH-$GIT_BRANCH_OR_TAG"
export LATEST_VERSION="$GIT_BRANCH_OR_TAG"
export FILENAME="obs-websocket-$VERSION.pkg" FILENAME_UNSIGNED="obs-websocket-$VERSION-Unsigned.pkg"
FILENAME="obs-websocket-$VERSION.pkg"
echo "[obs-websocket] Modifying obs-websocket.so" echo "[obs-websocket] Modifying obs-websocket.so"
install_name_tool \ install_name_tool \
-add_rpath @executable_path/../Frameworks/QtWidgets.framework/Versions/5/ \ -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets \
-add_rpath @executable_path/../Frameworks/QtGui.framework/Versions/5/ \ @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets \
-add_rpath @executable_path/../Frameworks/QtCore.framework/Versions/5/ \ -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui \
-change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @rpath/QtWidgets \ @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui \
-change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @rpath/QtGui \ -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore \
-change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \ @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore \
./build/obs-websocket.so ./build/obs-websocket.so
# Check if replacement worked # Check if replacement worked
echo "[obs-websocket] Dependencies for obs-websocket" echo "[obs-websocket] Dependencies for obs-websocket"
otool -L ./build/obs-websocket.so 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" echo "[obs-websocket] Actual package build"
packagesbuild ./CI/macos/obs-websocket.pkgproj packagesbuild ./CI/macos/obs-websocket.pkgproj
echo "[obs-websocket] Renaming obs-websocket.pkg to $FILENAME" echo "[obs-websocket] Renaming obs-websocket.pkg to $FILENAME"
mv ./release/obs-websocket.pkg ./release/$FILENAME mv ./release/obs-websocket.pkg ./release/$FILENAME_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

@ -17,7 +17,7 @@ PAGER="cat" sudo checkinstall -y --type=debian --fstrans=no --nodoc \
--pkglicense="GPLv2.0" --maintainer="stephane.lepin@gmail.com" \ --pkglicense="GPLv2.0" --maintainer="stephane.lepin@gmail.com" \
--pkggroup="video" \ --pkggroup="video" \
--pkgsource="https://github.com/Palakis/obs-websocket" \ --pkgsource="https://github.com/Palakis/obs-websocket" \
--requires="obs-studio,libqt5core5a,libqt5widgets5,qt5-image-formats-plugins" \ --requires="obs-studio \(\>= 25.0.7\), libqt5core5a, libqt5widgets5, qt5-image-formats-plugins" \
--pakdir="../package" --pakdir="../package"
sudo chmod ao+r ../package/* sudo chmod ao+r ../package/*

@ -9,4 +9,4 @@ REM Package ZIP archive
7z a "obs-websocket-%PackageVersion%-Windows.zip" "..\release\*" 7z a "obs-websocket-%PackageVersion%-Windows.zip" "..\release\*"
REM Build installer REM Build installer
iscc ..\installer\installer.iss /O. /F"obs-websocket-%PackageVersion%-Windows" iscc ..\installer\installer.iss /O. /F"obs-websocket-%PackageVersion%-Windows-Installer"

@ -1,12 +1,12 @@
cmake_minimum_required(VERSION 3.2) cmake_minimum_required(VERSION 3.5)
project(obs-websocket) project(obs-websocket VERSION 4.8.0)
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 11) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_definitions(-DASIO_STANDALONE) add_definitions(-DASIO_STANDALONE)
@ -85,7 +85,8 @@ if(WIN32)
endif() endif()
if(MSVC) if(MSVC)
add_compile_options("/MP") # Enable Multicore Builds and disable FH4 (to not depend on VCRUNTIME140_1.DLL)
add_definitions(/MP /d2FH4-)
endif() endif()
add_definitions(-D_WEBSOCKETPP_CPP11_STL_) add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
@ -179,20 +180,18 @@ 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_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)
install(TARGETS obs-websocket install(TARGETS obs-websocket
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/obs-plugins") LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins")
# Dirty fix for Ubuntu
install(TARGETS obs-websocket
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/${UNAME_MACHINE}-linux-gnu/obs-plugins")
install(FILES ${locale_files} install(FILES ${locale_files}
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/obs-websocket/locale") DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/obs/obs-plugins/obs-websocket/locale")
endif() endif()
# --- End of section --- # --- End of section ---

@ -5,7 +5,7 @@ WebSockets API for OBS Studio.
Follow the main author on Twitter for news & updates : [@LePalakis](https://twitter.com/LePalakis) Follow the main author on Twitter for news & updates : [@LePalakis](https://twitter.com/LePalakis)
[![Build Status - Windows](https://ci.appveyor.com/api/projects/status/github/Palakis/obs-websocket)](https://ci.appveyor.com/project/Palakis/obs-websocket/history) [![Build Status - Linux](https://travis-ci.org/Palakis/obs-websocket.svg?branch=master)](https://travis-ci.org/Palakis/obs-websocket) [![Build Status](https://dev.azure.com/Palakis/obs-websocket/_apis/build/status/Palakis.obs-websocket?branchName=4.x-current)](https://dev.azure.com/Palakis/obs-websocket/_build/latest?definitionId=2&branchName=4.x-current)
## Downloads ## Downloads
@ -20,21 +20,23 @@ It is **highly recommended** to protect obs-websocket with a password against un
### Possible use cases ### Possible use cases
- Remote control OBS from a phone or tablet on the same local network - Remote control OBS from a phone or tablet on the same local network
- Change your stream overlay/graphics based on the current scene (like the AGDQ overlay does) - Change your stream overlay/graphics based on the current scene
- 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 8+: [obs-websocket-java](https://github.com/Twasi/websocket-obs-java) by TwasiNET
- Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf - Golang: [go-obs-websocket](https://github.com/christopher-dG/go-obs-websocket) by Chris de Graaf
- HTTP API: [obs-websocket-http](https://github.com/IRLToolkit/obs-websocket-http) by tt2468
I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop me an email at `stephane /dot/ lepin /at/ gmail /dot/ com` ! I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop me an email at `stephane /dot/ lepin /at/ gmail /dot/ com` !
@ -46,19 +48,14 @@ See the [build instructions](BUILDING.md).
### Branches ### Branches
The two main development branches are: Development happens on `4.x-current`
- `4.x-current`: actively-maintained codebase for 4.x releases. Backwards-compatible (unless stated otherwise) with existing clients until 5.0.
- `5.x`: upcoming 5.0 version
**New features and fixes must be based off and contributed to `4.x-current`**, as obs-websocket 5.0 is not in active development yet.
### Pull Requests ### Pull Requests
Pull Requests must never be based off your fork's main branch (in our case, `4.x-current` or `5.x`). Start your work in a new branch Pull Requests must never be based off your fork's main branch (in this case, `4.x-current`). Start your work in a new branch
based on the main one (e.g.: `cool-new-feature`, `fix-palakis-mistakes`, ...) and open a Pull Request once you feel ready to show your work. based on the main one (e.g.: `cool-new-feature`, `fix-palakis-mistakes`, ...) and open a Pull Request once you feel ready to show your work.
If your Pull Request is not ready to merge yet, tag it with the `work in progress` label. You can also use the `help needed` label if you have questions, need a hand or want to ask for input. **If your Pull Request is not ready to merge yet, create it as a Draft Pull Request** (open the little arrow menu next to the "Create pull request" button, then select "Create draft pull request").
### Code style & formatting ### Code style & formatting

@ -1,3 +1,14 @@
variables:
isReleaseMode: ${{ startsWith(variables['Build.SourceBranch'], 'refs/tags/') }}
trigger:
branches:
include:
- master
tags:
include:
- '*'
jobs: jobs:
- job: 'GenerateDocs' - job: 'GenerateDocs'
condition: | condition: |
@ -149,8 +160,22 @@ jobs:
- script: ./CI/build-macos.sh - script: ./CI/build-macos.sh
displayName: 'Build obs-websocket' displayName: 'Build obs-websocket'
- task: InstallAppleCertificate@1
displayName: 'Install release signing certificates'
condition: eq(variables['isReleaseMode'], true)
inputs:
certSecureFile: 'Certificates.p12'
certPwd: $(secrets.macOS.certificatesImportPassword)
- script: ./CI/package-macos.sh - script: ./CI/package-macos.sh
displayName: 'Package obs-websocket' 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 - task: PublishBuildArtifacts@1
inputs: inputs:

@ -10,7 +10,7 @@ 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="WebSockets 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 - An unknown network error happened on your system. Try again by changing settings, restarting OBS or restarting your system." OBSWebsocket.Server.StartFailed.Message="The WebSockets server failed to start, maybe because:\n - TCP port %1 may currently be in use elsewhere on this system, possibly by another application. Try setting a different TCP port in the WebSocket server settings, or stop any application that could be using this port.\n - Error message: %2"
OBSWebsocket.ProfileChanged.Started="WebSockets server enabled in this profile. Server started." OBSWebsocket.ProfileChanged.Started="WebSockets server enabled in this profile. Server started."
OBSWebsocket.ProfileChanged.Stopped="WebSockets server disabled in this profile. Server stopped." OBSWebsocket.ProfileChanged.Stopped="WebSockets server disabled in this profile. Server stopped."
OBSWebsocket.ProfileChanged.Restarted="WebSockets server port changed in this profile. Server restarted." OBSWebsocket.ProfileChanged.Restarted="WebSockets server port changed in this profile. Server restarted."

@ -2473,7 +2473,9 @@
"lead": "", "lead": "",
"type": "class", "type": "class",
"examples": [] "examples": []
}, }
],
"scene items": [
{ {
"subheads": [], "subheads": [],
"description": "Scene items have been reordered.", "description": "Scene items have been reordered.",
@ -2485,7 +2487,7 @@
], ],
"api": "events", "api": "events",
"name": "SourceOrderChanged", "name": "SourceOrderChanged",
"category": "sources", "category": "scene items",
"since": "4.0.0", "since": "4.0.0",
"returns": [ "returns": [
{ {
@ -2518,7 +2520,7 @@
"categories": [ "categories": [
{ {
"name": "", "name": "",
"description": "sources" "description": "scene items"
} }
], ],
"sinces": [ "sinces": [
@ -2545,7 +2547,7 @@
], ],
"api": "events", "api": "events",
"name": "SceneItemAdded", "name": "SceneItemAdded",
"category": "sources", "category": "scene items",
"since": "4.0.0", "since": "4.0.0",
"returns": [ "returns": [
{ {
@ -2573,7 +2575,7 @@
"categories": [ "categories": [
{ {
"name": "", "name": "",
"description": "sources" "description": "scene items"
} }
], ],
"sinces": [ "sinces": [
@ -2600,7 +2602,7 @@
], ],
"api": "events", "api": "events",
"name": "SceneItemRemoved", "name": "SceneItemRemoved",
"category": "sources", "category": "scene items",
"since": "4.0.0", "since": "4.0.0",
"returns": [ "returns": [
{ {
@ -2628,7 +2630,7 @@
"categories": [ "categories": [
{ {
"name": "", "name": "",
"description": "sources" "description": "scene items"
} }
], ],
"sinces": [ "sinces": [
@ -2656,7 +2658,7 @@
], ],
"api": "events", "api": "events",
"name": "SceneItemVisibilityChanged", "name": "SceneItemVisibilityChanged",
"category": "sources", "category": "scene items",
"since": "4.0.0", "since": "4.0.0",
"returns": [ "returns": [
{ {
@ -2689,7 +2691,7 @@
"categories": [ "categories": [
{ {
"name": "", "name": "",
"description": "sources" "description": "scene items"
} }
], ],
"sinces": [ "sinces": [
@ -2717,8 +2719,8 @@
], ],
"api": "events", "api": "events",
"name": "SceneItemLockChanged", "name": "SceneItemLockChanged",
"category": "sources", "category": "scene items",
"since": "unreleased", "since": "4.8.0",
"returns": [ "returns": [
{ {
"type": "String", "type": "String",
@ -2750,13 +2752,13 @@
"categories": [ "categories": [
{ {
"name": "", "name": "",
"description": "sources" "description": "scene items"
} }
], ],
"sinces": [ "sinces": [
{ {
"name": "", "name": "",
"description": "unreleased" "description": "4.8.0"
} }
], ],
"heading": { "heading": {
@ -2778,7 +2780,7 @@
], ],
"api": "events", "api": "events",
"name": "SceneItemTransformChanged", "name": "SceneItemTransformChanged",
"category": "sources", "category": "scene items",
"since": "4.6.0", "since": "4.6.0",
"returns": [ "returns": [
{ {
@ -2811,7 +2813,7 @@
"categories": [ "categories": [
{ {
"name": "", "name": "",
"description": "sources" "description": "scene items"
} }
], ],
"sinces": [ "sinces": [
@ -2838,7 +2840,7 @@
], ],
"api": "events", "api": "events",
"name": "SceneItemSelected", "name": "SceneItemSelected",
"category": "sources", "category": "scene items",
"since": "4.6.0", "since": "4.6.0",
"returns": [ "returns": [
{ {
@ -2866,7 +2868,7 @@
"categories": [ "categories": [
{ {
"name": "", "name": "",
"description": "sources" "description": "scene items"
} }
], ],
"sinces": [ "sinces": [
@ -2893,7 +2895,7 @@
], ],
"api": "events", "api": "events",
"name": "SceneItemDeselected", "name": "SceneItemDeselected",
"category": "sources", "category": "scene items",
"since": "4.6.0", "since": "4.6.0",
"returns": [ "returns": [
{ {
@ -2921,7 +2923,7 @@
"categories": [ "categories": [
{ {
"name": "", "name": "",
"description": "sources" "description": "scene items"
} }
], ],
"sinces": [ "sinces": [
@ -3513,7 +3515,7 @@
"api": "requests", "api": "requests",
"name": "OpenProjector", "name": "OpenProjector",
"category": "general", "category": "general",
"since": "unreleased", "since": "4.8.0",
"params": [ "params": [
{ {
"type": "String (Optional)", "type": "String (Optional)",
@ -3551,7 +3553,7 @@
"sinces": [ "sinces": [
{ {
"name": "", "name": "",
"description": "unreleased" "description": "4.8.0"
} }
], ],
"heading": { "heading": {
@ -4383,11 +4385,14 @@
"subheads": [], "subheads": [],
"description": "Gets the scene specific properties of the specified source item.\nCoordinates are relative to the item's parent (the scene or group it belongs to).", "description": "Gets the scene specific properties of the specified source item.\nCoordinates are relative to the item's parent (the scene or group it belongs to).",
"param": [ "param": [
"{String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.", "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.",
"{String} `item` The name of the source." "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).",
"{String (optional)} `item.name` Scene Item name (if the `item` field is an object)",
"{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)"
], ],
"return": [ "return": [
"{String} `name` The name of the source.", "{String} `name` Scene Item name.",
"{int} `itemId` Scene Item ID.",
"{int} `position.x` The x position of the source from the left.", "{int} `position.x` The x position of the source from the left.",
"{int} `position.y` The y position of the source from the top.", "{int} `position.y` The y position of the source from the top.",
"{int} `position.alignment` The point on the source that the item is manipulated from.", "{int} `position.alignment` The point on the source that the item is manipulated from.",
@ -4421,7 +4426,12 @@
{ {
"type": "String", "type": "String",
"name": "name", "name": "name",
"description": "The name of the source." "description": "Scene Item name."
},
{
"type": "int",
"name": "itemId",
"description": "Scene Item ID."
}, },
{ {
"type": "int", "type": "int",
@ -4548,12 +4558,22 @@
{ {
"type": "String (optional)", "type": "String (optional)",
"name": "scene-name", "name": "scene-name",
"description": "the name of the scene that the source item belongs to. Defaults to the current scene." "description": "Name of the scene the scene item belongs to. Defaults to the current scene."
}, },
{ {
"type": "String", "type": "String | Object",
"name": "item", "name": "item",
"description": "The name of the source." "description": "Scene Item name (if this field is a string) or specification (if it is an object)."
},
{
"type": "String (optional)",
"name": "item.name",
"description": "Scene Item name (if the `item` field is an object)"
},
{
"type": "int (optional)",
"name": "item.id",
"description": "Scene Item ID (if the `item` field is an object)"
} }
], ],
"names": [ "names": [
@ -4586,8 +4606,10 @@
"subheads": [], "subheads": [],
"description": "Sets the scene specific properties of a source. Unspecified properties will remain unchanged.\nCoordinates are relative to the item's parent (the scene or group it belongs to).", "description": "Sets the scene specific properties of a source. Unspecified properties will remain unchanged.\nCoordinates are relative to the item's parent (the scene or group it belongs to).",
"param": [ "param": [
"{String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.", "{String (optional)} `scene-name` Name of the scene the source item belongs to. Defaults to the current scene.",
"{String} `item` The name of the source.", "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).",
"{String (optional)} `item.name` Scene Item name (if the `item` field is an object)",
"{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)",
"{int (optional)} `position.x` The new x position of the source.", "{int (optional)} `position.x` The new x position of the source.",
"{int (optional)} `position.y` The new y position of the source.", "{int (optional)} `position.y` The new y position of the source.",
"{int (optional)} `position.alignment` The new alignment of the source.", "{int (optional)} `position.alignment` The new alignment of the source.",
@ -4613,12 +4635,22 @@
{ {
"type": "String (optional)", "type": "String (optional)",
"name": "scene-name", "name": "scene-name",
"description": "the name of the scene that the source item belongs to. Defaults to the current scene." "description": "Name of the scene the source item belongs to. Defaults to the current scene."
}, },
{ {
"type": "String", "type": "String | Object",
"name": "item", "name": "item",
"description": "The name of the source." "description": "Scene Item name (if this field is a string) or specification (if it is an object)."
},
{
"type": "String (optional)",
"name": "item.name",
"description": "Scene Item name (if the `item` field is an object)"
},
{
"type": "int (optional)",
"name": "item.id",
"description": "Scene Item ID (if the `item` field is an object)"
}, },
{ {
"type": "int (optional)", "type": "int (optional)",
@ -4731,8 +4763,10 @@
"subheads": [], "subheads": [],
"description": "Reset a scene item.", "description": "Reset a scene item.",
"param": [ "param": [
"{String (optional)} `scene-name` Name of the scene the source belongs to. Defaults to the current scene.", "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.",
"{String} `item` Name of the source item." "{String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).",
"{String (optional)} `item.name` Scene Item name (if the `item` field is an object)",
"{int (optional)} `item.id` Scene Item ID (if the `item` field is an object)"
], ],
"api": "requests", "api": "requests",
"name": "ResetSceneItem", "name": "ResetSceneItem",
@ -4742,12 +4776,22 @@
{ {
"type": "String (optional)", "type": "String (optional)",
"name": "scene-name", "name": "scene-name",
"description": "Name of the scene the source belongs to. Defaults to the current scene." "description": "Name of the scene the scene item belongs to. Defaults to the current scene."
}, },
{ {
"type": "String", "type": "String | Object",
"name": "item", "name": "item",
"description": "Name of the source item." "description": "Scene Item name (if this field is a string) or specification (if it is an object)."
},
{
"type": "String (optional)",
"name": "item.name",
"description": "Scene Item name (if the `item` field is an object)"
},
{
"type": "int (optional)",
"name": "item.id",
"description": "Scene Item ID (if the `item` field is an object)"
} }
], ],
"names": [ "names": [
@ -4780,9 +4824,9 @@
"subheads": [], "subheads": [],
"description": "Show or hide a specified source item in a specified scene.", "description": "Show or hide a specified source item in a specified scene.",
"param": [ "param": [
"{String} `source` Scene item name in the specified scene.", "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the currently active scene.",
"{boolean} `render` true = shown ; false = hidden", "{String} `source` Scene Item name.",
"{String (optional)} `scene-name` Name of the scene where the source resides. Defaults to the currently active scene." "{boolean} `render` true = shown ; false = hidden"
], ],
"api": "requests", "api": "requests",
"name": "SetSceneItemRender", "name": "SetSceneItemRender",
@ -4790,20 +4834,20 @@
"since": "0.3", "since": "0.3",
"deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.", "deprecated": "Since 4.3.0. Prefer the use of SetSceneItemProperties.",
"params": [ "params": [
{
"type": "String (optional)",
"name": "scene-name",
"description": "Name of the scene the scene item belongs to. Defaults to the currently active scene."
},
{ {
"type": "String", "type": "String",
"name": "source", "name": "source",
"description": "Scene item name in the specified scene." "description": "Scene Item name."
}, },
{ {
"type": "boolean", "type": "boolean",
"name": "render", "name": "render",
"description": "true = shown ; false = hidden" "description": "true = shown ; false = hidden"
},
{
"type": "String (optional)",
"name": "scene-name",
"description": "Name of the scene where the source resides. Defaults to the currently active scene."
} }
], ],
"names": [ "names": [
@ -4842,8 +4886,8 @@
"subheads": [], "subheads": [],
"description": "Sets the coordinates of a specified source item.", "description": "Sets the coordinates of a specified source item.",
"param": [ "param": [
"{String (optional)} `scene-name` The name of the scene that the source item belongs to. Defaults to the current scene.", "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.",
"{String} `item` The name of the source item.", "{String} `item` Scene Item name.",
"{double} `x` X coordinate.", "{double} `x` X coordinate.",
"{double} `y` Y coordinate." "{double} `y` Y coordinate."
], ],
@ -4856,12 +4900,12 @@
{ {
"type": "String (optional)", "type": "String (optional)",
"name": "scene-name", "name": "scene-name",
"description": "The name of the scene that the source item belongs to. Defaults to the current scene." "description": "Name of the scene the scene item belongs to. Defaults to the current scene."
}, },
{ {
"type": "String", "type": "String",
"name": "item", "name": "item",
"description": "The name of the source item." "description": "Scene Item name."
}, },
{ {
"type": "double", "type": "double",
@ -4910,8 +4954,8 @@
"subheads": [], "subheads": [],
"description": "Set the transform of the specified source item.", "description": "Set the transform of the specified source item.",
"param": [ "param": [
"{String (optional)} `scene-name` The name of the scene that the source item belongs to. Defaults to the current scene.", "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.",
"{String} `item` The name of the source item.", "{String} `item` Scene Item name.",
"{double} `x-scale` Width scale factor.", "{double} `x-scale` Width scale factor.",
"{double} `y-scale` Height scale factor.", "{double} `y-scale` Height scale factor.",
"{double} `rotation` Source item rotation (in degrees)." "{double} `rotation` Source item rotation (in degrees)."
@ -4925,12 +4969,12 @@
{ {
"type": "String (optional)", "type": "String (optional)",
"name": "scene-name", "name": "scene-name",
"description": "The name of the scene that the source item belongs to. Defaults to the current scene." "description": "Name of the scene the scene item belongs to. Defaults to the current scene."
}, },
{ {
"type": "String", "type": "String",
"name": "item", "name": "item",
"description": "The name of the source item." "description": "Scene Item name."
}, },
{ {
"type": "double", "type": "double",
@ -4984,8 +5028,8 @@
"subheads": [], "subheads": [],
"description": "Sets the crop coordinates of the specified source item.", "description": "Sets the crop coordinates of the specified source item.",
"param": [ "param": [
"{String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.", "{String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.",
"{String} `item` The name of the source.", "{String} `item` Scene Item name.",
"{int} `top` Pixel position of the top of the source item.", "{int} `top` Pixel position of the top of the source item.",
"{int} `bottom` Pixel position of the bottom of the source item.", "{int} `bottom` Pixel position of the bottom of the source item.",
"{int} `left` Pixel position of the left of the source item.", "{int} `left` Pixel position of the left of the source item.",
@ -5000,12 +5044,12 @@
{ {
"type": "String (optional)", "type": "String (optional)",
"name": "scene-name", "name": "scene-name",
"description": "the name of the scene that the source item belongs to. Defaults to the current scene." "description": "Name of the scene the scene item belongs to. Defaults to the current scene."
}, },
{ {
"type": "String", "type": "String",
"name": "item", "name": "item",
"description": "The name of the source." "description": "Scene Item name."
}, },
{ {
"type": "int", "type": "int",
@ -5064,10 +5108,10 @@
"subheads": [], "subheads": [],
"description": "Deletes a scene item.", "description": "Deletes a scene item.",
"param": [ "param": [
"{String (optional)} `scene` Name of the scene the source belongs to. Defaults to the current scene.", "{String (optional)} `scene` Name of the scene the scene item belongs to. Defaults to the current scene.",
"{Object} `item` item to delete (required)", "{Object} `item` Scene item to delete (required)",
"{String} `item.name` name of the scene item (prefer `id`, including both is acceptable).", "{String} `item.name` Scene Item name (prefer `id`, including both is acceptable).",
"{int} `item.id` id of the scene item." "{int} `item.id` Scene Item ID."
], ],
"api": "requests", "api": "requests",
"name": "DeleteSceneItem", "name": "DeleteSceneItem",
@ -5077,22 +5121,22 @@
{ {
"type": "String (optional)", "type": "String (optional)",
"name": "scene", "name": "scene",
"description": "Name of the scene the source belongs to. Defaults to the current scene." "description": "Name of the scene the scene item belongs to. Defaults to the current scene."
}, },
{ {
"type": "Object", "type": "Object",
"name": "item", "name": "item",
"description": "item to delete (required)" "description": "Scene item to delete (required)"
}, },
{ {
"type": "String", "type": "String",
"name": "item.name", "name": "item.name",
"description": "name of the scene item (prefer `id`, including both is acceptable)." "description": "Scene Item name (prefer `id`, including both is acceptable)."
}, },
{ {
"type": "int", "type": "int",
"name": "item.id", "name": "item.id",
"description": "id of the scene item." "description": "Scene Item ID."
} }
], ],
"names": [ "names": [
@ -5127,9 +5171,9 @@
"param": [ "param": [
"{String (optional)} `fromScene` Name of the scene to copy the item from. Defaults to the current scene.", "{String (optional)} `fromScene` Name of the scene to copy the item from. Defaults to the current scene.",
"{String (optional)} `toScene` Name of the scene to create the item in. Defaults to the current scene.", "{String (optional)} `toScene` Name of the scene to create the item in. Defaults to the current scene.",
"{Object} `item` item to duplicate (required)", "{Object} `item` Scene Item to duplicate from the source scene (required)",
"{String} `item.name` name of the scene item (prefer `id`, including both is acceptable).", "{String} `item.name` Scene Item name (prefer `id`, including both is acceptable).",
"{int} `item.id` id of the scene item." "{int} `item.id` Scene Item ID."
], ],
"return": [ "return": [
"{String} `scene` Name of the scene where the new item was created", "{String} `scene` Name of the scene where the new item was created",
@ -5177,17 +5221,17 @@
{ {
"type": "Object", "type": "Object",
"name": "item", "name": "item",
"description": "item to duplicate (required)" "description": "Scene Item to duplicate from the source scene (required)"
}, },
{ {
"type": "String", "type": "String",
"name": "item.name", "name": "item.name",
"description": "name of the scene item (prefer `id`, including both is acceptable)." "description": "Scene Item name (prefer `id`, including both is acceptable)."
}, },
{ {
"type": "int", "type": "int",
"name": "item.id", "name": "item.id",
"description": "id of the scene item." "description": "Scene Item ID."
} }
], ],
"names": [ "names": [
@ -5313,7 +5357,7 @@
"description": "Get a list of scenes in the currently active profile.", "description": "Get a list of scenes in the currently active profile.",
"return": [ "return": [
"{String} `current-scene` Name of the currently active scene.", "{String} `current-scene` Name of the currently active scene.",
"{Array<Scene>} `scenes` Ordered list of the current profile's scenes (See `[GetCurrentScene](#getcurrentscene)` for more information)." "{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",
@ -5328,7 +5372,7 @@
{ {
"type": "Array<Scene>", "type": "Array<Scene>",
"name": "scenes", "name": "scenes",
"description": "Ordered list of the current profile's scenes (See `[GetCurrentScene](#getcurrentscene)` for more information)." "description": "Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information)."
} }
], ],
"names": [ "names": [
@ -6826,6 +6870,7 @@
"name": "GetBrowserSourceProperties", "name": "GetBrowserSourceProperties",
"category": "sources", "category": "sources",
"since": "4.1.0", "since": "4.1.0",
"deprecated": "Since 4.8.0. Prefer the use of GetSourceSettings.",
"returns": [ "returns": [
{ {
"type": "String", "type": "String",
@ -6898,6 +6943,12 @@
"description": "4.1.0" "description": "4.1.0"
} }
], ],
"deprecateds": [
{
"name": "",
"description": "Since 4.8.0. Prefer the use of GetSourceSettings."
}
],
"heading": { "heading": {
"level": 2, "level": 2,
"text": "GetBrowserSourceProperties" "text": "GetBrowserSourceProperties"
@ -6924,6 +6975,7 @@
"api": "requests", "api": "requests",
"name": "SetBrowserSourceProperties", "name": "SetBrowserSourceProperties",
"category": "sources", "category": "sources",
"deprecated": "Since 4.8.0. Prefer the use of SetSourceSettings.",
"since": "4.1.0", "since": "4.1.0",
"params": [ "params": [
{ {
@ -6989,6 +7041,12 @@
"description": "sources" "description": "sources"
} }
], ],
"deprecateds": [
{
"name": "",
"description": "Since 4.8.0. Prefer the use of SetSourceSettings."
}
],
"sinces": [ "sinces": [
{ {
"name": "", "name": "",

@ -99,6 +99,7 @@ auth_response = base64_encode(auth_response_hash)
+ [SourceFilterRemoved](#sourcefilterremoved) + [SourceFilterRemoved](#sourcefilterremoved)
+ [SourceFilterVisibilityChanged](#sourcefiltervisibilitychanged) + [SourceFilterVisibilityChanged](#sourcefiltervisibilitychanged)
+ [SourceFiltersReordered](#sourcefiltersreordered) + [SourceFiltersReordered](#sourcefiltersreordered)
* [Scene Items](#scene-items)
+ [SourceOrderChanged](#sourceorderchanged) + [SourceOrderChanged](#sourceorderchanged)
+ [SceneItemAdded](#sceneitemadded) + [SceneItemAdded](#sceneitemadded)
+ [SceneItemRemoved](#sceneitemremoved) + [SceneItemRemoved](#sceneitemremoved)
@ -148,7 +149,7 @@ auth_response = base64_encode(auth_response_hash)
+ [SetCurrentSceneCollection](#setcurrentscenecollection) + [SetCurrentSceneCollection](#setcurrentscenecollection)
+ [GetCurrentSceneCollection](#getcurrentscenecollection) + [GetCurrentSceneCollection](#getcurrentscenecollection)
+ [ListSceneCollections](#listscenecollections) + [ListSceneCollections](#listscenecollections)
* [Scene Items](#scene-items) * [Scene Items](#scene-items-1)
+ [GetSceneItemProperties](#getsceneitemproperties) + [GetSceneItemProperties](#getsceneitemproperties)
+ [SetSceneItemProperties](#setsceneitemproperties) + [SetSceneItemProperties](#setsceneitemproperties)
+ [ResetSceneItem](#resetsceneitem) + [ResetSceneItem](#resetsceneitem)
@ -1004,6 +1005,8 @@ Filters in a source have been reordered.
--- ---
## Scene Items
### SourceOrderChanged ### SourceOrderChanged
@ -1081,7 +1084,7 @@ An item's visibility has been toggled.
### SceneItemLockChanged ### SceneItemLockChanged
- Unreleased - Added in v4.8.0
An item's locked status has been toggled. An item's locked status has been toggled.
@ -1404,7 +1407,7 @@ _No specified parameters._
### OpenProjector ### OpenProjector
- Unreleased - Added in v4.8.0
Open a projector window or create a projector on a monitor. Requires OBS v24.0.4 or newer. Open a projector window or create a projector on a monitor. Requires OBS v24.0.4 or newer.
@ -1860,15 +1863,18 @@ Coordinates are relative to the item's parent (the scene or group it belongs to)
| Name | Type | Description | | Name | Type | Description |
| ---- | :---: | ------------| | ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | the name of the scene that the source item belongs to. Defaults to the current scene. | | `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `item` | _String_ | The name of the source. | | `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). |
| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) |
| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) |
**Response Items:** **Response Items:**
| Name | Type | Description | | Name | Type | Description |
| ---- | :---: | ------------| | ---- | :---: | ------------|
| `name` | _String_ | The name of the source. | | `name` | _String_ | Scene Item name. |
| `itemId` | _int_ | Scene Item ID. |
| `position.x` | _int_ | The x position of the source from the left. | | `position.x` | _int_ | The x position of the source from the left. |
| `position.y` | _int_ | The y position of the source from the top. | | `position.y` | _int_ | The y position of the source from the top. |
| `position.alignment` | _int_ | The point on the source that the item is manipulated from. | | `position.alignment` | _int_ | The point on the source that the item is manipulated from. |
@ -1909,8 +1915,10 @@ Coordinates are relative to the item's parent (the scene or group it belongs to)
| Name | Type | Description | | Name | Type | Description |
| ---- | :---: | ------------| | ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | the name of the scene that the source item belongs to. Defaults to the current scene. | | `scene-name` | _String (optional)_ | Name of the scene the source item belongs to. Defaults to the current scene. |
| `item` | _String_ | The name of the source. | | `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). |
| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) |
| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) |
| `position.x` | _int (optional)_ | The new x position of the source. | | `position.x` | _int (optional)_ | The new x position of the source. |
| `position.y` | _int (optional)_ | The new y position of the source. | | `position.y` | _int (optional)_ | The new y position of the source. |
| `position.alignment` | _int (optional)_ | The new alignment of the source. | | `position.alignment` | _int (optional)_ | The new alignment of the source. |
@ -1946,8 +1954,10 @@ Reset a scene item.
| Name | Type | Description | | Name | Type | Description |
| ---- | :---: | ------------| | ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | Name of the scene the source belongs to. Defaults to the current scene. | | `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `item` | _String_ | Name of the source item. | | `item` | _String \| Object_ | Scene Item name (if this field is a string) or specification (if it is an object). |
| `item.name` | _String (optional)_ | Scene Item name (if the `item` field is an object) |
| `item.id` | _int (optional)_ | Scene Item ID (if the `item` field is an object) |
**Response Items:** **Response Items:**
@ -1968,9 +1978,9 @@ Show or hide a specified source item in a specified scene.
| Name | Type | Description | | Name | Type | Description |
| ---- | :---: | ------------| | ---- | :---: | ------------|
| `source` | _String_ | Scene item name in the specified scene. | | `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the currently active scene. |
| `source` | _String_ | Scene Item name. |
| `render` | _boolean_ | true = shown ; false = hidden | | `render` | _boolean_ | true = shown ; false = hidden |
| `scene-name` | _String (optional)_ | Name of the scene where the source resides. Defaults to the currently active scene. |
**Response Items:** **Response Items:**
@ -1991,8 +2001,8 @@ Sets the coordinates of a specified source item.
| Name | Type | Description | | Name | Type | Description |
| ---- | :---: | ------------| | ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | The name of the scene that the source item belongs to. Defaults to the current scene. | | `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `item` | _String_ | The name of the source item. | | `item` | _String_ | Scene Item name. |
| `x` | _double_ | X coordinate. | | `x` | _double_ | X coordinate. |
| `y` | _double_ | Y coordinate. | | `y` | _double_ | Y coordinate. |
@ -2015,8 +2025,8 @@ Set the transform of the specified source item.
| Name | Type | Description | | Name | Type | Description |
| ---- | :---: | ------------| | ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | The name of the scene that the source item belongs to. Defaults to the current scene. | | `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `item` | _String_ | The name of the source item. | | `item` | _String_ | Scene Item name. |
| `x-scale` | _double_ | Width scale factor. | | `x-scale` | _double_ | Width scale factor. |
| `y-scale` | _double_ | Height scale factor. | | `y-scale` | _double_ | Height scale factor. |
| `rotation` | _double_ | Source item rotation (in degrees). | | `rotation` | _double_ | Source item rotation (in degrees). |
@ -2040,8 +2050,8 @@ Sets the crop coordinates of the specified source item.
| Name | Type | Description | | Name | Type | Description |
| ---- | :---: | ------------| | ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | the name of the scene that the source item belongs to. Defaults to the current scene. | | `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `item` | _String_ | The name of the source. | | `item` | _String_ | Scene Item name. |
| `top` | _int_ | Pixel position of the top of the source item. | | `top` | _int_ | Pixel position of the top of the source item. |
| `bottom` | _int_ | Pixel position of the bottom of the source item. | | `bottom` | _int_ | Pixel position of the bottom of the source item. |
| `left` | _int_ | Pixel position of the left of the source item. | | `left` | _int_ | Pixel position of the left of the source item. |
@ -2065,10 +2075,10 @@ Deletes a scene item.
| Name | Type | Description | | Name | Type | Description |
| ---- | :---: | ------------| | ---- | :---: | ------------|
| `scene` | _String (optional)_ | Name of the scene the source belongs to. Defaults to the current scene. | | `scene` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `item` | _Object_ | item to delete (required) | | `item` | _Object_ | Scene item to delete (required) |
| `item.name` | _String_ | name of the scene item (prefer `id`, including both is acceptable). | | `item.name` | _String_ | Scene Item name (prefer `id`, including both is acceptable). |
| `item.id` | _int_ | id of the scene item. | | `item.id` | _int_ | Scene Item ID. |
**Response Items:** **Response Items:**
@ -2090,9 +2100,9 @@ Duplicates a scene item.
| ---- | :---: | ------------| | ---- | :---: | ------------|
| `fromScene` | _String (optional)_ | Name of the scene to copy the item from. Defaults to the current scene. | | `fromScene` | _String (optional)_ | Name of the scene to copy the item from. Defaults to the current scene. |
| `toScene` | _String (optional)_ | Name of the scene to create the item in. Defaults to the current scene. | | `toScene` | _String (optional)_ | Name of the scene to create the item in. Defaults to the current scene. |
| `item` | _Object_ | item to duplicate (required) | | `item` | _Object_ | Scene Item to duplicate from the source scene (required) |
| `item.name` | _String_ | name of the scene item (prefer `id`, including both is acceptable). | | `item.name` | _String_ | Scene Item name (prefer `id`, including both is acceptable). |
| `item.id` | _int_ | id of the scene item. | | `item.id` | _int_ | Scene Item ID. |
**Response Items:** **Response Items:**
@ -2166,7 +2176,7 @@ _No specified parameters._
| Name | Type | Description | | Name | Type | Description |
| ---- | :---: | ------------| | ---- | :---: | ------------|
| `current-scene` | _String_ | Name of the currently active scene. | | `current-scene` | _String_ | Name of the currently active scene. |
| `scenes` | _Array&lt;Scene&gt;_ | Ordered list of the current profile's scenes (See `[GetCurrentScene](#getcurrentscene)` for more information). | | `scenes` | _Array&lt;Scene&gt;_ | Ordered list of the current profile's scenes (See [GetCurrentScene](#getcurrentscene) for more information). |
--- ---
@ -2633,6 +2643,7 @@ _No additional response items._
### GetBrowserSourceProperties ### GetBrowserSourceProperties
- **⚠️ Deprecated. Since 4.8.0. Prefer the use of GetSourceSettings. ⚠️**
- Added in v4.1.0 - Added in v4.1.0
@ -2664,6 +2675,7 @@ Get current properties for a Browser Source.
### SetBrowserSourceProperties ### SetBrowserSourceProperties
- **⚠️ Deprecated. Since 4.8.0. Prefer the use of SetSourceSettings. ⚠️**
- Added in v4.1.0 - Added in v4.1.0

@ -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.7.0" #define MyAppVersion "4.8.0"
#define MyAppPublisher "Stephane Lepin" #define MyAppPublisher "Stephane Lepin"
#define MyAppURL "http://github.com/Palakis/obs-websocket" #define MyAppURL "http://github.com/Palakis/obs-websocket"

@ -180,34 +180,6 @@ obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
return data; return data;
} }
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::GetSceneItemFromName(obs_scene_t* scene, QString name) { obs_sceneitem_t* Utils::GetSceneItemFromName(obs_scene_t* scene, QString name) {
if (!scene) { if (!scene) {
return nullptr; return nullptr;
@ -297,6 +269,49 @@ obs_sceneitem_t* Utils::GetSceneItemFromId(obs_scene_t* scene, int64_t id) {
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:

@ -39,11 +39,12 @@ namespace Utils {
obs_data_array_t* GetSceneItems(obs_source_t* source); obs_data_array_t* GetSceneItems(obs_source_t* source);
obs_data_t* GetSceneItemData(obs_sceneitem_t* item); obs_data_t* GetSceneItemData(obs_sceneitem_t* item);
// These two functions support nested lookup into groups // These functions support nested lookup into groups
obs_sceneitem_t* GetSceneItemFromName(obs_scene_t* scene, QString name); obs_sceneitem_t* GetSceneItemFromName(obs_scene_t* scene, QString name);
obs_sceneitem_t* GetSceneItemFromId(obs_scene_t* scene, int64_t id); 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* 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); obs_scene_t* GetSceneFromNameOrCurrent(QString sceneName);
obs_data_t* GetSceneItemPropertiesData(obs_sceneitem_t* item); obs_data_t* GetSceneItemPropertiesData(obs_sceneitem_t* item);

@ -240,10 +240,17 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private
void WSEvents::broadcastUpdate(const char* updateType, void WSEvents::broadcastUpdate(const char* updateType,
obs_data_t* additionalFields = nullptr) obs_data_t* additionalFields = nullptr)
{ {
uint64_t streamTime = getStreamingTime(); std::optional<uint64_t> streamTime;
uint64_t recordingTime = getRecordingTime(); if (obs_frontend_streaming_active()) {
RpcEvent event(QString(updateType), streamTime, recordingTime, additionalFields); streamTime = std::make_optional(getStreamingTime());
}
std::optional<uint64_t> recordingTime;
if (obs_frontend_recording_active()) {
recordingTime = std::make_optional(getRecordingTime());
}
RpcEvent event(QString(updateType), streamTime, recordingTime, additionalFields);
_srv->broadcast(event); _srv->broadcast(event);
} }
@ -1338,7 +1345,7 @@ void WSEvents::OnSourceFilterOrderChanged(void* param, calldata_t* data) {
* *
* @api events * @api events
* @name SourceOrderChanged * @name SourceOrderChanged
* @category sources * @category scene items
* @since 4.0.0 * @since 4.0.0
*/ */
void WSEvents::OnSceneReordered(void* param, calldata_t* data) { void WSEvents::OnSceneReordered(void* param, calldata_t* data) {
@ -1380,7 +1387,7 @@ void WSEvents::OnSceneReordered(void* param, calldata_t* data) {
* *
* @api events * @api events
* @name SceneItemAdded * @name SceneItemAdded
* @category sources * @category scene items
* @since 4.0.0 * @since 4.0.0
*/ */
void WSEvents::OnSceneItemAdd(void* param, calldata_t* data) { void WSEvents::OnSceneItemAdd(void* param, calldata_t* data) {
@ -1413,7 +1420,7 @@ void WSEvents::OnSceneItemAdd(void* param, calldata_t* data) {
* *
* @api events * @api events
* @name SceneItemRemoved * @name SceneItemRemoved
* @category sources * @category scene items
* @since 4.0.0 * @since 4.0.0
*/ */
void WSEvents::OnSceneItemDelete(void* param, calldata_t* data) { void WSEvents::OnSceneItemDelete(void* param, calldata_t* data) {
@ -1447,7 +1454,7 @@ void WSEvents::OnSceneItemDelete(void* param, calldata_t* data) {
* *
* @api events * @api events
* @name SceneItemVisibilityChanged * @name SceneItemVisibilityChanged
* @category sources * @category scene items
* @since 4.0.0 * @since 4.0.0
*/ */
void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) { void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) {
@ -1485,8 +1492,8 @@ void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) {
* *
* @api events * @api events
* @name SceneItemLockChanged * @name SceneItemLockChanged
* @category sources * @category scene items
* @since unreleased * @since 4.8.0
*/ */
void WSEvents::OnSceneItemLockChanged(void* param, calldata_t* data) { void WSEvents::OnSceneItemLockChanged(void* param, calldata_t* data) {
auto instance = reinterpret_cast<WSEvents*>(param); auto instance = reinterpret_cast<WSEvents*>(param);
@ -1523,7 +1530,7 @@ void WSEvents::OnSceneItemLockChanged(void* param, calldata_t* data) {
* *
* @api events * @api events
* @name SceneItemTransformChanged * @name SceneItemTransformChanged
* @category sources * @category scene items
* @since 4.6.0 * @since 4.6.0
*/ */
void WSEvents::OnSceneItemTransform(void* param, calldata_t* data) { void WSEvents::OnSceneItemTransform(void* param, calldata_t* data) {
@ -1559,7 +1566,7 @@ void WSEvents::OnSceneItemTransform(void* param, calldata_t* data) {
* *
* @api events * @api events
* @name SceneItemSelected * @name SceneItemSelected
* @category sources * @category scene items
* @since 4.6.0 * @since 4.6.0
*/ */
void WSEvents::OnSceneItemSelected(void* param, calldata_t* data) { void WSEvents::OnSceneItemSelected(void* param, calldata_t* data) {
@ -1594,7 +1601,7 @@ void WSEvents::OnSceneItemSelected(void* param, calldata_t* data) {
* *
* @api events * @api events
* @name SceneItemDeselected * @name SceneItemDeselected
* @category sources * @category scene items
* @since 4.6.0 * @since 4.6.0
*/ */
void WSEvents::OnSceneItemDeselected(void* param, calldata_t* data) { void WSEvents::OnSceneItemDeselected(void* param, calldata_t* data) {

@ -328,7 +328,7 @@ RpcResponse WSRequestHandler::GetVideoInfo(const RpcRequest& request) {
* @api requests * @api requests
* @name OpenProjector * @name OpenProjector
* @category general * @category general
* @since unreleased * @since 4.8.0
*/ */
RpcResponse WSRequestHandler::OpenProjector(const RpcRequest& request) { RpcResponse WSRequestHandler::OpenProjector(const RpcRequest& request) {
const char* type = obs_data_get_string(request.parameters(), "type"); const char* type = obs_data_get_string(request.parameters(), "type");

@ -6,10 +6,13 @@
* Gets the scene specific properties of the specified source item. * Gets the scene specific properties of the specified source item.
* Coordinates are relative to the item's parent (the scene or group it belongs to). * Coordinates are relative to the item's parent (the scene or group it belongs to).
* *
* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene. * @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.
* @param {String} `item` The name of the source. * @param {String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).
* @param {String (optional)} `item.name` Scene Item name (if the `item` field is an object)
* @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object)
* *
* @return {String} `name` The name of the source. * @return {String} `name` Scene Item name.
* @return {int} `itemId` Scene Item ID.
* @return {int} `position.x` The x position of the source from the left. * @return {int} `position.x` The x position of the source from the left.
* @return {int} `position.y` The y position of the source from the top. * @return {int} `position.y` The y position of the source from the top.
* @return {int} `position.alignment` The point on the source that the item is manipulated from. * @return {int} `position.alignment` The point on the source that the item is manipulated from.
@ -45,24 +48,25 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request)
return request.failed("missing request parameters"); return request.failed("missing request parameters");
} }
QString itemName = obs_data_get_string(request.parameters(), "item"); OBSData params = request.parameters();
if (itemName.isEmpty()) {
return request.failed("invalid request parameters");
}
QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); QString sceneName = obs_data_get_string(params, "scene-name");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return request.failed("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); OBSDataItemAutoRelease itemField = obs_data_item_byname(params, "item");
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField);
if (!sceneItem) { if (!sceneItem) {
return request.failed("specified scene item doesn't exist"); return request.failed("specified scene item doesn't exist");
} }
OBSDataAutoRelease data = Utils::GetSceneItemPropertiesData(sceneItem); OBSDataAutoRelease data = Utils::GetSceneItemPropertiesData(sceneItem);
obs_data_set_string(data, "name", itemName.toUtf8());
OBSSource sceneItemSource = obs_sceneitem_get_source(sceneItem);
obs_data_set_string(data, "name", obs_source_get_name(sceneItemSource));
obs_data_set_int(data, "itemId", obs_sceneitem_get_id(sceneItem));
return request.success(data); return request.success(data);
} }
@ -71,8 +75,10 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request)
* Sets the scene specific properties of a source. Unspecified properties will remain unchanged. * Sets the scene specific properties of a source. Unspecified properties will remain unchanged.
* Coordinates are relative to the item's parent (the scene or group it belongs to). * Coordinates are relative to the item's parent (the scene or group it belongs to).
* *
* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene. * @param {String (optional)} `scene-name` Name of the scene the source item belongs to. Defaults to the current scene.
* @param {String} `item` The name of the source. * @param {String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).
* @param {String (optional)} `item.name` Scene Item name (if the `item` field is an object)
* @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object)
* @param {int (optional)} `position.x` The new x position of the source. * @param {int (optional)} `position.x` The new x position of the source.
* @param {int (optional)} `position.y` The new y position of the source. * @param {int (optional)} `position.y` The new y position of the source.
* @param {int (optional)} `position.alignment` The new alignment of the source. * @param {int (optional)} `position.alignment` The new alignment of the source.
@ -100,19 +106,16 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
return request.failed("missing request parameters"); return request.failed("missing request parameters");
} }
QString itemName = obs_data_get_string(request.parameters(), "item"); OBSData params = request.parameters();
if (itemName.isEmpty()) {
return request.failed("invalid request parameters");
}
QString sceneName = obs_data_get_string(request.parameters(), "scene-name"); QString sceneName = obs_data_get_string(params, "scene-name");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return request.failed("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
OBSSceneItemAutoRelease sceneItem = OBSDataItemAutoRelease itemField = obs_data_item_byname(params, "item");
Utils::GetSceneItemFromName(scene, itemName); OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField);
if (!sceneItem) { if (!sceneItem) {
return request.failed("specified scene item doesn't exist"); return request.failed("specified scene item doesn't exist");
} }
@ -126,51 +129,59 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
vec2 oldPosition; vec2 oldPosition;
OBSDataAutoRelease positionError = obs_data_create(); OBSDataAutoRelease positionError = obs_data_create();
obs_sceneitem_get_pos(sceneItem, &oldPosition); obs_sceneitem_get_pos(sceneItem, &oldPosition);
OBSDataAutoRelease reqPosition = obs_data_get_obj(request.parameters(), "position");
OBSDataAutoRelease reqPosition = obs_data_get_obj(params, "position");
vec2 newPosition = oldPosition; vec2 newPosition = oldPosition;
if (obs_data_has_user_value(reqPosition, "x")) { if (obs_data_has_user_value(reqPosition, "x")) {
newPosition.x = obs_data_get_int(reqPosition, "x"); newPosition.x = obs_data_get_int(reqPosition, "x");
} }
if (obs_data_has_user_value(reqPosition, "y")) { if (obs_data_has_user_value(reqPosition, "y")) {
newPosition.y = obs_data_get_int(reqPosition, "y"); newPosition.y = obs_data_get_int(reqPosition, "y");
} }
if (obs_data_has_user_value(reqPosition, "alignment")) { if (obs_data_has_user_value(reqPosition, "alignment")) {
const uint32_t alignment = obs_data_get_int(reqPosition, "alignment"); const uint32_t alignment = obs_data_get_int(reqPosition, "alignment");
if (Utils::IsValidAlignment(alignment)) { if (Utils::IsValidAlignment(alignment)) {
obs_sceneitem_set_alignment(sceneItem, alignment); obs_sceneitem_set_alignment(sceneItem, alignment);
} } else {
else {
badRequest = true; badRequest = true;
obs_data_set_string(positionError, "alignment", "invalid"); obs_data_set_string(positionError, "alignment", "invalid");
obs_data_set_obj(errorData, "position", positionError); obs_data_set_obj(errorData, "position", positionError);
} }
} }
obs_sceneitem_set_pos(sceneItem, &newPosition); obs_sceneitem_set_pos(sceneItem, &newPosition);
} }
if (request.hasField("rotation")) { if (request.hasField("rotation")) {
obs_sceneitem_set_rot(sceneItem, (float)obs_data_get_double(request.parameters(), "rotation")); obs_sceneitem_set_rot(sceneItem, (float)obs_data_get_double(params, "rotation"));
} }
if (request.hasField("scale")) { if (request.hasField("scale")) {
vec2 oldScale; vec2 oldScale;
obs_sceneitem_get_scale(sceneItem, &oldScale); obs_sceneitem_get_scale(sceneItem, &oldScale);
OBSDataAutoRelease reqScale = obs_data_get_obj(request.parameters(), "scale");
vec2 newScale = oldScale; vec2 newScale = oldScale;
OBSDataAutoRelease reqScale = obs_data_get_obj(params, "scale");
if (obs_data_has_user_value(reqScale, "x")) { if (obs_data_has_user_value(reqScale, "x")) {
newScale.x = obs_data_get_double(reqScale, "x"); newScale.x = obs_data_get_double(reqScale, "x");
} }
if (obs_data_has_user_value(reqScale, "y")) { if (obs_data_has_user_value(reqScale, "y")) {
newScale.y = obs_data_get_double(reqScale, "y"); newScale.y = obs_data_get_double(reqScale, "y");
} }
obs_sceneitem_set_scale(sceneItem, &newScale); obs_sceneitem_set_scale(sceneItem, &newScale);
} }
if (request.hasField("crop")) { if (request.hasField("crop")) {
obs_sceneitem_crop oldCrop; obs_sceneitem_crop oldCrop;
obs_sceneitem_get_crop(sceneItem, &oldCrop); obs_sceneitem_get_crop(sceneItem, &oldCrop);
OBSDataAutoRelease reqCrop = obs_data_get_obj(request.parameters(), "crop");
OBSDataAutoRelease reqCrop = obs_data_get_obj(params, "crop");
obs_sceneitem_crop newCrop = oldCrop; obs_sceneitem_crop newCrop = oldCrop;
if (obs_data_has_user_value(reqCrop, "top")) { if (obs_data_has_user_value(reqCrop, "top")) {
newCrop.top = obs_data_get_int(reqCrop, "top"); newCrop.top = obs_data_get_int(reqCrop, "top");
} }
@ -183,21 +194,23 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
if (obs_data_has_user_value(reqCrop, "left")) { if (obs_data_has_user_value(reqCrop, "left")) {
newCrop.left = obs_data_get_int(reqCrop, "left"); newCrop.left = obs_data_get_int(reqCrop, "left");
} }
obs_sceneitem_set_crop(sceneItem, &newCrop); obs_sceneitem_set_crop(sceneItem, &newCrop);
} }
if (request.hasField("visible")) { if (request.hasField("visible")) {
obs_sceneitem_set_visible(sceneItem, obs_data_get_bool(request.parameters(), "visible")); obs_sceneitem_set_visible(sceneItem, obs_data_get_bool(params, "visible"));
} }
if (request.hasField("locked")) { if (request.hasField("locked")) {
obs_sceneitem_set_locked(sceneItem, obs_data_get_bool(request.parameters(), "locked")); obs_sceneitem_set_locked(sceneItem, obs_data_get_bool(params, "locked"));
} }
if (request.hasField("bounds")) { if (request.hasField("bounds")) {
bool badBounds = false; bool badBounds = false;
OBSDataAutoRelease boundsError = obs_data_create(); OBSDataAutoRelease boundsError = obs_data_create();
OBSDataAutoRelease reqBounds = obs_data_get_obj(request.parameters(), "bounds"); OBSDataAutoRelease reqBounds = obs_data_get_obj(params, "bounds");
if (obs_data_has_user_value(reqBounds, "type")) { if (obs_data_has_user_value(reqBounds, "type")) {
QString newBoundsType = obs_data_get_string(reqBounds, "type"); QString newBoundsType = obs_data_get_string(reqBounds, "type");
if (newBoundsType == "OBS_BOUNDS_NONE") { if (newBoundsType == "OBS_BOUNDS_NONE") {
@ -226,16 +239,20 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
obs_data_set_string(boundsError, "type", "invalid"); obs_data_set_string(boundsError, "type", "invalid");
} }
} }
vec2 oldBounds; vec2 oldBounds;
obs_sceneitem_get_bounds(sceneItem, &oldBounds); obs_sceneitem_get_bounds(sceneItem, &oldBounds);
vec2 newBounds = oldBounds; vec2 newBounds = oldBounds;
if (obs_data_has_user_value(reqBounds, "x")) { if (obs_data_has_user_value(reqBounds, "x")) {
newBounds.x = obs_data_get_double(reqBounds, "x"); newBounds.x = obs_data_get_double(reqBounds, "x");
} }
if (obs_data_has_user_value(reqBounds, "y")) { if (obs_data_has_user_value(reqBounds, "y")) {
newBounds.y = obs_data_get_double(reqBounds, "y"); newBounds.y = obs_data_get_double(reqBounds, "y");
} }
obs_sceneitem_set_bounds(sceneItem, &newBounds); obs_sceneitem_set_bounds(sceneItem, &newBounds);
if (obs_data_has_user_value(reqBounds, "alignment")) { if (obs_data_has_user_value(reqBounds, "alignment")) {
const uint32_t bounds_alignment = obs_data_get_int(reqBounds, "alignment"); const uint32_t bounds_alignment = obs_data_get_int(reqBounds, "alignment");
if (Utils::IsValidAlignment(bounds_alignment)) { if (Utils::IsValidAlignment(bounds_alignment)) {
@ -246,6 +263,7 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
obs_data_set_string(boundsError, "alignment", "invalid"); obs_data_set_string(boundsError, "alignment", "invalid");
} }
} }
if (badBounds) { if (badBounds) {
obs_data_set_obj(errorData, "bounds", boundsError); obs_data_set_obj(errorData, "bounds", boundsError);
} }
@ -263,8 +281,10 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
/** /**
* Reset a scene item. * Reset a scene item.
* *
* @param {String (optional)} `scene-name` Name of the scene the source belongs to. Defaults to the current scene. * @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.
* @param {String} `item` Name of the source item. * @param {String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).
* @param {String (optional)} `item.name` Scene Item name (if the `item` field is an object)
* @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object)
* *
* @api requests * @api requests
* @name ResetSceneItem * @name ResetSceneItem
@ -272,24 +292,20 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
* @since 4.2.0 * @since 4.2.0
*/ */
RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) { RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) {
// TODO: remove this request, or refactor it to ResetSource
if (!request.hasField("item")) { if (!request.hasField("item")) {
return request.failed("missing request parameters"); return request.failed("missing request parameters");
} }
const char* itemName = obs_data_get_string(request.parameters(), "item"); OBSData params = request.parameters();
if (!itemName) {
return request.failed("invalid request parameters");
}
const char* sceneName = obs_data_get_string(request.parameters(), "scene-name"); const char* sceneName = obs_data_get_string(params, "scene-name");
OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName);
if (!scene) { if (!scene) {
return request.failed("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromName(scene, itemName); OBSDataItemAutoRelease itemField = obs_data_item_byname(params, "item");
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField);
if (!sceneItem) { if (!sceneItem) {
return request.failed("specified scene item doesn't exist"); return request.failed("specified scene item doesn't exist");
} }
@ -305,9 +321,9 @@ RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) {
/** /**
* Show or hide a specified source item in a specified scene. * Show or hide a specified source item in a specified scene.
* *
* @param {String} `source` Scene item name in the specified scene. * @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the currently active scene.
* @param {String} `source` Scene Item name.
* @param {boolean} `render` true = shown ; false = hidden * @param {boolean} `render` true = shown ; false = hidden
* @param {String (optional)} `scene-name` Name of the scene where the source resides. Defaults to the currently active scene.
* *
* @api requests * @api requests
* @name SetSceneItemRender * @name SetSceneItemRender
@ -348,8 +364,8 @@ RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) {
/** /**
* Sets the coordinates of a specified source item. * Sets the coordinates of a specified source item.
* *
* @param {String (optional)} `scene-name` The name of the scene that the source item belongs to. Defaults to the current scene. * @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.
* @param {String} `item` The name of the source item. * @param {String} `item` Scene Item name.
* @param {double} `x` X coordinate. * @param {double} `x` X coordinate.
* @param {double} `y` Y coordinate. * @param {double} `y` Y coordinate.
@ -393,8 +409,8 @@ RpcResponse WSRequestHandler::SetSceneItemPosition(const RpcRequest& request) {
/** /**
* Set the transform of the specified source item. * Set the transform of the specified source item.
* *
* @param {String (optional)} `scene-name` The name of the scene that the source item belongs to. Defaults to the current scene. * @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.
* @param {String} `item` The name of the source item. * @param {String} `item` Scene Item name.
* @param {double} `x-scale` Width scale factor. * @param {double} `x-scale` Width scale factor.
* @param {double} `y-scale` Height scale factor. * @param {double} `y-scale` Height scale factor.
* @param {double} `rotation` Source item rotation (in degrees). * @param {double} `rotation` Source item rotation (in degrees).
@ -448,8 +464,8 @@ RpcResponse WSRequestHandler::SetSceneItemTransform(const RpcRequest& request) {
/** /**
* Sets the crop coordinates of the specified source item. * Sets the crop coordinates of the specified source item.
* *
* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene. * @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.
* @param {String} `item` The name of the source. * @param {String} `item` Scene Item name.
* @param {int} `top` Pixel position of the top of the source item. * @param {int} `top` Pixel position of the top of the source item.
* @param {int} `bottom` Pixel position of the bottom of the source item. * @param {int} `bottom` Pixel position of the bottom of the source item.
* @param {int} `left` Pixel position of the left of the source item. * @param {int} `left` Pixel position of the left of the source item.
@ -496,10 +512,10 @@ RpcResponse WSRequestHandler::SetSceneItemCrop(const RpcRequest& request) {
/** /**
* Deletes a scene item. * Deletes a scene item.
* *
* @param {String (optional)} `scene` Name of the scene the source belongs to. Defaults to the current scene. * @param {String (optional)} `scene` Name of the scene the scene item belongs to. Defaults to the current scene.
* @param {Object} `item` item to delete (required) * @param {Object} `item` Scene item to delete (required)
* @param {String} `item.name` name of the scene item (prefer `id`, including both is acceptable). * @param {String} `item.name` Scene Item name (prefer `id`, including both is acceptable).
* @param {int} `item.id` id of the scene item. * @param {int} `item.id` Scene Item ID.
* *
* @api requests * @api requests
* @name DeleteSceneItem * @name DeleteSceneItem
@ -517,8 +533,8 @@ RpcResponse WSRequestHandler::DeleteSceneItem(const RpcRequest& request) {
return request.failed("requested scene doesn't exist"); return request.failed("requested scene doesn't exist");
} }
OBSDataAutoRelease item = obs_data_get_obj(request.parameters(), "item"); OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item");
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item); OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField);
if (!sceneItem) { if (!sceneItem) {
return request.failed("item with id/name combination not found in specified scene"); return request.failed("item with id/name combination not found in specified scene");
} }
@ -533,9 +549,9 @@ RpcResponse WSRequestHandler::DeleteSceneItem(const RpcRequest& request) {
* *
* @param {String (optional)} `fromScene` Name of the scene to copy the item from. Defaults to the current scene. * @param {String (optional)} `fromScene` Name of the scene to copy the item from. Defaults to the current scene.
* @param {String (optional)} `toScene` Name of the scene to create the item in. Defaults to the current scene. * @param {String (optional)} `toScene` Name of the scene to create the item in. Defaults to the current scene.
* @param {Object} `item` item to duplicate (required) * @param {Object} `item` Scene Item to duplicate from the source scene (required)
* @param {String} `item.name` name of the scene item (prefer `id`, including both is acceptable). * @param {String} `item.name` Scene Item name (prefer `id`, including both is acceptable).
* @param {int} `item.id` id of the scene item. * @param {int} `item.id` Scene Item ID.
* *
* @return {String} `scene` Name of the scene where the new item was created * @return {String} `scene` Name of the scene where the new item was created
* @return {Object} `item` New item info * @return {Object} `item` New item info
@ -570,8 +586,8 @@ RpcResponse WSRequestHandler::DuplicateSceneItem(const RpcRequest& request) {
return request.failed("requested toScene doesn't exist"); return request.failed("requested toScene doesn't exist");
} }
OBSDataAutoRelease item = obs_data_get_obj(request.parameters(), "item"); OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item");
OBSSceneItemAutoRelease referenceItem = Utils::GetSceneItemFromItem(fromScene, item); OBSSceneItemAutoRelease referenceItem = Utils::GetSceneItemFromRequestField(fromScene, itemField);
if (!referenceItem) { if (!referenceItem) {
return request.failed("item with id/name combination not found in specified scene"); return request.failed("item with id/name combination not found in specified scene");
} }

@ -60,7 +60,7 @@ RpcResponse WSRequestHandler::GetCurrentScene(const RpcRequest& request) {
* 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

@ -8,6 +8,16 @@
#include "WSRequestHandler.h" #include "WSRequestHandler.h"
bool isTextGDIPlusSource(const QString& sourceKind)
{
return (sourceKind == "text_gdiplus" || sourceKind == "text_gdiplus_v2");
}
bool isTextFreeType2Source(const QString& sourceKind)
{
return (sourceKind == "text_ft2_source" || sourceKind == "text_ft2_source_v2");
}
/** /**
* List all sources available in the running OBS instance * List all sources available in the running OBS instance
* *
@ -536,8 +546,8 @@ RpcResponse WSRequestHandler::GetTextGDIPlusProperties(const RpcRequest& request
return request.failed("specified source doesn't exist"); return request.failed("specified source doesn't exist");
} }
QString sourceId = obs_source_get_id(source); QString sourceKind = obs_source_get_id(source);
if (sourceId != "text_gdiplus") { if (!isTextGDIPlusSource(sourceKind)) {
return request.failed("not a text gdi plus source"); return request.failed("not a text gdi plus source");
} }
@ -601,8 +611,8 @@ RpcResponse WSRequestHandler::SetTextGDIPlusProperties(const RpcRequest& request
return request.failed("specified source doesn't exist"); return request.failed("specified source doesn't exist");
} }
QString sourceId = obs_source_get_id(source); QString sourceKind = obs_source_get_id(source);
if (sourceId != "text_gdiplus") { if (!isTextGDIPlusSource(sourceKind)) {
return request.failed("not a text gdi plus source"); return request.failed("not a text gdi plus source");
} }
@ -767,8 +777,8 @@ RpcResponse WSRequestHandler::GetTextFreetype2Properties(const RpcRequest& reque
return request.failed("specified source doesn't exist"); return request.failed("specified source doesn't exist");
} }
QString sourceId = obs_source_get_id(source); QString sourceKind = obs_source_get_id(source);
if (sourceId != "text_ft2_source") { if (!isTextFreeType2Source(sourceKind)) {
return request.failed("not a freetype 2 source"); return request.failed("not a freetype 2 source");
} }
@ -815,8 +825,8 @@ RpcResponse WSRequestHandler::SetTextFreetype2Properties(const RpcRequest& reque
return request.failed("specified source doesn't exist"); return request.failed("specified source doesn't exist");
} }
QString sourceId = obs_source_get_id(source); QString sourceKind = obs_source_get_id(source);
if (sourceId != "text_ft2_source") { if (!isTextFreeType2Source(sourceKind)) {
return request.failed("not text freetype 2 source"); return request.failed("not text freetype 2 source");
} }
@ -909,6 +919,7 @@ RpcResponse WSRequestHandler::SetTextFreetype2Properties(const RpcRequest& reque
* @name GetBrowserSourceProperties * @name GetBrowserSourceProperties
* @category sources * @category sources
* @since 4.1.0 * @since 4.1.0
* @deprecated Since 4.8.0. Prefer the use of GetSourceSettings.
*/ */
RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& request) RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& request)
{ {
@ -950,6 +961,7 @@ RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& reque
* @api requests * @api requests
* @name SetBrowserSourceProperties * @name SetBrowserSourceProperties
* @category sources * @category sources
* @deprecated Since 4.8.0. Prefer the use of SetSourceSettings.
* @since 4.1.0 * @since 4.1.0
*/ */
RpcResponse WSRequestHandler::SetBrowserSourceProperties(const RpcRequest& request) RpcResponse WSRequestHandler::SetBrowserSourceProperties(const RpcRequest& request)

@ -44,6 +44,7 @@ WSServer::WSServer()
_connections(), _connections(),
_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);
_server.init_asio(); _server.init_asio();
#ifndef _WIN32 #ifndef _WIN32
_server.set_reuse_addr(true); _server.set_reuse_addr(true);
@ -83,7 +84,7 @@ void WSServer::start(quint16 port)
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 errorTitle = tr("OBSWebsocket.Server.StartFailed.Title");
QString errorMessage = tr("OBSWebsocket.Server.StartFailed.Message").arg(_serverPort); QString errorMessage = tr("OBSWebsocket.Server.StartFailed.Message").arg(_serverPort).arg(errorCodeMessage.c_str());
obs_frontend_pop_ui_translation(); obs_frontend_pop_ui_translation();
QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window()); QMainWindow* mainWindow = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());

@ -67,13 +67,15 @@ std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event)
QString updateType = event.updateType(); QString updateType = event.updateType();
obs_data_set_string(eventData, "update-type", updateType.toUtf8().constData()); obs_data_set_string(eventData, "update-type", updateType.toUtf8().constData());
if (obs_frontend_streaming_active()) { std::optional<uint64_t> streamTime = event.streamTime();
QString streamingTimecode = Utils::nsToTimestamp(event.streamTime()); if (streamTime.has_value()) {
QString streamingTimecode = Utils::nsToTimestamp(streamTime.value());
obs_data_set_string(eventData, "stream-timecode", streamingTimecode.toUtf8().constData()); obs_data_set_string(eventData, "stream-timecode", streamingTimecode.toUtf8().constData());
} }
if (obs_frontend_recording_active()) { std::optional<uint64_t> recordingTime = event.recordingTime();
QString recordingTimecode = Utils::nsToTimestamp(event.recordingTime()); if (recordingTime.has_value()) {
QString recordingTimecode = Utils::nsToTimestamp(recordingTime.value());
obs_data_set_string(eventData, "rec-timecode", recordingTimecode.toUtf8().constData()); obs_data_set_string(eventData, "rec-timecode", recordingTimecode.toUtf8().constData());
} }

@ -20,7 +20,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
RpcEvent::RpcEvent( RpcEvent::RpcEvent(
const QString& updateType, const QString& updateType,
uint64_t streamTime, uint64_t recordingTime, std::optional<uint64_t> streamTime, std::optional<uint64_t> recordingTime,
obs_data_t* additionalFields obs_data_t* additionalFields
) : ) :
_updateType(updateType), _updateType(updateType),

@ -18,6 +18,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#pragma once #pragma once
#include <optional>
#include <obs-data.h> #include <obs-data.h>
#include <QtCore/QString> #include <QtCore/QString>
@ -28,7 +29,7 @@ class RpcEvent
public: public:
explicit RpcEvent( explicit RpcEvent(
const QString& updateType, const QString& updateType,
uint64_t streamTime, uint64_t recordingTime, std::optional<uint64_t> streamTime, std::optional<uint64_t> recordingTime,
obs_data_t* additionalFields = nullptr obs_data_t* additionalFields = nullptr
); );
@ -37,12 +38,12 @@ public:
return _updateType; return _updateType;
} }
const uint64_t streamTime() const const std::optional<uint64_t> streamTime() const
{ {
return _streamTime; return _streamTime;
} }
const uint64_t recordingTime() const const std::optional<uint64_t> recordingTime() const
{ {
return _recordingTime; return _recordingTime;
} }
@ -54,7 +55,7 @@ public:
private: private:
QString _updateType; QString _updateType;
uint64_t _streamTime; std::optional<uint64_t> _streamTime;
uint64_t _recordingTime; std::optional<uint64_t> _recordingTime;
OBSDataAutoRelease _additionalFields; OBSDataAutoRelease _additionalFields;
}; };