Compare commits

...

33 Commits
1.6.1 ... 1.6.2

Author SHA1 Message Date
WarmUpTill
255d10e603 fix auto fill not wokring for modified entries on time tab 2020-08-09 20:48:49 +02:00
WarmUpTill
ab3e93ab84 add fallback to old format for sequence import 2020-08-02 20:22:13 +02:00
WarmUpTill
3b5f88c14f do not sort sequence and region on add 2020-08-02 19:38:32 +02:00
WarmUpTill
2f33b6bdc3 fix data loss for sequence delay 2020-08-02 19:37:37 +02:00
WarmUpTill
3c1e240722 add option to set startup behavior 2020-08-02 18:45:44 +02:00
WarmUpTill
91831a1075 fix autoStop not being persisted 2020-08-02 18:00:50 +02:00
WarmUpTill
ccc824abc1 fix loading of old delay values 2020-08-02 17:46:36 +02:00
WarmUpTill
6f39d4648b add included 'thread' and 'QAction' for Linux and macOS build 2020-08-01 20:34:12 +02:00
WarmUpTill
74cb400426 Merge branch 'master' of https://github.com/WarmUpTill/SceneSwitcher 2020-08-01 20:15:49 +02:00
WarmUpTill
910b1dde99 set sceneRoundTripDelayUnits 2020-08-01 16:19:57 +02:00
WarmUpTill
e462d7f420 fix sequence not stopping when manually switching scene 2020-08-01 16:11:54 +02:00
WarmUpTill
0cdd8bf1d0 move UI init of tabs to respective source files 2020-08-01 11:14:54 +02:00
WarmUpTill
f8ad8c31be scene sequence adjustments
use double internally instead of two ints
rework save / load to file to reuse existing functionality
add option to select between seconds / minutes / hours
2020-08-01 10:12:32 +02:00
WarmUpTill
93c889c215 fix MacOS CI issue 2020-07-31 22:18:52 +02:00
WarmUpTill
f8b505fa4e silence warnings 2020-07-31 22:05:55 +02:00
WarmUpTill
1516263d2a save and load tab order 2020-07-31 21:58:17 +02:00
WarmUpTill
79f5ec340d allow reordering of tabs and silence a few UI warnings 2020-07-29 20:23:03 +02:00
WarmUpTill
cd54cc17c0 fix switch priority falling back to default values on startup 2020-07-09 19:57:55 +02:00
WarmUpTill
098159dc0f increase window size on mac
currently the general tab will be compressed to an almost unusable level due to the differences in Windows and Macintosh styles
2020-06-30 20:11:44 +02:00
WarmUpTill
a4418996f4 disable mediaTime selection if restriction is set to none 2020-06-19 19:52:43 +02:00
WarmUpTill
340cb559ee add option to override transitionOverrides 2020-06-17 22:16:47 +02:00
WarmUpTill
eeefd8279f allow reordering of file/media/sequence/region 2020-06-16 20:25:25 +02:00
WarmUpTill
e62dc91cd7 fix linux/mac build error 2020-06-15 21:46:58 +02:00
WarmUpTill
536041abd2 Merge branch 'master' of https://github.com/WarmUpTill/SceneSwitcher 2020-06-15 21:20:02 +02:00
WarmUpTill
d3ab9e01c5 add support for remote file using libcurl 2020-06-15 21:19:34 +02:00
WarmUpTill
2113025877 undo switch to Visual Studio 15 2017
windows-latest image only supports Visual Studio 16 2019
2020-06-15 18:48:32 +02:00
WarmUpTill
6aaffeb5a4 use CMAKE_GENERATOR "Visual Studio 15 2017"
the 2017 version of the Microsoft Visual C++ Redistributable for Visual Studio is more widespread than the 2019 one and thus is less likely to require users to install the Redistributable manually
2020-06-15 18:32:01 +02:00
WarmUpTill
2a92df4f3c add draft of remote file support 2020-06-14 20:35:02 +02:00
WarmUpTill
560044bbbf implement export/import of settings 2020-06-14 15:22:34 +02:00
WarmUpTill
4bd8cdf5a0 move auto start/stop to gernal.cpp 2020-06-14 14:17:03 +02:00
WarmUpTill
c17cbb66d9 write to OBS log if verbose logging is enabled 2020-06-14 14:14:00 +02:00
WarmUpTill
8d23dd46b0 add export/import button and verbose log checkbox 2020-06-14 13:57:11 +02:00
WarmUpTill
bf70bd7c60 move saving and loading to separate files 2020-06-13 21:56:26 +02:00
18 changed files with 2909 additions and 1617 deletions

View File

@@ -35,6 +35,7 @@ jobs:
shell: bash
run: |
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}/CI/macos
brew update
brew bundle
cd -
- name: 'Install prerequisite: Pre-built dependencies'

View File

@@ -13,6 +13,7 @@ if(BUILD_OUT_OF_TREE)
find_package(LibObs REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Libcurl REQUIRED)
find_path(LIBOBS_FRONTEND_INCLUDE_DIR HINTS ${LIBOBS_INCLUDE_DIRS})
find_file(LIBOBS_FRONTEND_API_LIB NAMES libobs-frontend-api.* HINTS ${LIBOBS_LIB} )
@@ -96,14 +97,16 @@ if(BUILD_OUT_OF_TREE)
"${LIBOBS_INCLUDE_DIRS}"
"${LIBOBS_FRONTEND_INCLUDE_DIR}"
${Qt5Core_INCLUDES}
${Qt5Widgets_INCLUDES})
${Qt5Widgets_INCLUDES}
${LIBCURL_INCLUDE_DIRS})
target_link_libraries(advanced-scene-switcher
${advanced-scene-switcher_PLATFORM_LIBS}
${LIBOBS_LIB}
${LIBOBS_FRONTEND_API_LIB}
Qt5::Core
Qt5::Widgets)
Qt5::Widgets
${LIBCURL_LIBRARIES})
# Additional commands to install the module in the correct place.
# Find all the translation files so we can copy them to the correct
@@ -224,7 +227,8 @@ else ()
${advanced-scene-switcher_PLATFORM_LIBS}
obs-frontend-api
Qt5::Widgets
libobs)
libobs
${LIBCURL_LIBRARIES})
install_obs_plugin(advanced-scene-switcher data)
endif()

View File

@@ -37,15 +37,18 @@
<property name="currentIndex">
<number>0</number>
</property>
<property name="movable">
<bool>true</bool>
</property>
<widget class="QWidget" name="generalTab">
<attribute name="title">
<string notr="true">General</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_6">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_41">
<item>
<widget class="QLabel" name="label_55">
<widget class="QLabel" name="label_63">
<property name="text">
<string>WARNING! Condition checking interval must be greater than the duration of transitions to avoid scene locking</string>
</property>
@@ -62,6 +65,42 @@
</item>
<item>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="2">
<widget class="Line" name="line_13">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="10">
<spacer name="horizontalSpacer_47">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="5">
<spacer name="horizontalSpacer_52">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="1">
@@ -116,22 +155,6 @@
</item>
</layout>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="4">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
@@ -217,13 +240,6 @@
</item>
</layout>
</item>
<item row="0" column="2">
<widget class="Line" name="line_13">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_46">
<property name="orientation">
@@ -240,19 +256,55 @@
</property>
</spacer>
</item>
<item row="0" column="5">
<spacer name="horizontalSpacer_47">
<item row="0" column="9">
<widget class="QComboBox" name="startupBehavior"/>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
<width>10</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="7">
<spacer name="horizontalSpacer_53">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="6">
<widget class="Line" name="line_18">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="8">
<widget class="QLabel" name="label_64">
<property name="text">
<string>On startup:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
@@ -394,7 +446,7 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_44">
<layout class="QHBoxLayout" name="horizontalLayout_49">
<item>
<widget class="QLabel" name="label_56">
<property name="text">
@@ -447,6 +499,44 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_47">
<item>
<widget class="QPushButton" name="exportSettings">
<property name="text">
<string>Export settings to file</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="importSettings">
<property name="text">
<string>Import settings from file</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_48">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_15">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
@@ -454,6 +544,37 @@
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_14">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_48">
<item>
<widget class="QCheckBox" name="verboseLogging">
<property name="text">
<string>Enable verbose logging (Only needed for debugging)</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_49">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="close">
<property name="text">
@@ -470,6 +591,20 @@
<layout class="QVBoxLayout" name="verticalLayout_15">
<item>
<layout class="QVBoxLayout" name="verticalLayout_14">
<item>
<widget class="QCheckBox" name="transitionOverridecheckBox">
<property name="text">
<string>Scene transitions defined in the scene switcher take priority over Transition Overrides defined per scene</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_17">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_36">
<property name="text">
@@ -1610,7 +1745,7 @@
<attribute name="title">
<string notr="true">Region</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QVBoxLayout" name="verticalLayout_27">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
@@ -1810,7 +1945,7 @@
</sizepolicy>
</property>
<property name="sortingEnabled">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
@@ -1848,6 +1983,91 @@
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_50">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_16">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_51">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="screenRegionUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string>upArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="screenRegionDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string>downArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
@@ -1867,9 +2087,9 @@
</widget>
<widget class="QWidget" name="mediaTab">
<attribute name="title">
<string notr="true"> Media </string>
<string notr="true">Media</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_11">
<layout class="QVBoxLayout" name="verticalLayout_28">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_39">
<item>
@@ -1970,15 +2190,12 @@
<bool>true</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_40">
<property name="spacing">
<number>4</number>
</property>
<item>
<widget class="QPushButton" name="mediaAdd">
<property name="maximumSize">
@@ -2011,6 +2228,91 @@
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_106">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_34">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_107">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="mediaUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string>upArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mediaDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string>downArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_35">
<property name="orientation">
@@ -2129,10 +2431,20 @@
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_36">
<item>
<widget class="QLabel" name="label_62">
<property name="text">
<string>If the</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="fileType"/>
</item>
<item>
<widget class="QLabel" name="label_44">
<property name="text">
<string>If the file</string>
<string>file</string>
</property>
</widget>
</item>
@@ -2155,6 +2467,13 @@
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="remoteFileWarningLabel">
<property name="text">
<string>Please note that if you choose the remote option the scene switcher will try to access the remote location every x ms as specified on the General tab!</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="fileTextEdit"/>
</item>
@@ -2286,6 +2605,91 @@
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_108">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_35">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_109">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="fileUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string>upArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fileDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string>downArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_38">
<property name="orientation">
@@ -2527,10 +2931,7 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_41">
<property name="spacing">
<number>4</number>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_99">
<item>
<widget class="QPushButton" name="timeAdd">
<property name="maximumSize">
@@ -2788,7 +3189,7 @@
<attribute name="title">
<string notr="true">Sequence</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_7">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
@@ -2847,13 +3248,16 @@
<item>
<widget class="QDoubleSpinBox" name="sceneRoundTripSpinBox">
<property name="suffix">
<string>s</string>
<string/>
</property>
<property name="maximum">
<double>1000000.000000000000000</double>
<double>999999999.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="sceneRoundTripDelayUnits"/>
</item>
<item>
<widget class="QLabel" name="label_17">
<property name="text">
@@ -2895,15 +3299,12 @@
</sizepolicy>
</property>
<property name="sortingEnabled">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="spacing">
<number>4</number>
</property>
<item>
<widget class="QPushButton" name="sceneRoundTripAdd">
<property name="maximumSize">
@@ -2936,6 +3337,91 @@
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_112">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_37">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_113">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="sceneRoundTripUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string>upArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="sceneRoundTripDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string>downArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">

