general: convert indents to tabs

This commit is contained in:
Stéphane Lepin 2018-10-19 18:22:34 +02:00
parent f2792c0b40
commit 85a52ab01f
32 changed files with 2733 additions and 2724 deletions

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
[*]
insert_final_newline = true
[*.{c,cpp,h,hpp}]
indent_style = tab
[*.{yml,yaml}]
indent_style = space
indent_size = 2

View File

@ -5,10 +5,10 @@ set -ex
mkdir -p build && cd build mkdir -p build && cd build
cmake .. \ cmake .. \
-DQTDIR=/usr/local/opt/qt \ -DQTDIR=/usr/local/opt/qt \
-DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \ -DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \
-DLIBOBS_LIB=../../obs-studio/libobs \ -DLIBOBS_LIB=../../obs-studio/libobs \
-DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \ -DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_INSTALL_PREFIX=/usr \
&& make -j4 && make -j4

View File

@ -11,13 +11,13 @@ npm run build
echo "-- Documentation successfully generated." echo "-- Documentation successfully generated."
if git diff --quiet; then if git diff --quiet; then
echo "-- No documentation changes to commit." echo "-- No documentation changes to commit."
exit 0 exit 0
fi fi
if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "4.3-maintenance" ]; then if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "4.3-maintenance" ]; then
echo "-- Skipping documentation deployment because this is either a pull request or a non-master branch." echo "-- Skipping documentation deployment because this is either a pull request or a non-master branch."
exit 0 exit 0
fi fi
REMOTE_URL="$(git config remote.origin.url)" REMOTE_URL="$(git config remote.origin.url)"

View File

@ -9,6 +9,6 @@ OBSLatestTag=$(git describe --tags --abbrev=0)
git checkout $OBSLatestTag git checkout $OBSLatestTag
mkdir build && cd build mkdir build && cd build
cmake .. \ cmake .. \
-DDISABLE_PLUGINS=true \ -DDISABLE_PLUGINS=true \
-DCMAKE_PREFIX_PATH=/usr/local/opt/qt/lib/cmake \ -DCMAKE_PREFIX_PATH=/usr/local/opt/qt/lib/cmake \
&& make -j4 && make -j4

View File

@ -19,63 +19,63 @@ set "BuildOBS="
REM Check the last tag successfully built by CI. REM Check the last tag successfully built by CI.
if exist C:\projects\obs-studio-last-tag-built.txt ( if exist C:\projects\obs-studio-last-tag-built.txt (
set /p OBSLastTagBuilt=<C:\projects\obs-studio-last-tag-built.txt set /p OBSLastTagBuilt=<C:\projects\obs-studio-last-tag-built.txt
) else ( ) else (
set OBSLastTagBuilt=0 set OBSLastTagBuilt=0
) )
REM If obs-studio directory exists, run git pull and get the latest tag number. REM If obs-studio directory exists, run git pull and get the latest tag number.
if exist C:\projects\obs-studio\ ( if exist C:\projects\obs-studio\ (
echo obs-studio directory exists echo obs-studio directory exists
echo Updating tag info echo Updating tag info
cd C:\projects\obs-studio\ cd C:\projects\obs-studio\
git describe --tags --abbrev=0 > C:\projects\latest-obs-studio-tag-pre-pull.txt git describe --tags --abbrev=0 > C:\projects\latest-obs-studio-tag-pre-pull.txt
set /p OBSLatestTagPrePull=<C:\projects\latest-obs-studio-tag-pre-pull.txt set /p OBSLatestTagPrePull=<C:\projects\latest-obs-studio-tag-pre-pull.txt
git checkout master git checkout master
git pull git pull
git describe --tags --abbrev=0 > C:\projects\latest-obs-studio-tag-post-pull.txt git describe --tags --abbrev=0 > C:\projects\latest-obs-studio-tag-post-pull.txt
set /p OBSLatestTagPostPull=<C:\projects\latest-obs-studio-tag-post-pull.txt set /p OBSLatestTagPostPull=<C:\projects\latest-obs-studio-tag-post-pull.txt
set /p OBSLatestTag=<C:\projects\latest-obs-studio-tag-post-pull.txt set /p OBSLatestTag=<C:\projects\latest-obs-studio-tag-post-pull.txt
echo %OBSLatestTagPostPull%> C:\projects\latest-obs-studio-tag.txt echo %OBSLatestTagPostPull%> C:\projects\latest-obs-studio-tag.txt
) )
REM Check the obs-studio tags for mismatches. REM Check the obs-studio tags for mismatches.
REM If a new tag was pulled, set the build flag. REM If a new tag was pulled, set the build flag.
if not %OBSLatestTagPrePull%==%OBSLatestTagPostPull% ( if not %OBSLatestTagPrePull%==%OBSLatestTagPostPull% (
echo Latest tag pre-pull: %OBSLatestTagPrePull% echo Latest tag pre-pull: %OBSLatestTagPrePull%
echo Latest tag post-pull: %OBSLatestTagPostPull% echo Latest tag post-pull: %OBSLatestTagPostPull%
echo Tags do not match. Need to rebuild OBS. echo Tags do not match. Need to rebuild OBS.
set BuildOBS=true set BuildOBS=true
) )
REM If the latest git tag doesn't match the last built tag, set the build flag. REM If the latest git tag doesn't match the last built tag, set the build flag.
if not %OBSLatestTagPostPull%==%OBSLastTagBuilt% ( if not %OBSLatestTagPostPull%==%OBSLastTagBuilt% (
echo Last built OBS tag: %OBSLastTagBuilt% echo Last built OBS tag: %OBSLastTagBuilt%
echo Latest tag post-pull: %OBSLatestTagPostPull% echo Latest tag post-pull: %OBSLatestTagPostPull%
echo Tags do not match. Need to rebuild OBS. echo Tags do not match. Need to rebuild OBS.
set BuildOBS=true set BuildOBS=true
) )
REM If obs-studio directory does not exist, clone the git repo, get the latest REM If obs-studio directory does not exist, clone the git repo, get the latest
REM tag number, and set the build flag. REM tag number, and set the build flag.
if not exist C:\projects\obs-studio ( if not exist C:\projects\obs-studio (
echo obs-studio directory does not exist echo obs-studio directory does not exist
git clone https://github.com/obsproject/obs-studio git clone https://github.com/obsproject/obs-studio
cd C:\projects\obs-studio\ cd C:\projects\obs-studio\
git describe --tags --abbrev=0 > C:\projects\obs-studio-latest-tag.txt git describe --tags --abbrev=0 > C:\projects\obs-studio-latest-tag.txt
set /p OBSLatestTag=<C:\projects\obs-studio-latest-tag.txt set /p OBSLatestTag=<C:\projects\obs-studio-latest-tag.txt
set BuildOBS=true set BuildOBS=true
) )
REM If the needed obs-studio libs for this build_config do not exist, REM If the needed obs-studio libs for this build_config do not exist,
REM set the build flag. REM set the build flag.
if not exist C:\projects\obs-studio\build32\libobs\%build_config%\obs.lib ( if not exist C:\projects\obs-studio\build32\libobs\%build_config%\obs.lib (
echo obs-studio\build32\libobs\%build_config%\obs.lib does not exist echo obs-studio\build32\libobs\%build_config%\obs.lib does not exist
set BuildOBS=true set BuildOBS=true
) )
if not exist C:\projects\obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib ( if not exist C:\projects\obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib (
echo obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib does not exist echo obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib does not exist
set BuildOBS=true set BuildOBS=true
) )
REM Some debug info REM Some debug info
@ -86,44 +86,44 @@ echo Latest tag: %OBSLatestTag%
echo Last built OBS tag: %OBSLastTagBuilt% echo Last built OBS tag: %OBSLastTagBuilt%
if defined BuildOBS ( if defined BuildOBS (
echo BuildOBS: true echo BuildOBS: true
) else ( ) else (
echo BuildOBS: false echo BuildOBS: false
) )
echo: echo:
REM If the build flag is set, build obs-studio. REM If the build flag is set, build obs-studio.
if defined BuildOBS ( if defined BuildOBS (
echo Building obs-studio... echo Building obs-studio...
echo git checkout %OBSLatestTag% echo git checkout %OBSLatestTag%
git checkout %OBSLatestTag% git checkout %OBSLatestTag%
echo: echo:
echo Removing previous build dirs... echo Removing previous build dirs...
if exist build rmdir /s /q C:\projects\obs-studio\build if exist build rmdir /s /q C:\projects\obs-studio\build
if exist build32 rmdir /s /q C:\projects\obs-studio\build32 if exist build32 rmdir /s /q C:\projects\obs-studio\build32
if exist build64 rmdir /s /q C:\projects\obs-studio\build64 if exist build64 rmdir /s /q C:\projects\obs-studio\build64
echo Making new build dirs... echo Making new build dirs...
mkdir build mkdir build
mkdir build32 mkdir build32
mkdir build64 mkdir build64
echo Running cmake for obs-studio %OBSLatestTag% 32-bit... echo Running cmake for obs-studio %OBSLatestTag% 32-bit...
cd ./build32 cd ./build32
cmake -G "Visual Studio 15 2017" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. cmake -G "Visual Studio 15 2017" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
echo: echo:
echo: echo:
echo Running cmake for obs-studio %OBSLatestTag% 64-bit... echo Running cmake for obs-studio %OBSLatestTag% 64-bit...
cd ../build64 cd ../build64
cmake -G "Visual Studio 15 2017 Win64" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true .. cmake -G "Visual Studio 15 2017 Win64" -DDISABLE_PLUGINS=true -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
echo: echo:
echo: echo:
echo Building obs-studio %OBSLatestTag% 32-bit ^(Build Config: %build_config%^)... echo Building obs-studio %OBSLatestTag% 32-bit ^(Build Config: %build_config%^)...
call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build32\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build32\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
echo Building obs-studio %OBSLatestTag% 64-bit ^(Build Config: %build_config%^)... echo Building obs-studio %OBSLatestTag% 64-bit ^(Build Config: %build_config%^)...
call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build64\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build64\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
cd .. cd ..
git describe --tags --abbrev=0 > C:\projects\obs-studio-last-tag-built.txt git describe --tags --abbrev=0 > C:\projects\obs-studio-last-tag-built.txt
set /p OBSLastTagBuilt=<C:\projects\obs-studio-last-tag-built.txt set /p OBSLastTagBuilt=<C:\projects\obs-studio-last-tag-built.txt
) else ( ) else (
echo Last OBS tag built is: %OBSLastTagBuilt% echo Last OBS tag built is: %OBSLastTagBuilt%
echo No need to rebuild OBS. echo No need to rebuild OBS.
) )

View File

@ -1,175 +1,175 @@
# Patches for Qt must be at the very least submitted to Qt's Gerrit codereview # Patches for Qt must be at the very least submitted to Qt's Gerrit codereview
# rather than their bug-report Jira. The latter is rarely reviewed by Qt. # rather than their bug-report Jira. The latter is rarely reviewed by Qt.
class Qt < Formula class Qt < Formula
desc "Cross-platform application and UI framework" desc "Cross-platform application and UI framework"
homepage "https://www.qt.io/" homepage "https://www.qt.io/"
url "https://download.qt.io/official_releases/qt/5.10/5.10.0/single/qt-everywhere-src-5.10.0.tar.xz" url "https://download.qt.io/official_releases/qt/5.10/5.10.0/single/qt-everywhere-src-5.10.0.tar.xz"
mirror "https://www.mirrorservice.org/sites/download.qt-project.org/official_releases/qt/5.10/5.10.0/single/qt-everywhere-opensource-src-5.10.0.tar.xz" mirror "https://www.mirrorservice.org/sites/download.qt-project.org/official_releases/qt/5.10/5.10.0/single/qt-everywhere-opensource-src-5.10.0.tar.xz"
sha256 "936d4cf5d577298f4f9fdb220e85b008ae321554a5fcd38072dc327a7296230e" sha256 "936d4cf5d577298f4f9fdb220e85b008ae321554a5fcd38072dc327a7296230e"
head "https://code.qt.io/qt/qt5.git", :branch => "5.10", :shallow => false head "https://code.qt.io/qt/qt5.git", :branch => "5.10", :shallow => false
bottle do bottle do
sha256 "332ab2f3eb7c13510f460c13d28a562db03297149a3615feb9e3d467fafde56c" => :high_sierra sha256 "332ab2f3eb7c13510f460c13d28a562db03297149a3615feb9e3d467fafde56c" => :high_sierra
sha256 "c93cf6ead1774cfa7a369c92c7d6a69154bc0dd5b48a0e7ddf3a78202c4a3dc5" => :sierra sha256 "c93cf6ead1774cfa7a369c92c7d6a69154bc0dd5b48a0e7ddf3a78202c4a3dc5" => :sierra
sha256 "44425e23d8b9c2b8b2f50d850ca94dcd411cd89e20e057c8d6505c9056d06328" => :el_capitan sha256 "44425e23d8b9c2b8b2f50d850ca94dcd411cd89e20e057c8d6505c9056d06328" => :el_capitan
end end
keg_only "Qt 5 has CMake issues when linked" keg_only "Qt 5 has CMake issues when linked"
option "with-docs", "Build documentation" option "with-docs", "Build documentation"
option "with-examples", "Build examples" option "with-examples", "Build examples"
deprecated_option "with-mysql" => "with-mysql-client" deprecated_option "with-mysql" => "with-mysql-client"
# OS X 10.7 Lion is still supported in Qt 5.5, but is no longer a reference # OS X 10.7 Lion is still supported in Qt 5.5, but is no longer a reference
# configuration and thus untested in practice. Builds on OS X 10.7 have been # configuration and thus untested in practice. Builds on OS X 10.7 have been
# reported to fail: <https://github.com/Homebrew/homebrew/issues/45284>. # reported to fail: <https://github.com/Homebrew/homebrew/issues/45284>.
depends_on :macos => :mountain_lion depends_on :macos => :mountain_lion
depends_on "pkg-config" => :build depends_on "pkg-config" => :build
depends_on :xcode => :build depends_on :xcode => :build
depends_on "mysql-client" => :optional depends_on "mysql-client" => :optional
depends_on "postgresql" => :optional depends_on "postgresql" => :optional
# Restore `.pc` files for framework-based build of Qt 5 on OS X. This # Restore `.pc` files for framework-based build of Qt 5 on OS X. This
# partially reverts <https://codereview.qt-project.org/#/c/140954/> merged # partially reverts <https://codereview.qt-project.org/#/c/140954/> merged
# between the 5.5.1 and 5.6.0 releases. (Remove this as soon as feasible!) # between the 5.5.1 and 5.6.0 releases. (Remove this as soon as feasible!)
# #
# Core formulae known to fail without this patch (as of 2016-10-15): # Core formulae known to fail without this patch (as of 2016-10-15):
# * gnuplot (with `--with-qt` option) # * gnuplot (with `--with-qt` option)
# * mkvtoolnix (with `--with-qt` option, silent build failure) # * mkvtoolnix (with `--with-qt` option, silent build failure)
# * poppler (with `--with-qt` option) # * poppler (with `--with-qt` option)
patch do patch do
url "https://raw.githubusercontent.com/Homebrew/formula-patches/e8fe6567/qt5/restore-pc-files.patch" url "https://raw.githubusercontent.com/Homebrew/formula-patches/e8fe6567/qt5/restore-pc-files.patch"
sha256 "48ff18be2f4050de7288bddbae7f47e949512ac4bcd126c2f504be2ac701158b" sha256 "48ff18be2f4050de7288bddbae7f47e949512ac4bcd126c2f504be2ac701158b"
end end
# Fix compile error on macOS 10.13 around QFixed: # Fix compile error on macOS 10.13 around QFixed:
# https://github.com/Homebrew/homebrew-core/issues/27095 # https://github.com/Homebrew/homebrew-core/issues/27095
# https://bugreports.qt.io/browse/QTBUG-67545 # https://bugreports.qt.io/browse/QTBUG-67545
patch do patch do
url "https://raw.githubusercontent.com/z00m1n/formula-patches/0de0e229/qt/QTBUG-67545.patch" url "https://raw.githubusercontent.com/z00m1n/formula-patches/0de0e229/qt/QTBUG-67545.patch"
sha256 "4a115097c7582c7dce4207f5500d13feb8c990eb8a05a43f41953985976ebe6c" sha256 "4a115097c7582c7dce4207f5500d13feb8c990eb8a05a43f41953985976ebe6c"
end end
# Fix compile error on macOS 10.13 caused by qtlocation dependency # Fix compile error on macOS 10.13 caused by qtlocation dependency
# mapbox-gl-native using Boost 1.62.0 does not build with C++ 17: # mapbox-gl-native using Boost 1.62.0 does not build with C++ 17:
# https://github.com/Homebrew/homebrew-core/issues/27095 # https://github.com/Homebrew/homebrew-core/issues/27095
# https://bugreports.qt.io/browse/QTBUG-67810 # https://bugreports.qt.io/browse/QTBUG-67810
patch do patch do
url "https://raw.githubusercontent.com/z00m1n/formula-patches/a1a1f0dd/qt/QTBUG-67810.patch" url "https://raw.githubusercontent.com/z00m1n/formula-patches/a1a1f0dd/qt/QTBUG-67810.patch"
sha256 "8ee0bf71df1043f08ebae3aa35036be29c4d9ebff8a27e3b0411a6bd635e9382" sha256 "8ee0bf71df1043f08ebae3aa35036be29c4d9ebff8a27e3b0411a6bd635e9382"
end end
# Remove for > 5.10.0 # Remove for > 5.10.0
# Fix "error: 'loadFileURL:allowingReadAccessToURL:' is only available on # Fix "error: 'loadFileURL:allowingReadAccessToURL:' is only available on
# macOS 10.11 or newer [-Werror,-Wunguarded-availability]" # macOS 10.11 or newer [-Werror,-Wunguarded-availability]"
# Reported 8 Dec 2017 https://bugreports.qt.io/browse/QTBUG-65075 # Reported 8 Dec 2017 https://bugreports.qt.io/browse/QTBUG-65075
# Equivalent to upstream fix from 8 Dec 2017 https://codereview.qt-project.org/#/c/213993/ # Equivalent to upstream fix from 8 Dec 2017 https://codereview.qt-project.org/#/c/213993/
if MacOS::Xcode.version >= "9.0" if MacOS::Xcode.version >= "9.0"
patch do patch do
url "https://raw.githubusercontent.com/Homebrew/formula-patches/9c97726e2b153099049326ade23fe24b52b778fe/qt/QTBUG-65075.diff" url "https://raw.githubusercontent.com/Homebrew/formula-patches/9c97726e2b153099049326ade23fe24b52b778fe/qt/QTBUG-65075.diff"
sha256 "a51595868c6173ab53463107e0ee3355576002c32ab80897587c3607589cfd22" sha256 "a51595868c6173ab53463107e0ee3355576002c32ab80897587c3607589cfd22"
end end
end end
def install def install
args = %W[ args = %W[
-verbose -verbose
-prefix #{prefix} -prefix #{prefix}
-release -release
-opensource -confirm-license -opensource -confirm-license
-system-zlib -system-zlib
-qt-libpng -qt-libpng
-qt-libjpeg -qt-libjpeg
-qt-freetype -qt-freetype
-qt-pcre -qt-pcre
-nomake tests -nomake tests
-no-rpath -no-rpath
-pkg-config -pkg-config
-dbus-runtime -dbus-runtime
-no-assimp -no-assimp
] ]
args << "-nomake" << "examples" if build.without? "examples" args << "-nomake" << "examples" if build.without? "examples"
if build.with? "mysql-client" if build.with? "mysql-client"
args << "-plugin-sql-mysql" args << "-plugin-sql-mysql"
(buildpath/"brew_shim/mysql_config").write <<~EOS (buildpath/"brew_shim/mysql_config").write <<~EOS
#!/bin/sh #!/bin/sh
if [ x"$1" = x"--libs" ]; then if [ x"$1" = x"--libs" ]; then
mysql_config --libs | sed "s/-lssl -lcrypto//" mysql_config --libs | sed "s/-lssl -lcrypto//"
else else
exec mysql_config "$@" exec mysql_config "$@"
fi fi
EOS EOS
chmod 0755, "brew_shim/mysql_config" chmod 0755, "brew_shim/mysql_config"
args << "-mysql_config" << buildpath/"brew_shim/mysql_config" args << "-mysql_config" << buildpath/"brew_shim/mysql_config"
end end
args << "-plugin-sql-psql" if build.with? "postgresql" args << "-plugin-sql-psql" if build.with? "postgresql"
system "./configure", *args system "./configure", *args
system "make" system "make"
ENV.deparallelize ENV.deparallelize
system "make", "install" system "make", "install"
if build.with? "docs" if build.with? "docs"
system "make", "docs" system "make", "docs"
system "make", "install_docs" system "make", "install_docs"
end end
# Some config scripts will only find Qt in a "Frameworks" folder # Some config scripts will only find Qt in a "Frameworks" folder
frameworks.install_symlink Dir["#{lib}/*.framework"] frameworks.install_symlink Dir["#{lib}/*.framework"]
# The pkg-config files installed suggest that headers can be found in the # The pkg-config files installed suggest that headers can be found in the
# `include` directory. Make this so by creating symlinks from `include` to # `include` directory. Make this so by creating symlinks from `include` to
# the Frameworks' Headers folders. # the Frameworks' Headers folders.
Pathname.glob("#{lib}/*.framework/Headers") do |path| Pathname.glob("#{lib}/*.framework/Headers") do |path|
include.install_symlink path => path.parent.basename(".framework") include.install_symlink path => path.parent.basename(".framework")
end end
# Move `*.app` bundles into `libexec` to expose them to `brew linkapps` and # Move `*.app` bundles into `libexec` to expose them to `brew linkapps` and
# because we don't like having them in `bin`. # because we don't like having them in `bin`.
# (Note: This move breaks invocation of Assistant via the Help menu # (Note: This move breaks invocation of Assistant via the Help menu
# of both Designer and Linguist as that relies on Assistant being in `bin`.) # of both Designer and Linguist as that relies on Assistant being in `bin`.)
libexec.mkpath libexec.mkpath
Pathname.glob("#{bin}/*.app") { |app| mv app, libexec } Pathname.glob("#{bin}/*.app") { |app| mv app, libexec }
end end
def caveats; <<~EOS def caveats; <<~EOS
We agreed to the Qt opensource license for you. We agreed to the Qt opensource license for you.
If this is unacceptable you should uninstall. If this is unacceptable you should uninstall.
EOS EOS
end end
test do test do
(testpath/"hello.pro").write <<~EOS (testpath/"hello.pro").write <<~EOS
QT += core QT += core
QT -= gui QT -= gui
TARGET = hello TARGET = hello
CONFIG += console CONFIG += console
CONFIG -= app_bundle CONFIG -= app_bundle
TEMPLATE = app TEMPLATE = app
SOURCES += main.cpp SOURCES += main.cpp
EOS EOS
(testpath/"main.cpp").write <<~EOS (testpath/"main.cpp").write <<~EOS
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication a(argc, argv); QCoreApplication a(argc, argv);
qDebug() << "Hello World!"; qDebug() << "Hello World!";
return 0; return 0;
} }
EOS EOS
system bin/"qmake", testpath/"hello.pro" system bin/"qmake", testpath/"hello.pro"
system "make" system "make"
assert_predicate testpath/"hello", :exist? assert_predicate testpath/"hello", :exist?
assert_predicate testpath/"main.o", :exist? assert_predicate testpath/"main.o", :exist?
system "./hello" system "./hello"
end end
end end

