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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
set -e
@ -12,30 +12,79 @@ fi
echo "[obs-websocket] Preparing package build"
export QT_CELLAR_PREFIX="$(/usr/bin/find /usr/local/Cellar/qt -d 1 | sort -t '.' -k 1,1n -k 2,2n -k 3,3n | tail -n 1)"
export GIT_HASH=$(git rev-parse --short HEAD)
export GIT_BRANCH_OR_TAG=$(git name-rev --name-only HEAD | awk -F/ '{print $NF}')
GIT_HASH=$(git rev-parse --short HEAD)
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"
VERSION="$GIT_HASH-$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"
install_name_tool \
-add_rpath @executable_path/../Frameworks/QtWidgets.framework/Versions/5/ \
-add_rpath @executable_path/../Frameworks/QtGui.framework/Versions/5/ \
-add_rpath @executable_path/../Frameworks/QtCore.framework/Versions/5/ \
-change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @rpath/QtWidgets \
-change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @rpath/QtGui \
-change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
-change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets \
@executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets \
-change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui \
@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui \
-change /usr/local/opt/qt/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
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

View File

@ -17,7 +17,7 @@ PAGER="cat" sudo checkinstall -y --type=debian --fstrans=no --nodoc \
--pkglicense="GPLv2.0" --maintainer="stephane.lepin@gmail.com" \
--pkggroup="video" \
--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"
sudo chmod ao+r ../package/*

View File

@ -9,4 +9,4 @@ 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"
iscc ..\installer\installer.iss /O. /F"obs-websocket-%PackageVersion%-Windows-Installer"

View File

@ -1,12 +1,12 @@
cmake_minimum_required(VERSION 3.2)
project(obs-websocket)
cmake_minimum_required(VERSION 3.5)
project(obs-websocket VERSION 4.8.0)
set(CMAKE_PREFIX_PATH "${QTDIR}")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_definitions(-DASIO_STANDALONE)
@ -85,7 +85,8 @@ if(WIN32)
endif()
if(MSVC)
add_compile_options("/MP")
# Enable Multicore Builds and disable FH4 (to not depend on VCRUNTIME140_1.DLL)
add_definitions(/MP /d2FH4-)
endif()
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
@ -179,20 +180,18 @@ endif()
# --- Linux-specific build settings and tasks ---
if(UNIX AND NOT APPLE)
include(GNUInstallDirs)
set_target_properties(obs-websocket PROPERTIES PREFIX "")
target_link_libraries(obs-websocket obs-frontend-api)
file(GLOB locale_files data/locale/*.ini)
execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE UNAME_MACHINE)
install(TARGETS obs-websocket
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/obs-plugins")
# Dirty fix for Ubuntu
install(TARGETS obs-websocket
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/${UNAME_MACHINE}-linux-gnu/obs-plugins")
LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/obs-plugins")
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()
# --- End of section ---

View File

@ -5,7 +5,7 @@ WebSockets API for OBS Studio.
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
@ -20,21 +20,23 @@ It is **highly recommended** to protect obs-websocket with a password against un
### Possible use cases
- 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, ...)
### 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 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 :
- 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)
- 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.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
- 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` !
@ -46,19 +48,14 @@ See the [build instructions](BUILDING.md).
### Branches
The two main development branches are:
- `4.x-current`: actively-maintained codebase for 4.x releases. Backwards-compatible (unless stated otherwise) with existing clients until 5.0.
- `5.x`: upcoming 5.0 version
**New features and fixes must be based off and contributed to `4.x-current`**, as obs-websocket 5.0 is not in active development yet.
Development happens on `4.x-current`
### 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.
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

View File

@ -1,3 +1,14 @@
variables:
isReleaseMode: ${{ startsWith(variables['Build.SourceBranch'], 'refs/tags/') }}
trigger:
branches:
include:
- master
tags:
include:
- '*'
jobs:
- job: 'GenerateDocs'
condition: |
@ -149,8 +160,22 @@ jobs:
- script: ./CI/build-macos.sh
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
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:

View File

@ -10,7 +10,7 @@ OBSWebsocket.NotifyConnect.Message="Client %1 connected"
OBSWebsocket.NotifyDisconnect.Title="WebSocket client disconnected"
OBSWebsocket.NotifyDisconnect.Message="Client %1 disconnected"
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.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."

View File

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

View File

@ -99,6 +99,7 @@ auth_response = base64_encode(auth_response_hash)
+ [SourceFilterRemoved](#sourcefilterremoved)
+ [SourceFilterVisibilityChanged](#sourcefiltervisibilitychanged)
+ [SourceFiltersReordered](#sourcefiltersreordered)
* [Scene Items](#scene-items)
+ [SourceOrderChanged](#sourceorderchanged)
+ [SceneItemAdded](#sceneitemadded)
+ [SceneItemRemoved](#sceneitemremoved)
@ -148,7 +149,7 @@ auth_response = base64_encode(auth_response_hash)
+ [SetCurrentSceneCollection](#setcurrentscenecollection)
+ [GetCurrentSceneCollection](#getcurrentscenecollection)
+ [ListSceneCollections](#listscenecollections)
* [Scene Items](#scene-items)
* [Scene Items](#scene-items-1)
+ [GetSceneItemProperties](#getsceneitemproperties)
+ [SetSceneItemProperties](#setsceneitemproperties)
+ [ResetSceneItem](#resetsceneitem)
@ -1004,6 +1005,8 @@ Filters in a source have been reordered.
---
## Scene Items
### SourceOrderChanged
@ -1081,7 +1084,7 @@ An item's visibility has been toggled.
### SceneItemLockChanged
- Unreleased
- Added in v4.8.0
An item's locked status has been toggled.
@ -1404,7 +1407,7 @@ _No specified parameters._
### 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.
@ -1860,15 +1863,18 @@ Coordinates are relative to the item's parent (the scene or group it belongs to)
| Name | Type | Description |
| ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | the name of the scene that the source item belongs to. Defaults to the current scene. |
| `item` | _String_ | The name of the source. |
| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `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:**
| 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.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. |
@ -1909,8 +1915,10 @@ Coordinates are relative to the item's parent (the scene or group it belongs to)
| Name | Type | Description |
| ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | the name of the scene that the source item belongs to. Defaults to the current scene. |
| `item` | _String_ | The name of the source. |
| `scene-name` | _String (optional)_ | Name of the scene the source item belongs to. Defaults to the current scene. |
| `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.y` | _int (optional)_ | The new y position of the source. |
| `position.alignment` | _int (optional)_ | The new alignment of the source. |
@ -1946,8 +1954,10 @@ Reset a scene item.
| Name | Type | Description |
| ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | Name of the scene the source belongs to. Defaults to the current scene. |
| `item` | _String_ | Name of the source item. |
| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `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:**
@ -1968,9 +1978,9 @@ Show or hide a specified source item in a specified scene.
| 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 |
| `scene-name` | _String (optional)_ | Name of the scene where the source resides. Defaults to the currently active scene. |
**Response Items:**
@ -1991,8 +2001,8 @@ Sets the coordinates of a specified source item.
| Name | Type | Description |
| ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | The name of the scene that the source item belongs to. Defaults to the current scene. |
| `item` | _String_ | The name of the source item. |
| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `item` | _String_ | Scene Item name. |
| `x` | _double_ | X coordinate. |
| `y` | _double_ | Y coordinate. |
@ -2015,8 +2025,8 @@ Set the transform of the specified source item.
| Name | Type | Description |
| ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | The name of the scene that the source item belongs to. Defaults to the current scene. |
| `item` | _String_ | The name of the source item. |
| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `item` | _String_ | Scene Item name. |
| `x-scale` | _double_ | Width scale factor. |
| `y-scale` | _double_ | Height scale factor. |
| `rotation` | _double_ | Source item rotation (in degrees). |
@ -2040,8 +2050,8 @@ Sets the crop coordinates of the specified source item.
| Name | Type | Description |
| ---- | :---: | ------------|
| `scene-name` | _String (optional)_ | the name of the scene that the source item belongs to. Defaults to the current scene. |
| `item` | _String_ | The name of the source. |
| `scene-name` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `item` | _String_ | Scene Item name. |
| `top` | _int_ | Pixel position of the top 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. |
@ -2065,10 +2075,10 @@ Deletes a scene item.
| Name | Type | Description |
| ---- | :---: | ------------|
| `scene` | _String (optional)_ | Name of the scene the source belongs to. Defaults to the current scene. |
| `item` | _Object_ | item to delete (required) |
| `item.name` | _String_ | name of the scene item (prefer `id`, including both is acceptable). |
| `item.id` | _int_ | id of the scene item. |
| `scene` | _String (optional)_ | Name of the scene the scene item belongs to. Defaults to the current scene. |
| `item` | _Object_ | Scene item to delete (required) |
| `item.name` | _String_ | Scene Item name (prefer `id`, including both is acceptable). |
| `item.id` | _int_ | Scene Item ID. |
**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. |
| `toScene` | _String (optional)_ | Name of the scene to create the item in. Defaults to the current scene. |
| `item` | _Object_ | item to duplicate (required) |
| `item.name` | _String_ | name of the scene item (prefer `id`, including both is acceptable). |
| `item.id` | _int_ | id of the scene item. |
| `item` | _Object_ | Scene Item to duplicate from the source scene (required) |
| `item.name` | _String_ | Scene Item name (prefer `id`, including both is acceptable). |
| `item.id` | _int_ | Scene Item ID. |
**Response Items:**
@ -2166,7 +2176,7 @@ _No specified parameters._
| Name | Type | Description |
| ---- | :---: | ------------|
| `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
- **⚠️ Deprecated. Since 4.8.0. Prefer the use of GetSourceSettings. ⚠️**
- Added in v4.1.0
@ -2664,6 +2675,7 @@ Get current properties for a Browser Source.
### SetBrowserSourceProperties
- **⚠️ Deprecated. Since 4.8.0. Prefer the use of SetSourceSettings. ⚠️**
- Added in v4.1.0

View File

@ -2,7 +2,7 @@
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "obs-websocket"
#define MyAppVersion "4.7.0"
#define MyAppVersion "4.8.0"
#define MyAppPublisher "Stephane Lepin"
#define MyAppURL "http://github.com/Palakis/obs-websocket"

View File

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

View File

@ -39,11 +39,12 @@ namespace Utils {
obs_data_array_t* GetSceneItems(obs_source_t* source);
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* 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);
obs_data_t* GetSceneItemPropertiesData(obs_sceneitem_t* item);

View File

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

View File

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

View File

@ -6,10 +6,13 @@
* Gets the scene specific properties of the specified source item.
* Coordinates are relative to the item's parent (the scene or group it belongs to).
*
* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.
* @param {String} `item` The name of the source.
* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.
* @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.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.
@ -45,24 +48,25 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request)
return request.failed("missing request parameters");
}
QString itemName = obs_data_get_string(request.parameters(), "item");
if (itemName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSData params = 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);
if (!scene) {
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) {
return request.failed("specified scene item doesn't exist");
}
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);
}
@ -71,8 +75,10 @@ RpcResponse WSRequestHandler::GetSceneItemProperties(const RpcRequest& request)
* Sets the scene specific properties of a source. Unspecified properties will remain unchanged.
* Coordinates are relative to the item's parent (the scene or group it belongs to).
*
* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.
* @param {String} `item` The name of the source.
* @param {String (optional)} `scene-name` Name of the scene the source item belongs to. Defaults to the current scene.
* @param {String | Object} `item` Scene Item name (if this field is a string) or specification (if it is an object).
* @param {String (optional)} `item.name` Scene Item name (if the `item` field is an object)
* @param {int (optional)} `item.id` Scene Item ID (if the `item` field is an object)
* @param {int (optional)} `position.x` The new x position of the source.
* @param {int (optional)} `position.y` The new y position of the source.
* @param {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");
}
QString itemName = obs_data_get_string(request.parameters(), "item");
if (itemName.isEmpty()) {
return request.failed("invalid request parameters");
}
OBSData params = 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);
if (!scene) {
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) {
return request.failed("specified scene item doesn't exist");
}
@ -126,51 +129,59 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
vec2 oldPosition;
OBSDataAutoRelease positionError = obs_data_create();
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;
if (obs_data_has_user_value(reqPosition, "x")) {
newPosition.x = obs_data_get_int(reqPosition, "x");
}
if (obs_data_has_user_value(reqPosition, "y")) {
newPosition.y = obs_data_get_int(reqPosition, "y");
}
if (obs_data_has_user_value(reqPosition, "alignment")) {
const uint32_t alignment = obs_data_get_int(reqPosition, "alignment");
if (Utils::IsValidAlignment(alignment)) {
obs_sceneitem_set_alignment(sceneItem, alignment);
}
else {
} else {
badRequest = true;
obs_data_set_string(positionError, "alignment", "invalid");
obs_data_set_obj(errorData, "position", positionError);
}
}
obs_sceneitem_set_pos(sceneItem, &newPosition);
}
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")) {
vec2 oldScale;
obs_sceneitem_get_scale(sceneItem, &oldScale);
OBSDataAutoRelease reqScale = obs_data_get_obj(request.parameters(), "scale");
vec2 newScale = oldScale;
OBSDataAutoRelease reqScale = obs_data_get_obj(params, "scale");
if (obs_data_has_user_value(reqScale, "x")) {
newScale.x = obs_data_get_double(reqScale, "x");
}
if (obs_data_has_user_value(reqScale, "y")) {
newScale.y = obs_data_get_double(reqScale, "y");
}
obs_sceneitem_set_scale(sceneItem, &newScale);
}
if (request.hasField("crop")) {
obs_sceneitem_crop 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;
if (obs_data_has_user_value(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")) {
newCrop.left = obs_data_get_int(reqCrop, "left");
}
obs_sceneitem_set_crop(sceneItem, &newCrop);
}
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")) {
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")) {
bool badBounds = false;
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")) {
QString newBoundsType = obs_data_get_string(reqBounds, "type");
if (newBoundsType == "OBS_BOUNDS_NONE") {
@ -226,16 +239,20 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
obs_data_set_string(boundsError, "type", "invalid");
}
}
vec2 oldBounds;
obs_sceneitem_get_bounds(sceneItem, &oldBounds);
vec2 newBounds = oldBounds;
if (obs_data_has_user_value(reqBounds, "x")) {
newBounds.x = obs_data_get_double(reqBounds, "x");
}
if (obs_data_has_user_value(reqBounds, "y")) {
newBounds.y = obs_data_get_double(reqBounds, "y");
}
obs_sceneitem_set_bounds(sceneItem, &newBounds);
if (obs_data_has_user_value(reqBounds, "alignment")) {
const uint32_t bounds_alignment = obs_data_get_int(reqBounds, "alignment");
if (Utils::IsValidAlignment(bounds_alignment)) {
@ -246,6 +263,7 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
obs_data_set_string(boundsError, "alignment", "invalid");
}
}
if (badBounds) {
obs_data_set_obj(errorData, "bounds", boundsError);
}
@ -263,8 +281,10 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
/**
* Reset a scene item.
*
* @param {String (optional)} `scene-name` Name of the scene the source belongs to. Defaults to the current scene.
* @param {String} `item` Name of the source item.
* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.
* @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
* @name ResetSceneItem
@ -272,24 +292,20 @@ RpcResponse WSRequestHandler::SetSceneItemProperties(const RpcRequest& request)
* @since 4.2.0
*/
RpcResponse WSRequestHandler::ResetSceneItem(const RpcRequest& request) {
// TODO: remove this request, or refactor it to ResetSource
if (!request.hasField("item")) {
return request.failed("missing request parameters");
}
const char* itemName = obs_data_get_string(request.parameters(), "item");
if (!itemName) {
return request.failed("invalid request parameters");
}
OBSData params = 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);
if (!scene) {
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) {
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.
*
* @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 {String (optional)} `scene-name` Name of the scene where the source resides. Defaults to the currently active scene.
*
* @api requests
* @name SetSceneItemRender
@ -348,8 +364,8 @@ RpcResponse WSRequestHandler::SetSceneItemRender(const RpcRequest& request) {
/**
* 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} `item` The name of the source item.
* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.
* @param {String} `item` Scene Item name.
* @param {double} `x` X coordinate.
* @param {double} `y` Y coordinate.
@ -393,8 +409,8 @@ RpcResponse WSRequestHandler::SetSceneItemPosition(const RpcRequest& request) {
/**
* 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} `item` The name of the source item.
* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.
* @param {String} `item` Scene Item name.
* @param {double} `x-scale` Width scale factor.
* @param {double} `y-scale` Height scale factor.
* @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.
*
* @param {String (optional)} `scene-name` the name of the scene that the source item belongs to. Defaults to the current scene.
* @param {String} `item` The name of the source.
* @param {String (optional)} `scene-name` Name of the scene the scene item belongs to. Defaults to the current scene.
* @param {String} `item` Scene Item name.
* @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} `left` Pixel position of the left of the source item.
@ -496,10 +512,10 @@ RpcResponse WSRequestHandler::SetSceneItemCrop(const RpcRequest& request) {
/**
* Deletes a scene item.
*
* @param {String (optional)} `scene` Name of the scene the source belongs to. Defaults to the current scene.
* @param {Object} `item` item to delete (required)
* @param {String} `item.name` name of the scene item (prefer `id`, including both is acceptable).
* @param {int} `item.id` id of the scene item.
* @param {String (optional)} `scene` Name of the scene the scene item belongs to. Defaults to the current scene.
* @param {Object} `item` Scene item to delete (required)
* @param {String} `item.name` Scene Item name (prefer `id`, including both is acceptable).
* @param {int} `item.id` Scene Item ID.
*
* @api requests
* @name DeleteSceneItem
@ -517,8 +533,8 @@ RpcResponse WSRequestHandler::DeleteSceneItem(const RpcRequest& request) {
return request.failed("requested scene doesn't exist");
}
OBSDataAutoRelease item = obs_data_get_obj(request.parameters(), "item");
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromItem(scene, item);
OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item");
OBSSceneItemAutoRelease sceneItem = Utils::GetSceneItemFromRequestField(scene, itemField);
if (!sceneItem) {
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)} `toScene` Name of the scene to create the item in. Defaults to the current scene.
* @param {Object} `item` item to duplicate (required)
* @param {String} `item.name` name of the scene item (prefer `id`, including both is acceptable).
* @param {int} `item.id` id of the scene item.
* @param {Object} `item` Scene Item to duplicate from the source scene (required)
* @param {String} `item.name` Scene Item name (prefer `id`, including both is acceptable).
* @param {int} `item.id` Scene Item ID.
*
* @return {String} `scene` Name of the scene where the new item was created
* @return {Object} `item` New item info
@ -570,8 +586,8 @@ RpcResponse WSRequestHandler::DuplicateSceneItem(const RpcRequest& request) {
return request.failed("requested toScene doesn't exist");
}
OBSDataAutoRelease item = obs_data_get_obj(request.parameters(), "item");
OBSSceneItemAutoRelease referenceItem = Utils::GetSceneItemFromItem(fromScene, item);
OBSDataItemAutoRelease itemField = obs_data_item_byname(request.parameters(), "item");
OBSSceneItemAutoRelease referenceItem = Utils::GetSceneItemFromRequestField(fromScene, itemField);
if (!referenceItem) {
return request.failed("item with id/name combination not found in specified scene");
}

View File

@ -60,7 +60,7 @@ RpcResponse WSRequestHandler::GetCurrentScene(const RpcRequest& request) {
* Get a list of scenes in the currently active profile.
*
* @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
* @name GetSceneList

View File

@ -8,6 +8,16 @@
#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
*
@ -536,8 +546,8 @@ RpcResponse WSRequestHandler::GetTextGDIPlusProperties(const RpcRequest& request
return request.failed("specified source doesn't exist");
}
QString sourceId = obs_source_get_id(source);
if (sourceId != "text_gdiplus") {
QString sourceKind = obs_source_get_id(source);
if (!isTextGDIPlusSource(sourceKind)) {
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");
}
QString sourceId = obs_source_get_id(source);
if (sourceId != "text_gdiplus") {
QString sourceKind = obs_source_get_id(source);
if (!isTextGDIPlusSource(sourceKind)) {
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");
}
QString sourceId = obs_source_get_id(source);
if (sourceId != "text_ft2_source") {
QString sourceKind = obs_source_get_id(source);
if (!isTextFreeType2Source(sourceKind)) {
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");
}
QString sourceId = obs_source_get_id(source);
if (sourceId != "text_ft2_source") {
QString sourceKind = obs_source_get_id(source);
if (!isTextFreeType2Source(sourceKind)) {
return request.failed("not text freetype 2 source");
}
@ -909,6 +919,7 @@ RpcResponse WSRequestHandler::SetTextFreetype2Properties(const RpcRequest& reque
* @name GetBrowserSourceProperties
* @category sources
* @since 4.1.0
* @deprecated Since 4.8.0. Prefer the use of GetSourceSettings.
*/
RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& request)
{
@ -950,6 +961,7 @@ RpcResponse WSRequestHandler::GetBrowserSourceProperties(const RpcRequest& reque
* @api requests
* @name SetBrowserSourceProperties
* @category sources
* @deprecated Since 4.8.0. Prefer the use of SetSourceSettings.
* @since 4.1.0
*/
RpcResponse WSRequestHandler::SetBrowserSourceProperties(const RpcRequest& request)

View File

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

View File

@ -67,13 +67,15 @@ std::string OBSRemoteProtocol::encodeEvent(const RpcEvent& event)
QString updateType = event.updateType();
obs_data_set_string(eventData, "update-type", updateType.toUtf8().constData());
if (obs_frontend_streaming_active()) {
QString streamingTimecode = Utils::nsToTimestamp(event.streamTime());
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());
}
if (obs_frontend_recording_active()) {
QString recordingTimecode = Utils::nsToTimestamp(event.recordingTime());
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());
}

View File

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

View File

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