File diff suppressed because it is too large Load Diff

View File

@@ -189,7 +189,92 @@ void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
scene = s.mScene;
transition = s.mTransition;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher exec match");
break;
}
}
}
void SwitcherData::saveExecutableSwitches(obs_data_t *obj)
{
obs_data_array_t *executableArray = obs_data_array_create();
for (ExecutableSceneSwitch &s : switcher->executableSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.mScene);
obs_source_t *transition =
obs_weak_source_get_source(s.mTransition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "scene", sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "exefile",
s.mExe.toUtf8());
obs_data_set_bool(array_obj, "infocus", s.mInFocus);
obs_data_array_push_back(executableArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "executableSwitches", executableArray);
obs_data_array_release(executableArray);
}
void SwitcherData::loadExecutableSwitches(obs_data_t *obj)
{
switcher->executableSwitches.clear();
obs_data_array_t *executableArray =
obs_data_get_array(obj, "executableSwitches");
size_t count = obs_data_array_count(executableArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(executableArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *exe = obs_data_get_string(array_obj, "exefile");
bool infocus = obs_data_get_bool(array_obj, "infocus");
switcher->executableSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), exe, infocus);
obs_data_release(array_obj);
}
obs_data_array_release(executableArray);
}
void SceneSwitcher::setupExecutableTab()
{
populateSceneSelection(ui->executableScenes, false);
populateTransitionSelection(ui->executableTransitions);
QStringList processes;
GetProcessList(processes);
for (QString &process : processes)
ui->executable->addItem(process);
for (auto &s : switcher->executableSwitches) {
std::string sceneName = GetWeakSourceName(s.mScene);
std::string transitionName = GetWeakSourceName(s.mTransition);
QString text = MakeSwitchNameExecutable(sceneName.c_str(),
s.mExe,
transitionName.c_str(),
s.mInFocus);
QListWidgetItem *item =
new QListWidgetItem(text, ui->executables);
item->setData(Qt::UserRole, s.mExe);
}
}

View File

@@ -2,8 +2,13 @@
#include <QTextStream>
#include <QDateTime>
#include <obs.hpp>
#include <curl/curl.h>
#include "headers/advanced-scene-switcher.hpp"
#define LOCAL_FILE_IDX 0
#define REMOTE_FILE_IDX 1
void SceneSwitcher::on_browseButton_clicked()
{
QString path = QFileDialog::getOpenFileName(
@@ -110,54 +115,114 @@ void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
}
}
static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
void *userp)
{
((std::string *)userp)->append((char *)contents, size * nmemb);
return size * nmemb;
}
std::string getRemoteData(std::string &url)
{
CURL *curl;
CURLcode res;
std::string readBuffer;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
return readBuffer;
}
bool compareIgnoringLineEnding(QString &s1, QString &s2)
{
/*
Im using QTextStream here so the conversion between different lineendings is done by QT.
QT itself uses only the linefeed internally so the input by the user is always using that,
but the files selected by the user might use different line endings.
If you are reading this and know of a cleaner way to do this, please let me know :)
*/
QTextStream s1stream(&s1);
QTextStream s2stream(&s2);
while (!s1stream.atEnd() && !s2stream.atEnd()) {
QString s1s = s1stream.readLine();
QString s2s = s2stream.readLine();
if (QString::compare(s1s, s2s, Qt::CaseSensitive) != 0) {
return false;
}
}
return true;
}
bool checkRemoteFileContent(FileSwitch &s)
{
std::string data = getRemoteData(s.file);
bool ret = false;
if (s.useRegex) {
ret = QString::fromStdString(data).contains(
QRegularExpression(QString::fromStdString(s.text)));
return ret;
}
QString t = QString::fromStdString(s.text);
QString d = QString::fromStdString(data);
return compareIgnoringLineEnding(t, d);
}
bool checkLocalFileContent(FileSwitch &s)
{
bool equal = false;
QString t = QString::fromStdString(s.text);
QFile file(QString::fromStdString(s.file));
if (!file.open(QIODevice::ReadOnly))
return false;
if (s.useTime) {
QDateTime newLastMod = QFileInfo(file).lastModified();
if (s.lastMod == newLastMod)
return false;
s.lastMod = newLastMod;
}
if (s.useRegex) {
QTextStream in(&file);
QRegExp rx(t);
equal = rx.exactMatch(in.readAll());
} else {
QTextStream in(&file);
QString filedata = in.readAll();
equal = compareIgnoringLineEnding(filedata, t);
}
file.close();
return equal;
}
void SwitcherData::checkFileContent(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
for (FileSwitch &s : fileSwitches) {
bool equal = false;
QString t = QString::fromStdString(s.text);
QFile file(QString::fromStdString(s.file));
if (!file.open(QIODevice::ReadOnly))
continue;
if (s.useTime) {
QDateTime newLastMod = QFileInfo(file).lastModified();
if (s.lastMod == newLastMod)
continue;
s.lastMod = newLastMod;
}
if (s.useRegex) {
QTextStream in(&file);
QRegExp rx(t);
equal = rx.exactMatch(in.readAll());
if (s.remote) {
equal = checkRemoteFileContent(s);
} else {
/*Im using QTextStream here so the conversion between different lineendings is done by QT.
*QT itself uses only the linefeed internally so the input by the user is always using that,
*but the files selected by the user might use different line endings.
*If you are reading this and know of a cleaner way to do this, please let me know :)
*/
QTextStream in(&file);
QTextStream text(&t);
while (!in.atEnd() && !text.atEnd()) {
QString fileLine = in.readLine();
QString textLine = text.readLine();
if (QString::compare(fileLine, textLine,
Qt::CaseSensitive) != 0) {
equal = false;
break;
} else {
equal = true;
}
}
equal = checkLocalFileContent(s);
}
file.close();
if (equal) {
scene = s.scene;
transition = s.transition;
match = true;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher file match");
break;
}
}
@@ -172,12 +237,29 @@ void SceneSwitcher::on_browseButton_3_clicked()
ui->filePathLineEdit->setText(path);
}
void SceneSwitcher::on_fileType_currentIndexChanged(int idx)
{
if (idx == -1)
return;
if (idx == LOCAL_FILE_IDX) {
ui->browseButton_3->setDisabled(false);
ui->fileContentTimeCheckBox->setDisabled(false);
ui->remoteFileWarningLabel->hide();
}
if (idx == REMOTE_FILE_IDX) {
ui->browseButton_3->setDisabled(true);
ui->fileContentTimeCheckBox->setDisabled(true);
ui->remoteFileWarningLabel->show();
}
}
void SceneSwitcher::on_fileAdd_clicked()
{
QString sceneName = ui->fileScenes->currentText();
QString transitionName = ui->fileTransitions->currentText();
QString fileName = ui->filePathLineEdit->text();
QString text = ui->fileTextEdit->toPlainText();
bool remote = (ui->fileType->currentIndex() == REMOTE_FILE_IDX);
bool useRegex = ui->fileContentRegExCheckBox->isChecked();
bool useTime = ui->fileContentTimeCheckBox->isChecked();
@@ -199,8 +281,8 @@ void SceneSwitcher::on_fileAdd_clicked()
std::lock_guard<std::mutex> lock(switcher->m);
switcher->fileSwitches.emplace_back(source, transition,
fileName.toUtf8().constData(),
text.toUtf8().constData(), useRegex,
useTime);
text.toUtf8().constData(), remote,
useRegex, useTime);
}
void SceneSwitcher::on_fileRemove_clicked()
@@ -242,6 +324,152 @@ void SceneSwitcher::on_fileScenesList_currentRowChanged(int idx)
ui->fileTransitions->setCurrentText(transitionName.c_str());
ui->fileTextEdit->setPlainText(s.text.c_str());
ui->filePathLineEdit->setText(s.file.c_str());
if (s.remote)
ui->fileType->setCurrentIndex(REMOTE_FILE_IDX);
else
ui->fileType->setCurrentIndex(LOCAL_FILE_IDX);
ui->fileContentRegExCheckBox->setChecked(s.useRegex);
ui->fileContentTimeCheckBox->setChecked(s.useTime);
}
void SceneSwitcher::on_fileUp_clicked()
{
int index = ui->fileScenesList->currentRow();
if (index != -1 && index != 0) {
ui->fileScenesList->insertItem(
index - 1, ui->fileScenesList->takeItem(index));
ui->fileScenesList->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->fileSwitches.begin() + index,
switcher->fileSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_fileDown_clicked()
{
int index = ui->fileScenesList->currentRow();
if (index != -1 && index != ui->fileScenesList->count() - 1) {
ui->fileScenesList->insertItem(
index + 1, ui->fileScenesList->takeItem(index));
ui->fileScenesList->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->fileSwitches.begin() + index,
switcher->fileSwitches.begin() + index + 1);
}
}
void SwitcherData::saveFileSwitches(obs_data_t *obj)
{
obs_data_array_t *fileArray = obs_data_array_create();
for (FileSwitch &s : switcher->fileSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "scene", sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "file", s.file.c_str());
obs_data_set_string(array_obj, "text", s.text.c_str());
obs_data_set_bool(array_obj, "remote", s.remote);
obs_data_set_bool(array_obj, "useRegex", s.useRegex);
obs_data_set_bool(array_obj, "useTime", s.useTime);
obs_data_array_push_back(fileArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "fileSwitches", fileArray);
obs_data_array_release(fileArray);
obs_data_set_bool(obj, "readEnabled", switcher->fileIO.readEnabled);
obs_data_set_string(obj, "readPath", switcher->fileIO.readPath.c_str());
obs_data_set_bool(obj, "writeEnabled", switcher->fileIO.writeEnabled);
obs_data_set_string(obj, "writePath",
switcher->fileIO.writePath.c_str());
}
void SwitcherData::loadFileSwitches(obs_data_t *obj)
{
switcher->fileSwitches.clear();
obs_data_array_t *fileArray = obs_data_get_array(obj, "fileSwitches");
size_t count = obs_data_array_count(fileArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(fileArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *file = obs_data_get_string(array_obj, "file");
const char *text = obs_data_get_string(array_obj, "text");
bool remote = obs_data_get_bool(array_obj, "remote");
bool useRegex = obs_data_get_bool(array_obj, "useRegex");
bool useTime = obs_data_get_bool(array_obj, "useTime");
switcher->fileSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), file, text, remote,
useRegex, useTime);
obs_data_release(array_obj);
}
obs_data_array_release(fileArray);
obs_data_set_default_bool(obj, "readEnabled", false);
switcher->fileIO.readEnabled = obs_data_get_bool(obj, "readEnabled");
switcher->fileIO.readPath = obs_data_get_string(obj, "readPath");
obs_data_set_default_bool(obj, "writeEnabled", false);
switcher->fileIO.writeEnabled = obs_data_get_bool(obj, "writeEnabled");
switcher->fileIO.writePath = obs_data_get_string(obj, "writePath");
}
void SceneSwitcher::setupFileTab()
{
populateSceneSelection(ui->fileScenes, false);
populateTransitionSelection(ui->fileTransitions);
ui->fileType->addItem("local");
ui->fileType->addItem("remote");
ui->remoteFileWarningLabel->setText(
"Note that the scene switcher will try to access the remote location every " +
QString::number(switcher->interval) + "ms");
for (auto &s : switcher->fileSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString listText = MakeFileSwitchName(
sceneName.c_str(), transitionName.c_str(),
s.file.c_str(), s.text.c_str(), s.useRegex, s.useTime);
QListWidgetItem *item =
new QListWidgetItem(listText, ui->fileScenesList);
item->setData(Qt::UserRole, listText);
}
ui->readPathLineEdit->setText(
QString::fromStdString(switcher->fileIO.readPath.c_str()));
ui->readFileCheckBox->setChecked(switcher->fileIO.readEnabled);
ui->writePathLineEdit->setText(
QString::fromStdString(switcher->fileIO.writePath.c_str()));
if (ui->readFileCheckBox->checkState()) {
ui->browseButton_2->setDisabled(false);
ui->readPathLineEdit->setDisabled(false);
} else {
ui->browseButton_2->setDisabled(true);
ui->readPathLineEdit->setDisabled(true);
}
}