View File

@ -42,13 +42,13 @@ Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
// following function come from https://github.com/Xaymar/obs-studio_amf-encoder-plugin/blob/master/%23Resources/Installer.in.iss#L45 // following function come from https://github.com/Xaymar/obs-studio_amf-encoder-plugin/blob/master/%23Resources/Installer.in.iss#L45
function GetDirName(Value: string): string; function GetDirName(Value: string): string;
var var
InstallPath: string; InstallPath: string;
begin begin
// initialize default path, which will be returned when the following registry // initialize default path, which will be returned when the following registry
// key queries fail due to missing keys or for some different reason // key queries fail due to missing keys or for some different reason
Result := '{pf}\obs-studio'; Result := '{pf}\obs-studio';
// query the first registry value; if this succeeds, return the obtained value // query the first registry value; if this succeeds, return the obtained value
if RegQueryStringValue(HKLM32, 'SOFTWARE\OBS Studio', '', InstallPath) then if RegQueryStringValue(HKLM32, 'SOFTWARE\OBS Studio', '', InstallPath) then
Result := InstallPath Result := InstallPath
end; end;

View File

@ -39,158 +39,158 @@ with this program. If not, see <https://www.gnu.org/licenses/>
Config* Config::_instance = new Config(); Config* Config::_instance = new Config();
Config::Config() : Config::Config() :
ServerEnabled(true), ServerEnabled(true),
ServerPort(4444), ServerPort(4444),
DebugEnabled(false), DebugEnabled(false),
AlertsEnabled(true), AlertsEnabled(true),
AuthRequired(false), AuthRequired(false),
Secret(""), Secret(""),
Salt(""), Salt(""),
SettingsLoaded(false) SettingsLoaded(false)
{ {
// OBS Config defaults // OBS Config defaults
config_t* obsConfig = obs_frontend_get_global_config(); config_t* obsConfig = obs_frontend_get_global_config();
if (obsConfig) { if (obsConfig) {
config_set_default_bool(obsConfig, config_set_default_bool(obsConfig,
SECTION_NAME, PARAM_ENABLE, ServerEnabled); SECTION_NAME, PARAM_ENABLE, ServerEnabled);
config_set_default_uint(obsConfig, config_set_default_uint(obsConfig,
SECTION_NAME, PARAM_PORT, ServerPort); SECTION_NAME, PARAM_PORT, ServerPort);
config_set_default_bool(obsConfig, config_set_default_bool(obsConfig,
SECTION_NAME, PARAM_DEBUG, DebugEnabled); SECTION_NAME, PARAM_DEBUG, DebugEnabled);
config_set_default_bool(obsConfig, config_set_default_bool(obsConfig,
SECTION_NAME, PARAM_ALERT, AlertsEnabled); SECTION_NAME, PARAM_ALERT, AlertsEnabled);
config_set_default_bool(obsConfig, config_set_default_bool(obsConfig,
SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired); SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_default_string(obsConfig, config_set_default_string(obsConfig,
SECTION_NAME, PARAM_SECRET, QT_TO_UTF8(Secret)); SECTION_NAME, PARAM_SECRET, QT_TO_UTF8(Secret));
config_set_default_string(obsConfig, config_set_default_string(obsConfig,
SECTION_NAME, PARAM_SALT, QT_TO_UTF8(Salt)); SECTION_NAME, PARAM_SALT, QT_TO_UTF8(Salt));
} }
mbedtls_entropy_init(&entropy); mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&rng); mbedtls_ctr_drbg_init(&rng);
mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &entropy, nullptr, 0); mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &entropy, nullptr, 0);
SessionChallenge = GenerateSalt(); SessionChallenge = GenerateSalt();
} }
Config::~Config() { Config::~Config() {
mbedtls_ctr_drbg_free(&rng); mbedtls_ctr_drbg_free(&rng);
mbedtls_entropy_free(&entropy); mbedtls_entropy_free(&entropy);
} }
void Config::Load() { void Config::Load() {
config_t* obsConfig = obs_frontend_get_global_config(); config_t* obsConfig = obs_frontend_get_global_config();
ServerEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ENABLE); ServerEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ENABLE);
ServerPort = config_get_uint(obsConfig, SECTION_NAME, PARAM_PORT); ServerPort = config_get_uint(obsConfig, SECTION_NAME, PARAM_PORT);
DebugEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_DEBUG); DebugEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_DEBUG);
AlertsEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ALERT); AlertsEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ALERT);
AuthRequired = config_get_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED); AuthRequired = config_get_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED);
Secret = config_get_string(obsConfig, SECTION_NAME, PARAM_SECRET); Secret = config_get_string(obsConfig, SECTION_NAME, PARAM_SECRET);
Salt = config_get_string(obsConfig, SECTION_NAME, PARAM_SALT); Salt = config_get_string(obsConfig, SECTION_NAME, PARAM_SALT);
} }
void Config::Save() { void Config::Save() {
config_t* obsConfig = obs_frontend_get_global_config(); config_t* obsConfig = obs_frontend_get_global_config();
config_set_bool(obsConfig, SECTION_NAME, PARAM_ENABLE, ServerEnabled); config_set_bool(obsConfig, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
config_set_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort); config_set_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort);
config_set_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled); config_set_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
config_set_bool(obsConfig, SECTION_NAME, PARAM_ALERT, AlertsEnabled); config_set_bool(obsConfig, SECTION_NAME, PARAM_ALERT, AlertsEnabled);
config_set_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired); config_set_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_string(obsConfig, SECTION_NAME, PARAM_SECRET, config_set_string(obsConfig, SECTION_NAME, PARAM_SECRET,
QT_TO_UTF8(Secret)); QT_TO_UTF8(Secret));
config_set_string(obsConfig, SECTION_NAME, PARAM_SALT, config_set_string(obsConfig, SECTION_NAME, PARAM_SALT,
QT_TO_UTF8(Salt)); QT_TO_UTF8(Salt));
config_save(obsConfig); config_save(obsConfig);
} }
QString Config::GenerateSalt() { QString Config::GenerateSalt() {
// Generate 32 random chars // Generate 32 random chars
unsigned char* randomChars = (unsigned char*)bzalloc(32); unsigned char* randomChars = (unsigned char*)bzalloc(32);
mbedtls_ctr_drbg_random(&rng, randomChars, 32); mbedtls_ctr_drbg_random(&rng, randomChars, 32);
// Convert the 32 random chars to a base64 string // Convert the 32 random chars to a base64 string
char* salt = (char*)bzalloc(64); char* salt = (char*)bzalloc(64);
size_t saltBytes; size_t saltBytes;
mbedtls_base64_encode( mbedtls_base64_encode(
(unsigned char*)salt, 64, &saltBytes, (unsigned char*)salt, 64, &saltBytes,
randomChars, 32); randomChars, 32);
bfree(randomChars); bfree(randomChars);
return salt; return salt;
} }
QString Config::GenerateSecret(QString password, QString salt) { QString Config::GenerateSecret(QString password, QString salt) {
// Concatenate the password and the salt // Concatenate the password and the salt
QString passAndSalt = ""; QString passAndSalt = "";
passAndSalt += password; passAndSalt += password;
passAndSalt += salt; passAndSalt += salt;
// Generate a SHA256 hash of the password // Generate a SHA256 hash of the password
unsigned char* challengeHash = (unsigned char*)bzalloc(32); unsigned char* challengeHash = (unsigned char*)bzalloc(32);
mbedtls_sha256( mbedtls_sha256(
(unsigned char*)passAndSalt.toUtf8().constData(), passAndSalt.length(), (unsigned char*)passAndSalt.toUtf8().constData(), passAndSalt.length(),
challengeHash, 0); challengeHash, 0);
// Encode SHA256 hash to Base64 // Encode SHA256 hash to Base64
char* challenge = (char*)bzalloc(64); char* challenge = (char*)bzalloc(64);
size_t challengeBytes = 0; size_t challengeBytes = 0;
mbedtls_base64_encode( mbedtls_base64_encode(
(unsigned char*)challenge, 64, &challengeBytes, (unsigned char*)challenge, 64, &challengeBytes,
challengeHash, 32); challengeHash, 32);
bfree(challengeHash); bfree(challengeHash);
return challenge; return challenge;
} }
void Config::SetPassword(QString password) { void Config::SetPassword(QString password) {
QString newSalt = GenerateSalt(); QString newSalt = GenerateSalt();
QString newChallenge = GenerateSecret(password, newSalt); QString newChallenge = GenerateSecret(password, newSalt);
this->Salt = newSalt; this->Salt = newSalt;
this->Secret = newChallenge; this->Secret = newChallenge;
} }
bool Config::CheckAuth(QString response) { bool Config::CheckAuth(QString response) {
// Concatenate auth secret with the challenge sent to the user // Concatenate auth secret with the challenge sent to the user
QString challengeAndResponse = ""; QString challengeAndResponse = "";
challengeAndResponse += Secret; challengeAndResponse += Secret;
challengeAndResponse += SessionChallenge; challengeAndResponse += SessionChallenge;
// Generate a SHA256 hash of challengeAndResponse // Generate a SHA256 hash of challengeAndResponse
unsigned char* hash = (unsigned char*)bzalloc(32); unsigned char* hash = (unsigned char*)bzalloc(32);
mbedtls_sha256( mbedtls_sha256(
(unsigned char*)challengeAndResponse.toUtf8().constData(), (unsigned char*)challengeAndResponse.toUtf8().constData(),
challengeAndResponse.length(), challengeAndResponse.length(),
hash, 0); hash, 0);
// Encode the SHA256 hash to Base64 // Encode the SHA256 hash to Base64
char* expectedResponse = (char*)bzalloc(64); char* expectedResponse = (char*)bzalloc(64);
size_t base64_size = 0; size_t base64_size = 0;
mbedtls_base64_encode( mbedtls_base64_encode(
(unsigned char*)expectedResponse, 64, &base64_size, (unsigned char*)expectedResponse, 64, &base64_size,
hash, 32); hash, 32);
bool authSuccess = false; bool authSuccess = false;
if (response == QString(expectedResponse)) { if (response == QString(expectedResponse)) {
SessionChallenge = GenerateSalt(); SessionChallenge = GenerateSalt();
authSuccess = true; authSuccess = true;
} }
bfree(hash); bfree(hash);
bfree(expectedResponse); bfree(expectedResponse);
return authSuccess; return authSuccess;
} }
Config* Config::Current() { Config* Config::Current() {
return _instance; return _instance;
} }

View File

@ -25,36 +25,36 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <mbedtls/ctr_drbg.h> #include <mbedtls/ctr_drbg.h>
class Config { class Config {
public: public:
Config(); Config();
~Config(); ~Config();
void Load(); void Load();
void Save(); void Save();
void SetPassword(QString password); void SetPassword(QString password);
bool CheckAuth(QString userChallenge); bool CheckAuth(QString userChallenge);
QString GenerateSalt(); QString GenerateSalt();
static QString GenerateSecret( static QString GenerateSecret(
QString password, QString salt); QString password, QString salt);
bool ServerEnabled; bool ServerEnabled;
uint64_t ServerPort; uint64_t ServerPort;
bool DebugEnabled; bool DebugEnabled;
bool AlertsEnabled; bool AlertsEnabled;
bool AuthRequired; bool AuthRequired;
QString Secret; QString Secret;
QString Salt; QString Salt;
QString SessionChallenge; QString SessionChallenge;
bool SettingsLoaded; bool SettingsLoaded;
static Config* Current(); static Config* Current();
private: private:
static Config* _instance; static Config* _instance;
mbedtls_entropy_context entropy; mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context rng; mbedtls_ctr_drbg_context rng;
}; };
#endif // CONFIG_H #endif // CONFIG_H

View File

@ -29,487 +29,487 @@ with this program. If not, see <https://www.gnu.org/licenses/>
Q_DECLARE_METATYPE(OBSScene); Q_DECLARE_METATYPE(OBSScene);
obs_data_array_t* Utils::StringListToArray(char** strings, char* key) { obs_data_array_t* Utils::StringListToArray(char** strings, char* key) {
if (!strings) if (!strings)
return obs_data_array_create(); return obs_data_array_create();
obs_data_array_t* list = obs_data_array_create(); obs_data_array_t* list = obs_data_array_create();
char* value = ""; char* value = "";
for (int i = 0; value != nullptr; i++) { for (int i = 0; value != nullptr; i++) {
value = strings[i]; value = strings[i];
OBSDataAutoRelease item = obs_data_create(); OBSDataAutoRelease item = obs_data_create();
obs_data_set_string(item, key, value); obs_data_set_string(item, key, value);
if (value) if (value)
obs_data_array_push_back(list, item); obs_data_array_push_back(list, item);
} }
return list; return list;
} }
obs_data_array_t* Utils::GetSceneItems(obs_source_t* source) { obs_data_array_t* Utils::GetSceneItems(obs_source_t* source) {
obs_data_array_t* items = obs_data_array_create(); obs_data_array_t* items = obs_data_array_create();
OBSScene scene = obs_scene_from_source(source); OBSScene scene = obs_scene_from_source(source);
if (!scene) if (!scene)
return nullptr; return nullptr;
obs_scene_enum_items(scene, []( obs_scene_enum_items(scene, [](
obs_scene_t* scene, obs_scene_t* scene,
obs_sceneitem_t* currentItem, obs_sceneitem_t* currentItem,
void* param) void* param)
{ {
obs_data_array_t* data = static_cast<obs_data_array_t*>(param); obs_data_array_t* data = static_cast<obs_data_array_t*>(param);
OBSDataAutoRelease itemData = GetSceneItemData(currentItem); OBSDataAutoRelease itemData = GetSceneItemData(currentItem);
obs_data_array_insert(data, 0, itemData); obs_data_array_insert(data, 0, itemData);
return true; return true;
}, items); }, items);
return items; return items;
} }
obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) { obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
if (!item) if (!item)
return nullptr; return nullptr;
vec2 pos; vec2 pos;
obs_sceneitem_get_pos(item, &pos); obs_sceneitem_get_pos(item, &pos);
vec2 scale; vec2 scale;
obs_sceneitem_get_scale(item, &scale); obs_sceneitem_get_scale(item, &scale);
// obs_sceneitem_get_source doesn't increase the refcount // obs_sceneitem_get_source doesn't increase the refcount
OBSSource itemSource = obs_sceneitem_get_source(item); OBSSource itemSource = obs_sceneitem_get_source(item);
float item_width = float(obs_source_get_width(itemSource)); float item_width = float(obs_source_get_width(itemSource));
float item_height = float(obs_source_get_height(itemSource)); float item_height = float(obs_source_get_height(itemSource));
obs_data_t* data = obs_data_create(); obs_data_t* data = obs_data_create();
obs_data_set_string(data, "name", obs_data_set_string(data, "name",
obs_source_get_name(obs_sceneitem_get_source(item))); obs_source_get_name(obs_sceneitem_get_source(item)));
obs_data_set_string(data, "type", obs_data_set_string(data, "type",
obs_source_get_id(obs_sceneitem_get_source(item))); obs_source_get_id(obs_sceneitem_get_source(item)));
obs_data_set_double(data, "volume", obs_data_set_double(data, "volume",
obs_source_get_volume(obs_sceneitem_get_source(item))); obs_source_get_volume(obs_sceneitem_get_source(item)));
obs_data_set_double(data, "x", pos.x); obs_data_set_double(data, "x", pos.x);
obs_data_set_double(data, "y", pos.y); obs_data_set_double(data, "y", pos.y);
obs_data_set_int(data, "source_cx", (int)item_width); obs_data_set_int(data, "source_cx", (int)item_width);
obs_data_set_int(data, "source_cy", (int)item_height); obs_data_set_int(data, "source_cy", (int)item_height);
obs_data_set_double(data, "cx", item_width* scale.x); obs_data_set_double(data, "cx", item_width* scale.x);
obs_data_set_double(data, "cy", item_height* scale.y); obs_data_set_double(data, "cy", item_height* scale.y);
obs_data_set_bool(data, "render", obs_sceneitem_visible(item)); obs_data_set_bool(data, "render", obs_sceneitem_visible(item));
return data; return data;
} }
obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, QString name) { obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, QString name) {
struct current_search { struct current_search {
QString query; QString query;
obs_sceneitem_t* result; obs_sceneitem_t* result;
}; };
current_search search; current_search search;
search.query = name; search.query = name;
search.result = nullptr; search.result = nullptr;
OBSScene scene = obs_scene_from_source(source); OBSScene scene = obs_scene_from_source(source);
if (!scene) if (!scene)
return nullptr; return nullptr;
obs_scene_enum_items(scene, []( obs_scene_enum_items(scene, [](
obs_scene_t* scene, obs_scene_t* scene,
obs_sceneitem_t* currentItem, obs_sceneitem_t* currentItem,
void* param) void* param)
{ {
current_search* search = static_cast<current_search*>(param); current_search* search = static_cast<current_search*>(param);
QString currentItemName = QString currentItemName =
obs_source_get_name(obs_sceneitem_get_source(currentItem)); obs_source_get_name(obs_sceneitem_get_source(currentItem));
if (currentItemName == search->query) { if (currentItemName == search->query) {
search->result = currentItem; search->result = currentItem;
obs_sceneitem_addref(search->result); obs_sceneitem_addref(search->result);
return false; return false;
} }
return true; return true;
}, &search); }, &search);
return search.result; return search.result;
} }
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:
case OBS_ALIGN_LEFT: case OBS_ALIGN_LEFT:
case OBS_ALIGN_RIGHT: case OBS_ALIGN_RIGHT:
case OBS_ALIGN_TOP: case OBS_ALIGN_TOP:
case OBS_ALIGN_BOTTOM: case OBS_ALIGN_BOTTOM:
case OBS_ALIGN_TOP | OBS_ALIGN_LEFT: case OBS_ALIGN_TOP | OBS_ALIGN_LEFT:
case OBS_ALIGN_TOP | OBS_ALIGN_RIGHT: case OBS_ALIGN_TOP | OBS_ALIGN_RIGHT:
case OBS_ALIGN_BOTTOM | OBS_ALIGN_LEFT: case OBS_ALIGN_BOTTOM | OBS_ALIGN_LEFT:
case OBS_ALIGN_BOTTOM | OBS_ALIGN_RIGHT: { case OBS_ALIGN_BOTTOM | OBS_ALIGN_RIGHT: {
return true; return true;
} }
} }
return false; return false;
} }
obs_source_t* Utils::GetTransitionFromName(QString searchName) { obs_source_t* Utils::GetTransitionFromName(QString searchName) {
obs_source_t* foundTransition = nullptr; obs_source_t* foundTransition = nullptr;
obs_frontend_source_list transition_list = {}; obs_frontend_source_list transition_list = {};
obs_frontend_get_transitions(&transition_list); obs_frontend_get_transitions(&transition_list);
for (size_t i = 0; i < transition_list.sources.num; i++) { for (size_t i = 0; i < transition_list.sources.num; i++) {
obs_source_t* transition = transition_list.sources.array[i]; obs_source_t* transition = transition_list.sources.array[i];
QString transitionName = obs_source_get_name(transition); QString transitionName = obs_source_get_name(transition);
if (transitionName == searchName) { if (transitionName == searchName) {
foundTransition = transition; foundTransition = transition;
obs_source_addref(foundTransition); obs_source_addref(foundTransition);
break; break;
} }
} }
obs_frontend_source_list_free(&transition_list); obs_frontend_source_list_free(&transition_list);
return foundTransition; return foundTransition;
} }
obs_source_t* Utils::GetSceneFromNameOrCurrent(QString sceneName) { obs_source_t* Utils::GetSceneFromNameOrCurrent(QString sceneName) {
// Both obs_frontend_get_current_scene() and obs_get_source_by_name() // Both obs_frontend_get_current_scene() and obs_get_source_by_name()
// do addref on the return source, so no need to use an OBSSource helper // do addref on the return source, so no need to use an OBSSource helper
obs_source_t* scene = nullptr; obs_source_t* scene = nullptr;
if (sceneName.isEmpty() || sceneName.isNull()) if (sceneName.isEmpty() || sceneName.isNull())
scene = obs_frontend_get_current_scene(); scene = obs_frontend_get_current_scene();
else else
scene = obs_get_source_by_name(sceneName.toUtf8()); scene = obs_get_source_by_name(sceneName.toUtf8());
return scene; return scene;
} }
obs_data_array_t* Utils::GetScenes() { obs_data_array_t* Utils::GetScenes() {
obs_frontend_source_list sceneList = {}; obs_frontend_source_list sceneList = {};
obs_frontend_get_scenes(&sceneList); obs_frontend_get_scenes(&sceneList);
obs_data_array_t* scenes = obs_data_array_create(); obs_data_array_t* scenes = obs_data_array_create();
for (size_t i = 0; i < sceneList.sources.num; i++) { for (size_t i = 0; i < sceneList.sources.num; i++) {
obs_source_t* scene = sceneList.sources.array[i]; obs_source_t* scene = sceneList.sources.array[i];
OBSDataAutoRelease sceneData = GetSceneData(scene); OBSDataAutoRelease sceneData = GetSceneData(scene);
obs_data_array_push_back(scenes, sceneData); obs_data_array_push_back(scenes, sceneData);
} }
obs_frontend_source_list_free(&sceneList); obs_frontend_source_list_free(&sceneList);
return scenes; return scenes;
} }
obs_data_t* Utils::GetSceneData(obs_source_t* source) { obs_data_t* Utils::GetSceneData(obs_source_t* source) {
OBSDataArrayAutoRelease sceneItems = GetSceneItems(source); OBSDataArrayAutoRelease sceneItems = GetSceneItems(source);
obs_data_t* sceneData = obs_data_create(); obs_data_t* sceneData = obs_data_create();
obs_data_set_string(sceneData, "name", obs_source_get_name(source)); obs_data_set_string(sceneData, "name", obs_source_get_name(source));
obs_data_set_array(sceneData, "sources", sceneItems); obs_data_set_array(sceneData, "sources", sceneItems);
return sceneData; return sceneData;
} }
QSpinBox* Utils::GetTransitionDurationControl() { QSpinBox* Utils::GetTransitionDurationControl() {
QMainWindow* window = (QMainWindow*)obs_frontend_get_main_window(); QMainWindow* window = (QMainWindow*)obs_frontend_get_main_window();
return window->findChild<QSpinBox*>("transitionDuration"); return window->findChild<QSpinBox*>("transitionDuration");
} }
int Utils::GetTransitionDuration() { int Utils::GetTransitionDuration() {
QSpinBox* control = GetTransitionDurationControl(); QSpinBox* control = GetTransitionDurationControl();
if (control) if (control)
return control->value(); return control->value();
else else
return -1; return -1;
} }
void Utils::SetTransitionDuration(int ms) { void Utils::SetTransitionDuration(int ms) {
QSpinBox* control = GetTransitionDurationControl(); QSpinBox* control = GetTransitionDurationControl();
if (control && ms >= 0) if (control && ms >= 0)
control->setValue(ms); control->setValue(ms);
} }
bool Utils::SetTransitionByName(QString transitionName) { bool Utils::SetTransitionByName(QString transitionName) {
OBSSourceAutoRelease transition = GetTransitionFromName(transitionName); OBSSourceAutoRelease transition = GetTransitionFromName(transitionName);
if (transition) { if (transition) {
obs_frontend_set_current_transition(transition); obs_frontend_set_current_transition(transition);
return true; return true;
} else { } else {
return false; return false;
} }
} }
QPushButton* Utils::GetPreviewModeButtonControl() { QPushButton* Utils::GetPreviewModeButtonControl() {
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
return main->findChild<QPushButton*>("modeSwitch"); return main->findChild<QPushButton*>("modeSwitch");
} }
QListWidget* Utils::GetSceneListControl() { QListWidget* Utils::GetSceneListControl() {
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
return main->findChild<QListWidget*>("scenes"); return main->findChild<QListWidget*>("scenes");
} }
obs_scene_t* Utils::SceneListItemToScene(QListWidgetItem* item) { obs_scene_t* Utils::SceneListItemToScene(QListWidgetItem* item) {
if (!item) if (!item)
return nullptr; return nullptr;
QVariant itemData = item->data(static_cast<int>(Qt::UserRole)); QVariant itemData = item->data(static_cast<int>(Qt::UserRole));
return itemData.value<OBSScene>(); return itemData.value<OBSScene>();
} }
QLayout* Utils::GetPreviewLayout() { QLayout* Utils::GetPreviewLayout() {
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
return main->findChild<QLayout*>("previewLayout"); return main->findChild<QLayout*>("previewLayout");
} }
void Utils::TransitionToProgram() { void Utils::TransitionToProgram() {
if (!obs_frontend_preview_program_mode_active()) if (!obs_frontend_preview_program_mode_active())
return; return;
// WARNING : if the layout created in OBS' CreateProgramOptions() changes // WARNING : if the layout created in OBS' CreateProgramOptions() changes
// then this won't work as expected // then this won't work as expected
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
// The program options widget is the second item in the left-to-right layout // The program options widget is the second item in the left-to-right layout
QWidget* programOptions = GetPreviewLayout()->itemAt(1)->widget(); QWidget* programOptions = GetPreviewLayout()->itemAt(1)->widget();
// The "Transition" button lies in the mainButtonLayout // The "Transition" button lies in the mainButtonLayout
// which is the first itemin the program options' layout // which is the first itemin the program options' layout
QLayout* mainButtonLayout = programOptions->layout()->itemAt(1)->layout(); QLayout* mainButtonLayout = programOptions->layout()->itemAt(1)->layout();
QWidget* transitionBtnWidget = mainButtonLayout->itemAt(0)->widget(); QWidget* transitionBtnWidget = mainButtonLayout->itemAt(0)->widget();
// Try to cast that widget into a button // Try to cast that widget into a button
QPushButton* transitionBtn = qobject_cast<QPushButton*>(transitionBtnWidget); QPushButton* transitionBtn = qobject_cast<QPushButton*>(transitionBtnWidget);
// Perform a click on that button // Perform a click on that button
transitionBtn->click(); transitionBtn->click();
} }
QString Utils::OBSVersionString() { QString Utils::OBSVersionString() {
uint32_t version = obs_get_version(); uint32_t version = obs_get_version();
uint8_t major, minor, patch; uint8_t major, minor, patch;
major = (version >> 24) & 0xFF; major = (version >> 24) & 0xFF;
minor = (version >> 16) & 0xFF; minor = (version >> 16) & 0xFF;
patch = version & 0xFF; patch = version & 0xFF;
QString result = QString("%1.%2.%3") QString result = QString("%1.%2.%3")
.arg(major).arg(minor).arg(patch); .arg(major).arg(minor).arg(patch);
return result; return result;
} }
QSystemTrayIcon* Utils::GetTrayIcon() { QSystemTrayIcon* Utils::GetTrayIcon() {
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
if (!main) return nullptr; if (!main) return nullptr;
return main->findChildren<QSystemTrayIcon*>().first(); return main->findChildren<QSystemTrayIcon*>().first();
} }
void Utils::SysTrayNotify(QString &text, void Utils::SysTrayNotify(QString &text,
QSystemTrayIcon::MessageIcon icon, QString title) { QSystemTrayIcon::MessageIcon icon, QString title) {
if (!Config::Current()->AlertsEnabled || if (!Config::Current()->AlertsEnabled ||
!QSystemTrayIcon::isSystemTrayAvailable() || !QSystemTrayIcon::isSystemTrayAvailable() ||
!QSystemTrayIcon::supportsMessages()) !QSystemTrayIcon::supportsMessages())
{ {
return; return;
} }
QSystemTrayIcon* trayIcon = GetTrayIcon(); QSystemTrayIcon* trayIcon = GetTrayIcon();
if (trayIcon) if (trayIcon)
trayIcon->showMessage(title, text, icon); trayIcon->showMessage(title, text, icon);
} }
QString Utils::FormatIPAddress(QHostAddress &addr) { QString Utils::FormatIPAddress(QHostAddress &addr) {
QRegExp v4regex("(::ffff:)(((\\d).){3})", Qt::CaseInsensitive); QRegExp v4regex("(::ffff:)(((\\d).){3})", Qt::CaseInsensitive);
QString addrString = addr.toString(); QString addrString = addr.toString();
if (addrString.contains(v4regex)) { if (addrString.contains(v4regex)) {
addrString = QHostAddress(addr.toIPv4Address()).toString(); addrString = QHostAddress(addr.toIPv4Address()).toString();
} }
return addrString; return addrString;
} }
const char* Utils::GetRecordingFolder() { const char* Utils::GetRecordingFolder() {
config_t* profile = obs_frontend_get_profile_config(); config_t* profile = obs_frontend_get_profile_config();
QString outputMode = config_get_string(profile, "Output", "Mode"); QString outputMode = config_get_string(profile, "Output", "Mode");
if (outputMode == "Advanced") { if (outputMode == "Advanced") {
// Advanced mode // Advanced mode
return config_get_string(profile, "AdvOut", "RecFilePath"); return config_get_string(profile, "AdvOut", "RecFilePath");
} else { } else {
// Simple mode // Simple mode
return config_get_string(profile, "SimpleOutput", "FilePath"); return config_get_string(profile, "SimpleOutput", "FilePath");
} }
} }
bool Utils::SetRecordingFolder(const char* path) { bool Utils::SetRecordingFolder(const char* path) {
QDir dir(path); QDir dir(path);
if (!dir.exists()) if (!dir.exists())
dir.mkpath("."); dir.mkpath(".");
config_t* profile = obs_frontend_get_profile_config(); config_t* profile = obs_frontend_get_profile_config();
QString outputMode = config_get_string(profile, "Output", "Mode"); QString outputMode = config_get_string(profile, "Output", "Mode");
if (outputMode == "Advanced") { if (outputMode == "Advanced") {
config_set_string(profile, "AdvOut", "RecFilePath", path); config_set_string(profile, "AdvOut", "RecFilePath", path);
} else { } else {
config_set_string(profile, "SimpleOutput", "FilePath", path); config_set_string(profile, "SimpleOutput", "FilePath", path);
} }
config_save(profile); config_save(profile);
return true; return true;
} }
QString Utils::ParseDataToQueryString(obs_data_t* data) { QString Utils::ParseDataToQueryString(obs_data_t* data) {
if (!data) if (!data)
return QString(); return QString();
QString query; QString query;
obs_data_item_t* item = obs_data_first(data); obs_data_item_t* item = obs_data_first(data);
if (item) { if (item) {
bool isFirst = true; bool isFirst = true;
do { do {
if (!obs_data_item_has_user_value(item)) if (!obs_data_item_has_user_value(item))
continue; continue;
if (!isFirst) if (!isFirst)
query += "&"; query += "&";
else else
isFirst = false; isFirst = false;
QString attrName = obs_data_item_get_name(item); QString attrName = obs_data_item_get_name(item);
query += (attrName + "="); query += (attrName + "=");
switch (obs_data_item_gettype(item)) { switch (obs_data_item_gettype(item)) {
case OBS_DATA_BOOLEAN: case OBS_DATA_BOOLEAN:
query += (obs_data_item_get_bool(item) ? "true" : "false"); query += (obs_data_item_get_bool(item) ? "true" : "false");
break; break;
case OBS_DATA_NUMBER: case OBS_DATA_NUMBER:
switch (obs_data_item_numtype(item)) { switch (obs_data_item_numtype(item)) {
case OBS_DATA_NUM_DOUBLE: case OBS_DATA_NUM_DOUBLE:
query += query +=
QString::number(obs_data_item_get_double(item)); QString::number(obs_data_item_get_double(item));
break; break;
case OBS_DATA_NUM_INT: case OBS_DATA_NUM_INT:
query += query +=
QString::number(obs_data_item_get_int(item)); QString::number(obs_data_item_get_int(item));
break; break;
case OBS_DATA_NUM_INVALID: case OBS_DATA_NUM_INVALID:
break; break;
} }
break; break;
case OBS_DATA_STRING: case OBS_DATA_STRING:
query += query +=
QUrl::toPercentEncoding( QUrl::toPercentEncoding(
QString(obs_data_item_get_string(item))); QString(obs_data_item_get_string(item)));
break; break;
default: default:
//other types are not supported //other types are not supported
break; break;
} }
} while (obs_data_item_next(&item)); } while (obs_data_item_next(&item));
} }
return query; return query;
} }
obs_hotkey_t* Utils::FindHotkeyByName(QString name) { obs_hotkey_t* Utils::FindHotkeyByName(QString name) {
struct current_search { struct current_search {
QString query; QString query;
obs_hotkey_t* result; obs_hotkey_t* result;
}; };
current_search search; current_search search;
search.query = name; search.query = name;
search.result = nullptr; search.result = nullptr;
obs_enum_hotkeys([](void* data, obs_hotkey_id id, obs_hotkey_t* hotkey) { obs_enum_hotkeys([](void* data, obs_hotkey_id id, obs_hotkey_t* hotkey) {
current_search* search = static_cast<current_search*>(data); current_search* search = static_cast<current_search*>(data);
const char* hk_name = obs_hotkey_get_name(hotkey); const char* hk_name = obs_hotkey_get_name(hotkey);
if (hk_name == search->query) { if (hk_name == search->query) {
search->result = hotkey; search->result = hotkey;
return false; return false;
} }
return true; return true;
}, &search); }, &search);
return search.result; return search.result;
} }
bool Utils::ReplayBufferEnabled() { bool Utils::ReplayBufferEnabled() {
config_t* profile = obs_frontend_get_profile_config(); config_t* profile = obs_frontend_get_profile_config();
QString outputMode = config_get_string(profile, "Output", "Mode"); QString outputMode = config_get_string(profile, "Output", "Mode");
if (outputMode == "Simple") { if (outputMode == "Simple") {
return config_get_bool(profile, "SimpleOutput", "RecRB"); return config_get_bool(profile, "SimpleOutput", "RecRB");
} }
else if (outputMode == "Advanced") { else if (outputMode == "Advanced") {
return config_get_bool(profile, "AdvOut", "RecRB"); return config_get_bool(profile, "AdvOut", "RecRB");
} }
return false; return false;
} }
void Utils::StartReplayBuffer() { void Utils::StartReplayBuffer() {
if (obs_frontend_replay_buffer_active()) if (obs_frontend_replay_buffer_active())
return; return;
if (!IsRPHotkeySet()) { if (!IsRPHotkeySet()) {
obs_output_t* rpOutput = obs_frontend_get_replay_buffer_output(); obs_output_t* rpOutput = obs_frontend_get_replay_buffer_output();
OBSData outputHotkeys = obs_hotkeys_save_output(rpOutput); OBSData outputHotkeys = obs_hotkeys_save_output(rpOutput);
OBSData dummyBinding = obs_data_create(); OBSData dummyBinding = obs_data_create();
obs_data_set_bool(dummyBinding, "control", true); obs_data_set_bool(dummyBinding, "control", true);
obs_data_set_bool(dummyBinding, "alt", true); obs_data_set_bool(dummyBinding, "alt", true);
obs_data_set_bool(dummyBinding, "shift", true); obs_data_set_bool(dummyBinding, "shift", true);
obs_data_set_bool(dummyBinding, "command", true); obs_data_set_bool(dummyBinding, "command", true);
obs_data_set_string(dummyBinding, "key", "OBS_KEY_0"); obs_data_set_string(dummyBinding, "key", "OBS_KEY_0");
OBSDataArray rpSaveHotkey = obs_data_get_array( OBSDataArray rpSaveHotkey = obs_data_get_array(
outputHotkeys, "ReplayBuffer.Save"); outputHotkeys, "ReplayBuffer.Save");
obs_data_array_push_back(rpSaveHotkey, dummyBinding); obs_data_array_push_back(rpSaveHotkey, dummyBinding);
obs_hotkeys_load_output(rpOutput, outputHotkeys); obs_hotkeys_load_output(rpOutput, outputHotkeys);
obs_frontend_replay_buffer_start(); obs_frontend_replay_buffer_start();
obs_output_release(rpOutput); obs_output_release(rpOutput);
} }
else { else {
obs_frontend_replay_buffer_start(); obs_frontend_replay_buffer_start();
} }
} }
bool Utils::IsRPHotkeySet() { bool Utils::IsRPHotkeySet() {
OBSOutputAutoRelease rpOutput = obs_frontend_get_replay_buffer_output(); OBSOutputAutoRelease rpOutput = obs_frontend_get_replay_buffer_output();
OBSDataAutoRelease hotkeys = obs_hotkeys_save_output(rpOutput); OBSDataAutoRelease hotkeys = obs_hotkeys_save_output(rpOutput);
OBSDataArrayAutoRelease bindings = obs_data_get_array(hotkeys, OBSDataArrayAutoRelease bindings = obs_data_get_array(hotkeys,
"ReplayBuffer.Save"); "ReplayBuffer.Save");
size_t count = obs_data_array_count(bindings); size_t count = obs_data_array_count(bindings);
return (count > 0); return (count > 0);
} }
const char* Utils::GetFilenameFormatting() { const char* Utils::GetFilenameFormatting() {
config_t* profile = obs_frontend_get_profile_config(); config_t* profile = obs_frontend_get_profile_config();
return config_get_string(profile, "Output", "FilenameFormatting"); return config_get_string(profile, "Output", "FilenameFormatting");
} }
bool Utils::SetFilenameFormatting(const char* filenameFormatting) { bool Utils::SetFilenameFormatting(const char* filenameFormatting) {
config_t* profile = obs_frontend_get_profile_config(); config_t* profile = obs_frontend_get_profile_config();
config_set_string(profile, "Output", "FilenameFormatting", filenameFormatting); config_set_string(profile, "Output", "FilenameFormatting", filenameFormatting);
config_save(profile); config_save(profile);
return true; return true;
} }