View File

@@ -1,20 +1,15 @@
#include "headers/advanced-scene-switcher.hpp"
#include <obs-module.h>
#include <QFileDialog>
#include <QTextStream>
#include <QMessageBox>
#include "headers/advanced-scene-switcher.hpp"
void SceneSwitcher::on_close_clicked()
{
done(0);
}
void SceneSwitcher::on_startAtLaunch_toggled(bool value)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switcher->startAtLaunch = value;
}
void SceneSwitcher::UpdateNonMatchingScene(const QString &name)
{
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
@@ -57,6 +52,15 @@ void SceneSwitcher::on_noMatchRandomSwitch_clicked()
ui->noMatchSwitchScene->setEnabled(false);
}
void SceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switcher->startupBehavior = (StartupBehavior)index;
}
void SceneSwitcher::on_noMatchSwitchScene_currentTextChanged(const QString &text)
{
if (loading)
@@ -102,3 +106,474 @@ void SceneSwitcher::closeEvent(QCloseEvent *)
{
obs_frontend_save();
}
void SceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
UpdateAutoStopScene(text);
}
void SceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
if (!state) {
ui->autoStopScenes->setDisabled(true);
switcher->autoStopEnable = false;
} else {
ui->autoStopScenes->setDisabled(false);
switcher->autoStopEnable = true;
if (!switcher->autoStopScene)
UpdateAutoStopScene(ui->autoStopScenes->currentText());
}
}
void SceneSwitcher::UpdateAutoStopScene(const QString &name)
{
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
switcher->autoStopScene = ws;
obs_weak_source_release(ws);
obs_source_release(scene);
}
void SwitcherData::autoStopStreamAndRecording()
{
obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
if (ws && autoStopScene == ws) {
if (obs_frontend_streaming_active())
obs_frontend_streaming_stop();
if (obs_frontend_recording_active())
obs_frontend_recording_stop();
}
obs_source_release(currentSource);
obs_weak_source_release(ws);
}
void SceneSwitcher::on_verboseLogging_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switcher->verbose = state;
}
void SceneSwitcher::on_exportSettings_clicked()
{
QString directory = QFileDialog::getSaveFileName(
this, tr("Export Advanced Scene Switcher settings to file ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (directory.isEmpty())
return;
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
obs_data_t *obj = obs_data_create();
switcher->saveWindowTitleSwitches(obj);
switcher->saveScreenRegionSwitches(obj);
switcher->savePauseSwitches(obj);
switcher->saveSceneRoundTripSwitches(obj);
switcher->saveSceneTransitions(obj);
switcher->saveIdleSwitches(obj);
switcher->saveExecutableSwitches(obj);
switcher->saveRandomSwitches(obj);
switcher->saveFileSwitches(obj);
switcher->saveMediaSwitches(obj);
switcher->saveTimeSwitches(obj);
switcher->saveGeneralSettings(obj);
obs_data_save_json(obj, file.fileName().toUtf8().constData());
obs_data_release(obj);
}
void SceneSwitcher::on_importSettings_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
QString directory = QFileDialog::getOpenFileName(
this,
tr("Import Advanced Scene Switcher settings from file ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (directory.isEmpty())
return;
QFile file(directory);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
obs_data_t *obj = obs_data_create_from_json_file(
file.fileName().toUtf8().constData());
if (!obj) {
QMessageBox Msgbox;
Msgbox.setText(
"Advanced Scene Switcher failed to import settings");
Msgbox.exec();
return;
}
switcher->loadWindowTitleSwitches(obj);
switcher->loadScreenRegionSwitches(obj);
switcher->loadPauseSwitches(obj);
switcher->loadSceneRoundTripSwitches(obj);
switcher->loadSceneTransitions(obj);
switcher->loadIdleSwitches(obj);
switcher->loadExecutableSwitches(obj);
switcher->loadRandomSwitches(obj);
switcher->loadFileSwitches(obj);
switcher->loadMediaSwitches(obj);
switcher->loadTimeSwitches(obj);
switcher->loadGeneralSettings(obj);
obs_data_release(obj);
QMessageBox Msgbox;
Msgbox.setText(
"Advanced Scene Switcher settings imported successfully");
Msgbox.exec();
close();
}
int findTabIndex(QTabBar *bar, int pos)
{
int at = -1;
QString tabName = "";
switch (pos) {
case 0:
tabName = "General";
break;
case 1:
tabName = "Transition";
break;
case 2:
tabName = "Pause";
break;
case 3:
tabName = "Title";
break;
case 4:
tabName = "Executable";
break;
case 5:
tabName = "Region";
break;
case 6:
tabName = "Media";
break;
case 7:
tabName = "File";
break;
case 8:
tabName = "Random";
break;
case 9:
tabName = "Time";
break;
case 10:
tabName = "Idle";
break;
case 11:
tabName = "Sequence";
break;
}
for (int i = 0; i < bar->count(); ++i) {
if (bar->tabText(i).compare(tabName) == 0) {
at = i;
break;
}
}
if (at == -1)
blog(LOG_INFO, "Advanced Scene Switcher failed to find tab %s",
tabName.toUtf8().constData());
return at;
}
void SceneSwitcher::setTabOrder()
{
QTabBar *bar = ui->tabWidget->tabBar();
for (int i = 0; i < bar->count(); ++i) {
int curPos = findTabIndex(bar, switcher->tabOrder[i]);
if (i != curPos && curPos != -1)
bar->moveTab(curPos, i);
}
connect(bar, &QTabBar::tabMoved, this, &SceneSwitcher::on_tabMoved);
}
void SceneSwitcher::on_tabMoved(int from, int to)
{
if (loading)
return;
std::swap(switcher->tabOrder[from], switcher->tabOrder[to]);
}
void SwitcherData::saveGeneralSettings(obs_data_t *obj)
{
obs_data_set_int(obj, "interval", switcher->interval);
std::string nonMatchingSceneName =
GetWeakSourceName(switcher->nonMatchingScene);
obs_data_set_string(obj, "non_matching_scene",
nonMatchingSceneName.c_str());
obs_data_set_int(obj, "switch_if_not_matching",
switcher->switchIfNotMatching);
obs_data_set_bool(obj, "active", !switcher->stop);
obs_data_set_int(obj, "startup_behavior", switcher->startupBehavior);
std::string autoStopSceneName =
GetWeakSourceName(switcher->autoStopScene);
obs_data_set_bool(obj, "autoStopEnable", switcher->autoStopEnable);
obs_data_set_string(obj, "autoStopSceneName",
autoStopSceneName.c_str());
obs_data_set_bool(obj, "verbose", switcher->verbose);
obs_data_set_int(obj, "priority0",
switcher->functionNamesByPriority[0]);
obs_data_set_int(obj, "priority1",
switcher->functionNamesByPriority[1]);
obs_data_set_int(obj, "priority2",
switcher->functionNamesByPriority[2]);
obs_data_set_int(obj, "priority3",
switcher->functionNamesByPriority[3]);
obs_data_set_int(obj, "priority4",
switcher->functionNamesByPriority[4]);
obs_data_set_int(obj, "priority5",
switcher->functionNamesByPriority[5]);
obs_data_set_int(obj, "priority6",
switcher->functionNamesByPriority[6]);
obs_data_set_int(obj, "priority7",
switcher->functionNamesByPriority[7]);
obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]);
obs_data_set_int(obj, "transitionTabPos", switcher->tabOrder[1]);
obs_data_set_int(obj, "pauseTabPos", switcher->tabOrder[2]);
obs_data_set_int(obj, "titleTabPos", switcher->tabOrder[3]);
obs_data_set_int(obj, "exeTabPos", switcher->tabOrder[4]);
obs_data_set_int(obj, "regionTabPos", switcher->tabOrder[5]);
obs_data_set_int(obj, "mediaTabPos", switcher->tabOrder[6]);
obs_data_set_int(obj, "fileTabPos", switcher->tabOrder[7]);
obs_data_set_int(obj, "randomTabPos", switcher->tabOrder[8]);
obs_data_set_int(obj, "timeTabPos", switcher->tabOrder[9]);
obs_data_set_int(obj, "idleTabPos", switcher->tabOrder[10]);
obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]);
}
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
{
obs_data_set_default_int(obj, "interval", DEFAULT_INTERVAL);
switcher->interval = obs_data_get_int(obj, "interval");
obs_data_set_default_int(obj, "switch_if_not_matching", NO_SWITCH);
switcher->switchIfNotMatching =
(NoMatch)obs_data_get_int(obj, "switch_if_not_matching");
std::string nonMatchingScene =
obs_data_get_string(obj, "non_matching_scene");
switcher->nonMatchingScene =
GetWeakSourceByName(nonMatchingScene.c_str());
switcher->stop = !obs_data_get_bool(obj, "active");
switcher->startupBehavior =
(StartupBehavior)obs_data_get_int(obj, "startup_behavior");
if (switcher->startupBehavior == START)
switcher->stop = false;
if (switcher->startupBehavior == STOP)
switcher->stop = true;
std::string autoStopScene =
obs_data_get_string(obj, "autoStopSceneName");
switcher->autoStopEnable = obs_data_get_bool(obj, "autoStopEnable");
switcher->autoStopScene = GetWeakSourceByName(autoStopScene.c_str());
switcher->verbose = obs_data_get_bool(obj, "verbose");
obs_data_set_default_int(obj, "priority0", DEFAULT_PRIORITY_0);
obs_data_set_default_int(obj, "priority1", DEFAULT_PRIORITY_1);
obs_data_set_default_int(obj, "priority2", DEFAULT_PRIORITY_2);
obs_data_set_default_int(obj, "priority3", DEFAULT_PRIORITY_3);
obs_data_set_default_int(obj, "priority4", DEFAULT_PRIORITY_4);
obs_data_set_default_int(obj, "priority5", DEFAULT_PRIORITY_5);
obs_data_set_default_int(obj, "priority6", DEFAULT_PRIORITY_6);
obs_data_set_default_int(obj, "priority7", DEFAULT_PRIORITY_7);
switcher->functionNamesByPriority[0] =
(obs_data_get_int(obj, "priority0"));
switcher->functionNamesByPriority[1] =
(obs_data_get_int(obj, "priority1"));
switcher->functionNamesByPriority[2] =
(obs_data_get_int(obj, "priority2"));
switcher->functionNamesByPriority[3] =
(obs_data_get_int(obj, "priority3"));
switcher->functionNamesByPriority[4] =
(obs_data_get_int(obj, "priority4"));
switcher->functionNamesByPriority[5] =
(obs_data_get_int(obj, "priority5"));
switcher->functionNamesByPriority[6] =
(obs_data_get_int(obj, "priority6"));
switcher->functionNamesByPriority[7] =
(obs_data_get_int(obj, "priority7"));
if (!switcher->prioFuncsValid()) {
switcher->functionNamesByPriority[0] = (DEFAULT_PRIORITY_0);
switcher->functionNamesByPriority[1] = (DEFAULT_PRIORITY_1);
switcher->functionNamesByPriority[2] = (DEFAULT_PRIORITY_2);
switcher->functionNamesByPriority[3] = (DEFAULT_PRIORITY_3);
switcher->functionNamesByPriority[4] = (DEFAULT_PRIORITY_4);
switcher->functionNamesByPriority[5] = (DEFAULT_PRIORITY_5);
switcher->functionNamesByPriority[6] = (DEFAULT_PRIORITY_6);
switcher->functionNamesByPriority[7] = (DEFAULT_PRIORITY_7);
}
obs_data_set_default_int(obj, "threadPriority",
QThread::NormalPriority);
switcher->threadPriority = obs_data_get_int(obj, "threadPriority");
obs_data_set_default_int(obj, "generalTabPos", 0);
obs_data_set_default_int(obj, "transitionTabPos", 1);
obs_data_set_default_int(obj, "pauseTabPos", 2);
obs_data_set_default_int(obj, "titleTabPos", 3);
obs_data_set_default_int(obj, "exeTabPos", 4);
obs_data_set_default_int(obj, "regionTabPos", 5);
obs_data_set_default_int(obj, "mediaTabPos", 6);
obs_data_set_default_int(obj, "fileTabPos", 7);
obs_data_set_default_int(obj, "randomTabPos", 8);
obs_data_set_default_int(obj, "timeTabPos", 9);
obs_data_set_default_int(obj, "idleTabPos", 10);
obs_data_set_default_int(obj, "sequenceTabPos", 11);
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "generalTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "transitionTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "pauseTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "titleTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "exeTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "regionTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "mediaTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "fileTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "randomTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "timeTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "idleTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "sequenceTabPos")));
}
void SceneSwitcher::setupGeneralTab()
{
populateSceneSelection(ui->noMatchSwitchScene, false);
populateSceneSelection(ui->autoStopScenes, false);
if (switcher->switchIfNotMatching == SWITCH) {
ui->noMatchSwitch->setChecked(true);
ui->noMatchSwitchScene->setEnabled(true);
} else if (switcher->switchIfNotMatching == NO_SWITCH) {
ui->noMatchDontSwitch->setChecked(true);
ui->noMatchSwitchScene->setEnabled(false);
} else {
ui->noMatchRandomSwitch->setChecked(true);
ui->noMatchSwitchScene->setEnabled(false);
}
ui->noMatchSwitchScene->setCurrentText(
GetWeakSourceName(switcher->nonMatchingScene).c_str());
ui->checkInterval->setValue(switcher->interval);
ui->autoStopSceneCheckBox->setChecked(switcher->autoStopEnable);
ui->autoStopScenes->setCurrentText(
GetWeakSourceName(switcher->autoStopScene).c_str());
if (ui->autoStopSceneCheckBox->checkState()) {
ui->autoStopScenes->setDisabled(false);
} else {
ui->autoStopScenes->setDisabled(true);
}
ui->verboseLogging->setChecked(switcher->verbose);
for (int p : switcher->functionNamesByPriority) {
std::string s = "";
switch (p) {
case READ_FILE_FUNC:
s = "File Content";
break;
case ROUND_TRIP_FUNC:
s = "Scene Sequence";
break;
case IDLE_FUNC:
s = "Idle Detection";
break;
case EXE_FUNC:
s = "Executable";
break;
case SCREEN_REGION_FUNC:
s = "Screen Region";
break;
case WINDOW_TITLE_FUNC:
s = "Window Title";
break;
case MEDIA_FUNC:
s = "Media";
break;
case TIME_FUNC:
s = "Time";
break;
}
QString text(s.c_str());
QListWidgetItem *item =
new QListWidgetItem(text, ui->priorityList);
item->setData(Qt::UserRole, text);
}
for (int i = 0; i < switcher->threadPriorities.size(); ++i) {
ui->threadPriority->addItem(
switcher->threadPriorities[i].name.c_str());
ui->threadPriority->setItemData(
i, switcher->threadPriorities[i].description.c_str(),
Qt::ToolTipRole);
if (switcher->threadPriority ==
switcher->threadPriorities[i].value) {
ui->threadPriority->setCurrentText(
switcher->threadPriorities[i].name.c_str());
}
}
ui->startupBehavior->addItem(
"Start the scene switcher if it was running");
ui->startupBehavior->addItem("Always start the scene switcher");
ui->startupBehavior->addItem("Do not start the scene switcher");
ui->startupBehavior->setCurrentIndex(switcher->startupBehavior);
if (switcher->th && switcher->th->isRunning())
SetStarted();
else
SetStopped();
}