View File

@ -34,52 +34,52 @@ with this program. If not, see <https://www.gnu.org/licenses/>
class Utils { class Utils {
public: public:
static obs_data_array_t* StringListToArray(char** strings, char* key); static obs_data_array_t* StringListToArray(char** strings, char* key);
static obs_data_array_t* GetSceneItems(obs_source_t* source); static obs_data_array_t* GetSceneItems(obs_source_t* source);
static obs_data_t* GetSceneItemData(obs_sceneitem_t* item); static obs_data_t* GetSceneItemData(obs_sceneitem_t* item);
static obs_sceneitem_t* GetSceneItemFromName( static obs_sceneitem_t* GetSceneItemFromName(
obs_source_t* source, QString name); obs_source_t* source, QString name);
static obs_source_t* GetTransitionFromName(QString transitionName); static obs_source_t* GetTransitionFromName(QString transitionName);
static obs_source_t* GetSceneFromNameOrCurrent(QString sceneName); static obs_source_t* GetSceneFromNameOrCurrent(QString sceneName);
static bool IsValidAlignment(const uint32_t alignment); static bool IsValidAlignment(const uint32_t alignment);
static obs_data_array_t* GetScenes(); static obs_data_array_t* GetScenes();
static obs_data_t* GetSceneData(obs_source_t* source); static obs_data_t* GetSceneData(obs_source_t* source);
static QSpinBox* GetTransitionDurationControl(); static QSpinBox* GetTransitionDurationControl();
static int GetTransitionDuration(); static int GetTransitionDuration();
static void SetTransitionDuration(int ms); static void SetTransitionDuration(int ms);
static bool SetTransitionByName(QString transitionName); static bool SetTransitionByName(QString transitionName);
static QPushButton* GetPreviewModeButtonControl(); static QPushButton* GetPreviewModeButtonControl();
static QLayout* GetPreviewLayout(); static QLayout* GetPreviewLayout();
static QListWidget* GetSceneListControl(); static QListWidget* GetSceneListControl();
static obs_scene_t* SceneListItemToScene(QListWidgetItem* item); static obs_scene_t* SceneListItemToScene(QListWidgetItem* item);
static void TransitionToProgram(); static void TransitionToProgram();
static QString OBSVersionString(); static QString OBSVersionString();
static QSystemTrayIcon* GetTrayIcon(); static QSystemTrayIcon* GetTrayIcon();
static void SysTrayNotify( static void SysTrayNotify(
QString &text, QString &text,
QSystemTrayIcon::MessageIcon n, QSystemTrayIcon::MessageIcon n,
QString title = QString("obs-websocket")); QString title = QString("obs-websocket"));
static QString FormatIPAddress(QHostAddress &addr); static QString FormatIPAddress(QHostAddress &addr);
static const char* GetRecordingFolder(); static const char* GetRecordingFolder();
static bool SetRecordingFolder(const char* path); static bool SetRecordingFolder(const char* path);
static QString ParseDataToQueryString(obs_data_t* data); static QString ParseDataToQueryString(obs_data_t* data);
static obs_hotkey_t* FindHotkeyByName(QString name); static obs_hotkey_t* FindHotkeyByName(QString name);
static bool ReplayBufferEnabled(); static bool ReplayBufferEnabled();
static void StartReplayBuffer(); static void StartReplayBuffer();
static bool IsRPHotkeySet(); static bool IsRPHotkeySet();
static const char* GetFilenameFormatting(); static const char* GetFilenameFormatting();
static bool SetFilenameFormatting(const char* filenameFormatting); static bool SetFilenameFormatting(const char* filenameFormatting);
}; };
#endif // UTILS_H #endif // UTILS_H

File diff suppressed because it is too large Load Diff

View File

@ -28,83 +28,83 @@ with this program. If not, see <https://www.gnu.org/licenses/>
class WSEvents : public QObject { class WSEvents : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit WSEvents(WSServer* srv); explicit WSEvents(WSServer* srv);
~WSEvents(); ~WSEvents();
static void FrontendEventHandler( static void FrontendEventHandler(
enum obs_frontend_event event, void* privateData); enum obs_frontend_event event, void* privateData);
static WSEvents* Instance; static WSEvents* Instance;
void connectSceneSignals(obs_source_t* scene); void connectSceneSignals(obs_source_t* scene);
void hookTransitionBeginEvent(); void hookTransitionBeginEvent();
uint64_t GetStreamingTime(); uint64_t GetStreamingTime();
const char* GetStreamingTimecode(); const char* GetStreamingTimecode();
uint64_t GetRecordingTime(); uint64_t GetRecordingTime();
const char* GetRecordingTimecode(); const char* GetRecordingTimecode();
bool HeartbeatIsActive; bool HeartbeatIsActive;
private slots: private slots:
void deferredInitOperations(); void deferredInitOperations();
void StreamStatus(); void StreamStatus();
void Heartbeat(); void Heartbeat();
void TransitionDurationChanged(int ms); void TransitionDurationChanged(int ms);
private: private:
WSServer* _srv; WSServer* _srv;
OBSSource currentScene; OBSSource currentScene;
bool pulse; bool pulse;
bool _streamingActive; bool _streamingActive;
bool _recordingActive; bool _recordingActive;
uint64_t _streamStarttime; uint64_t _streamStarttime;
uint64_t _recStarttime; uint64_t _recStarttime;
uint64_t _lastBytesSent; uint64_t _lastBytesSent;
uint64_t _lastBytesSentTime; uint64_t _lastBytesSentTime;
void broadcastUpdate(const char* updateType, void broadcastUpdate(const char* updateType,
obs_data_t* additionalFields); obs_data_t* additionalFields);
void OnSceneChange(); void OnSceneChange();
void OnSceneListChange(); void OnSceneListChange();
void OnSceneCollectionChange(); void OnSceneCollectionChange();
void OnSceneCollectionListChange(); void OnSceneCollectionListChange();
void OnTransitionChange(); void OnTransitionChange();
void OnTransitionListChange(); void OnTransitionListChange();
void OnProfileChange(); void OnProfileChange();
void OnProfileListChange(); void OnProfileListChange();
void OnStreamStarting(); void OnStreamStarting();
void OnStreamStarted(); void OnStreamStarted();
void OnStreamStopping(); void OnStreamStopping();
void OnStreamStopped(); void OnStreamStopped();
void OnRecordingStarting(); void OnRecordingStarting();
void OnRecordingStarted(); void OnRecordingStarted();
void OnRecordingStopping(); void OnRecordingStopping();
void OnRecordingStopped(); void OnRecordingStopped();
void OnReplayStarting(); void OnReplayStarting();
void OnReplayStarted(); void OnReplayStarted();
void OnReplayStopping(); void OnReplayStopping();
void OnReplayStopped(); void OnReplayStopped();
void OnStudioModeSwitched(bool enabled); void OnStudioModeSwitched(bool enabled);
void OnPreviewSceneChanged(); void OnPreviewSceneChanged();
void OnExit(); void OnExit();
static void OnTransitionBegin(void* param, calldata_t* data); static void OnTransitionBegin(void* param, calldata_t* data);
static void OnSceneReordered(void* param, calldata_t* data); static void OnSceneReordered(void* param, calldata_t* data);
static void OnSceneItemAdd(void* param, calldata_t* data); static void OnSceneItemAdd(void* param, calldata_t* data);
static void OnSceneItemDelete(void* param, calldata_t* data); static void OnSceneItemDelete(void* param, calldata_t* data);
static void OnSceneItemVisibilityChanged(void* param, calldata_t* data); static void OnSceneItemVisibilityChanged(void* param, calldata_t* data);
}; };
#endif // WSEVENTS_H #endif // WSEVENTS_H

View File

@ -25,62 +25,62 @@
#include "WSRequestHandler.h" #include "WSRequestHandler.h"
QHash<QString, void(*)(WSRequestHandler*)> WSRequestHandler::messageMap { QHash<QString, void(*)(WSRequestHandler*)> WSRequestHandler::messageMap {
{ "GetVersion", WSRequestHandler::HandleGetVersion }, { "GetVersion", WSRequestHandler::HandleGetVersion },
{ "GetAuthRequired", WSRequestHandler::HandleGetAuthRequired }, { "GetAuthRequired", WSRequestHandler::HandleGetAuthRequired },
{ "Authenticate", WSRequestHandler::HandleAuthenticate }, { "Authenticate", WSRequestHandler::HandleAuthenticate },
{ "SetHeartbeat", WSRequestHandler::HandleSetHeartbeat }, { "SetHeartbeat", WSRequestHandler::HandleSetHeartbeat },
{ "SetFilenameFormatting", WSRequestHandler::HandleSetFilenameFormatting }, { "SetFilenameFormatting", WSRequestHandler::HandleSetFilenameFormatting },
{ "GetFilenameFormatting", WSRequestHandler::HandleGetFilenameFormatting }, { "GetFilenameFormatting", WSRequestHandler::HandleGetFilenameFormatting },
{ "SetCurrentScene", WSRequestHandler::HandleSetCurrentScene }, { "SetCurrentScene", WSRequestHandler::HandleSetCurrentScene },
{ "GetCurrentScene", WSRequestHandler::HandleGetCurrentScene }, { "GetCurrentScene", WSRequestHandler::HandleGetCurrentScene },
{ "GetSceneList", WSRequestHandler::HandleGetSceneList }, { "GetSceneList", WSRequestHandler::HandleGetSceneList },
{ "SetSourceRender", WSRequestHandler::HandleSetSceneItemRender }, // Retrocompat { "SetSourceRender", WSRequestHandler::HandleSetSceneItemRender }, // Retrocompat
{ "SetSceneItemRender", WSRequestHandler::HandleSetSceneItemRender }, { "SetSceneItemRender", WSRequestHandler::HandleSetSceneItemRender },
{ "SetSceneItemPosition", WSRequestHandler::HandleSetSceneItemPosition }, { "SetSceneItemPosition", WSRequestHandler::HandleSetSceneItemPosition },
{ "SetSceneItemTransform", WSRequestHandler::HandleSetSceneItemTransform }, { "SetSceneItemTransform", WSRequestHandler::HandleSetSceneItemTransform },
{ "SetSceneItemCrop", WSRequestHandler::HandleSetSceneItemCrop }, { "SetSceneItemCrop", WSRequestHandler::HandleSetSceneItemCrop },
{ "GetSceneItemProperties", WSRequestHandler::HandleGetSceneItemProperties }, { "GetSceneItemProperties", WSRequestHandler::HandleGetSceneItemProperties },
{ "SetSceneItemProperties", WSRequestHandler::HandleSetSceneItemProperties }, { "SetSceneItemProperties", WSRequestHandler::HandleSetSceneItemProperties },
{ "ResetSceneItem", WSRequestHandler::HandleResetSceneItem }, { "ResetSceneItem", WSRequestHandler::HandleResetSceneItem },
{ "GetStreamingStatus", WSRequestHandler::HandleGetStreamingStatus }, { "GetStreamingStatus", WSRequestHandler::HandleGetStreamingStatus },
{ "StartStopStreaming", WSRequestHandler::HandleStartStopStreaming }, { "StartStopStreaming", WSRequestHandler::HandleStartStopStreaming },
{ "StartStopRecording", WSRequestHandler::HandleStartStopRecording }, { "StartStopRecording", WSRequestHandler::HandleStartStopRecording },
{ "StartStreaming", WSRequestHandler::HandleStartStreaming }, { "StartStreaming", WSRequestHandler::HandleStartStreaming },
{ "StopStreaming", WSRequestHandler::HandleStopStreaming }, { "StopStreaming", WSRequestHandler::HandleStopStreaming },
{ "StartRecording", WSRequestHandler::HandleStartRecording }, { "StartRecording", WSRequestHandler::HandleStartRecording },
{ "StopRecording", WSRequestHandler::HandleStopRecording }, { "StopRecording", WSRequestHandler::HandleStopRecording },
{ "StartStopReplayBuffer", WSRequestHandler::HandleStartStopReplayBuffer }, { "StartStopReplayBuffer", WSRequestHandler::HandleStartStopReplayBuffer },
{ "StartReplayBuffer", WSRequestHandler::HandleStartReplayBuffer }, { "StartReplayBuffer", WSRequestHandler::HandleStartReplayBuffer },
{ "StopReplayBuffer", WSRequestHandler::HandleStopReplayBuffer }, { "StopReplayBuffer", WSRequestHandler::HandleStopReplayBuffer },
{ "SaveReplayBuffer", WSRequestHandler::HandleSaveReplayBuffer }, { "SaveReplayBuffer", WSRequestHandler::HandleSaveReplayBuffer },
{ "SetRecordingFolder", WSRequestHandler::HandleSetRecordingFolder }, { "SetRecordingFolder", WSRequestHandler::HandleSetRecordingFolder },
{ "GetRecordingFolder", WSRequestHandler::HandleGetRecordingFolder }, { "GetRecordingFolder", WSRequestHandler::HandleGetRecordingFolder },
{ "GetTransitionList", WSRequestHandler::HandleGetTransitionList }, { "GetTransitionList", WSRequestHandler::HandleGetTransitionList },
{ "GetCurrentTransition", WSRequestHandler::HandleGetCurrentTransition }, { "GetCurrentTransition", WSRequestHandler::HandleGetCurrentTransition },
{ "SetCurrentTransition", WSRequestHandler::HandleSetCurrentTransition }, { "SetCurrentTransition", WSRequestHandler::HandleSetCurrentTransition },
{ "SetTransitionDuration", WSRequestHandler::HandleSetTransitionDuration }, { "SetTransitionDuration", WSRequestHandler::HandleSetTransitionDuration },
{ "GetTransitionDuration", WSRequestHandler::HandleGetTransitionDuration }, { "GetTransitionDuration", WSRequestHandler::HandleGetTransitionDuration },
{ "SetVolume", WSRequestHandler::HandleSetVolume }, { "SetVolume", WSRequestHandler::HandleSetVolume },
{ "GetVolume", WSRequestHandler::HandleGetVolume }, { "GetVolume", WSRequestHandler::HandleGetVolume },
{ "ToggleMute", WSRequestHandler::HandleToggleMute }, { "ToggleMute", WSRequestHandler::HandleToggleMute },
{ "SetMute", WSRequestHandler::HandleSetMute }, { "SetMute", WSRequestHandler::HandleSetMute },
{ "GetMute", WSRequestHandler::HandleGetMute }, { "GetMute", WSRequestHandler::HandleGetMute },
{ "SetSyncOffset", WSRequestHandler::HandleSetSyncOffset }, { "SetSyncOffset", WSRequestHandler::HandleSetSyncOffset },
{ "GetSyncOffset", WSRequestHandler::HandleGetSyncOffset }, { "GetSyncOffset", WSRequestHandler::HandleGetSyncOffset },
{ "GetSpecialSources", WSRequestHandler::HandleGetSpecialSources }, { "GetSpecialSources", WSRequestHandler::HandleGetSpecialSources },
{ "GetSourcesList", WSRequestHandler::HandleGetSourcesList }, { "GetSourcesList", WSRequestHandler::HandleGetSourcesList },
{ "GetSourceTypesList", WSRequestHandler::HandleGetSourceTypesList }, { "GetSourceTypesList", WSRequestHandler::HandleGetSourceTypesList },
{ "GetSourceSettings", WSRequestHandler::HandleGetSourceSettings }, { "GetSourceSettings", WSRequestHandler::HandleGetSourceSettings },
{ "SetSourceSettings", WSRequestHandler::HandleSetSourceSettings }, { "SetSourceSettings", WSRequestHandler::HandleSetSourceSettings },
{ "GetSourceFilters", WSRequestHandler::HandleGetSourceFilters }, { "GetSourceFilters", WSRequestHandler::HandleGetSourceFilters },
{ "AddFilterToSource", WSRequestHandler::HandleAddFilterToSource }, { "AddFilterToSource", WSRequestHandler::HandleAddFilterToSource },
@ -89,136 +89,136 @@ QHash<QString, void(*)(WSRequestHandler*)> WSRequestHandler::messageMap {
{ "MoveSourceFilter", WSRequestHandler::HandleMoveSourceFilter }, { "MoveSourceFilter", WSRequestHandler::HandleMoveSourceFilter },
{ "SetSourceFilterSettings", WSRequestHandler::HandleSetSourceFilterSettings }, { "SetSourceFilterSettings", WSRequestHandler::HandleSetSourceFilterSettings },
{ "SetCurrentSceneCollection", WSRequestHandler::HandleSetCurrentSceneCollection }, { "SetCurrentSceneCollection", WSRequestHandler::HandleSetCurrentSceneCollection },
{ "GetCurrentSceneCollection", WSRequestHandler::HandleGetCurrentSceneCollection }, { "GetCurrentSceneCollection", WSRequestHandler::HandleGetCurrentSceneCollection },
{ "ListSceneCollections", WSRequestHandler::HandleListSceneCollections }, { "ListSceneCollections", WSRequestHandler::HandleListSceneCollections },
{ "SetCurrentProfile", WSRequestHandler::HandleSetCurrentProfile }, { "SetCurrentProfile", WSRequestHandler::HandleSetCurrentProfile },
{ "GetCurrentProfile", WSRequestHandler::HandleGetCurrentProfile }, { "GetCurrentProfile", WSRequestHandler::HandleGetCurrentProfile },
{ "ListProfiles", WSRequestHandler::HandleListProfiles }, { "ListProfiles", WSRequestHandler::HandleListProfiles },
{ "SetStreamSettings", WSRequestHandler::HandleSetStreamSettings }, { "SetStreamSettings", WSRequestHandler::HandleSetStreamSettings },
{ "GetStreamSettings", WSRequestHandler::HandleGetStreamSettings }, { "GetStreamSettings", WSRequestHandler::HandleGetStreamSettings },
{ "SaveStreamSettings", WSRequestHandler::HandleSaveStreamSettings }, { "SaveStreamSettings", WSRequestHandler::HandleSaveStreamSettings },
{ "GetStudioModeStatus", WSRequestHandler::HandleGetStudioModeStatus }, { "GetStudioModeStatus", WSRequestHandler::HandleGetStudioModeStatus },
{ "GetPreviewScene", WSRequestHandler::HandleGetPreviewScene }, { "GetPreviewScene", WSRequestHandler::HandleGetPreviewScene },
{ "SetPreviewScene", WSRequestHandler::HandleSetPreviewScene }, { "SetPreviewScene", WSRequestHandler::HandleSetPreviewScene },
{ "TransitionToProgram", WSRequestHandler::HandleTransitionToProgram }, { "TransitionToProgram", WSRequestHandler::HandleTransitionToProgram },
{ "EnableStudioMode", WSRequestHandler::HandleEnableStudioMode }, { "EnableStudioMode", WSRequestHandler::HandleEnableStudioMode },
{ "DisableStudioMode", WSRequestHandler::HandleDisableStudioMode }, { "DisableStudioMode", WSRequestHandler::HandleDisableStudioMode },
{ "ToggleStudioMode", WSRequestHandler::HandleToggleStudioMode }, { "ToggleStudioMode", WSRequestHandler::HandleToggleStudioMode },
{ "SetTextGDIPlusProperties", WSRequestHandler::HandleSetTextGDIPlusProperties }, { "SetTextGDIPlusProperties", WSRequestHandler::HandleSetTextGDIPlusProperties },
{ "GetTextGDIPlusProperties", WSRequestHandler::HandleGetTextGDIPlusProperties }, { "GetTextGDIPlusProperties", WSRequestHandler::HandleGetTextGDIPlusProperties },
{ "GetBrowserSourceProperties", WSRequestHandler::HandleGetBrowserSourceProperties }, { "GetBrowserSourceProperties", WSRequestHandler::HandleGetBrowserSourceProperties },
{ "SetBrowserSourceProperties", WSRequestHandler::HandleSetBrowserSourceProperties } { "SetBrowserSourceProperties", WSRequestHandler::HandleSetBrowserSourceProperties }
}; };
QSet<QString> WSRequestHandler::authNotRequired { QSet<QString> WSRequestHandler::authNotRequired {
"GetVersion", "GetVersion",
"GetAuthRequired", "GetAuthRequired",
"Authenticate" "Authenticate"
}; };
WSRequestHandler::WSRequestHandler(QWebSocket* client) : WSRequestHandler::WSRequestHandler(QWebSocket* client) :
_messageId(0), _messageId(0),
_requestType(""), _requestType(""),
data(nullptr), data(nullptr),
_client(client) _client(client)
{ {
} }
void WSRequestHandler::processIncomingMessage(QString textMessage) { void WSRequestHandler::processIncomingMessage(QString textMessage) {
QByteArray msgData = textMessage.toUtf8(); QByteArray msgData = textMessage.toUtf8();
const char* msg = msgData.constData(); const char* msg = msgData.constData();
data = obs_data_create_from_json(msg); data = obs_data_create_from_json(msg);
if (!data) { if (!data) {
if (!msg) if (!msg)
msg = "<null pointer>"; msg = "<null pointer>";
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg); blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
SendErrorResponse("invalid JSON payload"); SendErrorResponse("invalid JSON payload");
return; return;
} }
if (Config::Current()->DebugEnabled) { if (Config::Current()->DebugEnabled) {
blog(LOG_DEBUG, "Request >> '%s'", msg); blog(LOG_DEBUG, "Request >> '%s'", msg);
} }
if (!hasField("request-type") if (!hasField("request-type")
|| !hasField("message-id")) || !hasField("message-id"))
{ {
SendErrorResponse("missing request parameters"); SendErrorResponse("missing request parameters");
return; return;
} }
_requestType = obs_data_get_string(data, "request-type"); _requestType = obs_data_get_string(data, "request-type");
_messageId = obs_data_get_string(data, "message-id"); _messageId = obs_data_get_string(data, "message-id");
if (Config::Current()->AuthRequired if (Config::Current()->AuthRequired
&& (_client->property(PROP_AUTHENTICATED).toBool() == false) && (_client->property(PROP_AUTHENTICATED).toBool() == false)
&& (authNotRequired.find(_requestType) == authNotRequired.end())) && (authNotRequired.find(_requestType) == authNotRequired.end()))
{ {
SendErrorResponse("Not Authenticated"); SendErrorResponse("Not Authenticated");
return; return;
} }
void (*handlerFunc)(WSRequestHandler*) = (messageMap[_requestType]); void (*handlerFunc)(WSRequestHandler*) = (messageMap[_requestType]);
if (handlerFunc != nullptr) if (handlerFunc != nullptr)
handlerFunc(this); handlerFunc(this);
else else
SendErrorResponse("invalid request type"); SendErrorResponse("invalid request type");
} }
WSRequestHandler::~WSRequestHandler() { WSRequestHandler::~WSRequestHandler() {
} }
void WSRequestHandler::SendOKResponse(obs_data_t* additionalFields) { void WSRequestHandler::SendOKResponse(obs_data_t* additionalFields) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "status", "ok"); obs_data_set_string(response, "status", "ok");
obs_data_set_string(response, "message-id", _messageId); obs_data_set_string(response, "message-id", _messageId);
if (additionalFields) if (additionalFields)
obs_data_apply(response, additionalFields); obs_data_apply(response, additionalFields);
SendResponse(response); SendResponse(response);
} }
void WSRequestHandler::SendErrorResponse(const char* errorMessage) { void WSRequestHandler::SendErrorResponse(const char* errorMessage) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "status", "error"); obs_data_set_string(response, "status", "error");
obs_data_set_string(response, "error", errorMessage); obs_data_set_string(response, "error", errorMessage);
obs_data_set_string(response, "message-id", _messageId); obs_data_set_string(response, "message-id", _messageId);
SendResponse(response); SendResponse(response);
} }
void WSRequestHandler::SendErrorResponse(obs_data_t* additionalFields) { void WSRequestHandler::SendErrorResponse(obs_data_t* additionalFields) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "status", "error"); obs_data_set_string(response, "status", "error");
obs_data_set_string(response, "message-id", _messageId); obs_data_set_string(response, "message-id", _messageId);
if (additionalFields) if (additionalFields)
obs_data_set_obj(response, "error", additionalFields); obs_data_set_obj(response, "error", additionalFields);
SendResponse(response); SendResponse(response);
} }
void WSRequestHandler::SendResponse(obs_data_t* response) { void WSRequestHandler::SendResponse(obs_data_t* response) {
QString json = obs_data_get_json(response); QString json = obs_data_get_json(response);
_client->sendTextMessage(json); _client->sendTextMessage(json);
if (Config::Current()->DebugEnabled) if (Config::Current()->DebugEnabled)
blog(LOG_DEBUG, "Response << '%s'", json.toUtf8().constData()); blog(LOG_DEBUG, "Response << '%s'", json.toUtf8().constData());
} }
bool WSRequestHandler::hasField(QString name) { bool WSRequestHandler::hasField(QString name) {
if (!data || name.isEmpty() || name.isNull()) if (!data || name.isEmpty() || name.isNull())
return false; return false;
return obs_data_has_user_value(data, name.toUtf8()); return obs_data_has_user_value(data, name.toUtf8());
} }

View File

@ -31,81 +31,81 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "obs-websocket.h" #include "obs-websocket.h"
class WSRequestHandler : public QObject { class WSRequestHandler : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit WSRequestHandler(QWebSocket* client); explicit WSRequestHandler(QWebSocket* client);
~WSRequestHandler(); ~WSRequestHandler();
void processIncomingMessage(QString textMessage); void processIncomingMessage(QString textMessage);
bool hasField(QString name); bool hasField(QString name);
private: private:
QWebSocket* _client; QWebSocket* _client;
const char* _messageId; const char* _messageId;
const char* _requestType; const char* _requestType;
OBSDataAutoRelease data; OBSDataAutoRelease data;
void SendOKResponse(obs_data_t* additionalFields = NULL); void SendOKResponse(obs_data_t* additionalFields = NULL);
void SendErrorResponse(const char* errorMessage); void SendErrorResponse(const char* errorMessage);
void SendErrorResponse(obs_data_t* additionalFields = NULL); void SendErrorResponse(obs_data_t* additionalFields = NULL);
void SendResponse(obs_data_t* response); void SendResponse(obs_data_t* response);
static QHash<QString, void(*)(WSRequestHandler*)> messageMap; static QHash<QString, void(*)(WSRequestHandler*)> messageMap;
static QSet<QString> authNotRequired; static QSet<QString> authNotRequired;
static void HandleGetVersion(WSRequestHandler* req); static void HandleGetVersion(WSRequestHandler* req);
static void HandleGetAuthRequired(WSRequestHandler* req); static void HandleGetAuthRequired(WSRequestHandler* req);
static void HandleAuthenticate(WSRequestHandler* req); static void HandleAuthenticate(WSRequestHandler* req);
static void HandleSetHeartbeat(WSRequestHandler* req); static void HandleSetHeartbeat(WSRequestHandler* req);
static void HandleSetFilenameFormatting(WSRequestHandler* req); static void HandleSetFilenameFormatting(WSRequestHandler* req);
static void HandleGetFilenameFormatting(WSRequestHandler* req); static void HandleGetFilenameFormatting(WSRequestHandler* req);
static void HandleSetCurrentScene(WSRequestHandler* req); static void HandleSetCurrentScene(WSRequestHandler* req);
static void HandleGetCurrentScene(WSRequestHandler* req); static void HandleGetCurrentScene(WSRequestHandler* req);
static void HandleGetSceneList(WSRequestHandler* req); static void HandleGetSceneList(WSRequestHandler* req);
static void HandleSetSceneItemRender(WSRequestHandler* req); static void HandleSetSceneItemRender(WSRequestHandler* req);
static void HandleSetSceneItemPosition(WSRequestHandler* req); static void HandleSetSceneItemPosition(WSRequestHandler* req);
static void HandleSetSceneItemTransform(WSRequestHandler* req); static void HandleSetSceneItemTransform(WSRequestHandler* req);
static void HandleSetSceneItemCrop(WSRequestHandler* req); static void HandleSetSceneItemCrop(WSRequestHandler* req);
static void HandleGetSceneItemProperties(WSRequestHandler* req); static void HandleGetSceneItemProperties(WSRequestHandler* req);
static void HandleSetSceneItemProperties(WSRequestHandler* req); static void HandleSetSceneItemProperties(WSRequestHandler* req);
static void HandleResetSceneItem(WSRequestHandler* req); static void HandleResetSceneItem(WSRequestHandler* req);
static void HandleGetStreamingStatus(WSRequestHandler* req); static void HandleGetStreamingStatus(WSRequestHandler* req);
static void HandleStartStopStreaming(WSRequestHandler* req); static void HandleStartStopStreaming(WSRequestHandler* req);
static void HandleStartStopRecording(WSRequestHandler* req); static void HandleStartStopRecording(WSRequestHandler* req);
static void HandleStartStreaming(WSRequestHandler* req); static void HandleStartStreaming(WSRequestHandler* req);
static void HandleStopStreaming(WSRequestHandler* req); static void HandleStopStreaming(WSRequestHandler* req);
static void HandleStartRecording(WSRequestHandler* req); static void HandleStartRecording(WSRequestHandler* req);
static void HandleStopRecording(WSRequestHandler* req); static void HandleStopRecording(WSRequestHandler* req);
static void HandleStartStopReplayBuffer(WSRequestHandler* req); static void HandleStartStopReplayBuffer(WSRequestHandler* req);
static void HandleStartReplayBuffer(WSRequestHandler* req); static void HandleStartReplayBuffer(WSRequestHandler* req);
static void HandleStopReplayBuffer(WSRequestHandler* req); static void HandleStopReplayBuffer(WSRequestHandler* req);
static void HandleSaveReplayBuffer(WSRequestHandler* req); static void HandleSaveReplayBuffer(WSRequestHandler* req);
static void HandleSetRecordingFolder(WSRequestHandler* req); static void HandleSetRecordingFolder(WSRequestHandler* req);
static void HandleGetRecordingFolder(WSRequestHandler* req); static void HandleGetRecordingFolder(WSRequestHandler* req);
static void HandleGetTransitionList(WSRequestHandler* req); static void HandleGetTransitionList(WSRequestHandler* req);
static void HandleGetCurrentTransition(WSRequestHandler* req); static void HandleGetCurrentTransition(WSRequestHandler* req);
static void HandleSetCurrentTransition(WSRequestHandler* req); static void HandleSetCurrentTransition(WSRequestHandler* req);
static void HandleSetVolume(WSRequestHandler* req); static void HandleSetVolume(WSRequestHandler* req);
static void HandleGetVolume(WSRequestHandler* req); static void HandleGetVolume(WSRequestHandler* req);
static void HandleToggleMute(WSRequestHandler* req); static void HandleToggleMute(WSRequestHandler* req);
static void HandleSetMute(WSRequestHandler* req); static void HandleSetMute(WSRequestHandler* req);
static void HandleGetMute(WSRequestHandler* req); static void HandleGetMute(WSRequestHandler* req);
static void HandleSetSyncOffset(WSRequestHandler* req); static void HandleSetSyncOffset(WSRequestHandler* req);
static void HandleGetSyncOffset(WSRequestHandler* req); static void HandleGetSyncOffset(WSRequestHandler* req);
static void HandleGetSpecialSources(WSRequestHandler* req); static void HandleGetSpecialSources(WSRequestHandler* req);
static void HandleGetSourcesList(WSRequestHandler* req); static void HandleGetSourcesList(WSRequestHandler* req);
static void HandleGetSourceTypesList(WSRequestHandler* req); static void HandleGetSourceTypesList(WSRequestHandler* req);
static void HandleGetSourceSettings(WSRequestHandler* req); static void HandleGetSourceSettings(WSRequestHandler* req);
static void HandleSetSourceSettings(WSRequestHandler* req); static void HandleSetSourceSettings(WSRequestHandler* req);
static void HandleGetSourceFilters(WSRequestHandler* req); static void HandleGetSourceFilters(WSRequestHandler* req);
static void HandleAddFilterToSource(WSRequestHandler* req); static void HandleAddFilterToSource(WSRequestHandler* req);
@ -114,33 +114,33 @@ class WSRequestHandler : public QObject {
static void HandleMoveSourceFilter(WSRequestHandler* req); static void HandleMoveSourceFilter(WSRequestHandler* req);
static void HandleSetSourceFilterSettings(WSRequestHandler* req); static void HandleSetSourceFilterSettings(WSRequestHandler* req);
static void HandleSetCurrentSceneCollection(WSRequestHandler* req); static void HandleSetCurrentSceneCollection(WSRequestHandler* req);
static void HandleGetCurrentSceneCollection(WSRequestHandler* req); static void HandleGetCurrentSceneCollection(WSRequestHandler* req);
static void HandleListSceneCollections(WSRequestHandler* req); static void HandleListSceneCollections(WSRequestHandler* req);
static void HandleSetCurrentProfile(WSRequestHandler* req); static void HandleSetCurrentProfile(WSRequestHandler* req);
static void HandleGetCurrentProfile(WSRequestHandler* req); static void HandleGetCurrentProfile(WSRequestHandler* req);
static void HandleListProfiles(WSRequestHandler* req); static void HandleListProfiles(WSRequestHandler* req);
static void HandleSetStreamSettings(WSRequestHandler* req); static void HandleSetStreamSettings(WSRequestHandler* req);
static void HandleGetStreamSettings(WSRequestHandler* req); static void HandleGetStreamSettings(WSRequestHandler* req);
static void HandleSaveStreamSettings(WSRequestHandler* req); static void HandleSaveStreamSettings(WSRequestHandler* req);
static void HandleSetTransitionDuration(WSRequestHandler* req); static void HandleSetTransitionDuration(WSRequestHandler* req);
static void HandleGetTransitionDuration(WSRequestHandler* req); static void HandleGetTransitionDuration(WSRequestHandler* req);
static void HandleGetStudioModeStatus(WSRequestHandler* req); static void HandleGetStudioModeStatus(WSRequestHandler* req);
static void HandleGetPreviewScene(WSRequestHandler* req); static void HandleGetPreviewScene(WSRequestHandler* req);
static void HandleSetPreviewScene(WSRequestHandler* req); static void HandleSetPreviewScene(WSRequestHandler* req);
static void HandleTransitionToProgram(WSRequestHandler* req); static void HandleTransitionToProgram(WSRequestHandler* req);
static void HandleEnableStudioMode(WSRequestHandler* req); static void HandleEnableStudioMode(WSRequestHandler* req);
static void HandleDisableStudioMode(WSRequestHandler* req); static void HandleDisableStudioMode(WSRequestHandler* req);
static void HandleToggleStudioMode(WSRequestHandler* req); static void HandleToggleStudioMode(WSRequestHandler* req);
static void HandleSetTextGDIPlusProperties(WSRequestHandler* req); static void HandleSetTextGDIPlusProperties(WSRequestHandler* req);
static void HandleGetTextGDIPlusProperties(WSRequestHandler* req); static void HandleGetTextGDIPlusProperties(WSRequestHandler* req);
static void HandleSetBrowserSourceProperties(WSRequestHandler* req); static void HandleSetBrowserSourceProperties(WSRequestHandler* req);
static void HandleGetBrowserSourceProperties(WSRequestHandler* req); static void HandleGetBrowserSourceProperties(WSRequestHandler* req);
}; };
#endif // WSPROTOCOL_H #endif // WSPROTOCOL_H

View File