View File

@@ -49,6 +49,24 @@ public:
void UpdateIdleDataTransition(const QString &name);
void UpdateIdleDataScene(const QString &name);
void loadUI();
void populateSceneSelection(QComboBox *sel, bool addPrevious);
void populateTransitionSelection(QComboBox *sel);
void populateWindowSelection(QComboBox *sel);
void setupGeneralTab();
void setupTitleTab();
void setupExecutableTab();
void setupRegionTab();
void setupPauseTab();
void setupSequenceTab();
void setupTransitionsTab();
void setupIdleTab();
void setupRandomTab();
void setupMediaTab();
void setupFileTab();
void setupTimeTab();
void setTabOrder();
public slots:
void on_switches_currentRowChanged(int idx);
void on_up_clicked();
@@ -58,14 +76,17 @@ public slots:
void on_noMatchDontSwitch_clicked();
void on_noMatchSwitch_clicked();
void on_noMatchRandomSwitch_clicked();
void on_startAtLaunch_toggled(bool value);
void on_startupBehavior_currentIndexChanged(int index);
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
void on_checkInterval_valueChanged(int value);
void on_toggleStartButton_clicked();
void on_tabMoved(int from, int to);
void on_screenRegions_currentRowChanged(int idx);
void on_screenRegionAdd_clicked();
void on_screenRegionRemove_clicked();
void on_screenRegionUp_clicked();
void on_screenRegionDown_clicked();
void on_pauseScenes_currentRowChanged(int idx);
void on_pauseScenesAdd_clicked();
@@ -84,19 +105,29 @@ public slots:
void on_sceneRoundTripRemove_clicked();
void on_sceneRoundTripSave_clicked();
void on_sceneRoundTripLoad_clicked();
void on_sceneRoundTripUp_clicked();
void on_sceneRoundTripDown_clicked();
void on_sceneRoundTripDelayUnits_currentIndexChanged(int index);
void on_autoStopSceneCheckBox_stateChanged(int state);
void on_autoStopScenes_currentTextChanged(const QString &text);
void on_verboseLogging_stateChanged(int state);
void on_exportSettings_clicked();
void on_importSettings_clicked();
void on_sceneTransitions_currentRowChanged(int idx);
void on_transitionsAdd_clicked();
void on_transitionsRemove_clicked();
void on_defaultTransitions_currentRowChanged(int idx);
void on_defaultTransitionsAdd_clicked();
void on_defaultTransitionsRemove_clicked();
void on_transitionOverridecheckBox_stateChanged(int state);
void on_browseButton_clicked();
void on_readFileCheckBox_stateChanged(int state);
void on_fileType_currentIndexChanged(int idx);
void on_readPathLineEdit_textChanged(const QString &text);
void on_writePathLineEdit_textChanged(const QString &text);
void on_browseButton_2_clicked();
@@ -123,10 +154,15 @@ public slots:
void on_fileRemove_clicked();
void on_fileScenesList_currentRowChanged(int idx);
void on_browseButton_3_clicked();
void on_fileUp_clicked();
void on_fileDown_clicked();
void on_mediaSwitches_currentRowChanged(int idx);
void on_mediaAdd_clicked();
void on_mediaRemove_clicked();
void on_mediaUp_clicked();
void on_mediaDown_clicked();
void on_mediaTimeRestrictions_currentIndexChanged(int idx);
void on_timeSwitches_currentRowChanged(int idx);
void on_timeAdd_clicked();
@@ -171,9 +207,20 @@ bool isInFocus(const QString &executable);
struct obs_weak_source;
typedef struct obs_weak_source obs_weak_source_t;
obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
obs_weak_source_t *scene2);
typedef struct transitionData {
std::string name = "";
int duration = 0;
} transitionData;
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
OBSWeakSource &transition,
bool &transitionOverrideOverride, transitionData &td);
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
obs_source_t *transition, transitionData &td);
void restoreTransitionOverride(obs_source_t *scene, transitionData td);
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
bool &transitionOverrideOverride,
std::unique_lock<std::mutex> &lock);
/********************************************************************************

View File

@@ -98,13 +98,13 @@ struct SceneRoundTripSwitch {
OBSWeakSource scene1;
OBSWeakSource scene2;
OBSWeakSource transition;
int delay;
double delay;
bool usePreviousScene;
std::string sceneRoundTripStr;
inline SceneRoundTripSwitch(OBSWeakSource scene1_,
OBSWeakSource scene2_,
OBSWeakSource transition_, int delay_,
OBSWeakSource transition_, double delay_,
bool usePreviousScene_, std::string str)
: scene1(scene1_),
scene2(scene2_),
@@ -169,17 +169,19 @@ struct FileSwitch {
OBSWeakSource transition;
std::string file;
std::string text;
bool remote = false;
bool useRegex = false;
bool useTime = false;
QDateTime lastMod;
inline FileSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
const char *file_, const char *text_, bool useRegex_,
bool useTime_)
const char *file_, const char *text_, bool remote_,
bool useRegex_, bool useTime_)
: scene(scene_),
transition(transition_),
file(file_),
text(text_),
remote(remote_),
useRegex(useRegex_),
useTime(useTime_),
lastMod()
@@ -208,7 +210,7 @@ struct MediaSwitch {
OBSWeakSource source;
OBSWeakSource transition;
obs_media_state state;
uint64_t time;
int64_t time;
time_restriction restriction;
bool matched;
bool usePreviousScene;
@@ -251,6 +253,7 @@ struct TimeSwitch {
};
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
class SwitcherThread;
@@ -265,8 +268,9 @@ struct SwitcherData {
bool transitionActive = false;
bool waitForTransition = false;
std::condition_variable transitionCv;
bool startAtLaunch = false;
bool stop = false;
bool verbose = false;
bool tansitionOverrideOverride = false;
int interval = DEFAULT_INTERVAL;
@@ -276,6 +280,7 @@ struct SwitcherData {
OBSWeakSource lastRandomScene;
OBSWeakSource nonMatchingScene;
NoMatch switchIfNotMatching = NO_SWITCH;
StartupBehavior startupBehavior = PERSIST;
std::vector<WindowSceneSwitch> windowSwitches;
std::vector<std::string> ignoreIdleWindows;
@@ -290,6 +295,7 @@ struct SwitcherData {
std::vector<std::string> ignoreWindowsSwitches;
std::vector<SceneRoundTripSwitch> sceneRoundTripSwitches;
int sceneRoundTripUnitMultiplier = 1;
std::vector<RandomSwitch> randomSwitches;
@@ -341,6 +347,8 @@ struct SwitcherData {
uint32_t threadPriority = QThread::NormalPriority;
std::vector<int> tabOrder;
void Thread();
void Start();
void Stop();
@@ -373,6 +381,32 @@ struct SwitcherData {
void checkTimeSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
void saveWindowTitleSwitches(obs_data_t *obj);
void saveScreenRegionSwitches(obs_data_t *obj);
void savePauseSwitches(obs_data_t *obj);
void saveSceneRoundTripSwitches(obs_data_t *obj);
void saveSceneTransitions(obs_data_t *obj);
void saveIdleSwitches(obs_data_t *obj);
void saveExecutableSwitches(obs_data_t *obj);
void saveRandomSwitches(obs_data_t *obj);
void saveFileSwitches(obs_data_t *obj);
void saveMediaSwitches(obs_data_t *obj);
void saveTimeSwitches(obs_data_t *obj);
void saveGeneralSettings(obs_data_t *obj);
void loadWindowTitleSwitches(obs_data_t *obj);
void loadScreenRegionSwitches(obs_data_t *obj);
void loadPauseSwitches(obs_data_t *obj);
void loadSceneRoundTripSwitches(obs_data_t *obj);
void loadSceneTransitions(obs_data_t *obj);
void loadIdleSwitches(obs_data_t *obj);
void loadExecutableSwitches(obs_data_t *obj);
void loadRandomSwitches(obs_data_t *obj);
void loadFileSwitches(obs_data_t *obj);
void loadMediaSwitches(obs_data_t *obj);
void loadTimeSwitches(obs_data_t *obj);
void loadGeneralSettings(obs_data_t *obj);
void Prune()
{
for (size_t i = 0; i < windowSwitches.size(); i++) {

View File

@@ -41,6 +41,9 @@ void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
transition = idleData.transition;
match = true;
idleData.alreadySwitched = true;
if (verbose)
blog(LOG_INFO, "Advanced Scene Switcher idle match");
} else
idleData.alreadySwitched = false;
}
@@ -199,3 +202,96 @@ int SceneSwitcher::IgnoreIdleWindowsFindByData(const QString &window)
return idx;
}
void SwitcherData::saveIdleSwitches(obs_data_t *obj)
{
obs_data_array_t *ignoreIdleWindowsArray = obs_data_array_create();
for (std::string &window : switcher->ignoreIdleWindows) {
obs_data_t *array_obj = obs_data_create();
obs_data_set_string(array_obj, "window", window.c_str());
obs_data_array_push_back(ignoreIdleWindowsArray, array_obj);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "ignoreIdleWindows", ignoreIdleWindowsArray);
obs_data_array_release(ignoreIdleWindowsArray);
std::string idleSceneName = GetWeakSourceName(switcher->idleData.scene);
std::string idleTransitionName =
GetWeakSourceName(switcher->idleData.transition);
obs_data_set_bool(obj, "idleEnable", switcher->idleData.idleEnable);
obs_data_set_string(obj, "idleSceneName",
switcher->idleData.usePreviousScene
? PREVIOUS_SCENE_NAME
: idleSceneName.c_str());
obs_data_set_string(obj, "idleTransitionName",
idleTransitionName.c_str());
obs_data_set_int(obj, "idleTime", switcher->idleData.time);
}
void SwitcherData::loadIdleSwitches(obs_data_t *obj)
{
switcher->ignoreIdleWindows.clear();
obs_data_array_t *ignoreIdleWindowsArray =
obs_data_get_array(obj, "ignoreIdleWindows");
size_t count = obs_data_array_count(ignoreIdleWindowsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(ignoreIdleWindowsArray, i);
const char *window = obs_data_get_string(array_obj, "window");
switcher->ignoreIdleWindows.emplace_back(window);
obs_data_release(array_obj);
}
obs_data_array_release(ignoreIdleWindowsArray);
std::string idleSceneName = obs_data_get_string(obj, "idleSceneName");
std::string idleTransitionName =
obs_data_get_string(obj, "idleTransitionName");
switcher->idleData.scene = GetWeakSourceByName(idleSceneName.c_str());
switcher->idleData.transition =
GetWeakTransitionByName(idleTransitionName.c_str());
obs_data_set_default_bool(obj, "idleEnable", false);
switcher->idleData.idleEnable = obs_data_get_bool(obj, "idleEnable");
obs_data_set_default_int(obj, "idleTime", DEFAULT_IDLE_TIME);
switcher->idleData.time = obs_data_get_int(obj, "idleTime");
switcher->idleData.usePreviousScene =
(idleSceneName == PREVIOUS_SCENE_NAME);
}
void SceneSwitcher::setupIdleTab()
{
populateSceneSelection(ui->idleScenes, true);
populateTransitionSelection(ui->idleTransitions);
populateWindowSelection(ui->ignoreIdleWindowsWindows);
for (auto &window : switcher->ignoreIdleWindows) {
QString text = QString::fromStdString(window);
QListWidgetItem *item =
new QListWidgetItem(text, ui->ignoreIdleWindows);
item->setData(Qt::UserRole, text);
}
ui->idleCheckBox->setChecked(switcher->idleData.idleEnable);
ui->idleScenes->setCurrentText(
switcher->idleData.usePreviousScene
? PREVIOUS_SCENE_NAME
: GetWeakSourceName(switcher->idleData.scene).c_str());
ui->idleTransitions->setCurrentText(
GetWeakSourceName(switcher->idleData.transition).c_str());
ui->idleSpinBox->setValue(switcher->idleData.time);
if (ui->idleCheckBox->checkState()) {
ui->idleScenes->setDisabled(false);
ui->idleSpinBox->setDisabled(false);
ui->idleTransitions->setDisabled(false);
} else {
ui->idleScenes->setDisabled(true);
ui->idleSpinBox->setDisabled(true);
ui->idleTransitions->setDisabled(true);
}
}

View File

@@ -92,6 +92,49 @@ void SceneSwitcher::on_mediaRemove_clicked()
delete item;
}
void SceneSwitcher::on_mediaUp_clicked()
{
int index = ui->mediaSwitches->currentRow();
if (index != -1 && index != 0) {
ui->mediaSwitches->insertItem(
index - 1, ui->mediaSwitches->takeItem(index));
ui->mediaSwitches->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->mediaSwitches.begin() + index,
switcher->mediaSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_mediaDown_clicked()
{
int index = ui->mediaSwitches->currentRow();
if (index != -1 && index != ui->mediaSwitches->count() - 1) {
ui->mediaSwitches->insertItem(
index + 1, ui->mediaSwitches->takeItem(index));
ui->mediaSwitches->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->mediaSwitches.begin() + index,
switcher->mediaSwitches.begin() + index + 1);
}
}
void SceneSwitcher::on_mediaTimeRestrictions_currentIndexChanged(int idx)
{
if (idx == -1)
return;
if ((time_restriction)ui->mediaTimeRestrictions->currentIndex() ==
TIME_RESTRICTION_NONE) {
ui->mediaTime->setDisabled(true);
} else {
ui->mediaTime->setDisabled(false);
}
}
void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
@@ -122,8 +165,137 @@ void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
? previousScene
: mediaSwitch.scene;
transition = mediaSwitch.transition;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher meida match");
}
mediaSwitch.matched = matched;
obs_source_release(source);
}
}
void SwitcherData::saveMediaSwitches(obs_data_t *obj)
{
obs_data_array_t *mediaArray = obs_data_array_create();
for (MediaSwitch &s : switcher->mediaSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.source);
obs_source_t *sceneSource = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if ((s.usePreviousScene || sceneSource) && source &&
transition) {
const char *sourceName = obs_source_get_name(source);
const char *sceneName =
obs_source_get_name(sceneSource);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "source", sourceName);
obs_data_set_string(array_obj, "scene",
s.usePreviousScene
? PREVIOUS_SCENE_NAME
: sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_int(array_obj, "state", s.state);
obs_data_set_int(array_obj, "restriction",
s.restriction);
obs_data_set_int(array_obj, "time", s.time);
obs_data_array_push_back(mediaArray, array_obj);
}
obs_source_release(source);
obs_source_release(sceneSource);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "mediaSwitches", mediaArray);
obs_data_array_release(mediaArray);
}
void SwitcherData::loadMediaSwitches(obs_data_t *obj)
{
obs_data_array_t *mediaArray = obs_data_get_array(obj, "mediaSwitches");
switcher->mediaSwitches.clear();
size_t count = obs_data_array_count(mediaArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(mediaArray, i);
const char *source = obs_data_get_string(array_obj, "source");
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
obs_media_state state =
(obs_media_state)obs_data_get_int(array_obj, "state");
time_restriction restriction =
(time_restriction)obs_data_get_int(array_obj,
"restriction");
uint64_t time = obs_data_get_int(array_obj, "time");
std::string mediaStr = MakeMediaSwitchName(source, scene,
transition, state,
restriction, time)
.toUtf8()
.constData();
switcher->mediaSwitches.emplace_back(
GetWeakSourceByName(scene), GetWeakSourceByName(source),
GetWeakTransitionByName(transition), state, restriction,
time, (strcmp(scene, PREVIOUS_SCENE_NAME) == 0),
mediaStr);
obs_data_release(array_obj);
}
obs_data_array_release(mediaArray);
}
void SceneSwitcher::setupMediaTab()
{
populateSceneSelection(ui->mediaScenes, true);
populateTransitionSelection(ui->mediaTransitions);
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
QComboBox *combo = reinterpret_cast<QComboBox *>(data);
if (strcmp(obs_source_get_id(source), "ffmpeg_source") == 0) {
const char *name = obs_source_get_name(source);
combo->addItem(name);
}
return true;
};
obs_enum_sources(sourceEnum, ui->mediaSources);
ui->mediaStates->addItem("None");
ui->mediaStates->addItem("Playing");
ui->mediaStates->addItem("Opening");
ui->mediaStates->addItem("Buffering");
ui->mediaStates->addItem("Paused");
ui->mediaStates->addItem("Stopped");
ui->mediaStates->addItem("Ended");
ui->mediaStates->addItem("Error");
ui->mediaTimeRestrictions->addItem("None");
ui->mediaTimeRestrictions->addItem("Time shorter");
ui->mediaTimeRestrictions->addItem("Time longer");
ui->mediaTimeRestrictions->addItem("Time remaining shorter");
ui->mediaTimeRestrictions->addItem("Time remaining longer");
for (auto &s : switcher->mediaSwitches) {
std::string sourceName = GetWeakSourceName(s.source);
std::string sceneName = (s.usePreviousScene)
? PREVIOUS_SCENE_NAME
: GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString listText = MakeMediaSwitchName(
sourceName.c_str(), sceneName.c_str(),
transitionName.c_str(), s.state, s.restriction, s.time);
QListWidgetItem *item =
new QListWidgetItem(listText, ui->mediaSwitches);
item->setData(Qt::UserRole, listText);
}
}

View File

@@ -219,5 +219,104 @@ bool SwitcherData::checkPause()
}
}
}
if (verbose && pause)
blog(LOG_INFO, "Advanced Scene Switcher pause match");
return pause;
}
void SwitcherData::savePauseSwitches(obs_data_t *obj)
{
obs_data_array_t *pauseScenesArray = obs_data_array_create();
for (OBSWeakSource &scene : switcher->pauseScenesSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(scene);
if (source) {
const char *n = obs_source_get_name(source);
obs_data_set_string(array_obj, "pauseScene", n);
obs_data_array_push_back(pauseScenesArray, array_obj);
obs_source_release(source);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "pauseScenes", pauseScenesArray);
obs_data_array_release(pauseScenesArray);
obs_data_array_t *pauseWindowsArray = obs_data_array_create();
for (std::string &window : switcher->pauseWindowsSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_data_set_string(array_obj, "pauseWindow", window.c_str());
obs_data_array_push_back(pauseWindowsArray, array_obj);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "pauseWindows", pauseWindowsArray);
obs_data_array_release(pauseWindowsArray);
}
void SwitcherData::loadPauseSwitches(obs_data_t *obj)
{
switcher->pauseScenesSwitches.clear();
obs_data_array_t *pauseScenesArray =
obs_data_get_array(obj, "pauseScenes");
size_t count = obs_data_array_count(pauseScenesArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(pauseScenesArray, i);
const char *scene =
obs_data_get_string(array_obj, "pauseScene");
switcher->pauseScenesSwitches.emplace_back(
GetWeakSourceByName(scene));
obs_data_release(array_obj);
}
obs_data_array_release(pauseScenesArray);
switcher->pauseWindowsSwitches.clear();
obs_data_array_t *pauseWindowsArray =
obs_data_get_array(obj, "pauseWindows");
count = obs_data_array_count(pauseWindowsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(pauseWindowsArray, i);
const char *window =
obs_data_get_string(array_obj, "pauseWindow");
switcher->pauseWindowsSwitches.emplace_back(window);
obs_data_release(array_obj);
}
obs_data_array_release(pauseWindowsArray);
}
void SceneSwitcher::setupPauseTab()
{
populateSceneSelection(ui->pauseScenesScenes, false);
populateWindowSelection(ui->pauseWindowsWindows);
for (auto &scene : switcher->pauseScenesSwitches) {
std::string sceneName = GetWeakSourceName(scene);
QString text = QString::fromStdString(sceneName);
QListWidgetItem *item =
new QListWidgetItem(text, ui->pauseScenes);
item->setData(Qt::UserRole, text);
}
for (auto &window : switcher->pauseWindowsSwitches) {
QString text = QString::fromStdString(window);
QListWidgetItem *item =
new QListWidgetItem(text, ui->pauseWindows);
item->setData(Qt::UserRole, text);
}
}

View File

@@ -132,6 +132,84 @@ void SwitcherData::checkRandom(bool &match, OBSWeakSource &scene,
delay = (int)r.delay * 1000;
match = true;
lastRandomScene = r.scene;
if (verbose)
blog(LOG_INFO, "Advanced Scene Switcher random match");
break;
}
}
void SwitcherData::saveRandomSwitches(obs_data_t *obj)
{
obs_data_array_t *randomArray = obs_data_array_create();
for (RandomSwitch &s : switcher->randomSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "scene", sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_double(array_obj, "delay", s.delay);
obs_data_set_string(array_obj, "str",
s.randomSwitchStr.c_str());
obs_data_array_push_back(randomArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "randomSwitches", randomArray);
obs_data_array_release(randomArray);
}
void SwitcherData::loadRandomSwitches(obs_data_t *obj)
{
switcher->randomSwitches.clear();
obs_data_array_t *randomArray =
obs_data_get_array(obj, "randomSwitches");
size_t count = obs_data_array_count(randomArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(randomArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
double delay = obs_data_get_double(array_obj, "delay");
const char *str = obs_data_get_string(array_obj, "str");
switcher->randomSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), delay, str);
obs_data_release(array_obj);
}
obs_data_array_release(randomArray);
}
void SceneSwitcher::setupRandomTab()
{
populateSceneSelection(ui->randomScenes, false);
populateTransitionSelection(ui->randomTransitions);
for (auto &s : switcher->randomSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeRandomSwitchName(
sceneName.c_str(), transitionName.c_str(), s.delay);
QListWidgetItem *item =
new QListWidgetItem(text, ui->randomScenesList);
item->setData(Qt::UserRole, text);
}
}

View File

@@ -1,9 +1,50 @@
#include <QFileDialog>
#include <QTextStream>
#include <QMessageBox>
#include <obs.hpp>
#include "headers/advanced-scene-switcher.hpp"
#define SECONDS_INDEX 0
#define MINUTES_INDEX 1
#define HOURS_INDEX 2
void SceneSwitcher::on_sceneRoundTripDelayUnits_currentIndexChanged(int index)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
double val = ui->sceneRoundTripSpinBox->value();
switch (switcher->sceneRoundTripUnitMultiplier) {
case 1:
val /= pow(60, index);
break;
case 60:
val /= pow(60, index - 1);
break;
case 3600:
val /= pow(60, index - 2);
break;
}
ui->sceneRoundTripSpinBox->setValue(val);
switch (index) {
case SECONDS_INDEX:
switcher->sceneRoundTripUnitMultiplier = 1;
break;
case MINUTES_INDEX:
switcher->sceneRoundTripUnitMultiplier = 60;
break;
case HOURS_INDEX:
switcher->sceneRoundTripUnitMultiplier = 3600;
break;
}
}
void SceneSwitcher::on_sceneRoundTripAdd_clicked()
{
QString scene1Name = ui->sceneRoundTripScenes1->currentText();
@@ -13,7 +54,8 @@ void SceneSwitcher::on_sceneRoundTripAdd_clicked()
if (scene1Name.isEmpty() || scene2Name.isEmpty())
return;
double delay = ui->sceneRoundTripSpinBox->value();
double delay = ui->sceneRoundTripSpinBox->value() *
switcher->sceneRoundTripUnitMultiplier;
if (scene1Name == scene2Name)
return;
@@ -35,7 +77,7 @@ void SceneSwitcher::on_sceneRoundTripAdd_clicked()
std::lock_guard<std::mutex> lock(switcher->m);
switcher->sceneRoundTripSwitches.emplace_back(
source1, source2, transition, int(delay * 1000),
source1, source2, transition, delay,
(scene2Name == QString(PREVIOUS_SCENE_NAME)),
text.toUtf8().constData());
} else {
@@ -47,7 +89,7 @@ void SceneSwitcher::on_sceneRoundTripAdd_clicked()
for (auto &s : switcher->sceneRoundTripSwitches) {
if (s.scene1 == source1) {
s.scene2 = source2;
s.delay = int(delay * 1000);
s.delay = delay;
s.transition = transition;
s.usePreviousScene =
(scene2Name ==
@@ -58,8 +100,6 @@ void SceneSwitcher::on_sceneRoundTripAdd_clicked()
}
}
}
ui->sceneRoundTrips->sortItems();
}
}
@@ -89,70 +129,65 @@ void SceneSwitcher::on_sceneRoundTripRemove_clicked()
delete item;
}
void SceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
if (!state) {
ui->autoStopScenes->setDisabled(true);
switcher->autoStopEnable = false;
} else {
ui->autoStopScenes->setDisabled(false);
switcher->autoStopEnable = true;
}
}
void SceneSwitcher::UpdateAutoStopScene(const QString &name)
{
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
switcher->autoStopScene = ws;
obs_weak_source_release(ws);
obs_source_release(scene);
}
void SceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
UpdateAutoStopScene(text);
}
void SceneSwitcher::on_sceneRoundTripSave_clicked()
{
QString directory = QFileDialog::getSaveFileName(
this, tr("Save Scene Round Trip to file ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (!directory.isEmpty()) {
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
QTextStream out(&file);
for (SceneRoundTripSwitch s :
switcher->sceneRoundTripSwitches) {
out << QString::fromStdString(
GetWeakSourceName(s.scene1))
<< "\n";
if (s.usePreviousScene)
out << (PREVIOUS_SCENE_NAME) << "\n";
else
out << QString::fromStdString(
GetWeakSourceName(s.scene2))
<< "\n";
out << s.delay << "\n";
out << QString::fromStdString(s.sceneRoundTripStr)
<< "\n";
out << QString::fromStdString(
GetWeakSourceName(s.transition))
<< "\n";
if (directory.isEmpty())
return;
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
obs_data_t *obj = obs_data_create();
switcher->saveSceneRoundTripSwitches(obj);
obs_data_save_json(obj, file.fileName().toUtf8().constData());
obs_data_release(obj);
}
// to be removed once support for old file format is dropped
bool oldSceneRoundTripLoad(QFile *file)
{
QTextStream in(file);
std::vector<QString> lines;
std::vector<SceneRoundTripSwitch> newSceneRoundTripSwitch;
while (!in.atEnd()) {
QString line = in.readLine();
lines.push_back(line);
if (lines.size() == 5) {
OBSWeakSource scene1 = GetWeakSourceByQString(lines[0]);
OBSWeakSource scene2 = GetWeakSourceByQString(lines[1]);
OBSWeakSource transition =
GetWeakTransitionByQString(lines[4]);
if (WeakSourceValid(scene1) &&
((lines[1] == QString(PREVIOUS_SCENE_NAME)) ||
(WeakSourceValid(scene2))) &&
WeakSourceValid(transition)) {
newSceneRoundTripSwitch.emplace_back(
SceneRoundTripSwitch(
GetWeakSourceByQString(lines[0]),
GetWeakSourceByQString(lines[1]),
GetWeakTransitionByQString(
lines[4]),
lines[2].toDouble() / 1000.,
(lines[1] ==
QString(PREVIOUS_SCENE_NAME)),
lines[3].toStdString()));
}
lines.clear();
}
}
if (lines.size() != 0 || newSceneRoundTripSwitch.size() == 0)
return false;
switcher->sceneRoundTripSwitches.clear();
switcher->sceneRoundTripSwitches = newSceneRoundTripSwitch;
return true;
}
void SceneSwitcher::on_sceneRoundTripLoad_clicked()
@@ -162,65 +197,45 @@ void SceneSwitcher::on_sceneRoundTripLoad_clicked()
QString directory = QFileDialog::getOpenFileName(
this, tr("Select a file to read Scene Round Trip from ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (!directory.isEmpty()) {
QFile file(directory);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
if (directory.isEmpty())
return;
QTextStream in(&file);
std::vector<QString> lines;
QFile file(directory);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
std::vector<SceneRoundTripSwitch> newSceneRoundTripSwitch;
obs_data_t *obj = obs_data_create_from_json_file(
file.fileName().toUtf8().constData());
while (!in.atEnd()) {
QString line = in.readLine();
lines.push_back(line);
if (lines.size() == 5) {
OBSWeakSource scene1 =
GetWeakSourceByQString(lines[0]);
OBSWeakSource scene2 =
GetWeakSourceByQString(lines[1]);
OBSWeakSource transition =
GetWeakTransitionByQString(lines[4]);
if (WeakSourceValid(scene1) &&
((lines[1] ==
QString(PREVIOUS_SCENE_NAME)) ||
(WeakSourceValid(scene2))) &&
WeakSourceValid(transition)) {
newSceneRoundTripSwitch.emplace_back(
SceneRoundTripSwitch(
GetWeakSourceByQString(
lines[0]),
GetWeakSourceByQString(
lines[1]),
GetWeakTransitionByQString(
lines[4]),
lines[2].toInt(),
(lines[1] ==
QString(PREVIOUS_SCENE_NAME)),
lines[3].toStdString()));
}
lines.clear();
}
}
if (lines.size() != 0 || newSceneRoundTripSwitch.size() == 0)
return;
switcher->sceneRoundTripSwitches.clear();
ui->sceneRoundTrips->clear();
switcher->sceneRoundTripSwitches = newSceneRoundTripSwitch;
for (SceneRoundTripSwitch s :
switcher->sceneRoundTripSwitches) {
QListWidgetItem *item = new QListWidgetItem(
QString::fromStdString(s.sceneRoundTripStr),
ui->sceneRoundTrips);
item->setData(
Qt::UserRole,
QString::fromStdString(s.sceneRoundTripStr));
if (!obj) {
QMessageBox Msgbox;
QString txt =
"Advanced Scene Switcher failed to import settings!\n";
txt += "Falling back to old format.";
Msgbox.setText(txt);
Msgbox.exec();
if (oldSceneRoundTripLoad(&file)) {
QString txt =
"Advanced Scene Switcher settings imported successfully!\n";
txt += "Please resave settings as support for old format will be dropped in next version.";
Msgbox.setText(txt);
Msgbox.exec();
close();
} else {
Msgbox.setText(
"Advanced Scene Switcher failed to import settings!");
Msgbox.exec();
}
return;
}
switcher->loadSceneRoundTripSwitches(obj);
obs_data_release(obj);
QMessageBox Msgbox;
Msgbox.setText(
"Advanced Scene Switcher settings imported successfully!");
Msgbox.exec();
close();
}
void SwitcherData::checkSceneRoundTrip(bool &match, OBSWeakSource &scene,
@@ -234,11 +249,18 @@ void SwitcherData::checkSceneRoundTrip(bool &match, OBSWeakSource &scene,
for (SceneRoundTripSwitch &s : sceneRoundTripSwitches) {
if (s.scene1 == ws) {
sceneRoundTripActive = true;
int dur = s.delay - interval;
int dur = s.delay * 1000 - interval;
if (dur > 0) {
waitScene = currentSource;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher sequence sleep %d",
dur);
cv.wait_for(lock,
std::chrono::milliseconds(dur));
waitScene = nullptr;
}
obs_source_t *currentSource2 =
obs_frontend_get_current_scene();
@@ -276,15 +298,15 @@ void SceneSwitcher::on_sceneRoundTrips_currentRowChanged(int idx)
std::string scene2 = GetWeakSourceName(s.scene2);
std::string transitionName =
GetWeakSourceName(s.transition);
int delay = s.delay;
double delay = s.delay /
switcher->sceneRoundTripUnitMultiplier;
ui->sceneRoundTripScenes1->setCurrentText(
scene1.c_str());
ui->sceneRoundTripScenes2->setCurrentText(
scene2.c_str());
ui->sceneRoundTripTransitions->setCurrentText(
transitionName.c_str());
ui->sceneRoundTripSpinBox->setValue((double)delay /
1000);
ui->sceneRoundTripSpinBox->setValue(delay);
break;
}
}
@@ -309,17 +331,170 @@ int SceneSwitcher::SceneRoundTripFindByData(const QString &scene1)
return idx;
}
void SwitcherData::autoStopStreamAndRecording()
void SceneSwitcher::on_sceneRoundTripUp_clicked()
{
obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
int index = ui->sceneRoundTrips->currentRow();
if (index != -1 && index != 0) {
ui->sceneRoundTrips->insertItem(
index - 1, ui->sceneRoundTrips->takeItem(index));
ui->sceneRoundTrips->setCurrentRow(index - 1);
if (ws && autoStopScene == ws) {
if (obs_frontend_streaming_active())
obs_frontend_streaming_stop();
if (obs_frontend_recording_active())
obs_frontend_recording_stop();
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->sceneRoundTripSwitches.begin() + index,
switcher->sceneRoundTripSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_sceneRoundTripDown_clicked()
{
int index = ui->sceneRoundTrips->currentRow();
if (index != -1 && index != ui->sceneRoundTrips->count() - 1) {
ui->sceneRoundTrips->insertItem(
index + 1, ui->sceneRoundTrips->takeItem(index));
ui->sceneRoundTrips->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->sceneRoundTripSwitches.begin() + index,
switcher->sceneRoundTripSwitches.begin() + index + 1);
}
}
void SwitcherData::saveSceneRoundTripSwitches(obs_data_t *obj)
{
obs_data_array_t *sceneRoundTripArray = obs_data_array_create();
for (SceneRoundTripSwitch &s : switcher->sceneRoundTripSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source1 = obs_weak_source_get_source(s.scene1);
obs_source_t *source2 = obs_weak_source_get_source(s.scene2);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source1 && (s.usePreviousScene || source2) && transition) {
const char *sceneName1 = obs_source_get_name(source1);
const char *sceneName2 = obs_source_get_name(source2);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "sceneRoundTripScene1",
sceneName1);
obs_data_set_string(array_obj, "sceneRoundTripScene2",
s.usePreviousScene
? PREVIOUS_SCENE_NAME
: sceneName2);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_double(array_obj, "delay", s.delay);
obs_data_set_string(array_obj, "sceneRoundTripStr",
s.sceneRoundTripStr.c_str());
obs_data_array_push_back(sceneRoundTripArray,
array_obj);
obs_source_release(source1);
obs_source_release(source2);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "sceneRoundTrip", sceneRoundTripArray);
obs_data_array_release(sceneRoundTripArray);
}
void SwitcherData::loadSceneRoundTripSwitches(obs_data_t *obj)
{
switcher->sceneRoundTripSwitches.clear();
obs_data_array_t *sceneRoundTripArray =
obs_data_get_array(obj, "sceneRoundTrip");
size_t count = obs_data_array_count(sceneRoundTripArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(sceneRoundTripArray, i);
const char *scene1 =
obs_data_get_string(array_obj, "sceneRoundTripScene1");
const char *scene2 =
obs_data_get_string(array_obj, "sceneRoundTripScene2");
const char *transition =
obs_data_get_string(array_obj, "transition");
double delay = 0;
// To be removed in future version
// to be compatible with older versions
if (!obs_data_has_user_value(array_obj, "delay")) {
delay = double(obs_data_get_int(array_obj,
"sceneRoundTripDelay"));
delay = delay * 1000. +
double(obs_data_get_int(
array_obj, "sceneRoundTripDelayMs"));
delay /= 1000.;
} else {
delay = obs_data_get_double(array_obj, "delay");
}
std::string str = MakeSceneRoundTripSwitchName(
scene1, scene2, transition, delay)
.toUtf8()
.constData();
const char *sceneRoundTripStr = str.c_str();
switcher->sceneRoundTripSwitches.emplace_back(
GetWeakSourceByName(scene1),
GetWeakSourceByName(scene2),
GetWeakTransitionByName(transition), delay,
(strcmp(scene2, PREVIOUS_SCENE_NAME) == 0),
sceneRoundTripStr);
obs_data_release(array_obj);
}
obs_data_array_release(sceneRoundTripArray);
}
void SceneSwitcher::setupSequenceTab()
{
populateSceneSelection(ui->sceneRoundTripScenes1, false);
populateSceneSelection(ui->sceneRoundTripScenes2, true);
populateTransitionSelection(ui->sceneRoundTripTransitions);
double smallestDelay = double(switcher->interval) / 1000;
for (auto &s : switcher->sceneRoundTripSwitches) {
std::string sceneName1 = GetWeakSourceName(s.scene1);
std::string sceneName2 = (s.usePreviousScene)
? PREVIOUS_SCENE_NAME
: GetWeakSourceName(s.scene2);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeSceneRoundTripSwitchName(
sceneName1.c_str(), sceneName2.c_str(),
transitionName.c_str(), s.delay);
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneRoundTrips);
item->setData(Qt::UserRole, text);
if (s.delay < smallestDelay)
smallestDelay = s.delay;
}
(smallestDelay * 1000 < switcher->interval)
? ui->intervalWarning->setVisible(true)
: ui->intervalWarning->setVisible(false);
ui->sceneRoundTripDelayUnits->addItem("seconds");
ui->sceneRoundTripDelayUnits->addItem("minutes");
ui->sceneRoundTripDelayUnits->addItem("hours");
switch (switcher->sceneRoundTripUnitMultiplier) {
case 1:
ui->sceneRoundTripDelayUnits->setCurrentIndex(0);
break;
case 60:
ui->sceneRoundTripDelayUnits->setCurrentIndex(1);
break;
case 3600:
ui->sceneRoundTripDelayUnits->setCurrentIndex(2);
break;
default:
break;
}
obs_source_release(currentSource);
obs_weak_source_release(ws);
}

View File

@@ -162,6 +162,11 @@ void SwitcherData::setDefaultSceneTransitions()
//This might cancel the current transition
//There is no way to be sure when the previous transition finished
obs_frontend_set_current_transition(transition);
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher default transition set");
obs_source_release(transition);
break;
}
@@ -264,6 +269,19 @@ void SceneSwitcher::on_defaultTransitions_currentRowChanged(int idx)
}
}
void SceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
if (!state) {
switcher->tansitionOverrideOverride = false;
} else {
switcher->tansitionOverrideOverride = true;
}
}
obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
obs_weak_source_t *scene2)
{
@@ -277,3 +295,221 @@ obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
}
return ws;
}
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
obs_source_t *transition, transitionData &td)
{
obs_source_t *scene = obs_weak_source_get_source(sceneWs);
obs_data_t *data = obs_source_get_private_settings(scene);
td.name = obs_data_get_string(data, "transition");
td.duration = obs_data_get_int(data, "duration");
const char *name = obs_source_get_name(transition);
int duration = obs_frontend_get_transition_duration();
obs_data_set_string(data, "transition", name);
obs_data_set_int(data, "transition_duration", duration);
obs_data_release(data);
obs_source_release(scene);
}
void restoreTransitionOverride(obs_source_t *scene, transitionData td)
{
obs_data_t *data = obs_source_get_private_settings(scene);
obs_data_set_string(data, "transition", td.name.c_str());
obs_data_set_int(data, "transition_duration", td.duration);
obs_data_release(data);
}
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
OBSWeakSource &transition,
bool &transitionOverrideOverride, transitionData &td)
{
obs_weak_source_t *currentScene =
obs_source_get_weak_source(currentSource);
obs_weak_source_t *nextTransitionWs =
getNextTransition(currentScene, targetScene);
obs_weak_source_release(currentScene);
obs_source_t *nextTransition = nullptr;
if (nextTransitionWs) {
nextTransition = obs_weak_source_get_source(nextTransitionWs);
} else if (transition) {
nextTransition = obs_weak_source_get_source(transition);
}
if (nextTransition) {
obs_frontend_set_current_transition(nextTransition);
}
if (transitionOverrideOverride)
overwriteTransitionOverride(targetScene, nextTransition, td);
obs_weak_source_release(nextTransitionWs);
obs_source_release(nextTransition);
}
void SwitcherData::saveSceneTransitions(obs_data_t *obj)
{
obs_data_array_t *sceneTransitionsArray = obs_data_array_create();
for (SceneTransition &s : switcher->sceneTransitions) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source1 = obs_weak_source_get_source(s.scene1);
obs_source_t *source2 = obs_weak_source_get_source(s.scene2);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source1 && source2 && transition) {
const char *sceneName1 = obs_source_get_name(source1);
const char *sceneName2 = obs_source_get_name(source2);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "Scene1", sceneName1);
obs_data_set_string(array_obj, "Scene2", sceneName2);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "Str",
s.sceneTransitionStr.c_str());
obs_data_array_push_back(sceneTransitionsArray,
array_obj);
obs_source_release(source1);
obs_source_release(source2);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "sceneTransitions", sceneTransitionsArray);
obs_data_array_release(sceneTransitionsArray);
obs_data_array_t *defaultTransitionsArray = obs_data_array_create();
for (DefaultSceneTransition &s : switcher->defaultSceneTransitions) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "Scene", sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "Str",
s.sceneTransitionStr.c_str());
obs_data_array_push_back(defaultTransitionsArray,
array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray);
obs_data_array_release(defaultTransitionsArray);
obs_data_set_bool(obj, "tansitionOverrideOverride",
switcher->tansitionOverrideOverride);
}
void SwitcherData::loadSceneTransitions(obs_data_t *obj)
{
switcher->sceneTransitions.clear();
obs_data_array_t *sceneTransitionsArray =
obs_data_get_array(obj, "sceneTransitions");
size_t count = obs_data_array_count(sceneTransitionsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(sceneTransitionsArray, i);
const char *scene1 = obs_data_get_string(array_obj, "Scene1");
const char *scene2 = obs_data_get_string(array_obj, "Scene2");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *sceneTransitionsStr =
obs_data_get_string(array_obj, "Str");
switcher->sceneTransitions.emplace_back(
GetWeakSourceByName(scene1),
GetWeakSourceByName(scene2),
GetWeakTransitionByName(transition),
sceneTransitionsStr);
obs_data_release(array_obj);
}
obs_data_array_release(sceneTransitionsArray);
switcher->defaultSceneTransitions.clear();
obs_data_array_t *defaultTransitionsArray =
obs_data_get_array(obj, "defaultTransitions");
count = obs_data_array_count(defaultTransitionsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(defaultTransitionsArray, i);
const char *scene = obs_data_get_string(array_obj, "Scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *sceneTransitionsStr =
obs_data_get_string(array_obj, "Str");
switcher->defaultSceneTransitions.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition),
sceneTransitionsStr);
obs_data_release(array_obj);
}
obs_data_array_release(defaultTransitionsArray);
switcher->tansitionOverrideOverride =
obs_data_get_bool(obj, "tansitionOverrideOverride");
}
void SceneSwitcher::setupTransitionsTab()
{
populateSceneSelection(ui->transitionsScene1, false);
populateSceneSelection(ui->transitionsScene2, false);
populateSceneSelection(ui->defaultTransitionsScene, false);
populateTransitionSelection(ui->transitionsTransitions);
populateTransitionSelection(ui->defaultTransitionsTransitions);
for (auto &s : switcher->sceneTransitions) {
std::string sceneName1 = GetWeakSourceName(s.scene1);
std::string sceneName2 = GetWeakSourceName(s.scene2);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeSceneTransitionName(sceneName1.c_str(),
sceneName2.c_str(),
transitionName.c_str());
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneTransitions);
item->setData(Qt::UserRole, text);
}
//(transitionDurationLongerThanInterval(switcher->interval))
// ? ui->transitionWarning->setVisible(true)
// : ui->transitionWarning->setVisible(false);
for (auto &s : switcher->defaultSceneTransitions) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeDefaultSceneTransitionName(
sceneName.c_str(), transitionName.c_str());
QListWidgetItem *item =
new QListWidgetItem(text, ui->defaultTransitions);
item->setData(Qt::UserRole, text);
}
ui->transitionOverridecheckBox->setChecked(
switcher->tansitionOverrideOverride);
}

View File

@@ -1,3 +1,5 @@
#include <QTimer>
#include "headers/advanced-scene-switcher.hpp"
void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
@@ -15,6 +17,12 @@ void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
scene = s.scene;
transition = s.transition;
minRegionSize = regionSize;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher region match");
break;
}
}
}
@@ -78,8 +86,6 @@ void SceneSwitcher::on_screenRegionAdd_clicked()
}
}
}
ui->screenRegions->sortItems();
}
}
@@ -155,3 +161,124 @@ int SceneSwitcher::ScreenRegionFindByData(const QString &region)
return idx;
}
void SceneSwitcher::on_screenRegionUp_clicked()
{
int index = ui->screenRegions->currentRow();
if (index != -1 && index != 0) {
ui->screenRegions->insertItem(
index - 1, ui->screenRegions->takeItem(index));
ui->screenRegions->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->screenRegionSwitches.begin() + index,
switcher->screenRegionSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_screenRegionDown_clicked()
{
int index = ui->screenRegions->currentRow();
if (index != -1 && index != ui->screenRegions->count() - 1) {
ui->screenRegions->insertItem(
index + 1, ui->screenRegions->takeItem(index));
ui->screenRegions->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->screenRegionSwitches.begin() + index,
switcher->screenRegionSwitches.begin() + index + 1);
}
}
void SwitcherData::saveScreenRegionSwitches(obs_data_t *obj)
{
obs_data_array_t *screenRegionArray = obs_data_array_create();
for (ScreenRegionSwitch &s : switcher->screenRegionSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "screenRegionScene",
sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_int(array_obj, "minX", s.minX);
obs_data_set_int(array_obj, "minY", s.minY);
obs_data_set_int(array_obj, "maxX", s.maxX);
obs_data_set_int(array_obj, "maxY", s.maxY);
obs_data_set_string(array_obj, "screenRegionStr",
s.regionStr.c_str());
obs_data_array_push_back(screenRegionArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "screenRegion", screenRegionArray);
obs_data_array_release(screenRegionArray);
}
void SwitcherData::loadScreenRegionSwitches(obs_data_t *obj)
{
switcher->screenRegionSwitches.clear();
obs_data_array_t *screenRegionArray =
obs_data_get_array(obj, "screenRegion");
size_t count = obs_data_array_count(screenRegionArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(screenRegionArray, i);
const char *scene =
obs_data_get_string(array_obj, "screenRegionScene");
const char *transition =
obs_data_get_string(array_obj, "transition");
int minX = obs_data_get_int(array_obj, "minX");
int minY = obs_data_get_int(array_obj, "minY");
int maxX = obs_data_get_int(array_obj, "maxX");
int maxY = obs_data_get_int(array_obj, "maxY");
std::string regionStr =
obs_data_get_string(array_obj, "screenRegionStr");
switcher->screenRegionSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), minX, minY, maxX,
maxY, regionStr);
obs_data_release(array_obj);
}
obs_data_array_release(screenRegionArray);
}
void SceneSwitcher::setupRegionTab()
{
populateSceneSelection(ui->screenRegionScenes, false);
populateTransitionSelection(ui->screenRegionsTransitions);
for (auto &s : switcher->screenRegionSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeScreenRegionSwitchName(
sceneName.c_str(), transitionName.c_str(), s.minX,
s.minY, s.maxX, s.maxY);
QListWidgetItem *item =
new QListWidgetItem(text, ui->screenRegions);
item->setData(Qt::UserRole, s.regionStr.c_str());
}
// screen region cursor position
QTimer *screenRegionTimer = new QTimer(this);
connect(screenRegionTimer, SIGNAL(timeout()), this,
SLOT(updateScreenRegionCursorPos()));
screenRegionTimer->start(1000);
}

View File

@@ -71,6 +71,7 @@ void SceneSwitcher::on_timeAdd_clicked()
} else {
QListWidgetItem *item = ui->timeSwitches->item(idx);
item->setText(text);
item->setData(Qt::UserRole, v);
{
std::lock_guard<std::mutex> lock(switcher->m);
@@ -83,7 +84,6 @@ void SceneSwitcher::on_timeAdd_clicked()
QString(PREVIOUS_SCENE_NAME));
s.timeSwitchStr =
text.toUtf8().constData();
;
break;
}
}
@@ -142,7 +142,97 @@ void SwitcherData::checkTimeSwitch(bool &match, OBSWeakSource &scene,
scene = (s.usePreviousScene) ? previousScene : s.scene;
transition = s.transition;
match = true;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher time match");
break;
}
}
}
void SwitcherData::saveTimeSwitches(obs_data_t *obj)
{
obs_data_array_t *timeArray = obs_data_array_create();
for (TimeSwitch &s : switcher->timeSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *sceneSource = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if ((s.usePreviousScene || sceneSource) && transition) {
const char *sceneName =
obs_source_get_name(sceneSource);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "scene",
s.usePreviousScene
? PREVIOUS_SCENE_NAME
: sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(
array_obj, "time",
s.time.toString().toStdString().c_str());
obs_data_array_push_back(timeArray, array_obj);
}
obs_source_release(sceneSource);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "timeSwitches", timeArray);
obs_data_array_release(timeArray);
}
void SwitcherData::loadTimeSwitches(obs_data_t *obj)
{
switcher->timeSwitches.clear();
obs_data_array_t *timeArray = obs_data_get_array(obj, "timeSwitches");
size_t count = obs_data_array_count(timeArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(timeArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
QTime time = QTime::fromString(
obs_data_get_string(array_obj, "time"));
std::string timeSwitchStr =
MakeTimeSwitchName(scene, transition, time)
.toUtf8()
.constData();
switcher->timeSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), time,
(strcmp(scene, PREVIOUS_SCENE_NAME) == 0),
timeSwitchStr);
obs_data_release(array_obj);
}
obs_data_array_release(timeArray);
}
void SceneSwitcher::setupTimeTab()
{
populateSceneSelection(ui->timeScenes, true);
populateTransitionSelection(ui->timeTransitions);
for (auto &s : switcher->timeSwitches) {
std::string sceneName = (s.usePreviousScene)
? PREVIOUS_SCENE_NAME
: GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString listText = MakeTimeSwitchName(
sceneName.c_str(), transitionName.c_str(), s.time);
QListWidgetItem *item =
new QListWidgetItem(listText, ui->timeSwitches);
item->setData(Qt::UserRole, listText);
}
}

View File

@@ -312,3 +312,121 @@ void SwitcherData::checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
}
}
}
void SwitcherData::saveWindowTitleSwitches(obs_data_t *obj)
{
obs_data_array_t *windowTitleArray = obs_data_array_create();
for (WindowSceneSwitch &s : switcher->windowSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "scene", sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "window_title",
s.window.c_str());
obs_data_set_bool(array_obj, "fullscreen",
s.fullscreen);
obs_data_set_bool(array_obj, "focus", s.focus);
obs_data_array_push_back(windowTitleArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "switches", windowTitleArray);
obs_data_array_release(windowTitleArray);
obs_data_array_t *ignoreWindowsArray = obs_data_array_create();
for (std::string &window : switcher->ignoreWindowsSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_data_set_string(array_obj, "ignoreWindow", window.c_str());
obs_data_array_push_back(ignoreWindowsArray, array_obj);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "ignoreWindows", ignoreWindowsArray);
obs_data_array_release(ignoreWindowsArray);
}
void SwitcherData::loadWindowTitleSwitches(obs_data_t *obj)
{
switcher->windowSwitches.clear();
obs_data_array_t *windowTitleArray =
obs_data_get_array(obj, "switches");
size_t count = obs_data_array_count(windowTitleArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(windowTitleArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *window =
obs_data_get_string(array_obj, "window_title");
bool fullscreen = obs_data_get_bool(array_obj, "fullscreen");
bool focus = obs_data_get_bool(array_obj, "focus") ||
!obs_data_has_user_value(array_obj, "focus");
switcher->windowSwitches.emplace_back(
GetWeakSourceByName(scene), window,
GetWeakTransitionByName(transition), fullscreen, focus);
obs_data_release(array_obj);
}
obs_data_array_release(windowTitleArray);
switcher->ignoreWindowsSwitches.clear();
obs_data_array_t *ignoreWindowsArray =
obs_data_get_array(obj, "ignoreWindows");
count = obs_data_array_count(ignoreWindowsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(ignoreWindowsArray, i);
const char *window =
obs_data_get_string(array_obj, "ignoreWindow");
switcher->ignoreWindowsSwitches.emplace_back(window);
obs_data_release(array_obj);
}
obs_data_array_release(ignoreWindowsArray);
}
void SceneSwitcher::setupTitleTab()
{
populateSceneSelection(ui->scenes, false);
populateTransitionSelection(ui->transitions);
populateWindowSelection(ui->windows);
populateWindowSelection(ui->ignoreWindowsWindows);
for (auto &s : switcher->windowSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeSwitchName(sceneName.c_str(),
s.window.c_str(),
transitionName.c_str(),
s.fullscreen, s.focus);
QListWidgetItem *item = new QListWidgetItem(text, ui->switches);
item->setData(Qt::UserRole, s.window.c_str());
}
for (auto &window : switcher->ignoreWindowsSwitches) {
QString text = QString::fromStdString(window);
QListWidgetItem *item =
new QListWidgetItem(text, ui->ignoreWindows);
item->setData(Qt::UserRole, text);
}
}