@ -20,24 +20,24 @@
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleGetVersion(WSRequestHandler* req) { void WSRequestHandler::HandleGetVersion(WSRequestHandler* req) {
QString obsVersion = Utils::OBSVersionString(); QString obsVersion = Utils::OBSVersionString();
QList<QString> names = req->messageMap.keys(); QList<QString> names = req->messageMap.keys();
names.sort(Qt::CaseInsensitive); names.sort(Qt::CaseInsensitive);
// (Palakis) OBS' data arrays only support object arrays, so I improvised. // (Palakis) OBS' data arrays only support object arrays, so I improvised.
QString requests; QString requests;
requests += names.takeFirst(); requests += names.takeFirst();
for (QString reqName : names) { for (QString reqName : names) {
requests += ("," + reqName); requests += ("," + reqName);
} }
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "obs-websocket-version", OBS_WEBSOCKET_VERSION); obs_data_set_string(data, "obs-websocket-version", OBS_WEBSOCKET_VERSION);
obs_data_set_string(data, "obs-studio-version", obsVersion.toUtf8()); obs_data_set_string(data, "obs-studio-version", obsVersion.toUtf8());
obs_data_set_string(data, "available-requests", requests.toUtf8()); obs_data_set_string(data, "available-requests", requests.toUtf8());
req->SendOKResponse(data); req->SendOKResponse(data);
} }
/** /**
@ -54,19 +54,19 @@
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler* req) { void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler* req) {
bool authRequired = Config::Current()->AuthRequired; bool authRequired = Config::Current()->AuthRequired;
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "authRequired", authRequired); obs_data_set_bool(data, "authRequired", authRequired);
if (authRequired) { if (authRequired) {
obs_data_set_string(data, "challenge", obs_data_set_string(data, "challenge",
Config::Current()->SessionChallenge.toUtf8()); Config::Current()->SessionChallenge.toUtf8());
obs_data_set_string(data, "salt", obs_data_set_string(data, "salt",
Config::Current()->Salt.toUtf8()); Config::Current()->Salt.toUtf8());
} }
req->SendOKResponse(data); req->SendOKResponse(data);
} }
/** /**
@ -80,25 +80,25 @@ void WSRequestHandler::HandleGetAuthRequired(WSRequestHandler* req) {
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) { void WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) {
if (!req->hasField("auth")) { if (!req->hasField("auth")) {
req->SendErrorResponse("missing request parameters"); req->SendErrorResponse("missing request parameters");
return; return;
} }
QString auth = obs_data_get_string(req->data, "auth"); QString auth = obs_data_get_string(req->data, "auth");
if (auth.isEmpty()) { if (auth.isEmpty()) {
req->SendErrorResponse("auth not specified!"); req->SendErrorResponse("auth not specified!");
return; return;
} }
if ((req->_client->property(PROP_AUTHENTICATED).toBool() == false) if ((req->_client->property(PROP_AUTHENTICATED).toBool() == false)
&& Config::Current()->CheckAuth(auth)) && Config::Current()->CheckAuth(auth))
{ {
req->_client->setProperty(PROP_AUTHENTICATED, true); req->_client->setProperty(PROP_AUTHENTICATED, true);
req->SendOKResponse(); req->SendOKResponse();
} else { } else {
req->SendErrorResponse("Authentication Failed."); req->SendErrorResponse("Authentication Failed.");
} }
} }
/** /**
@ -112,18 +112,18 @@ void WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) {
* @since 4.3.0 * @since 4.3.0
*/ */
void WSRequestHandler::HandleSetHeartbeat(WSRequestHandler* req) { void WSRequestHandler::HandleSetHeartbeat(WSRequestHandler* req) {
if (!req->hasField("enable")) { if (!req->hasField("enable")) {
req->SendErrorResponse("Heartbeat <enable> parameter missing"); req->SendErrorResponse("Heartbeat <enable> parameter missing");
return; return;
} }
WSEvents::Instance->HeartbeatIsActive = WSEvents::Instance->HeartbeatIsActive =
obs_data_get_bool(req->data, "enable"); obs_data_get_bool(req->data, "enable");
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_bool(response, "enable", obs_data_set_bool(response, "enable",
WSEvents::Instance->HeartbeatIsActive); WSEvents::Instance->HeartbeatIsActive);
req->SendOKResponse(response); req->SendOKResponse(response);
} }
/** /**
@ -137,18 +137,18 @@ void WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) {
* @since 4.3.0 * @since 4.3.0
*/ */
void WSRequestHandler::HandleSetFilenameFormatting(WSRequestHandler* req) { void WSRequestHandler::HandleSetFilenameFormatting(WSRequestHandler* req) {
if (!req->hasField("filename-formatting")) { if (!req->hasField("filename-formatting")) {
req->SendErrorResponse("<filename-formatting> parameter missing"); req->SendErrorResponse("<filename-formatting> parameter missing");
return; return;
} }
QString filenameFormatting = obs_data_get_string(req->data, "filename-formatting"); QString filenameFormatting = obs_data_get_string(req->data, "filename-formatting");
if (!filenameFormatting.isEmpty()) { if (!filenameFormatting.isEmpty()) {
Utils::SetFilenameFormatting(filenameFormatting.toUtf8()); Utils::SetFilenameFormatting(filenameFormatting.toUtf8());
req->SendOKResponse(); req->SendOKResponse();
} else { } else {
req->SendErrorResponse("invalid request parameters"); req->SendErrorResponse("invalid request parameters");
} }
} }
/** /**
@ -162,7 +162,7 @@ void WSRequestHandler::HandleSetFilenameFormatting(WSRequestHandler* req) {
* @since 4.3.0 * @since 4.3.0
*/ */
void WSRequestHandler::HandleGetFilenameFormatting(WSRequestHandler* req) { void WSRequestHandler::HandleGetFilenameFormatting(WSRequestHandler* req) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "filename-formatting", Utils::GetFilenameFormatting()); obs_data_set_string(response, "filename-formatting", Utils::GetFilenameFormatting());
req->SendOKResponse(response); req->SendOKResponse(response);
} }

View File

@ -14,19 +14,19 @@
* @since 4.0.0 * @since 4.0.0
*/ */
void WSRequestHandler::HandleSetCurrentProfile(WSRequestHandler* req) { void WSRequestHandler::HandleSetCurrentProfile(WSRequestHandler* req) {
if (!req->hasField("profile-name")) { if (!req->hasField("profile-name")) {
req->SendErrorResponse("missing request parameters"); req->SendErrorResponse("missing request parameters");
return; return;
} }
QString profileName = obs_data_get_string(req->data, "profile-name"); QString profileName = obs_data_get_string(req->data, "profile-name");
if (!profileName.isEmpty()) { if (!profileName.isEmpty()) {
// TODO : check if profile exists // TODO : check if profile exists
obs_frontend_set_current_profile(profileName.toUtf8()); obs_frontend_set_current_profile(profileName.toUtf8());
req->SendOKResponse(); req->SendOKResponse();
} else { } else {
req->SendErrorResponse("invalid request parameters"); req->SendErrorResponse("invalid request parameters");
} }
} }
/** /**
@ -40,11 +40,11 @@
* @since 4.0.0 * @since 4.0.0
*/ */
void WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler* req) { void WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler* req) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "profile-name", obs_data_set_string(response, "profile-name",
obs_frontend_get_current_profile()); obs_frontend_get_current_profile());
req->SendOKResponse(response); req->SendOKResponse(response);
} }
/** /**
@ -58,13 +58,13 @@ void WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler* req) {
* @since 4.0.0 * @since 4.0.0
*/ */
void WSRequestHandler::HandleListProfiles(WSRequestHandler* req) { void WSRequestHandler::HandleListProfiles(WSRequestHandler* req) {
char** profiles = obs_frontend_get_profiles(); char** profiles = obs_frontend_get_profiles();
OBSDataArrayAutoRelease list = OBSDataArrayAutoRelease list =
Utils::StringListToArray(profiles, "profile-name"); Utils::StringListToArray(profiles, "profile-name");
bfree(profiles); bfree(profiles);
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_array(response, "profiles", list); obs_data_set_array(response, "profiles", list);
req->SendOKResponse(response); req->SendOKResponse(response);
} }

View File

@ -12,12 +12,12 @@
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req) { void WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req) {
if (obs_frontend_recording_active()) if (obs_frontend_recording_active())
obs_frontend_recording_stop(); obs_frontend_recording_stop();
else else
obs_frontend_recording_start(); obs_frontend_recording_start();
req->SendOKResponse(); req->SendOKResponse();
} }
/** /**
@ -30,12 +30,12 @@
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleStartRecording(WSRequestHandler* req) { void WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
if (obs_frontend_recording_active() == false) { if (obs_frontend_recording_active() == false) {
obs_frontend_recording_start(); obs_frontend_recording_start();
req->SendOKResponse(); req->SendOKResponse();
} else { } else {
req->SendErrorResponse("recording already active"); req->SendErrorResponse("recording already active");
} }
} }
/** /**
@ -48,12 +48,12 @@
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleStopRecording(WSRequestHandler* req) { void WSRequestHandler::HandleStopRecording(WSRequestHandler* req) {
if (obs_frontend_recording_active() == true) { if (obs_frontend_recording_active() == true) {
obs_frontend_recording_stop(); obs_frontend_recording_stop();
req->SendOKResponse(); req->SendOKResponse();
} else { } else {
req->SendErrorResponse("recording not active"); req->SendErrorResponse("recording not active");
} }
} }
/** /**
@ -67,17 +67,17 @@ void WSRequestHandler::HandleStopRecording(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleSetRecordingFolder(WSRequestHandler* req) { void WSRequestHandler::HandleSetRecordingFolder(WSRequestHandler* req) {
if (!req->hasField("rec-folder")) { if (!req->hasField("rec-folder")) {
req->SendErrorResponse("missing request parameters"); req->SendErrorResponse("missing request parameters");
return; return;
} }
const char* newRecFolder = obs_data_get_string(req->data, "rec-folder"); const char* newRecFolder = obs_data_get_string(req->data, "rec-folder");
bool success = Utils::SetRecordingFolder(newRecFolder); bool success = Utils::SetRecordingFolder(newRecFolder);
if (success) if (success)
req->SendOKResponse(); req->SendOKResponse();
else else
req->SendErrorResponse("invalid request parameters"); req->SendErrorResponse("invalid request parameters");
} }
/** /**
@ -91,10 +91,10 @@ void WSRequestHandler::HandleStopRecording(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleGetRecordingFolder(WSRequestHandler* req) { void WSRequestHandler::HandleGetRecordingFolder(WSRequestHandler* req) {
const char* recFolder = Utils::GetRecordingFolder(); const char* recFolder = Utils::GetRecordingFolder();
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "rec-folder", recFolder); obs_data_set_string(response, "rec-folder", recFolder);
req->SendOKResponse(response); req->SendOKResponse(response);
} }

View File

@ -12,12 +12,12 @@
* @since 4.2.0 * @since 4.2.0
*/ */
void WSRequestHandler::HandleStartStopReplayBuffer(WSRequestHandler* req) { void WSRequestHandler::HandleStartStopReplayBuffer(WSRequestHandler* req) {
if (obs_frontend_replay_buffer_active()) { if (obs_frontend_replay_buffer_active()) {
obs_frontend_replay_buffer_stop(); obs_frontend_replay_buffer_stop();
} else { } else {
Utils::StartReplayBuffer(); Utils::StartReplayBuffer();
} }
req->SendOKResponse(); req->SendOKResponse();
} }
/** /**
@ -33,18 +33,18 @@ void WSRequestHandler::HandleStartStopReplayBuffer(WSRequestHandler* req) {
* @since 4.2.0 * @since 4.2.0
*/ */
void WSRequestHandler::HandleStartReplayBuffer(WSRequestHandler* req) { void WSRequestHandler::HandleStartReplayBuffer(WSRequestHandler* req) {
if (!Utils::ReplayBufferEnabled()) { if (!Utils::ReplayBufferEnabled()) {
req->SendErrorResponse("replay buffer disabled in settings"); req->SendErrorResponse("replay buffer disabled in settings");
return; return;
} }
if (obs_frontend_replay_buffer_active() == true) { if (obs_frontend_replay_buffer_active() == true) {
req->SendErrorResponse("replay buffer already active"); req->SendErrorResponse("replay buffer already active");
return; return;
} }
Utils::StartReplayBuffer(); Utils::StartReplayBuffer();
req->SendOKResponse(); req->SendOKResponse();
} }
/** /**
@ -57,12 +57,12 @@ void WSRequestHandler::HandleStartReplayBuffer(WSRequestHandler* req) {
* @since 4.2.0 * @since 4.2.0
*/ */
void WSRequestHandler::HandleStopReplayBuffer(WSRequestHandler* req) { void WSRequestHandler::HandleStopReplayBuffer(WSRequestHandler* req) {
if (obs_frontend_replay_buffer_active() == true) { if (obs_frontend_replay_buffer_active() == true) {
obs_frontend_replay_buffer_stop(); obs_frontend_replay_buffer_stop();
req->SendOKResponse(); req->SendOKResponse();
} else { } else {
req->SendErrorResponse("replay buffer not active"); req->SendErrorResponse("replay buffer not active");
} }
} }
/** /**
@ -76,17 +76,17 @@ void WSRequestHandler::HandleStopReplayBuffer(WSRequestHandler* req) {
* @since 4.2.0 * @since 4.2.0
*/ */
void WSRequestHandler::HandleSaveReplayBuffer(WSRequestHandler* req) { void WSRequestHandler::HandleSaveReplayBuffer(WSRequestHandler* req) {
if (!obs_frontend_replay_buffer_active()) { if (!obs_frontend_replay_buffer_active()) {
req->SendErrorResponse("replay buffer not active"); req->SendErrorResponse("replay buffer not active");
return; return;
} }
OBSOutputAutoRelease replayOutput = obs_frontend_get_replay_buffer_output(); OBSOutputAutoRelease replayOutput = obs_frontend_get_replay_buffer_output();
calldata_t cd = { 0 }; calldata_t cd = { 0 };
proc_handler_t* ph = obs_output_get_proc_handler(replayOutput); proc_handler_t* ph = obs_output_get_proc_handler(replayOutput);
proc_handler_call(ph, "save", &cd); proc_handler_call(ph, "save", &cd);
calldata_free(&cd); calldata_free(&cd);
req->SendOKResponse(); req->SendOKResponse();
} }

View File

@ -14,19 +14,19 @@
* @since 4.0.0 * @since 4.0.0
*/ */
void WSRequestHandler::HandleSetCurrentSceneCollection(WSRequestHandler* req) { void WSRequestHandler::HandleSetCurrentSceneCollection(WSRequestHandler* req) {
if (!req->hasField("sc-name")) { if (!req->hasField("sc-name")) {
req->SendErrorResponse("missing request parameters"); req->SendErrorResponse("missing request parameters");
return; return;
} }
QString sceneCollection = obs_data_get_string(req->data, "sc-name"); QString sceneCollection = obs_data_get_string(req->data, "sc-name");
if (!sceneCollection.isEmpty()) { if (!sceneCollection.isEmpty()) {
// TODO : Check if specified profile exists and if changing is allowed // TODO : Check if specified profile exists and if changing is allowed
obs_frontend_set_current_scene_collection(sceneCollection.toUtf8()); obs_frontend_set_current_scene_collection(sceneCollection.toUtf8());
req->SendOKResponse(); req->SendOKResponse();
} else { } else {
req->SendErrorResponse("invalid request parameters"); req->SendErrorResponse("invalid request parameters");
} }
} }
/** /**
@ -40,11 +40,11 @@
* @since 4.0.0 * @since 4.0.0
*/ */
void WSRequestHandler::HandleGetCurrentSceneCollection(WSRequestHandler* req) { void WSRequestHandler::HandleGetCurrentSceneCollection(WSRequestHandler* req) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "sc-name", obs_data_set_string(response, "sc-name",
obs_frontend_get_current_scene_collection()); obs_frontend_get_current_scene_collection());
req->SendOKResponse(response); req->SendOKResponse(response);
} }
/** /**
@ -59,13 +59,13 @@ void WSRequestHandler::HandleGetCurrentSceneCollection(WSRequestHandler* req) {
* @since 4.0.0 * @since 4.0.0
*/ */
void WSRequestHandler::HandleListSceneCollections(WSRequestHandler* req) { void WSRequestHandler::HandleListSceneCollections(WSRequestHandler* req) {
char** sceneCollections = obs_frontend_get_scene_collections(); char** sceneCollections = obs_frontend_get_scene_collections();
OBSDataArrayAutoRelease list = OBSDataArrayAutoRelease list =
Utils::StringListToArray(sceneCollections, "sc-name"); Utils::StringListToArray(sceneCollections, "sc-name");
bfree(sceneCollections); bfree(sceneCollections);
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_array(response, "scene-collections", list); obs_data_set_array(response, "scene-collections", list);
req->SendOKResponse(response); req->SendOKResponse(response);
} }

View File

@ -14,20 +14,20 @@
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleSetCurrentScene(WSRequestHandler* req) { void WSRequestHandler::HandleSetCurrentScene(WSRequestHandler* req) {
if (!req->hasField("scene-name")) { if (!req->hasField("scene-name")) {
req->SendErrorResponse("missing request parameters"); req->SendErrorResponse("missing request parameters");
return; return;
} }
const char* sceneName = obs_data_get_string(req->data, "scene-name"); const char* sceneName = obs_data_get_string(req->data, "scene-name");
OBSSourceAutoRelease source = obs_get_source_by_name(sceneName); OBSSourceAutoRelease source = obs_get_source_by_name(sceneName);
if (source) { if (source) {
obs_frontend_set_current_scene(source); obs_frontend_set_current_scene(source);
req->SendOKResponse(); req->SendOKResponse();
} else { } else {
req->SendErrorResponse("requested scene does not exist"); req->SendErrorResponse("requested scene does not exist");
} }
} }
/** /**
@ -42,14 +42,14 @@
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleGetCurrentScene(WSRequestHandler* req) { void WSRequestHandler::HandleGetCurrentScene(WSRequestHandler* req) {
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene(); OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(currentScene); OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(currentScene);
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "name", obs_source_get_name(currentScene)); obs_data_set_string(data, "name", obs_source_get_name(currentScene));
obs_data_set_array(data, "sources", sceneItems); obs_data_set_array(data, "sources", sceneItems);
req->SendOKResponse(data); req->SendOKResponse(data);
} }
/** /**
@ -64,13 +64,13 @@ void WSRequestHandler::HandleGetCurrentScene(WSRequestHandler* req) {
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleGetSceneList(WSRequestHandler* req) { void WSRequestHandler::HandleGetSceneList(WSRequestHandler* req) {
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene(); OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
OBSDataArrayAutoRelease scenes = Utils::GetScenes(); OBSDataArrayAutoRelease scenes = Utils::GetScenes();
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "current-scene", obs_data_set_string(data, "current-scene",
obs_source_get_name(currentScene)); obs_source_get_name(currentScene));
obs_data_set_array(data, "scenes", scenes); obs_data_set_array(data, "scenes", scenes);
req->SendOKResponse(data); req->SendOKResponse(data);
} }

File diff suppressed because it is too large Load Diff

View File

@ -21,25 +21,25 @@
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req) { void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req) {
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "streaming", obs_frontend_streaming_active()); obs_data_set_bool(data, "streaming", obs_frontend_streaming_active());
obs_data_set_bool(data, "recording", obs_frontend_recording_active()); obs_data_set_bool(data, "recording", obs_frontend_recording_active());
obs_data_set_bool(data, "preview-only", false); obs_data_set_bool(data, "preview-only", false);
const char* tc = nullptr; const char* tc = nullptr;
if (obs_frontend_streaming_active()) { if (obs_frontend_streaming_active()) {
tc = WSEvents::Instance->GetStreamingTimecode(); tc = WSEvents::Instance->GetStreamingTimecode();
obs_data_set_string(data, "stream-timecode", tc); obs_data_set_string(data, "stream-timecode", tc);
bfree((void*)tc); bfree((void*)tc);
} }
if (obs_frontend_recording_active()) { if (obs_frontend_recording_active()) {
tc = WSEvents::Instance->GetRecordingTimecode(); tc = WSEvents::Instance->GetRecordingTimecode();
obs_data_set_string(data, "rec-timecode", tc); obs_data_set_string(data, "rec-timecode", tc);
bfree((void*)tc); bfree((void*)tc);
} }
req->SendOKResponse(data); req->SendOKResponse(data);
} }
/** /**
@ -51,10 +51,10 @@
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleStartStopStreaming(WSRequestHandler* req) { void WSRequestHandler::HandleStartStopStreaming(WSRequestHandler* req) {
if (obs_frontend_streaming_active()) if (obs_frontend_streaming_active())
HandleStopStreaming(req); HandleStopStreaming(req);
else else
HandleStartStreaming(req); HandleStartStreaming(req);
} }
/** /**
@ -77,90 +77,90 @@
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleStartStreaming(WSRequestHandler* req) { void WSRequestHandler::HandleStartStreaming(WSRequestHandler* req) {
if (obs_frontend_streaming_active() == false) { if (obs_frontend_streaming_active() == false) {
OBSService configuredService = obs_frontend_get_streaming_service(); OBSService configuredService = obs_frontend_get_streaming_service();
OBSService newService = nullptr; OBSService newService = nullptr;
// TODO: fix service memory leak // TODO: fix service memory leak
if (req->hasField("stream")) { if (req->hasField("stream")) {
OBSDataAutoRelease streamData = obs_data_get_obj(req->data, "stream"); OBSDataAutoRelease streamData = obs_data_get_obj(req->data, "stream");
OBSDataAutoRelease newSettings = obs_data_get_obj(streamData, "settings"); OBSDataAutoRelease newSettings = obs_data_get_obj(streamData, "settings");
OBSDataAutoRelease newMetadata = obs_data_get_obj(streamData, "metadata"); OBSDataAutoRelease newMetadata = obs_data_get_obj(streamData, "metadata");
OBSDataAutoRelease csHotkeys = OBSDataAutoRelease csHotkeys =
obs_hotkeys_save_service(configuredService); obs_hotkeys_save_service(configuredService);
QString currentType = obs_service_get_type(configuredService); QString currentType = obs_service_get_type(configuredService);
QString newType = obs_data_get_string(streamData, "type"); QString newType = obs_data_get_string(streamData, "type");
if (newType.isEmpty() || newType.isNull()) { if (newType.isEmpty() || newType.isNull()) {
newType = currentType; newType = currentType;
} }
//Supporting adding metadata parameters to key query string //Supporting adding metadata parameters to key query string
QString query = Utils::ParseDataToQueryString(newMetadata); QString query = Utils::ParseDataToQueryString(newMetadata);
if (!query.isEmpty() if (!query.isEmpty()
&& obs_data_has_user_value(newSettings, "key")) && obs_data_has_user_value(newSettings, "key"))
{ {
const char* key = obs_data_get_string(newSettings, "key"); const char* key = obs_data_get_string(newSettings, "key");
int keylen = strlen(key); int keylen = strlen(key);
bool hasQuestionMark = false; bool hasQuestionMark = false;
for (int i = 0; i < keylen; i++) { for (int i = 0; i < keylen; i++) {
if (key[i] == '?') { if (key[i] == '?') {
hasQuestionMark = true; hasQuestionMark = true;
break; break;
} }
} }
if (hasQuestionMark) { if (hasQuestionMark) {
query.prepend('&'); query.prepend('&');
} else { } else {
query.prepend('?'); query.prepend('?');
} }
query.prepend(key); query.prepend(key);
obs_data_set_string(newSettings, "key", query.toUtf8()); obs_data_set_string(newSettings, "key", query.toUtf8());
} }
if (newType == currentType) { if (newType == currentType) {
// Service type doesn't change: apply settings to current service // Service type doesn't change: apply settings to current service
// By doing this, you can send a request to the websocket // By doing this, you can send a request to the websocket
// that only contains settings you want to change, instead of // that only contains settings you want to change, instead of
// having to do a get and then change them // having to do a get and then change them
OBSDataAutoRelease currentSettings = obs_service_get_settings(configuredService); OBSDataAutoRelease currentSettings = obs_service_get_settings(configuredService);
OBSDataAutoRelease updatedSettings = obs_data_create(); OBSDataAutoRelease updatedSettings = obs_data_create();
obs_data_apply(updatedSettings, currentSettings); //first apply the existing settings obs_data_apply(updatedSettings, currentSettings); //first apply the existing settings
obs_data_apply(updatedSettings, newSettings); //then apply the settings from the request should they exist obs_data_apply(updatedSettings, newSettings); //then apply the settings from the request should they exist
newService = obs_service_create( newService = obs_service_create(
newType.toUtf8(), STREAM_SERVICE_ID, newType.toUtf8(), STREAM_SERVICE_ID,
updatedSettings, csHotkeys); updatedSettings, csHotkeys);
} }
else { else {
// Service type changed: override service settings // Service type changed: override service settings
newService = obs_service_create( newService = obs_service_create(
newType.toUtf8(), STREAM_SERVICE_ID, newType.toUtf8(), STREAM_SERVICE_ID,
newSettings, csHotkeys); newSettings, csHotkeys);
} }
obs_frontend_set_streaming_service(newService); obs_frontend_set_streaming_service(newService);
} }
obs_frontend_streaming_start(); obs_frontend_streaming_start();
// Stream settings provided in StartStreaming are not persisted to disk // Stream settings provided in StartStreaming are not persisted to disk
if (newService != nullptr) { if (newService != nullptr) {
obs_frontend_set_streaming_service(configuredService); obs_frontend_set_streaming_service(configuredService);
} }
req->SendOKResponse(); req->SendOKResponse();
} else { } else {
req->SendErrorResponse("streaming already active"); req->SendErrorResponse("streaming already active");
} }
} }
/** /**
@ -173,12 +173,12 @@
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) { void WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) {
if (obs_frontend_streaming_active() == true) { if (obs_frontend_streaming_active() == true) {
obs_frontend_streaming_stop(); obs_frontend_streaming_stop();
req->SendOKResponse(); req->SendOKResponse();
} else { } else {
req->SendErrorResponse("streaming not active"); req->SendErrorResponse("streaming not active");
} }
} }
/** /**
@ -199,50 +199,50 @@ void WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleSetStreamSettings(WSRequestHandler* req) { void WSRequestHandler::HandleSetStreamSettings(WSRequestHandler* req) {
OBSService service = obs_frontend_get_streaming_service(); OBSService service = obs_frontend_get_streaming_service();
OBSDataAutoRelease requestSettings = obs_data_get_obj(req->data, "settings"); OBSDataAutoRelease requestSettings = obs_data_get_obj(req->data, "settings");
if (!requestSettings) { if (!requestSettings) {
req->SendErrorResponse("'settings' are required'"); req->SendErrorResponse("'settings' are required'");
return; return;
} }
QString serviceType = obs_service_get_type(service); QString serviceType = obs_service_get_type(service);
QString requestedType = obs_data_get_string(req->data, "type"); QString requestedType = obs_data_get_string(req->data, "type");
if (requestedType != nullptr && requestedType != serviceType) { if (requestedType != nullptr && requestedType != serviceType) {
OBSDataAutoRelease hotkeys = obs_hotkeys_save_service(service); OBSDataAutoRelease hotkeys = obs_hotkeys_save_service(service);
service = obs_service_create( service = obs_service_create(
requestedType.toUtf8(), STREAM_SERVICE_ID, requestSettings, hotkeys); requestedType.toUtf8(), STREAM_SERVICE_ID, requestSettings, hotkeys);
} else { } else {
// If type isn't changing, we should overlay the settings we got // If type isn't changing, we should overlay the settings we got
// to the existing settings. By doing so, you can send a request that // to the existing settings. By doing so, you can send a request that
// only contains the settings you want to change, instead of having to // only contains the settings you want to change, instead of having to
// do a get and then change them // do a get and then change them
OBSDataAutoRelease existingSettings = obs_service_get_settings(service); OBSDataAutoRelease existingSettings = obs_service_get_settings(service);
OBSDataAutoRelease newSettings = obs_data_create(); OBSDataAutoRelease newSettings = obs_data_create();
// Apply existing settings // Apply existing settings
obs_data_apply(newSettings, existingSettings); obs_data_apply(newSettings, existingSettings);
// Then apply the settings from the request // Then apply the settings from the request
obs_data_apply(newSettings, requestSettings); obs_data_apply(newSettings, requestSettings);
obs_service_update(service, newSettings); obs_service_update(service, newSettings);
} }
//if save is specified we should immediately save the streaming service //if save is specified we should immediately save the streaming service
if (obs_data_get_bool(req->data, "save")) { if (obs_data_get_bool(req->data, "save")) {
obs_frontend_save_streaming_service(); obs_frontend_save_streaming_service();
} }
OBSDataAutoRelease serviceSettings = obs_service_get_settings(service); OBSDataAutoRelease serviceSettings = obs_service_get_settings(service);
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "type", requestedType.toUtf8()); obs_data_set_string(response, "type", requestedType.toUtf8());
obs_data_set_obj(response, "settings", serviceSettings); obs_data_set_obj(response, "settings", serviceSettings);
req->SendOKResponse(response); req->SendOKResponse(response);
} }
/** /**
@ -262,16 +262,16 @@ void WSRequestHandler::HandleStopStreaming(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleGetStreamSettings(WSRequestHandler* req) { void WSRequestHandler::HandleGetStreamSettings(WSRequestHandler* req) {
OBSService service = obs_frontend_get_streaming_service(); OBSService service = obs_frontend_get_streaming_service();
const char* serviceType = obs_service_get_type(service); const char* serviceType = obs_service_get_type(service);
OBSDataAutoRelease settings = obs_service_get_settings(service); OBSDataAutoRelease settings = obs_service_get_settings(service);
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "type", serviceType); obs_data_set_string(response, "type", serviceType);
obs_data_set_obj(response, "settings", settings); obs_data_set_obj(response, "settings", settings);
req->SendOKResponse(response); req->SendOKResponse(response);
} }
/** /**
@ -283,6 +283,6 @@ void WSRequestHandler::HandleGetStreamSettings(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleSaveStreamSettings(WSRequestHandler* req) { void WSRequestHandler::HandleSaveStreamSettings(WSRequestHandler* req) {
obs_frontend_save_streaming_service(); obs_frontend_save_streaming_service();
req->SendOKResponse(); req->SendOKResponse();
} }

View File

@ -14,12 +14,12 @@
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler* req) { void WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler* req) {
bool previewActive = obs_frontend_preview_program_mode_active(); bool previewActive = obs_frontend_preview_program_mode_active();
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_bool(response, "studio-mode", previewActive); obs_data_set_bool(response, "studio-mode", previewActive);
req->SendOKResponse(response); req->SendOKResponse(response);
} }
/** /**
@ -35,19 +35,19 @@
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler* req) { void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler* req) {
if (!obs_frontend_preview_program_mode_active()) { if (!obs_frontend_preview_program_mode_active()) {
req->SendErrorResponse("studio mode not enabled"); req->SendErrorResponse("studio mode not enabled");
return; return;
} }
OBSSourceAutoRelease scene = obs_frontend_get_current_preview_scene(); OBSSourceAutoRelease scene = obs_frontend_get_current_preview_scene();
OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(scene); OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(scene);
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "name", obs_source_get_name(scene)); obs_data_set_string(data, "name", obs_source_get_name(scene));
obs_data_set_array(data, "sources", sceneItems); obs_data_set_array(data, "sources", sceneItems);
req->SendOKResponse(data); req->SendOKResponse(data);
} }
/** /**
@ -62,25 +62,25 @@ void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler* req) { void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler* req) {
if (!obs_frontend_preview_program_mode_active()) { if (!obs_frontend_preview_program_mode_active()) {
req->SendErrorResponse("studio mode not enabled"); req->SendErrorResponse("studio mode not enabled");
return; return;
} }
if (!req->hasField("scene-name")) { if (!req->hasField("scene-name")) {
req->SendErrorResponse("missing request parameters"); req->SendErrorResponse("missing request parameters");
return; return;
} }
const char* scene_name = obs_data_get_string(req->data, "scene-name"); const char* scene_name = obs_data_get_string(req->data, "scene-name");
OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(scene_name); OBSSourceAutoRelease scene = Utils::GetSceneFromNameOrCurrent(scene_name);
if (scene) { if (scene) {
obs_frontend_set_current_preview_scene(scene); obs_frontend_set_current_preview_scene(scene);
req->SendOKResponse(); req->SendOKResponse();
} else { } else {
req->SendErrorResponse("specified scene doesn't exist"); req->SendErrorResponse("specified scene doesn't exist");
} }
} }
/** /**
@ -97,39 +97,39 @@ void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler* req) { void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler* req) {
if (!obs_frontend_preview_program_mode_active()) { if (!obs_frontend_preview_program_mode_active()) {
req->SendErrorResponse("studio mode not enabled"); req->SendErrorResponse("studio mode not enabled");
return; return;
} }
if (req->hasField("with-transition")) { if (req->hasField("with-transition")) {
OBSDataAutoRelease transitionInfo = OBSDataAutoRelease transitionInfo =
obs_data_get_obj(req->data, "with-transition"); obs_data_get_obj(req->data, "with-transition");
if (obs_data_has_user_value(transitionInfo, "name")) { if (obs_data_has_user_value(transitionInfo, "name")) {
QString transitionName = QString transitionName =
obs_data_get_string(transitionInfo, "name"); obs_data_get_string(transitionInfo, "name");
if (transitionName.isEmpty()) { if (transitionName.isEmpty()) {
req->SendErrorResponse("invalid request parameters"); req->SendErrorResponse("invalid request parameters");
return; return;
} }
bool success = Utils::SetTransitionByName(transitionName); bool success = Utils::SetTransitionByName(transitionName);
if (!success) { if (!success) {
req->SendErrorResponse("specified transition doesn't exist"); req->SendErrorResponse("specified transition doesn't exist");
return; return;
} }
} }
if (obs_data_has_user_value(transitionInfo, "duration")) { if (obs_data_has_user_value(transitionInfo, "duration")) {
int transitionDuration = int transitionDuration =
obs_data_get_int(transitionInfo, "duration"); obs_data_get_int(transitionInfo, "duration");
Utils::SetTransitionDuration(transitionDuration); Utils::SetTransitionDuration(transitionDuration);
} }
} }
Utils::TransitionToProgram(); Utils::TransitionToProgram();
req->SendOKResponse(); req->SendOKResponse();
} }
/** /**
@ -141,8 +141,8 @@ void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleEnableStudioMode(WSRequestHandler* req) { void WSRequestHandler::HandleEnableStudioMode(WSRequestHandler* req) {
obs_frontend_set_preview_program_mode(true); obs_frontend_set_preview_program_mode(true);
req->SendOKResponse(); req->SendOKResponse();
} }
/** /**
@ -154,8 +154,8 @@ void WSRequestHandler::HandleEnableStudioMode(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleDisableStudioMode(WSRequestHandler* req) { void WSRequestHandler::HandleDisableStudioMode(WSRequestHandler* req) {
obs_frontend_set_preview_program_mode(false); obs_frontend_set_preview_program_mode(false);
req->SendOKResponse(); req->SendOKResponse();
} }
/** /**
@ -167,7 +167,7 @@ void WSRequestHandler::HandleDisableStudioMode(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleToggleStudioMode(WSRequestHandler* req) { void WSRequestHandler::HandleToggleStudioMode(WSRequestHandler* req) {
bool previewProgramMode = obs_frontend_preview_program_mode_active(); bool previewProgramMode = obs_frontend_preview_program_mode_active();
obs_frontend_set_preview_program_mode(!previewProgramMode); obs_frontend_set_preview_program_mode(!previewProgramMode);
req->SendOKResponse(); req->SendOKResponse();
} }

View File

@ -16,26 +16,26 @@
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleGetTransitionList(WSRequestHandler* req) { void WSRequestHandler::HandleGetTransitionList(WSRequestHandler* req) {
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
obs_frontend_source_list transitionList = {}; obs_frontend_source_list transitionList = {};
obs_frontend_get_transitions(&transitionList); obs_frontend_get_transitions(&transitionList);
OBSDataArrayAutoRelease transitions = obs_data_array_create(); OBSDataArrayAutoRelease transitions = obs_data_array_create();
for (size_t i = 0; i < transitionList.sources.num; i++) { for (size_t i = 0; i < transitionList.sources.num; i++) {
OBSSource transition = transitionList.sources.array[i]; OBSSource transition = transitionList.sources.array[i];
OBSDataAutoRelease obj = obs_data_create(); OBSDataAutoRelease obj = obs_data_create();
obs_data_set_string(obj, "name", obs_source_get_name(transition)); obs_data_set_string(obj, "name", obs_source_get_name(transition));
obs_data_array_push_back(transitions, obj); obs_data_array_push_back(transitions, obj);
} }
obs_frontend_source_list_free(&transitionList); obs_frontend_source_list_free(&transitionList);
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "current-transition", obs_data_set_string(response, "current-transition",
obs_source_get_name(currentTransition)); obs_source_get_name(currentTransition));
obs_data_set_array(response, "transitions", transitions); obs_data_set_array(response, "transitions", transitions);
req->SendOKResponse(response); req->SendOKResponse(response);
} }
/** /**
@ -50,16 +50,16 @@
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler* req) { void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler* req) {
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition(); OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_string(response, "name", obs_data_set_string(response, "name",
obs_source_get_name(currentTransition)); obs_source_get_name(currentTransition));
if (!obs_transition_fixed(currentTransition)) if (!obs_transition_fixed(currentTransition))
obs_data_set_int(response, "duration", Utils::GetTransitionDuration()); obs_data_set_int(response, "duration", Utils::GetTransitionDuration());
req->SendOKResponse(response); req->SendOKResponse(response);
} }
/** /**
@ -73,17 +73,17 @@ void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler* req) {
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler* req) { void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler* req) {
if (!req->hasField("transition-name")) { if (!req->hasField("transition-name")) {
req->SendErrorResponse("missing request parameters"); req->SendErrorResponse("missing request parameters");
return; return;
} }
QString name = obs_data_get_string(req->data, "transition-name"); QString name = obs_data_get_string(req->data, "transition-name");
bool success = Utils::SetTransitionByName(name); bool success = Utils::SetTransitionByName(name);
if (success) if (success)
req->SendOKResponse(); req->SendOKResponse();
else else
req->SendErrorResponse("requested transition does not exist"); req->SendErrorResponse("requested transition does not exist");
} }
/** /**
@ -97,14 +97,14 @@ void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler* req) {
* @since 4.0.0 * @since 4.0.0
*/ */
void WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler* req) { void WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler* req) {
if (!req->hasField("duration")) { if (!req->hasField("duration")) {
req->SendErrorResponse("missing request parameters"); req->SendErrorResponse("missing request parameters");
return; return;
} }
int ms = obs_data_get_int(req->data, "duration"); int ms = obs_data_get_int(req->data, "duration");
Utils::SetTransitionDuration(ms); Utils::SetTransitionDuration(ms);
req->SendOKResponse(); req->SendOKResponse();
} }
/** /**
@ -118,9 +118,9 @@ void WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
void WSRequestHandler::HandleGetTransitionDuration(WSRequestHandler* req) { void WSRequestHandler::HandleGetTransitionDuration(WSRequestHandler* req) {
OBSDataAutoRelease response = obs_data_create(); OBSDataAutoRelease response = obs_data_create();
obs_data_set_int(response, "transition-duration", obs_data_set_int(response, "transition-duration",
Utils::GetTransitionDuration()); Utils::GetTransitionDuration());
req->SendOKResponse(response); req->SendOKResponse(response);
} }

View File

@ -33,136 +33,136 @@ QT_USE_NAMESPACE
WSServer* WSServer::Instance = nullptr; WSServer* WSServer::Instance = nullptr;
WSServer::WSServer(QObject* parent) WSServer::WSServer(QObject* parent)
: QObject(parent), : QObject(parent),
_wsServer(Q_NULLPTR), _wsServer(Q_NULLPTR),
_clients(), _clients(),
_clMutex(QMutex::Recursive) _clMutex(QMutex::Recursive)
{ {
_wsServer = new QWebSocketServer( _wsServer = new QWebSocketServer(
QStringLiteral("obs-websocket"), QStringLiteral("obs-websocket"),
QWebSocketServer::NonSecureMode); QWebSocketServer::NonSecureMode);
} }
WSServer::~WSServer() { WSServer::~WSServer() {
Stop(); Stop();
} }
void WSServer::Start(quint16 port) { void WSServer::Start(quint16 port) {
if (port == _wsServer->serverPort()) if (port == _wsServer->serverPort())
return; return;
if(_wsServer->isListening()) if(_wsServer->isListening())
Stop(); Stop();
bool serverStarted = _wsServer->listen(QHostAddress::Any, port); bool serverStarted = _wsServer->listen(QHostAddress::Any, port);
if (serverStarted) { if (serverStarted) {
blog(LOG_INFO, "server started successfully on TCP port %d", port); blog(LOG_INFO, "server started successfully on TCP port %d", port);
connect(_wsServer, SIGNAL(newConnection()), connect(_wsServer, SIGNAL(newConnection()),
this, SLOT(onNewConnection())); this, SLOT(onNewConnection()));
} }
else { else {
QString errorString = _wsServer->errorString(); QString errorString = _wsServer->errorString();
blog(LOG_ERROR, blog(LOG_ERROR,
"error: failed to start server on TCP port %d: %s", "error: failed to start server on TCP port %d: %s",
port, errorString.toUtf8().constData()); port, errorString.toUtf8().constData());
QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window(); QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window();
obs_frontend_push_ui_translation(obs_module_get_string); obs_frontend_push_ui_translation(obs_module_get_string);
QString title = tr("OBSWebsocket.Server.StartFailed.Title"); QString title = tr("OBSWebsocket.Server.StartFailed.Title");
QString msg = tr("OBSWebsocket.Server.StartFailed.Message").arg(port); QString msg = tr("OBSWebsocket.Server.StartFailed.Message").arg(port);
obs_frontend_pop_ui_translation(); obs_frontend_pop_ui_translation();
QMessageBox::warning(mainWindow, title, msg); QMessageBox::warning(mainWindow, title, msg);
} }
} }
void WSServer::Stop() { void WSServer::Stop() {
QMutexLocker locker(&_clMutex); QMutexLocker locker(&_clMutex);
for(QWebSocket* pClient : _clients) { for(QWebSocket* pClient : _clients) {
pClient->close(); pClient->close();
} }
locker.unlock(); locker.unlock();
_wsServer->close(); _wsServer->close();
blog(LOG_INFO, "server stopped successfully"); blog(LOG_INFO, "server stopped successfully");
} }
void WSServer::broadcast(QString message) { void WSServer::broadcast(QString message) {
QMutexLocker locker(&_clMutex); QMutexLocker locker(&_clMutex);
for(QWebSocket* pClient : _clients) { for(QWebSocket* pClient : _clients) {
if (Config::Current()->AuthRequired if (Config::Current()->AuthRequired
&& (pClient->property(PROP_AUTHENTICATED).toBool() == false)) { && (pClient->property(PROP_AUTHENTICATED).toBool() == false)) {
// Skip this client if unauthenticated // Skip this client if unauthenticated
continue; continue;
} }
pClient->sendTextMessage(message); pClient->sendTextMessage(message);
} }
} }
void WSServer::onNewConnection() { void WSServer::onNewConnection() {
QWebSocket* pSocket = _wsServer->nextPendingConnection(); QWebSocket* pSocket = _wsServer->nextPendingConnection();
if (pSocket) { if (pSocket) {
connect(pSocket, SIGNAL(textMessageReceived(const QString&)), connect(pSocket, SIGNAL(textMessageReceived(const QString&)),
this, SLOT(onTextMessageReceived(QString))); this, SLOT(onTextMessageReceived(QString)));
connect(pSocket, SIGNAL(disconnected()), connect(pSocket, SIGNAL(disconnected()),
this, SLOT(onSocketDisconnected())); this, SLOT(onSocketDisconnected()));
pSocket->setProperty(PROP_AUTHENTICATED, false); pSocket->setProperty(PROP_AUTHENTICATED, false);
QMutexLocker locker(&_clMutex); QMutexLocker locker(&_clMutex);
_clients << pSocket; _clients << pSocket;
locker.unlock(); locker.unlock();
QHostAddress clientAddr = pSocket->peerAddress(); QHostAddress clientAddr = pSocket->peerAddress();
QString clientIp = Utils::FormatIPAddress(clientAddr); QString clientIp = Utils::FormatIPAddress(clientAddr);
blog(LOG_INFO, "new client connection from %s:%d", blog(LOG_INFO, "new client connection from %s:%d",
clientIp.toUtf8().constData(), pSocket->peerPort()); clientIp.toUtf8().constData(), pSocket->peerPort());
obs_frontend_push_ui_translation(obs_module_get_string); obs_frontend_push_ui_translation(obs_module_get_string);
QString title = tr("OBSWebsocket.NotifyConnect.Title"); QString title = tr("OBSWebsocket.NotifyConnect.Title");
QString msg = tr("OBSWebsocket.NotifyConnect.Message") QString msg = tr("OBSWebsocket.NotifyConnect.Message")
.arg(Utils::FormatIPAddress(clientAddr)); .arg(Utils::FormatIPAddress(clientAddr));
obs_frontend_pop_ui_translation(); obs_frontend_pop_ui_translation();
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title); Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
} }
} }
void WSServer::onTextMessageReceived(QString message) { void WSServer::onTextMessageReceived(QString message) {
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender()); QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
if (pSocket) { if (pSocket) {
WSRequestHandler handler(pSocket); WSRequestHandler handler(pSocket);
handler.processIncomingMessage(message); handler.processIncomingMessage(message);
} }
} }
void WSServer::onSocketDisconnected() { void WSServer::onSocketDisconnected() {
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender()); QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
if (pSocket) { if (pSocket) {
pSocket->setProperty(PROP_AUTHENTICATED, false); pSocket->setProperty(PROP_AUTHENTICATED, false);
QMutexLocker locker(&_clMutex); QMutexLocker locker(&_clMutex);
_clients.removeAll(pSocket); _clients.removeAll(pSocket);
locker.unlock(); locker.unlock();
pSocket->deleteLater(); pSocket->deleteLater();
QHostAddress clientAddr = pSocket->peerAddress(); QHostAddress clientAddr = pSocket->peerAddress();
QString clientIp = Utils::FormatIPAddress(clientAddr); QString clientIp = Utils::FormatIPAddress(clientAddr);
blog(LOG_INFO, "client %s:%d disconnected", blog(LOG_INFO, "client %s:%d disconnected",
clientIp.toUtf8().constData(), pSocket->peerPort()); clientIp.toUtf8().constData(), pSocket->peerPort());
obs_frontend_push_ui_translation(obs_module_get_string); obs_frontend_push_ui_translation(obs_module_get_string);
QString title = tr("OBSWebsocket.NotifyDisconnect.Title"); QString title = tr("OBSWebsocket.NotifyDisconnect.Title");
QString msg = tr("OBSWebsocket.NotifyDisconnect.Message") QString msg = tr("OBSWebsocket.NotifyDisconnect.Message")
.arg(Utils::FormatIPAddress(clientAddr)); .arg(Utils::FormatIPAddress(clientAddr));
obs_frontend_pop_ui_translation(); obs_frontend_pop_ui_translation();
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title); Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
} }
} }

View File

@ -29,24 +29,24 @@ QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
QT_FORWARD_DECLARE_CLASS(QWebSocket) QT_FORWARD_DECLARE_CLASS(QWebSocket)
class WSServer : public QObject { class WSServer : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit WSServer(QObject* parent = Q_NULLPTR); explicit WSServer(QObject* parent = Q_NULLPTR);
virtual ~WSServer(); virtual ~WSServer();
void Start(quint16 port); void Start(quint16 port);
void Stop(); void Stop();
void broadcast(QString message); void broadcast(QString message);
static WSServer* Instance; static WSServer* Instance;
private slots: private slots:
void onNewConnection(); void onNewConnection();
void onTextMessageReceived(QString message); void onTextMessageReceived(QString message);
void onSocketDisconnected(); void onSocketDisconnected();
private: private:
QWebSocketServer* _wsServer; QWebSocketServer* _wsServer;
QList<QWebSocket*> _clients; QList<QWebSocket*> _clients;
QMutex _clMutex; QMutex _clMutex;
}; };
#endif // WSSERVER_H #endif // WSSERVER_H

View File

@ -26,79 +26,79 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#define CHANGE_ME "changeme" #define CHANGE_ME "changeme"
SettingsDialog::SettingsDialog(QWidget* parent) : SettingsDialog::SettingsDialog(QWidget* parent) :
QDialog(parent, Qt::Dialog), QDialog(parent, Qt::Dialog),
ui(new Ui::SettingsDialog) ui(new Ui::SettingsDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
connect(ui->authRequired, &QCheckBox::stateChanged, connect(ui->authRequired, &QCheckBox::stateChanged,
this, &SettingsDialog::AuthCheckboxChanged); this, &SettingsDialog::AuthCheckboxChanged);
connect(ui->buttonBox, &QDialogButtonBox::accepted, connect(ui->buttonBox, &QDialogButtonBox::accepted,
this, &SettingsDialog::FormAccepted); this, &SettingsDialog::FormAccepted);
AuthCheckboxChanged(); AuthCheckboxChanged();
} }
void SettingsDialog::showEvent(QShowEvent* event) { void SettingsDialog::showEvent(QShowEvent* event) {
Config* conf = Config::Current(); Config* conf = Config::Current();
ui->serverEnabled->setChecked(conf->ServerEnabled); ui->serverEnabled->setChecked(conf->ServerEnabled);
ui->serverPort->setValue(conf->ServerPort); ui->serverPort->setValue(conf->ServerPort);
ui->debugEnabled->setChecked(conf->DebugEnabled); ui->debugEnabled->setChecked(conf->DebugEnabled);
ui->alertsEnabled->setChecked(conf->AlertsEnabled); ui->alertsEnabled->setChecked(conf->AlertsEnabled);
ui->authRequired->setChecked(conf->AuthRequired); ui->authRequired->setChecked(conf->AuthRequired);
ui->password->setText(CHANGE_ME); ui->password->setText(CHANGE_ME);
} }
void SettingsDialog::ToggleShowHide() { void SettingsDialog::ToggleShowHide() {
if (!isVisible()) if (!isVisible())
setVisible(true); setVisible(true);
else else
setVisible(false); setVisible(false);
} }
void SettingsDialog::AuthCheckboxChanged() { void SettingsDialog::AuthCheckboxChanged() {
if (ui->authRequired->isChecked()) if (ui->authRequired->isChecked())
ui->password->setEnabled(true); ui->password->setEnabled(true);
else else
ui->password->setEnabled(false); ui->password->setEnabled(false);
} }
void SettingsDialog::FormAccepted() { void SettingsDialog::FormAccepted() {
Config* conf = Config::Current(); Config* conf = Config::Current();
conf->ServerEnabled = ui->serverEnabled->isChecked(); conf->ServerEnabled = ui->serverEnabled->isChecked();
conf->ServerPort = ui->serverPort->value(); conf->ServerPort = ui->serverPort->value();
conf->DebugEnabled = ui->debugEnabled->isChecked(); conf->DebugEnabled = ui->debugEnabled->isChecked();
conf->AlertsEnabled = ui->alertsEnabled->isChecked(); conf->AlertsEnabled = ui->alertsEnabled->isChecked();
if (ui->authRequired->isChecked()) { if (ui->authRequired->isChecked()) {
if (ui->password->text() != CHANGE_ME) { if (ui->password->text() != CHANGE_ME) {
conf->SetPassword(ui->password->text()); conf->SetPassword(ui->password->text());
} }
if (!Config::Current()->Secret.isEmpty()) if (!Config::Current()->Secret.isEmpty())
conf->AuthRequired = true; conf->AuthRequired = true;
else else
conf->AuthRequired = false; conf->AuthRequired = false;
} }
else else
{ {
conf->AuthRequired = false; conf->AuthRequired = false;
} }
conf->Save(); conf->Save();
if (conf->ServerEnabled) if (conf->ServerEnabled)
WSServer::Instance->Start(conf->ServerPort); WSServer::Instance->Start(conf->ServerPort);
else else
WSServer::Instance->Stop(); WSServer::Instance->Stop();
} }
SettingsDialog::~SettingsDialog() { SettingsDialog::~SettingsDialog() {
delete ui; delete ui;
} }

View File

@ -25,20 +25,20 @@ with this program. If not, see <https://www.gnu.org/licenses/>
class SettingsDialog : public QDialog class SettingsDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit SettingsDialog(QWidget* parent = 0); explicit SettingsDialog(QWidget* parent = 0);
~SettingsDialog(); ~SettingsDialog();
void showEvent(QShowEvent* event); void showEvent(QShowEvent* event);
void ToggleShowHide(); void ToggleShowHide();
private Q_SLOTS: private Q_SLOTS:
void AuthCheckboxChanged(); void AuthCheckboxChanged();
void FormAccepted(); void FormAccepted();
private: private:
Ui::SettingsDialog* ui; Ui::SettingsDialog* ui;
}; };
#endif // SETTINGSDIALOG_H #endif // SETTINGSDIALOG_H

View File

@ -2,150 +2,150 @@
<ui version="4.0"> <ui version="4.0">
<class>SettingsDialog</class> <class>SettingsDialog</class>
<widget class="QDialog" name="SettingsDialog"> <widget class="QDialog" name="SettingsDialog">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>407</width> <width>407</width>
<height>195</height> <height>195</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>OBSWebsocket.Settings.DialogTitle</string> <string>OBSWebsocket.Settings.DialogTitle</string>
</property> </property>
<property name="sizeGripEnabled"> <property name="sizeGripEnabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint"> <property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum> <enum>QLayout::SetDefaultConstraint</enum>
</property> </property>
<item> <item>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
<item row="3" column="1"> <item row="3" column="1">
<widget class="QCheckBox" name="authRequired"> <widget class="QCheckBox" name="authRequired">
<property name="text"> <property name="text">
<string>OBSWebsocket.Settings.AuthRequired</string> <string>OBSWebsocket.Settings.AuthRequired</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="4" column="0">
<widget class="QLabel" name="lbl_password"> <widget class="QLabel" name="lbl_password">
<property name="text"> <property name="text">
<string>OBSWebsocket.Settings.Password</string> <string>OBSWebsocket.Settings.Password</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="4" column="1">
<widget class="QLineEdit" name="password"> <widget class="QLineEdit" name="password">
<property name="echoMode"> <property name="echoMode">
<enum>QLineEdit::Password</enum> <enum>QLineEdit::Password</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QCheckBox" name="serverEnabled"> <widget class="QCheckBox" name="serverEnabled">
<property name="text"> <property name="text">
<string>OBSWebsocket.Settings.ServerEnable</string> <string>OBSWebsocket.Settings.ServerEnable</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="lbl_serverPort"> <widget class="QLabel" name="lbl_serverPort">
<property name="text"> <property name="text">
<string>OBSWebsocket.Settings.ServerPort</string> <string>OBSWebsocket.Settings.ServerPort</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QSpinBox" name="serverPort"> <widget class="QSpinBox" name="serverPort">
<property name="minimum"> <property name="minimum">
<number>1024</number> <number>1024</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>65535</number> <number>65535</number>
</property> </property>
<property name="value"> <property name="value">
<number>4444</number> <number>4444</number>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="5" column="1">
<widget class="QCheckBox" name="alertsEnabled"> <widget class="QCheckBox" name="alertsEnabled">
<property name="text"> <property name="text">
<string>OBSWebsocket.Settings.AlertsEnable</string> <string>OBSWebsocket.Settings.AlertsEnable</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="6" column="1">
<widget class="QCheckBox" name="debugEnabled"> <widget class="QCheckBox" name="debugEnabled">
<property name="text"> <property name="text">
<string>OBSWebsocket.Settings.DebugEnable</string> <string>OBSWebsocket.Settings.DebugEnable</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>false</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>
<signal>accepted()</signal> <signal>accepted()</signal>
<receiver>SettingsDialog</receiver> <receiver>SettingsDialog</receiver>
<slot>accept()</slot> <slot>accept()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>248</x> <x>248</x>
<y>294</y> <y>294</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>157</x> <x>157</x>
<y>314</y> <y>314</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>
<signal>rejected()</signal> <signal>rejected()</signal>
<receiver>SettingsDialog</receiver> <receiver>SettingsDialog</receiver>
<slot>reject()</slot> <slot>reject()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>316</x> <x>316</x>
<y>300</y> <y>300</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>286</x> <x>286</x>
<y>314</y> <y>314</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>
</connections> </connections>
</ui> </ui>

View File

@ -40,41 +40,41 @@ OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
SettingsDialog* settings_dialog; SettingsDialog* settings_dialog;
bool obs_module_load(void) { bool obs_module_load(void) {
blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION); blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION);
blog(LOG_INFO, "qt version (compile-time): %s ; qt version (run-time): %s", blog(LOG_INFO, "qt version (compile-time): %s ; qt version (run-time): %s",
QT_VERSION_STR, qVersion()); QT_VERSION_STR, qVersion());
// Core setup // Core setup
Config* config = Config::Current(); Config* config = Config::Current();
config->Load(); config->Load();
WSServer::Instance = new WSServer(); WSServer::Instance = new WSServer();
WSEvents::Instance = new WSEvents(WSServer::Instance); WSEvents::Instance = new WSEvents(WSServer::Instance);
if (config->ServerEnabled) if (config->ServerEnabled)
WSServer::Instance->Start(config->ServerPort); WSServer::Instance->Start(config->ServerPort);
// UI setup // UI setup
QAction* menu_action = (QAction*)obs_frontend_add_tools_menu_qaction( QAction* menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(
obs_module_text("OBSWebsocket.Menu.SettingsItem")); obs_module_text("OBSWebsocket.Menu.SettingsItem"));
obs_frontend_push_ui_translation(obs_module_get_string); obs_frontend_push_ui_translation(obs_module_get_string);
QMainWindow* main_window = (QMainWindow*)obs_frontend_get_main_window(); QMainWindow* main_window = (QMainWindow*)obs_frontend_get_main_window();
settings_dialog = new SettingsDialog(main_window); settings_dialog = new SettingsDialog(main_window);
obs_frontend_pop_ui_translation(); obs_frontend_pop_ui_translation();
auto menu_cb = [] { auto menu_cb = [] {
settings_dialog->ToggleShowHide(); settings_dialog->ToggleShowHide();
}; };
menu_action->connect(menu_action, &QAction::triggered, menu_cb); menu_action->connect(menu_action, &QAction::triggered, menu_cb);
// Loading finished // Loading finished
blog(LOG_INFO, "module loaded!"); blog(LOG_INFO, "module loaded!");
return true; return true;
} }
void obs_module_unload() { void obs_module_unload() {
blog(LOG_INFO, "goodbye!"); blog(LOG_INFO, "goodbye!");
} }