Compare commits

..

130 Commits

Author SHA1 Message Date
WarmUpTill
4056fc180d Create codeql.yml 2022-04-03 14:19:54 +02:00
WarmUpTill
07f11c63f0 Don't allow manipulating area selection when highlighting match 2022-04-02 12:51:57 -07:00
WarmUpTill
6685a1ccaa Do not spam first time setup message 2022-04-02 09:14:26 -07:00
WarmUpTill
ecee919e0b Hide legacy tabs by default and adjust default tab order 2022-04-02 09:14:26 -07:00
WarmUpTill
e8c1b673c0 Allow accessing macro properties even without selecting any macro 2022-04-02 09:14:26 -07:00
WarmUpTill
1a8b185f7d Make date "At"-type check more robust
Previously the "At" check could skip the desired time window if e.g another
macro was performing a long wait wait action or condition was taking
a long time to check.
The date condition will now take into consideration when the last time
was the condition checked a date instead of simply applying a fixed
window of "switcher->interval".
2022-04-02 09:13:01 -07:00
WarmUpTill
eb8a9df627 Create cropped screenshot depending on selected area 2022-04-02 00:29:29 -07:00
WarmUpTill
123d308a0d Add option to select OSB' main video output 2022-03-29 10:02:44 -07:00
WarmUpTill
102b93d3b5 Fix deadlock when changing condition / action type
The video preview dialogs could not exit as the destructor was holding
the main mutex while joining the preview thread, which in turn was
waiting to get the access to the mutex to exit its main loop
2022-03-29 10:02:44 -07:00
WarmUpTill
eeceb7cbd9 Fix random action not being random 2022-03-27 11:45:50 -07:00
WarmUpTill
92add4f090 Update .gitignore 2022-03-26 15:55:48 -07:00
ubuntu
3ed7727c85 Fix warnings 2022-03-26 15:40:19 -07:00
ubuntu
a922d6a73d Add missing reordering of action widgets 2022-03-26 15:40:19 -07:00
WarmUpTill
9ba8a02a11 Add hotkey to move current macro segment selection up / down 2022-03-26 10:05:50 -07:00
WarmUpTill
e61539a878 Reuse macro segment widgets if possible 2022-03-26 10:05:50 -07:00
WarmUpTill
5a78c99703 Ignore delete hotkey if macro tab is not in focus 2022-03-26 10:05:50 -07:00
WarmUpTill
dbbcf04b8a Auto scroll during drag if touching top or bottom areas of list widget 2022-03-26 10:05:50 -07:00
WarmUpTill
6a8066795b Improve drag & drop behaviour of macro segments
- Depending on the drop position you can either drop before or after a
  widget
- Added a visual indicator where the macro segment will be dropped
2022-03-26 10:05:50 -07:00
WarmUpTill
e0763a4957 Improve performance for drag and drop
- Changing the style sheets for the selection frame dynamically considerably
  slowed down the handling of the mouse events.
  Instead two separate widgets with fixed style sheets are now used.
- Recreating the macro segments after dropping a widget took too much
  time leading to unresponsive drop operations
2022-03-26 10:05:50 -07:00
WarmUpTill
1ab9a38914 Selection idx handling was moved to macro segment list widget 2022-03-26 10:05:50 -07:00
WarmUpTill
a6ca266dab Fix potential crash / memory leak when clearing layouts 2022-03-26 10:05:50 -07:00
WarmUpTill
0a8f279e97 Rework macro segment list widget
- Allows drag and drop reorder
- Handles selection change
- Handles highlight
- Handles changing collapse change
- Handles add, insert, remove, and clear functionality
2022-03-26 10:05:50 -07:00
WarmUpTill
1b63978acb Add media seek action 2022-03-26 10:05:40 -07:00
WarmUpTill
8487ef4716 clean up locale 2022-03-26 10:05:40 -07:00
WarmUpTill
ddc2ee4fa5 Remove "Played to end" and replace it with "Playlist end" state
"Ended" can now be used to also detect the end of individual playlist
items.
2022-03-26 10:05:40 -07:00
WarmUpTill
11fede6cc3 Fix start streaming action only working every second attempt 2022-03-18 13:08:31 -07:00
WarmUpTill
1d45072c58 Recurse in setLayoutVisible() 2022-03-15 10:58:29 -07:00
WarmUpTill
d450210d39 Add option to visually select area to check 2022-03-15 10:58:29 -07:00
WarmUpTill
5462334693 Add option to perform video check only on parts of the video source
This will significantly reduce CPU usage if the video source larger than
the selected area and make it easier to only check parts of a video
source output image.
2022-03-15 10:58:29 -07:00
WarmUpTill
293d3dd16c Work around macro handle buttons not being redrawn
Not sure what exaclty causes this - investigating it further is not
worth the time at the moment
2022-03-12 06:35:59 -08:00
WarmUpTill
71b6ae4d78 Add additional settings dialog to macro tab
This settings dialog currently contains debug features to highlight
recently executed macros, actions and conditions.
2022-03-12 06:35:59 -08:00
WarmUpTill
69d6d63dfd Increase macroSplitter size by moving condition controls into handle 2022-03-12 06:35:59 -08:00
WarmUpTill
d693dbc844 Add highlighting of onChagne if actions were prevented recently 2022-03-12 06:35:59 -08:00
WarmUpTill
428e114a0a Highlight recently executed action and true conditions 2022-03-12 06:35:59 -08:00
WarmUpTill
001d8b4714 Move hightlight and update of paused state into MacroListEntryWidget 2022-03-12 06:35:59 -08:00
WarmUpTill
37734445e7 CI: Add automated check for invalid locale files
Also clean up issues found in the existing locale files
2022-03-05 17:09:58 -08:00
WarmUpTill
d1fe5beaeb Fix fade animation triggering unexpectedly for controls 2022-03-05 17:09:58 -08:00
WarmUpTill
036afb4a4f Add option to abort active fade when starting new fade 2022-03-05 15:38:46 -08:00
WarmUpTill
c87589d534 reduce duplicate code 2022-03-05 15:38:46 -08:00
WarmUpTill
1df513585d Add option to specifiy rate of change instead of duration for audio fade 2022-03-05 15:38:46 -08:00
WarmUpTill
e1bacd75b6 Move audio fade threads to generic _helperThreads of macro
This will allow waiting for and stopping audio fades when calling Stop()
for a particular macro
2022-03-05 15:38:46 -08:00
WarmUpTill
7095f4668c Rework wait action to support being interrupted by calling Stop() 2022-03-05 15:38:46 -08:00
WarmUpTill
8131ad3c24 Block when calling Stop() until macro is stopped 2022-03-05 15:38:46 -08:00
WarmUpTill
ac5b2c3f9e Rework handling of fade threads
Previous implementation did not allow ongoing fades to be stopped.
New implementations allows waiting for fade to complete.
2022-03-05 15:38:46 -08:00
WarmUpTill
a86f7d0fd4 Set background of openVR controls transparent 2022-03-05 07:40:14 -08:00
WarmUpTill
34736ffbda Update zh-CN locale (by qwe1154323937) 2022-03-05 01:19:48 -08:00
WarmUpTill
da28fea2e0 Fix potential freeze or crash with "Show match" dialog
This is a workaround to handle random segfaults triggered when using ...
QMetaObject::invokeMethod(this, "RedrawImage", -Qt::QueuedConnection,
		   Q_ARG(QImage, image));
... from within CheckForMatchLoop().
Even using BlockingQueuedConnection causes deadlocks.
2022-02-26 16:08:55 -08:00
WarmUpTill
b6b0531dbc Fix size ofi new collapsed segments containing ResizingPlainTextEdit 2022-02-26 16:08:55 -08:00
WarmUpTill
2de0a94756 Fix highlight animations never stopping 2022-02-26 16:08:55 -08:00
WarmUpTill
d018740442 Fix potential deadlock in ShowMatchDialog 2022-02-26 16:08:55 -08:00
WarmUpTill
d89f8a7972 Clear pixmap if screenshot creation failed 2022-02-26 16:08:55 -08:00
WarmUpTill
f7a2aa09cb Fix potential crash 2022-02-26 16:08:55 -08:00
WarmUpTill
04825e4b88 Fix warning 2022-02-26 08:23:10 -08:00
WarmUpTill
1cfd981245 Reduce text edit areas size of file, filter, source, transform segments 2022-02-26 08:23:10 -08:00
WarmUpTill
6fb8297275 Use QPropertyAnimation::DeleteWhenStopped PulseWidget() when once is set
Also fixes potential crash when waiting for single shot timer when
looping and widget and animations are deleted in the meantime
2022-02-26 08:23:10 -08:00
WarmUpTill
4253c99cf2 Fade out segment controls which will likely not be needed
This was done to give some hints which controls will likely need to be
used to avoid mistakenly clicking on the action controls when editing
conditions.
2022-02-26 08:23:10 -08:00
WarmUpTill
ae13a68cae Double click on list item in run and sequence action to edit 2022-02-26 08:23:10 -08:00
WarmUpTill
4e5aae7e5f Add 'OBS stats' condition type (#414)
It supports various different types of stats:
* fps
* cpu
* ram
* frame time
* skipped frames
* missed frames
* dropped frames
* data size sent
* bitrate
2022-02-26 11:37:54 +01:00
WarmUpTill
88dff37776 Allow drag and drop reordering of macros 2022-02-24 14:15:23 -08:00
WarmUpTill
83df1ab658 Skip collapse animation if section is already in the desired state 2022-02-24 14:15:23 -08:00
WarmUpTill
5298b14803 Use name of macro to be copied in addNewMacro() dialog 2022-02-24 14:15:23 -08:00
WarmUpTill
eeedf11c6a Add hotkey to delete macro segments 2022-02-24 14:15:23 -08:00
WarmUpTill
89efb6d1c8 Allow selecting either an action or condition but not both 2022-02-24 14:15:23 -08:00
WarmUpTill
1054bce4a7 Remove slight movement of macro segment when (de-)selecting them 2022-02-24 14:15:23 -08:00
WarmUpTill
d3cbddf0a4 Allow selecting scene groups in preview scene change action 2022-02-24 14:15:05 -08:00
WarmUpTill
a91fed1583 Simplify waitForTransitionChange()
Will now also work if the target scene is the already currently active
scene.
2022-02-24 14:15:05 -08:00
WarmUpTill
c94a791d19 Allow switching to already active scene if in studio mode
Preview and program scene can differ in regards to the active scene
items.
Thus it can make sense to switch to the already active scene.
2022-02-24 14:15:05 -08:00
WarmUpTill
02e380a9f2 Allow selecting current and previous scene in preview scene action 2022-02-24 14:15:05 -08:00
WarmUpTill
4349a98614 CI: Switch to Visual Studio 17 2022 2022-02-22 11:40:33 -08:00
WarmUpTill
f294283306 CI: Bump OBS version 2022-02-21 12:09:23 -08:00
WarmUpTill
cc9d5897da Add deprecation warning for legacy tabs 2022-02-20 04:50:12 -08:00
WarmUpTill
8e3eb8519b Fix potential crashes when adding or removing macro segments
I wrongly assumed that std::deque would guarantee that pointers to
elements in deque would not be invalidated by insert() or erase() but
this is not the case it seems.

""
An erase in the middle of the deque invalidates all the iterators and
references to elements of the deque. An erase at either end of the deque
invalidates only the iterators and the references to the erased
elements.
""

I guess I got lucky noone ran into these sorts of crashes for now?
2022-02-20 04:50:12 -08:00
WarmUpTill
354ef56070 Do not select any item if pressing add without any active selection 2022-02-20 04:50:12 -08:00
WarmUpTill
352dac5e32 Add MacroSegmentList to allow deselecting items
Previously once a macro segement was selected you could only deselect it
by clicking on different macro segment, thus never allowing the user to
deselect all macro segments
2022-02-20 04:50:12 -08:00
WarmUpTill
584fe4b3b6 Copy logic type when adding conditions 2022-02-20 04:50:12 -08:00
WarmUpTill
bdd489aea5 Fix crashes related to PulseWidget()
* Fix crash if widget was deleted during animation
* Fix crash if multiple animations were started at the same time
2022-02-20 04:50:12 -08:00
WarmUpTill
0b2e725471 Do not run collapse animation when calling SetContent() 2022-02-20 04:50:12 -08:00
WarmUpTill
45e8378021 Remove old macro segment controls 2022-02-20 04:50:12 -08:00
WarmUpTill
955f308c59 Add list-like controls to macro actions and conditions 2022-02-20 04:50:12 -08:00
WarmUpTill
2c25bb4dd4 Make slider background transparent
Sliders, e.g. in the video condition, would otherwise block parts of the
highlight frame around macro segments.
2022-02-18 13:40:33 -08:00
WarmUpTill
d8dcd91ed9 Split video condition into multiple files 2022-02-18 13:40:33 -08:00
WarmUpTill
03900e936b Rename class AdvSSScreenshotObj to ScreenshotHelper 2022-02-18 13:40:33 -08:00
WarmUpTill
6d2360eb87 Clean up AdvSSScreenshotObj usage in video condition 2022-02-18 13:40:33 -08:00
WarmUpTill
ab4a70a758 Refresh contents of "Show match" dialog every second 2022-02-18 13:40:33 -08:00
WarmUpTill
b48276bd5c Add option to check for OBS shutdown to MacroConditionPluginState 2022-02-18 13:35:15 -08:00
WarmUpTill
34167366db Add day of week based check to date condition 2022-02-12 17:57:35 -08:00
WarmUpTill
bfa8f4d9d5 Visually clean up date condition
- add "and" separator if "between" check is chosen
- split dateTimeEdit into a dateEdit and a timeEdit to make editing
  easier
- move ignore* checkboxes in front of respective edit widgets to make
  their intention more clear and add tooltips to further clarify their
  use
2022-02-12 17:57:35 -08:00
WarmUpTill
f8e3a3e275 CI: Fix MacOS build
Packages version bump
2022-02-10 12:19:56 -08:00
WarmUpTill
1328896801 Reopen last opened tab in current session
Will reset to General tab on startup / scene collection change
2022-02-10 11:29:44 -08:00
WarmUpTill
0effbd8ace Add context menu entry for expanding and collapsing edit areas 2022-02-04 11:38:42 -08:00
WarmUpTill
08a8157f3a Reduce spacing between macro segments 2022-02-04 11:38:42 -08:00
WarmUpTill
262bb3f1ee Keep size of individual macro sgements consistent
Previously the segments would expand if there was still room in the
condition and action area.
2022-02-04 11:38:42 -08:00
WarmUpTill
415f63c46c Fix warnings generated about missing icons 2022-02-04 11:38:42 -08:00
WarmUpTill
0771f939a6 Restore original color after PulseWidget() completes 2022-02-04 11:38:42 -08:00
WarmUpTill
a81ee605d6 Rename function 2022-02-04 11:38:42 -08:00
WarmUpTill
1e7726bcfc Add option to highlight recently executed macros on the Macro tab 2022-02-04 11:38:42 -08:00
WarmUpTill
dc698e426d Add MacroListEntryWidget
To be used for macro tab's macro list as regular QListWidgetItem are too
limited
2022-02-04 11:38:42 -08:00
WarmUpTill
a4f66dbbcd Fix warnings 2022-02-04 11:38:42 -08:00
WarmUpTill
1516b9a939 Add option to hide legacy tabs 2022-02-04 11:38:42 -08:00
WarmUpTill
e13f20c860 Adding frame highlighting individual macro segments 2022-02-04 11:38:42 -08:00
WarmUpTill
727992f1d0 Clean up stylesheet usage 2022-02-04 11:38:42 -08:00
WarmUpTill
4e75a15118 Clean up unnecessary variable 2022-02-04 11:38:42 -08:00
WarmUpTill
48368f4bd6 Move vertical / horizontal control handling to macro segment 2022-02-04 11:38:42 -08:00
WarmUpTill
61d9031933 Add option to toggle between vertical and horizontal macro controls 2022-02-04 11:38:42 -08:00
WarmUpTill
487c735e13 Delay show and hide of macro controls 2022-02-04 11:38:42 -08:00
WarmUpTill
7a34c334bd Highlight macro segments new position when moving it up or down 2022-02-04 11:38:42 -08:00
WarmUpTill
4988683e22 Resize conditions and actions areas on expand / collapse all 2022-02-04 11:38:42 -08:00
WarmUpTill
cb681643c8 Fix scene switch action blocking unexpectedly
If "wait for transition" was checked and no valid scene was selected the
waitForTransitionChange() function will wait for its maximum allowed
duration as switcher->currentScene will never be null.
2022-02-02 09:55:35 -08:00
WarmUpTill
d79e10ec97 Add tr-TR.ini locale (Provided by dargat) 2022-01-30 19:15:49 +01:00
WarmUpTill
af188b55df Move plugins to separate folder (#400) 2022-01-29 15:47:16 +01:00
WarmUpTill
f287a1041d Add option to also check for configured volume level 2022-01-25 12:46:16 -08:00
WarmUpTill
fda9b1c3ea Fix dock location and size not saving correctly
OBS will handle the docking position / window size and position as long
as an object name is set
2022-01-25 10:33:41 -08:00
WarmUpTill
9eb7f58525 CI: Save debian build result 2022-01-25 10:33:02 -08:00
WarmUpTill
abd6fd6b7e Add sequence action (#395) 2022-01-22 18:07:26 +01:00
WarmUpTill
2909b1300e Fix empty statusControl of general tab blocking mouse input 2022-01-22 06:14:03 -08:00
WarmUpTill
d1331a60b9 Fix crash on exit caused by status dock 2022-01-22 06:14:03 -08:00
WarmUpTill
d5fc3befb8 Remove QtConcurrent dependency 2022-01-22 06:14:03 -08:00
WarmUpTill
80f9c79907 CI: Bump windows QT version to 5.15.2 2022-01-22 06:14:03 -08:00
WarmUpTill
e4bd1fc180 Reduce amount of signals emitted on repopulate 2022-01-22 06:14:03 -08:00
WarmUpTill
59c87ca027 Emphasize that a duration of 0s means using default duration 2022-01-21 09:51:00 -08:00
WarmUpTill
eda161de39 Sort condition and action selection alphabetically 2022-01-21 09:51:00 -08:00
WarmUpTill
6033426a6e Enable word wrap for macro priority warning 2022-01-21 09:51:00 -08:00
WarmUpTill
caea16c6ad Hide selected macro for run or stop action
Running a macro from within itself is not supported and stopping it from
withint itself does not seem useful either.
2022-01-21 09:51:00 -08:00
WarmUpTill
e3a39a5df7 Add label clarifying the condition's intent 2022-01-21 09:51:00 -08:00
WarmUpTill
38b7e08711 Add macro action to stop other macros 2022-01-21 09:51:00 -08:00
WarmUpTill
7349841758 Fix unexpectedly returning true for hotkey condition
The condition would return true after unpause, if a configured hotkey was pressed
during the time the macro was paused.
2022-01-21 09:50:19 -08:00
WarmUpTill
5d58269b02 Add function to access parent macro from condition or action 2022-01-21 09:50:19 -08:00
WarmUpTill
8a4aef0f50 Update bug_report.md 2022-01-19 20:22:26 +01:00
WarmUpTill
4b877d865c Add issue templates 2022-01-16 19:43:51 +01:00
WarmUpTill
82503286ea Use the same widget for main settings window and status dock 2022-01-14 09:11:36 -08:00
154 changed files with 11193 additions and 5143 deletions

35
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,35 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Logs**
Please provide a log of your issue with verbose logging enabled (See General tab of the plugin).
See [here](https://obsproject.com/forum/threads/please-post-a-log-with-your-issue-heres-how.23074/) for a description where to find the log files and how to share them.
Please share the currently used plugin settings by exporting them them to a file (See General tab of the plugin).
If applicable, add screenshots to help explain your problem.
**Version information**
- OS: [e.g. Windows 10]
- OBS Version [e.g. 27.1.3]
- Plugin Version [e.g. 1.17.1]
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -46,3 +46,10 @@ jobs:
- name: build - name: build
run: | run: |
debuild --no-lintian --no-sign debuild --no-lintian --no-sign
mv ../*.deb .
- name: Publish
if: success()
uses: actions/upload-artifact@v2.2.1
with:
name: "obs-scene-switcher.deb"
path: ${{ github.workspace }}/*.deb

View File

@@ -5,7 +5,7 @@ on: [push, pull_request, workflow_dispatch]
env: env:
PLUGIN_NAME: SceneSwitcher PLUGIN_NAME: SceneSwitcher
LIB_NAME: advanced-scene-switcher LIB_NAME: advanced-scene-switcher
OBS_TAG: 27.1.3 OBS_TAG: 27.2.0
jobs: jobs:
macos64: macos64:
@@ -85,7 +85,7 @@ jobs:
run: | run: |
curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg
sudo hdiutil attach ./Packages.dmg sudo hdiutil attach ./Packages.dmg
sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target / sudo installer -pkg /Volumes/Packages\ 1.2.10/Install\ Packages.pkg -target /
- name: Package - name: Package
if: success() if: success()
shell: bash shell: bash
@@ -184,7 +184,8 @@ jobs:
libx11-xcb-dev \ libx11-xcb-dev \
libxcb-xfixes0-dev \ libxcb-xfixes0-dev \
libopencv-dev \ libopencv-dev \
libprocps-dev libprocps-dev \
libpci-dev
- name: "Configure" - name: "Configure"
shell: bash shell: bash
run: | run: |
@@ -206,8 +207,9 @@ jobs:
mv ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/${{ env.LIB_NAME }}.so ./${{ env.LIB_NAME }}/bin/64bit/${{ env.LIB_NAME }}.so mv ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/${{ env.LIB_NAME }}.so ./${{ env.LIB_NAME }}/bin/64bit/${{ env.LIB_NAME }}.so
mv ./UI/frontend-plugins/${{ env.PLUGIN_NAME }}/data ./${{ env.LIB_NAME }}/data mv ./UI/frontend-plugins/${{ env.PLUGIN_NAME }}/data ./${{ env.LIB_NAME }}/data
# Macro modules # Macro modules
mkdir -p ./${{ env.LIB_NAME }}/bin/64bit/adv-ss-plugins
strip -d ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/src/external-macro-modules/opencv/advanced-scene-switcher-opencv.so strip -d ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/src/external-macro-modules/opencv/advanced-scene-switcher-opencv.so
mv ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/src/external-macro-modules/opencv/advanced-scene-switcher-opencv.so ./${{ env.LIB_NAME }}/bin/64bit/ mv ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/src/external-macro-modules/opencv/advanced-scene-switcher-opencv.so ./${{ env.LIB_NAME }}/bin/64bit/adv-ss-plugins
tar -cvzf "${FILE_NAME}" ${{ env.LIB_NAME }} tar -cvzf "${FILE_NAME}" ${{ env.LIB_NAME }}
- name: "Publish" - name: "Publish"
uses: actions/upload-artifact@v2.2.1 uses: actions/upload-artifact@v2.2.1
@@ -221,8 +223,8 @@ jobs:
matrix: matrix:
arch: [32, 64] arch: [32, 64]
env: env:
QT_VERSION: 5.10.1 QT_VERSION: 5.15.2
CMAKE_GENERATOR: "Visual Studio 16 2019" CMAKE_GENERATOR: "Visual Studio 17 2022"
CMAKE_SYSTEM_VERSION: "10.0.18363.657" CMAKE_SYSTEM_VERSION: "10.0.18363.657"
WINDOWS_DEPS_VERSION: "2019" WINDOWS_DEPS_VERSION: "2019"
steps: steps:
@@ -275,11 +277,11 @@ jobs:
Get-Location Get-Location
if ( ${{ matrix.arch }} -eq 32 ) if ( ${{ matrix.arch }} -eq 32 )
{ {
cmake -G"${{ env.CMAKE_GENERATOR }}" -A"Win32" -DOpenCV_DIR="${{ github.workspace }}/UI/frontend-plugins/SceneSwitcher/deps/opencv/build/" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win32" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2017" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE .. cmake -G"${{ env.CMAKE_GENERATOR }}" -A"Win32" -DOpenCV_DIR="${{ github.workspace }}/UI/frontend-plugins/SceneSwitcher/deps/opencv/build/" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win32" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2019" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE ..
} }
else else
{ {
cmake -G"${{ env.CMAKE_GENERATOR }}" -A"x64" -DOpenCV_DIR="${{ github.workspace }}/UI/frontend-plugins/SceneSwitcher/deps/opencv/build/" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win64" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2017_64" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE .. cmake -G"${{ env.CMAKE_GENERATOR }}" -A"x64" -DOpenCV_DIR="${{ github.workspace }}/UI/frontend-plugins/SceneSwitcher/deps/opencv/build/" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win64" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2019_64" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE ..
} }
- name: Build - name: Build
run: | run: |
@@ -291,9 +293,10 @@ jobs:
$env:FILE_NAME="${{ env.PLUGIN_NAME }}-${env:FILE_DATE}-${{ github.sha }}-windows" $env:FILE_NAME="${{ env.PLUGIN_NAME }}-${env:FILE_DATE}-${{ github.sha }}-windows"
echo "FILE_NAME=${env:FILE_NAME}" >> ${env:GITHUB_ENV} echo "FILE_NAME=${env:FILE_NAME}" >> ${env:GITHUB_ENV}
robocopy .\build${{ matrix.arch }}\rundir\RelWithDebInfo\obs-plugins\${{ matrix.arch }}bit\ .\package\obs-plugins\${{ matrix.arch }}bit ${{ env.LIB_NAME }}* /E /XF .gitignore robocopy .\build${{ matrix.arch }}\rundir\RelWithDebInfo\obs-plugins\${{ matrix.arch }}bit\ .\package\obs-plugins\${{ matrix.arch }}bit ${{ env.LIB_NAME }}* /E /XF .gitignore
robocopy .\build${{ matrix.arch }}\rundir\RelWithDebInfo\obs-plugins\${{ matrix.arch }}bit\ .\package\obs-plugins\${{ matrix.arch }}bit adv-ss-plugins /E /XF .gitignore
robocopy .\build${{ matrix.arch }}\rundir\RelWithDebInfo\data\obs-plugins\${{ env.LIB_NAME }}\ .\package\data\obs-plugins\${{ env.LIB_NAME }}\ /E /XF .gitignore robocopy .\build${{ matrix.arch }}\rundir\RelWithDebInfo\data\obs-plugins\${{ env.LIB_NAME }}\ .\package\data\obs-plugins\${{ env.LIB_NAME }}\ /E /XF .gitignore
cp UI/frontend-plugins/${{ env.PLUGIN_NAME }}/deps/opencv/build/bin/Release/*dll package/obs-plugins/${{ matrix.arch }}bit cp UI/frontend-plugins/${{ env.PLUGIN_NAME }}/deps/opencv/build/bin/Release/*dll package/obs-plugins/${{ matrix.arch }}bit/adv-ss-plugins
cp UI/frontend-plugins/${{ env.PLUGIN_NAME }}/deps/openvr/bin/win${{ matrix.arch }}/*dll package/obs-plugins/${{ matrix.arch }}bit cp UI/frontend-plugins/${{ env.PLUGIN_NAME }}/deps/openvr/bin/win${{ matrix.arch }}/*dll package/obs-plugins/${{ matrix.arch }}bit/adv-ss-plugins
exit 0 exit 0
- name: Publish zip - name: Publish zip
if: success() if: success()

107
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,107 @@
name: "CodeQL"
on:
push:
branches: [master]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
env:
OBS_TAG: 27.2.0
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
repository: obsproject/obs-studio
submodules: "recursive"
ref: "refs/tags/${{ env.OBS_TAG }}"
- name: "Checkout plugin"
uses: actions/checkout@v2.3.4
with:
path: UI/frontend-plugins/SceneSwitcher
submodules: "recursive"
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: cpp
- name: Add plugin to obs cmake
shell: bash
run: echo "add_subdirectory(SceneSwitcher)" >> UI/frontend-plugins/CMakeLists.txt
- name: Fetch Git Tags
run: git fetch --prune --tags --unshallow
- name: Install prerequisites (Apt)
shell: bash
run: |
sudo dpkg --add-architecture amd64
sudo apt-get -qq update
sudo apt-get install -y \
build-essential \
checkinstall \
cmake \
libasound2-dev \
libavcodec-dev \
libavdevice-dev \
libavfilter-dev \
libavformat-dev \
libavutil-dev \
libcurl4-openssl-dev \
libfdk-aac-dev \
libfontconfig-dev \
libfreetype6-dev \
libgl1-mesa-dev \
libjack-jackd2-dev \
libjansson-dev \
libluajit-5.1-dev \
libpulse-dev \
libqt5x11extras5-dev \
libspeexdsp-dev \
libswresample-dev \
libswscale-dev \
libudev-dev \
libv4l-dev \
libva-dev \
libvlc-dev \
libx11-dev \
libx264-dev \
libxcb-randr0-dev \
libxcb-shm0-dev \
libxcb-xinerama0-dev \
libxcomposite-dev \
libxinerama-dev \
libxtst-dev \
libmbedtls-dev \
pkg-config \
python3-dev \
qtbase5-dev \
qtbase5-private-dev \
libqt5svg5-dev \
swig \
libxss-dev \
libx11-xcb-dev \
libxcb-xfixes0-dev \
libopencv-dev \
libprocps-dev \
libpci-dev
- name: "Configure"
shell: bash
run: |
mkdir ./build
cd ./build
cmake -DENABLE_PIPEWIRE=OFF -DUNIX_STRUCTURE=0 -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/obs-studio-portable" -DWITH_RTMPS=OFF -DBUILD_BROWSER=OFF ..
- name: "Build"
shell: bash
working-directory: ${{ github.workspace }}/build
run: make -j4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

14
.github/workflows/locale-check.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: Check locale
on: [push, pull_request]
jobs:
ubuntu64:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Check locale files
run: |
python3 ./CI/checkLocale.py -p data/locale/

9
.gitignore vendored
View File

@@ -92,6 +92,15 @@ Thumbs.db
*.iml *.iml
*.ipr *.ipr
*.sublime* *.sublime*
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
.history/
*.vsix
forms/advanced-scene-switcher.ui.autosave
# Directories # # Directories #
############### ###############

93
CI/checkLocale.py Normal file
View File

@@ -0,0 +1,93 @@
#!/usr/bin/env python3
import os
import sys
import argparse
defaultLocaleFile = "en-US.ini"
class localeEntry:
locale = ""
placeholders = []
def __init__(self, locale, widgets) -> None:
self.locale = locale
self.placeholders = widgets
def getNonDefaultLocales(dir):
files = []
for filename in os.listdir(dir):
f = os.path.join(dir, filename)
if os.path.isfile(f) and not f.endswith(defaultLocaleFile):
files.append(f)
return files
def getAllLocaleEntriesWithWidgetPlaceholders(file):
localeEntries = []
with open(file, 'r', encoding='UTF-8') as f:
for line in f.readlines():
widgetPlaceholders = []
for word in line.split("{{"):
if not "}}" in word:
continue
word = "{{" + word[:word.rfind("}}")] + "}}"
widgetPlaceholders.append(word)
localeEntries.append(localeEntry(
line.split("=")[0], widgetPlaceholders))
return localeEntries
def getLocaleEntryFrom(entry, list):
for element in list:
if element.locale == entry.locale:
return element
return None
def checkWidgetPlacehodlers(file, expectedPlaceholders):
localeEntries = getAllLocaleEntriesWithWidgetPlaceholders(file)
result = True
for localeEntry in localeEntries:
expectedEntry = getLocaleEntryFrom(localeEntry, expectedPlaceholders)
if expectedEntry is None:
print(
"WARNING: Locale entry \"{}\" from \"{}\" not found in \"{}\"".format(localeEntry.locale, file, defaultLocaleFile))
continue
for p in localeEntry.placeholders:
if p not in expectedEntry.placeholders:
print("WARNING: Locale entry \"{}\" from \"{}\" does contain \"{}\" while \"{}\" does not".format(
localeEntry.locale, file, p, defaultLocaleFile))
for p in expectedEntry.placeholders:
if p not in localeEntry.placeholders:
result = False
print("ERROR: Locale entry \"{}\" from \"{}\" does not contain \"{}\"".format(
localeEntry.locale, file, p))
return result
def main():
parser = argparse.ArgumentParser(
description='Checks for inconsistencies regarding widget placeholders in the different locale files.')
parser.add_argument(
'-p', '--path', help='Path to locale folder', required=True)
args = parser.parse_args()
placeholders = getAllLocaleEntriesWithWidgetPlaceholders(
os.path.join(args.path, defaultLocaleFile))
nonDefaultLocales = getNonDefaultLocales(args.path)
result = True
for f in nonDefaultLocales:
if checkWidgetPlacehodlers(f, placeholders) == False:
result = False
if result == False:
sys.exit(1)
print("SUCCESS: No issues found!")
sys.exit(0)
if __name__ == "__main__":
main()

View File

@@ -30,6 +30,327 @@
<dict> <dict>
<key>CHILDREN</key> <key>CHILDREN</key>
<array> <array>
<dict>
<key>CHILDREN</key>
<array>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../../../../build/UI/frontend-plugins/SceneSwitcher/src/external-macro-modules/opencv/advanced-scene-switcher-opencv.so</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_objdetect.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_features2d.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_core.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_calib3d.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_calib3d.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_imgproc.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_features2d.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_core.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_flann.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_calib3d.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_objdetect.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_imgproc.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_flann.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_imgproc.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_flann.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_features2d.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_core.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_objdetect.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>adv-ss-plugins</string>
<key>PATH_TYPE</key>
<integer>0</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>2</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict> <dict>
<key>CHILDREN</key> <key>CHILDREN</key>
<array/> <array/>
@@ -46,310 +367,6 @@
<key>UID</key> <key>UID</key>
<integer>0</integer> <integer>0</integer>
</dict> </dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../../../../build/UI/frontend-plugins/SceneSwitcher/src/external-macro-modules/opencv/advanced-scene-switcher-opencv.so</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_objdetect.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_features2d.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_core.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_calib3d.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_calib3d.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_imgproc.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_features2d.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_core.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_flann.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_calib3d.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_objdetect.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_imgproc.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_flann.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_imgproc.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_flann.4.5.3.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_features2d.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_core.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../deps/opencv/build/lib/libopencv_objdetect.4.5.dylib</string>
<key>PATH_TYPE</key>
<integer>3</integer>
<key>PERMISSIONS</key>
<integer>493</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array> </array>
<key>GID</key> <key>GID</key>
<integer>80</integer> <integer>80</integer>

View File

@@ -211,6 +211,7 @@ set(advanced-scene-switcher_HEADERS
src/headers/macro-action-scene-transform.hpp src/headers/macro-action-scene-transform.hpp
src/headers/macro-action-scene-visibility.hpp src/headers/macro-action-scene-visibility.hpp
src/headers/macro-action-screenshot.hpp src/headers/macro-action-screenshot.hpp
src/headers/macro-action-sequence.hpp
src/headers/macro-action-source.hpp src/headers/macro-action-source.hpp
src/headers/macro-action-streaming.hpp src/headers/macro-action-streaming.hpp
src/headers/macro-action-systray.hpp src/headers/macro-action-systray.hpp
@@ -228,6 +229,7 @@ set(advanced-scene-switcher_HEADERS
src/headers/macro-condition-idle.hpp src/headers/macro-condition-idle.hpp
src/headers/macro-condition-macro.hpp src/headers/macro-condition-macro.hpp
src/headers/macro-condition-media.hpp src/headers/macro-condition-media.hpp
src/headers/macro-condition-obs-stats.hpp
src/headers/macro-condition-plugin-state.hpp src/headers/macro-condition-plugin-state.hpp
src/headers/macro-condition-process.hpp src/headers/macro-condition-process.hpp
src/headers/macro-condition-recording.hpp src/headers/macro-condition-recording.hpp
@@ -244,8 +246,10 @@ set(advanced-scene-switcher_HEADERS
src/headers/macro-condition-virtual-cam.hpp src/headers/macro-condition-virtual-cam.hpp
src/headers/macro-condition-window.hpp src/headers/macro-condition-window.hpp
src/headers/macro.hpp src/headers/macro.hpp
src/headers/macro-controls.hpp src/headers/macro-list-entry-widget.hpp
src/headers/macro-properties.hpp
src/headers/macro-segment.hpp src/headers/macro-segment.hpp
src/headers/macro-segment-list.hpp
src/headers/macro-selection.hpp src/headers/macro-selection.hpp
src/headers/curl-helper.hpp src/headers/curl-helper.hpp
src/headers/hotkey.hpp src/headers/hotkey.hpp
@@ -257,8 +261,9 @@ set(advanced-scene-switcher_HEADERS
src/headers/duration-control.hpp src/headers/duration-control.hpp
src/headers/file-selection.hpp src/headers/file-selection.hpp
src/headers/section.hpp src/headers/section.hpp
src/headers/status-dock.hpp src/headers/status-control.hpp
src/headers/platform-funcs.hpp src/headers/platform-funcs.hpp
src/headers/resizing-text-edit.hpp
src/headers/utility.hpp src/headers/utility.hpp
src/headers/volume-control.hpp src/headers/volume-control.hpp
src/headers/version.h) src/headers/version.h)
@@ -310,6 +315,7 @@ set(advanced-scene-switcher_SOURCES
src/macro-action-scene-transform.cpp src/macro-action-scene-transform.cpp
src/macro-action-scene-visibility.cpp src/macro-action-scene-visibility.cpp
src/macro-action-screenshot.cpp src/macro-action-screenshot.cpp
src/macro-action-sequence.cpp
src/macro-action-source.cpp src/macro-action-source.cpp
src/macro-action-streaming.cpp src/macro-action-streaming.cpp
src/macro-action-systray.cpp src/macro-action-systray.cpp
@@ -327,6 +333,7 @@ set(advanced-scene-switcher_SOURCES
src/macro-condition-idle.cpp src/macro-condition-idle.cpp
src/macro-condition-macro.cpp src/macro-condition-macro.cpp
src/macro-condition-media.cpp src/macro-condition-media.cpp
src/macro-condition-obs-stats.cpp
src/macro-condition-plugin-state.cpp src/macro-condition-plugin-state.cpp
src/macro-condition-process.cpp src/macro-condition-process.cpp
src/macro-condition-recording.cpp src/macro-condition-recording.cpp
@@ -343,8 +350,10 @@ set(advanced-scene-switcher_SOURCES
src/macro-condition-virtual-cam.cpp src/macro-condition-virtual-cam.cpp
src/macro-condition-window.cpp src/macro-condition-window.cpp
src/macro.cpp src/macro.cpp
src/macro-controls.cpp src/macro-list-entry-widget.cpp
src/macro-properties.cpp
src/macro-segment.cpp src/macro-segment.cpp
src/macro-segment-list.cpp
src/macro-selection.cpp src/macro-selection.cpp
src/macro-tab.cpp src/macro-tab.cpp
src/curl-helper.cpp src/curl-helper.cpp
@@ -353,8 +362,9 @@ set(advanced-scene-switcher_SOURCES
src/screenshot-helper.cpp src/screenshot-helper.cpp
src/transition-selection.cpp src/transition-selection.cpp
src/name-dialog.cpp src/name-dialog.cpp
src/resizing-text-edit.cpp
src/duration-control.cpp src/duration-control.cpp
src/status-dock.cpp src/status-control.cpp
src/section.cpp src/section.cpp
src/utility.cpp src/utility.cpp
src/volume-control.cpp src/volume-control.cpp

View File

@@ -117,7 +117,6 @@ AdvSceneSwitcher.mediaTab.states.Paused="Pausiert"
AdvSceneSwitcher.mediaTab.states.stopped="Gestoppt" AdvSceneSwitcher.mediaTab.states.stopped="Gestoppt"
AdvSceneSwitcher.mediaTab.states.ended="Beendet" AdvSceneSwitcher.mediaTab.states.ended="Beendet"
AdvSceneSwitcher.mediaTab.states.error="Fehler" AdvSceneSwitcher.mediaTab.states.error="Fehler"
AdvSceneSwitcher.mediaTab.states.playedToEnd="Zu Ende gespielt"
AdvSceneSwitcher.mediaTab.states.any="Beliebig" AdvSceneSwitcher.mediaTab.states.any="Beliebig"
AdvSceneSwitcher.mediaTab.timeRestriction.none="Keine Auswahl" AdvSceneSwitcher.mediaTab.timeRestriction.none="Keine Auswahl"
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Dauer kürzer" AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Dauer kürzer"
@@ -223,7 +222,6 @@ AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Dieser Tab ermöglicht es
; Network Tab ; Network Tab
AdvSceneSwitcher.networkTab.title="Netzwerk" AdvSceneSwitcher.networkTab.title="Netzwerk"
AdvSceneSwitcher.networkTab.warning="Die Verwendung des Servers außerhalb eines lokalen Netzwerks kann dazu führen, dass die aktive Szene von dritten Personen ausgelesen werden kann." AdvSceneSwitcher.networkTab.warning="Die Verwendung des Servers außerhalb eines lokalen Netzwerks kann dazu führen, dass die aktive Szene von dritten Personen ausgelesen werden kann."
AdvSceneSwitcher.networkTab.Disabledwarning="Diese Funktionalität musste unter macOS leider aufgrund von Bibliotheksinkompatibilitäten mit dem obs-websocket plugin deaktiviert werden."
AdvSceneSwitcher.networkTab.server="Server starten (Sendet Szenenwechselnachrichten zu allen verbundenen Clients)" AdvSceneSwitcher.networkTab.server="Server starten (Sendet Szenenwechselnachrichten zu allen verbundenen Clients)"
AdvSceneSwitcher.networkTab.server.port="Port" AdvSceneSwitcher.networkTab.server.port="Port"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Nur IPv4 verwenden (deaktiviert IPv6)" AdvSceneSwitcher.networkTab.server.lockToIPv4="Nur IPv4 verwenden (deaktiviert IPv6)"

View File

@@ -30,6 +30,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose loggi
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size" AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications" AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints" AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
AdvSceneSwitcher.generalTab.priority="Priority" AdvSceneSwitcher.generalTab.priority="Priority"
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)" AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
AdvSceneSwitcher.generalTab.priority.threadPriority="Use thread priority" AdvSceneSwitcher.generalTab.priority.threadPriority="Use thread priority"
@@ -76,6 +77,11 @@ AdvSceneSwitcher.macroTab.exists="Macro name exists already"
AdvSceneSwitcher.macroTab.copy="Create copy" AdvSceneSwitcher.macroTab.copy="Create copy"
AdvSceneSwitcher.macroTab.expandAll="Expand all" AdvSceneSwitcher.macroTab.expandAll="Expand all"
AdvSceneSwitcher.macroTab.collapseAll="Collapse all" AdvSceneSwitcher.macroTab.collapseAll="Collapse all"
AdvSceneSwitcher.macroTab.maximize="Maximize"
AdvSceneSwitcher.macroTab.minimize="Minimize"
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros"
AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently"
AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro"
; Macro Logic ; Macro Logic
AdvSceneSwitcher.logic.none="Ignore entry" AdvSceneSwitcher.logic.none="Ignore entry"
@@ -89,10 +95,13 @@ AdvSceneSwitcher.logic.not="If not"
; Macro Conditions ; Macro Conditions
AdvSceneSwitcher.condition.audio="Audio" AdvSceneSwitcher.condition.audio="Audio"
AdvSceneSwitcher.condition.audio.state.below="below" AdvSceneSwitcher.condition.audio.state.below="below"
AdvSceneSwitcher.condition.audio.state.exact="exactly"
AdvSceneSwitcher.condition.audio.state.above="above" AdvSceneSwitcher.condition.audio.state.above="above"
AdvSceneSwitcher.condition.audio.state.mute="muted" AdvSceneSwitcher.condition.audio.state.mute="muted"
AdvSceneSwitcher.condition.audio.state.unmute="unmuted" AdvSceneSwitcher.condition.audio.state.unmute="unmuted"
AdvSceneSwitcher.condition.audio.entry="Volume of {{audioSources}} is {{condition}} {{volume}}" AdvSceneSwitcher.condition.audio.type.output="Output volume"
AdvSceneSwitcher.condition.audio.type.volume="Configured volume level"
AdvSceneSwitcher.condition.audio.entry="{{checkType}} of {{audioSources}} is {{condition}} {{volume}}"
AdvSceneSwitcher.condition.cursor="Cursor" AdvSceneSwitcher.condition.cursor="Cursor"
AdvSceneSwitcher.condition.cursor.type.region="is in region" AdvSceneSwitcher.condition.cursor.type.region="is in region"
AdvSceneSwitcher.condition.cursor.type.moving="is moving" AdvSceneSwitcher.condition.cursor.type.moving="is moving"
@@ -119,6 +128,7 @@ AdvSceneSwitcher.condition.media="Media"
AdvSceneSwitcher.condition.media.anyOnScene="Any media source on" AdvSceneSwitcher.condition.media.anyOnScene="Any media source on"
AdvSceneSwitcher.condition.media.allOnScene="All media sources on" AdvSceneSwitcher.condition.media.allOnScene="All media sources on"
AdvSceneSwitcher.condition.media.matchOnChange="Only match on change (Note: This option will be removed in a future version - please use time constraints instead)" AdvSceneSwitcher.condition.media.matchOnChange="Only match on change (Note: This option will be removed in a future version - please use time constraints instead)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!"
AdvSceneSwitcher.condition.media.entry="{{mediaSources}}{{scenes}} state is {{states}} and {{timeRestrictions}} {{time}}" AdvSceneSwitcher.condition.media.entry="{{mediaSources}}{{scenes}} state is {{states}} and {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.video="Video" AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="exactly matches" AdvSceneSwitcher.condition.video.condition.match="exactly matches"
@@ -140,16 +150,25 @@ AdvSceneSwitcher.condition.video.objectScaleThreshold="Scale factor: "
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load." AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load."
AdvSceneSwitcher.condition.video.minNeighborDescription="A higher minimum neighbors value will result in fewer but higher quality matches." AdvSceneSwitcher.condition.video.minNeighborDescription="A higher minimum neighbors value will result in fewer but higher quality matches."
AdvSceneSwitcher.condition.video.showMatch="Show match" AdvSceneSwitcher.condition.video.showMatch="Show match"
AdvSceneSwitcher.condition.video.showMatch.loading="Checking for match"
AdvSceneSwitcher.condition.video.screenshotFail="Failed to get screenshot of source!" AdvSceneSwitcher.condition.video.screenshotFail="Failed to get screenshot of source!"
AdvSceneSwitcher.condition.video.screenshotEmpty="Screenshot is empty - Is the source visible?"
AdvSceneSwitcher.condition.video.patternMatchFail="Pattern was not found!" AdvSceneSwitcher.condition.video.patternMatchFail="Pattern was not found!"
AdvSceneSwitcher.condition.video.patternMatchSuccess="Pattern is highlighted in red"
AdvSceneSwitcher.condition.video.objectMatchFail="Object was not found!" AdvSceneSwitcher.condition.video.objectMatchFail="Object was not found!"
AdvSceneSwitcher.condition.video.objectMatchSuccess="Object is highlighted in red"
AdvSceneSwitcher.condition.video.modelLoadFail="Model data could not be loaded!" AdvSceneSwitcher.condition.video.modelLoadFail="Model data could not be loaded!"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}" AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Model data (haar cascade classifier): {{modelDataPath}}" AdvSceneSwitcher.condition.video.entry.modelPath="Model data (haar cascade classifier): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum neighbors: {{minNeighbors}}" AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum neighbors: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.minSize="Minimum size: {{minSizeX}} x {{minSizeY}}"
AdvSceneSwitcher.condition.video.entry.maxSize="Maximum size: {{maxSizeX}} x {{maxSizeY}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduce CPU load by performing check only every {{throttleCount}} milliseconds" AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduce CPU load by performing check only every {{throttleCount}} milliseconds"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}Perform check only in area {{checkArea}} {{selectArea}}"
AdvSceneSwitcher.condition.video.minSize="Minimum size:"
AdvSceneSwitcher.condition.video.maxSize="Maximum size:"
AdvSceneSwitcher.condition.video.selectArea="Select area"
AdvSceneSwitcher.condition.video.selectArea.status="Only highlighted area will be checked"
AdvSceneSwitcher.condition.video.width="Width"
AdvSceneSwitcher.condition.video.height="Height"
AdvSceneSwitcher.condition.stream="Streaming" AdvSceneSwitcher.condition.stream="Streaming"
AdvSceneSwitcher.condition.stream.state.start="Stream running" AdvSceneSwitcher.condition.stream.state.start="Stream running"
AdvSceneSwitcher.condition.stream.state.stop="Stream stopped" AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
@@ -168,6 +187,7 @@ AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duratio
AdvSceneSwitcher.condition.pluginState="Plugin state" AdvSceneSwitcher.condition.pluginState="Plugin state"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval" AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval"
AdvSceneSwitcher.condition.pluginState.state.running="Advanced scene switcher is running" AdvSceneSwitcher.condition.pluginState.state.running="Advanced scene switcher is running"
AdvSceneSwitcher.condition.pluginState.state.shutdown="OBS is shutting down"
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}" AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
AdvSceneSwitcher.condition.timer="Timer" AdvSceneSwitcher.condition.timer="Timer"
AdvSceneSwitcher.condition.timer.type.fixed="Fixed" AdvSceneSwitcher.condition.timer.type.fixed="Fixed"
@@ -222,19 +242,34 @@ AdvSceneSwitcher.condition.sceneOrder.entry="On{{scenes}}{{sources}}{{conditions
AdvSceneSwitcher.condition.hotkey="Hotkey" AdvSceneSwitcher.condition.hotkey="Hotkey"
AdvSceneSwitcher.condition.hotkey.name="Macro trigger hotkey" AdvSceneSwitcher.condition.hotkey.name="Macro trigger hotkey"
AdvSceneSwitcher.condition.hotkey.tip="Note: You can configure the keybindings for this hotkey in the OBS settings window" AdvSceneSwitcher.condition.hotkey.tip="Note: You can configure the keybindings for this hotkey in the OBS settings window"
AdvSceneSwitcher.condition.hotkey.entry="Name: {{name}}" AdvSceneSwitcher.condition.hotkey.entry.line1="Hotkey is pressed"
AdvSceneSwitcher.condition.hotkey.entry.line2="Name: {{name}}"
AdvSceneSwitcher.condition.replay="Replay buffer" AdvSceneSwitcher.condition.replay="Replay buffer"
AdvSceneSwitcher.condition.replay.state.stopped="Replay buffer stopped" AdvSceneSwitcher.condition.replay.state.stopped="Replay buffer stopped"
AdvSceneSwitcher.condition.replay.state.started="Replay buffer started" AdvSceneSwitcher.condition.replay.state.started="Replay buffer started"
AdvSceneSwitcher.condition.replay.state.saved="Replay buffer saved" AdvSceneSwitcher.condition.replay.state.saved="Replay buffer saved"
AdvSceneSwitcher.condition.replay.entry="{{state}}" AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="Date" AdvSceneSwitcher.condition.date="Date"
AdvSceneSwitcher.condition.date.anyDay="Any day"
AdvSceneSwitcher.condition.date.monday="Monday"
AdvSceneSwitcher.condition.date.tuesday="Tuesday"
AdvSceneSwitcher.condition.date.wednesday="Wednesday"
AdvSceneSwitcher.condition.date.thursday="Thursday"
AdvSceneSwitcher.condition.date.friday="Friday"
AdvSceneSwitcher.condition.date.saturday="Saturday"
AdvSceneSwitcher.condition.date.sunday="Sunday"
AdvSceneSwitcher.condition.date.state.at="At" AdvSceneSwitcher.condition.date.state.at="At"
AdvSceneSwitcher.condition.date.state.after="After" AdvSceneSwitcher.condition.date.state.after="After"
AdvSceneSwitcher.condition.date.state.before="Before" AdvSceneSwitcher.condition.date.state.before="Before"
AdvSceneSwitcher.condition.date.state.between="Between" AdvSceneSwitcher.condition.date.state.between="Between"
AdvSceneSwitcher.condition.date.entry.line1="{{condition}} {{dateTime}} {{dateTime2}} {{ignoreDate}} Ignore date component {{ignoreTime}} Ignore time component" AdvSceneSwitcher.condition.date.separator="and"
AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} Repeat every {{duration}} on date match" AdvSceneSwitcher.condition.date.ignoreDate="If unchecked the date component will be ignored"
AdvSceneSwitcher.condition.date.ignoreTime="If unchecked the time component will be ignored"
AdvSceneSwitcher.condition.date.showAdvancedSettings="Show advanced settings"
AdvSceneSwitcher.condition.date.showSimpleSettings="Show simple settings"
AdvSceneSwitcher.condition.date.entry.simple="On {{dayOfWeek}} at {{weekTime}}"
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} Repeat every {{duration}} on date match"
AdvSceneSwitcher.condition.sceneTransform="Scene item transform" AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform" AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
AdvSceneSwitcher.condition.sceneTransform.regex="Use regular expressions" AdvSceneSwitcher.condition.sceneTransform.regex="Use regular expressions"
@@ -264,6 +299,25 @@ AdvSceneSwitcher.condition.errorStatus="OpenVR error: "
AdvSceneSwitcher.condition.openvr.entry.line1="HMD is in ..." AdvSceneSwitcher.condition.openvr.entry.line1="HMD is in ..."
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}" AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
AdvSceneSwitcher.condition.openvr.entry.line3="HMD is currently at {{xPos}} x {{yPos}} x {{zPos}}" AdvSceneSwitcher.condition.openvr.entry.line3="HMD is currently at {{xPos}} x {{yPos}} x {{zPos}}"
AdvSceneSwitcher.condition.stats="OBS stats"
AdvSceneSwitcher.condition.stats.type.fps="FPS"
AdvSceneSwitcher.condition.stats.type.CPUUsage="CPU Usage"
AdvSceneSwitcher.condition.stats.type.HDDSpaceAvailable="Disk space available"
AdvSceneSwitcher.condition.stats.type.memoryUsage="Memory Usage"
AdvSceneSwitcher.condition.stats.type.averageTimeToRender="Average time to render frame"
AdvSceneSwitcher.condition.stats.type.skippedFrames="Skipped frames due to encoding lag"
AdvSceneSwitcher.condition.stats.type.missedFrames="Frames missed due to rendering lag"
AdvSceneSwitcher.condition.stats.type.droppedFrames.stream="Stream dropped frames"
AdvSceneSwitcher.condition.stats.type.megabytesSent.stream="Stream total data output"
AdvSceneSwitcher.condition.stats.type.bitrate.stream="Stream bitrate"
AdvSceneSwitcher.condition.stats.type.droppedFrames.recording="Recording dropped frames"
AdvSceneSwitcher.condition.stats.type.megabytesSent.recording="Recording total data output"
AdvSceneSwitcher.condition.stats.type.bitrate.recording="Recording bitrate"
AdvSceneSwitcher.condition.stats.condition.above="above"
AdvSceneSwitcher.condition.stats.condition.equals="equal to"
AdvSceneSwitcher.condition.stats.condition.below="below"
AdvSceneSwitcher.condition.stats.dockHint="You can open the \"Stats\" dock to view the current status"
AdvSceneSwitcher.condition.stats.entry="{{stats}} is {{condition}} {{value}}"
; Macro Actions ; Macro Actions
AdvSceneSwitcher.action.switchScene="Switch scene" AdvSceneSwitcher.action.switchScene="Switch scene"
@@ -279,7 +333,12 @@ AdvSceneSwitcher.action.audio.type.mute="Mute"
AdvSceneSwitcher.action.audio.type.unmute="Unmute" AdvSceneSwitcher.action.audio.type.unmute="Unmute"
AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume" AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume"
AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume" AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume"
AdvSceneSwitcher.action.audio.fade="{{fade}}Fade over {{duration}} seconds" AdvSceneSwitcher.action.audio.fade.type.duration="over a duration of"
AdvSceneSwitcher.action.audio.fade.type.rate="at a rate of"
AdvSceneSwitcher.action.audio.fade.duration="{{fade}}Fade {{fadeTypes}} {{duration}} seconds."
AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade {{fadeTypes}} {{rate}}per second."
AdvSceneSwitcher.action.audio.fade.wait="Wait for fade to complete."
AdvSceneSwitcher.action.audio.fade.abort="Abort already active fade."
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}" AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.recording="Recording" AdvSceneSwitcher.action.recording="Recording"
AdvSceneSwitcher.action.recording.type.stop="Stop recording" AdvSceneSwitcher.action.recording.type.stop="Stop recording"
@@ -328,12 +387,14 @@ AdvSceneSwitcher.action.media.type.stop="Stop"
AdvSceneSwitcher.action.media.type.restart="Restart" AdvSceneSwitcher.action.media.type.restart="Restart"
AdvSceneSwitcher.action.media.type.next="Next" AdvSceneSwitcher.action.media.type.next="Next"
AdvSceneSwitcher.action.media.type.previous="Previous" AdvSceneSwitcher.action.media.type.previous="Previous"
AdvSceneSwitcher.action.media.entry="{{actions}} {{mediaSources}}" AdvSceneSwitcher.action.media.type.seek="Seek to"
AdvSceneSwitcher.action.media.entry="{{actions}}{{duration}}{{mediaSources}}"
AdvSceneSwitcher.action.macro="Macro" AdvSceneSwitcher.action.macro="Macro"
AdvSceneSwitcher.action.macro.type.pause="Pause" AdvSceneSwitcher.action.macro.type.pause="Pause"
AdvSceneSwitcher.action.macro.type.unpause="Unpause" AdvSceneSwitcher.action.macro.type.unpause="Unpause"
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter" AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
AdvSceneSwitcher.action.macro.type.run="Run" AdvSceneSwitcher.action.macro.type.run="Run"
AdvSceneSwitcher.action.macro.type.stop="Stop"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}" AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.pluginState="Plugin state" AdvSceneSwitcher.action.pluginState="Plugin state"
AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin" AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin"
@@ -396,6 +457,12 @@ AdvSceneSwitcher.action.profile.entry="Switch active profile to {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Scene collection" AdvSceneSwitcher.action.sceneCollection="Scene collection"
AdvSceneSwitcher.action.sceneCollection.entry="Switch active scene collection to {{sceneCollections}}" AdvSceneSwitcher.action.sceneCollection.entry="Switch active scene collection to {{sceneCollections}}"
AdvSceneSwitcher.action.sceneCollection.warning="Note: Any actions following after this will not be executed as the changing scene collection will also reload the scene switcher settings.\nThe scene collection action will be ignored while the settings window is opened." AdvSceneSwitcher.action.sceneCollection.warning="Note: Any actions following after this will not be executed as the changing scene collection will also reload the scene switcher settings.\nThe scene collection action will be ignored while the settings window is opened."
AdvSceneSwitcher.action.sequence="Sequence"
AdvSceneSwitcher.action.sequence.entry="Each time this action is performed run the next macro in the list (paused macros are ignored)"
AdvSceneSwitcher.action.sequence.status="Last executed macro: %1 - Next macro to be executed: %2"
AdvSceneSwitcher.action.sequence.status.none="none"
AdvSceneSwitcher.action.sequence.restart="Restart from beginning once end of list is reached"
AdvSceneSwitcher.action.sequence.continueFrom="Continue with selected item"
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition" AdvSceneSwitcher.transitionTab.title="Transition"
@@ -462,7 +529,7 @@ AdvSceneSwitcher.mediaTab.states.Paused="Paused"
AdvSceneSwitcher.mediaTab.states.stopped="Stopped" AdvSceneSwitcher.mediaTab.states.stopped="Stopped"
AdvSceneSwitcher.mediaTab.states.ended="Ended" AdvSceneSwitcher.mediaTab.states.ended="Ended"
AdvSceneSwitcher.mediaTab.states.error="Error" AdvSceneSwitcher.mediaTab.states.error="Error"
AdvSceneSwitcher.mediaTab.states.playedToEnd="Played to end" AdvSceneSwitcher.mediaTab.states.playlistEnd="Ended(Playlist)"
AdvSceneSwitcher.mediaTab.states.any="Any" AdvSceneSwitcher.mediaTab.states.any="Any"
AdvSceneSwitcher.mediaTab.timeRestriction.none="None" AdvSceneSwitcher.mediaTab.timeRestriction.none="None"
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Time shorter" AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Time shorter"
@@ -569,7 +636,6 @@ AdvSceneSwitcher.videoTab.help="<html><head/><body><p>This tab will allow you to
AdvSceneSwitcher.networkTab.title="Network" AdvSceneSwitcher.networkTab.title="Network"
AdvSceneSwitcher.networkTab.description="This tab will allow you to remotely control the active scene of another OBS instance.\nPlease note that the scene names have to match exactly on all OBS instances." AdvSceneSwitcher.networkTab.description="This tab will allow you to remotely control the active scene of another OBS instance.\nPlease note that the scene names have to match exactly on all OBS instances."
AdvSceneSwitcher.networkTab.warning="Running the server outside of a local network will allow third parties to read the active scene." AdvSceneSwitcher.networkTab.warning="Running the server outside of a local network will allow third parties to read the active scene."
AdvSceneSwitcher.networkTab.DisabledWarning="This functionality unfortunately had to be disabled on macOS due to library incompatibilities when running the obs-websocket plugin in parallel."
AdvSceneSwitcher.networkTab.server="Start server (Sends scene switch messages to all connected clients)" AdvSceneSwitcher.networkTab.server="Start server (Sends scene switch messages to all connected clients)"
AdvSceneSwitcher.networkTab.server.port="Port" AdvSceneSwitcher.networkTab.server.port="Port"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Lock server to only using IPv4" AdvSceneSwitcher.networkTab.server.lockToIPv4="Lock server to only using IPv4"
@@ -642,6 +708,9 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Toggle Start/Stop for the
AdvSceneSwitcher.hotkey.macro.pause="Pause macro %1" AdvSceneSwitcher.hotkey.macro.pause="Pause macro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Unpause macro %1" AdvSceneSwitcher.hotkey.macro.unpause="Unpause macro %1"
AdvSceneSwitcher.hotkey.macro.togglePause="Toggle pause of macro %1" AdvSceneSwitcher.hotkey.macro.togglePause="Toggle pause of macro %1"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Move macro segment selection up"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Move macro segment selection down"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Remove selected macro segment"
AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?" AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?"
AdvSceneSwitcher.askForMacro="Select macro {{macroSelection}}" AdvSceneSwitcher.askForMacro="Select macro {{macroSelection}}"
@@ -660,6 +729,7 @@ AdvSceneSwitcher.selectWindow="--select window--"
AdvSceneSwitcher.selectSource="--select source--" AdvSceneSwitcher.selectSource="--select source--"
AdvSceneSwitcher.selectAudioSource="--select audio source--" AdvSceneSwitcher.selectAudioSource="--select audio source--"
AdvSceneSwitcher.selectVideoSource="--select video source--" AdvSceneSwitcher.selectVideoSource="--select video source--"
AdvSceneSwitcher.OBSVideoOutput="OBS video output"
AdvSceneSwitcher.selectMediaSource="--select media source--" AdvSceneSwitcher.selectMediaSource="--select media source--"
AdvSceneSwitcher.selectProcess="--select process--" AdvSceneSwitcher.selectProcess="--select process--"
AdvSceneSwitcher.selectFilter="--select filter--" AdvSceneSwitcher.selectFilter="--select filter--"
@@ -681,6 +751,8 @@ AdvSceneSwitcher.stopped="Plugin stopped"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>This seems to be the first time the Advanced Scene Switcher was started.<br>Please have a look at the <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> for a list of guides and examples.<br>Do not hesitate to ask questions in the plugin's <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">thread</span></a> on the OBS forums!</p></body></html>" AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>This seems to be the first time the Advanced Scene Switcher was started.<br>Please have a look at the <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> for a list of guides and examples.<br>Do not hesitate to ask questions in the plugin's <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">thread</span></a> on the OBS forums!</p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="Development for this tab stopped!\nPlease consider transitioning to using Macros instead.\nThis hint can be disabled on the General tab."
AdvSceneSwitcher.unit.milliseconds="milliseconds" AdvSceneSwitcher.unit.milliseconds="milliseconds"
AdvSceneSwitcher.unit.secends="seconds" AdvSceneSwitcher.unit.secends="seconds"
AdvSceneSwitcher.unit.minutes="minutes" AdvSceneSwitcher.unit.minutes="minutes"

View File

@@ -78,23 +78,16 @@ AdvSceneSwitcher.logic.rootNone="Если"
AdvSceneSwitcher.logic.not="Если нет" AdvSceneSwitcher.logic.not="Если нет"
; Macro Conditions ; Macro Conditions
AdvSceneSwitcher.condition.audio="Аудио" AdvSceneSwitcher.condition.audio="Аудио"
AdvSceneSwitcher.ondition.audio.state.below="Ниже"
AdvSceneSwitcher.ondition.audio.state.above="Выше"
AdvSceneSwitcher.condition.audio.entry="Громкость {{audioSources}} равна {{condition}} {{volume}} в течении {{duration}} секунд"
AdvSceneSwitcher.condition.cursor="Область экрана" AdvSceneSwitcher.condition.cursor="Область экрана"
AdvSceneSwitcher.condition.scene="Сцена" AdvSceneSwitcher.condition.scene="Сцена"
AdvSceneSwitcher.condition.scene.type.current="Текущий" AdvSceneSwitcher.condition.scene.type.current="Текущий"
AdvSceneSwitcher.condition.scene.type.previous="Предыдущий" AdvSceneSwitcher.condition.scene.type.previous="Предыдущий"
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} сцена является {{scenes}} на {{duration}}"
AdvSceneSwitcher.condition.window="Окно" AdvSceneSwitcher.condition.window="Окно"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} существует и ..." AdvSceneSwitcher.condition.window.entry.line1="{{windows}} существует и ..."
AdvSceneSwitcher.condition.window.entry.line2="... это {{fullscreen}} полноэкранный {{maximized}} максимизированный {{focused}} сфокусированный"
AdvSceneSwitcher.condition.file="Файл" AdvSceneSwitcher.condition.file="Файл"
AdvSceneSwitcher.condition.file.entry.line1="Содержимое {{fileType}} {{filePath}} {{browseButton}} соответствует:"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}" AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.media="Медиа" AdvSceneSwitcher.condition.media="Медиа"
AdvSceneSwitcher.condition.media.entry="{{mediaSources}} состояние {{states}} и {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.video="Видео" AdvSceneSwitcher.condition.video="Видео"
AdvSceneSwitcher.condition.video.condition.match="точно соответствует" AdvSceneSwitcher.condition.video.condition.match="точно соответствует"
AdvSceneSwitcher.condition.video.condition.differ="не совпадает" AdvSceneSwitcher.condition.video.condition.differ="не совпадает"
@@ -104,16 +97,13 @@ AdvSceneSwitcher.condition.video.condition.noImage="не имеет вывода
AdvSceneSwitcher.condition.video.askFileAction="Вы хотите использовать существующий файл или создать скриншот текущего выбранного источника?" AdvSceneSwitcher.condition.video.askFileAction="Вы хотите использовать существующий файл или создать скриншот текущего выбранного источника?"
AdvSceneSwitcher.condition.video.askFileAction.file="Использовать существующий файл" AdvSceneSwitcher.condition.video.askFileAction.file="Использовать существующий файл"
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Создать скриншот" AdvSceneSwitcher.condition.video.askFileAction.screenshot="Создать скриншот"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{filePath}} {{browseButton}} для {{duration}}"
AdvSceneSwitcher.condition.stream="Потоковое вещание" AdvSceneSwitcher.condition.stream="Потоковое вещание"
AdvSceneSwitcher.condition.stream.state.start="Поток запущен" AdvSceneSwitcher.condition.stream.state.start="Поток запущен"
AdvSceneSwitcher.condition.stream.state.stop="Поток остановлен" AdvSceneSwitcher.condition.stream.state.stop="Поток остановлен"
AdvSceneSwitcher.condition.stream.entry="{{streamState}} для {{duration}}"
AdvSceneSwitcher.condition.record="Запись" AdvSceneSwitcher.condition.record="Запись"
AdvSceneSwitcher.condition.record.state.start="Запись запущена" AdvSceneSwitcher.condition.record.state.start="Запись запущена"
AdvSceneSwitcher.condition.record.state.pause="Запись приостановлена" AdvSceneSwitcher.condition.record.state.pause="Запись приостановлена"
AdvSceneSwitcher.condition.record.state.stop="Запись остановлена" AdvSceneSwitcher.condition.record.state.stop="Запись остановлена"
AdvSceneSwitcher.condition.record.entry="{{recordState}} для {{duration}}"
AdvSceneSwitcher.condition.process="Процесс" AdvSceneSwitcher.condition.process="Процесс"
AdvSceneSwitcher.condition.process.entry="{{processes}} запущен {{focused}} и сфокусирован" AdvSceneSwitcher.condition.process.entry="{{processes}} запущен {{focused}} и сфокусирован"
AdvSceneSwitcher.condition.idle="Простой" AdvSceneSwitcher.condition.idle="Простой"
@@ -153,7 +143,6 @@ AdvSceneSwitcher.action.streaming.type.stop="Остановить потоков
AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание" AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание"
AdvSceneSwitcher.action.streaming.entry="{{actions}}" AdvSceneSwitcher.action.streaming.entry="{{actions}}"
AdvSceneSwitcher.action.run="Запустить" AdvSceneSwitcher.action.run="Запустить"
AdvSceneSwitcher.action.run.entry="Запустить {{filePath}} {{browseButton}}"
; Transition Tab ; Transition Tab
@@ -221,7 +210,6 @@ AdvSceneSwitcher.mediaTab.states.Paused="Приостановлено"
AdvSceneSwitcher.mediaTab.states.stopped="Остановлено" AdvSceneSwitcher.mediaTab.states.stopped="Остановлено"
AdvSceneSwitcher.mediaTab.states.ended="Закончилось" AdvSceneSwitcher.mediaTab.states.ended="Закончилось"
AdvSceneSwitcher.mediaTab.states.error="Ошибка" AdvSceneSwitcher.mediaTab.states.error="Ошибка"
AdvSceneSwitcher.mediaTab.states.playedToEnd="Воспроизведено до конца"
AdvSceneSwitcher.mediaTab.states.any="Любой" AdvSceneSwitcher.mediaTab.states.any="Любой"
AdvSceneSwitcher.mediaTab.timeRestriction.none="Нет" AdvSceneSwitcher.mediaTab.timeRestriction.none="Нет"
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Время короче" AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Время короче"
@@ -327,7 +315,6 @@ AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Эта вкладка по
; Network Tab ; Network Tab
AdvSceneSwitcher.networkTab.title="Сеть" AdvSceneSwitcher.networkTab.title="Сеть"
AdvSceneSwitcher.networkTab.warning="Запуск сервера вне локальной сети позволит третьим лицам читать активную сцену." AdvSceneSwitcher.networkTab.warning="Запуск сервера вне локальной сети позволит третьим лицам читать активную сцену."
AdvSceneSwitcher.networkTab.DisabledWarning="ту функциональность, к сожалению, пришлось отключить на macOS из-за несовместимости библиотек при параллельном запуске плагина obs-websocket."
AdvSceneSwitcher.networkTab.server="Запустить сервер (отправляет сообщения о переключении сцены всем подключенным клиентам)" AdvSceneSwitcher.networkTab.server="Запустить сервер (отправляет сообщения о переключении сцены всем подключенным клиентам)"
AdvSceneSwitcher.networkTab.server.port="Порт" AdvSceneSwitcher.networkTab.server.port="Порт"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Заблокировать сервер на использование только IPv4" AdvSceneSwitcher.networkTab.server.lockToIPv4="Заблокировать сервер на использование только IPv4"

682
data/locale/tr-TR.ini Normal file
View File

@@ -0,0 +1,682 @@
AdvSceneSwitcher.pluginName="Gelismis Otomatik Sahne Degistirici"
AdvSceneSwitcher.windowTitle="Gelismis Otomatik Sahne Degistirici"
; General Tab
AdvSceneSwitcher.generalTab.title="Genel"
AdvSceneSwitcher.generalTab.status="Durum"
AdvSceneSwitcher.generalTab.status.hotkeytips="OBS ayarlarinda yapılandırılabilir Kısayol tusları"
AdvSceneSwitcher.generalTab.status.currentStatus="Gelismis Otomatik Sahne Degistirici:"
AdvSceneSwitcher.generalTab.status.onStartup="OBS'yi baslatirken:"
AdvSceneSwitcher.generalTab.status.onStartup.asLastRun="Etkinse, sahne değiştiriciyi etkinleştirin"
AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart="Her zaman sahne değiştiriciyi etkinleştirin"
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="Sahne değiştiriciyi başlatma"
AdvSceneSwitcher.generalTab.status.start="Başlat"
AdvSceneSwitcher.generalTab.status.stop="Durdur"
AdvSceneSwitcher.generalTab.status.autoStart="Aşağıdaki durumlarda sahne değiştiriciyi otomatik olarak başlatın:"
AdvSceneSwitcher.generalTab.status.autoStart.never="Asla"
AdvSceneSwitcher.generalTab.status.autoStart.recording="Kayıt"
AdvSceneSwitcher.generalTab.status.autoStart.streaming="Yayın"
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Kayıt veya Yayın"
AdvSceneSwitcher.generalTab.status.checkInterval="Anahtar koşullarını her seferinde kontrol edin"
AdvSceneSwitcher.generalTab.generalBehavior="Genel davranış"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="...için herhangi bir işlem yapılmazsa"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Yalnızca yapılandırılmış kontrol aralığı kadar doğru olacaktır."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Geçiş Yapma"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Rastgele sekmesinde herhangi bir sahneye geçin"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Değiştirmek:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Bir eşleşmeden sonra aşağıdakiler için işlem yapmayın:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="Bu süre zarfında olası eşleşmeler göz ardı edilecektir!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ayrıntılı günlük kaydını etkinleştir"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Pencere konumunu ve boyutunu kaydet"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Sistem tepsisi bildirimlerini göster"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="UI ipuçlarını devre dışı bırak"
AdvSceneSwitcher.generalTab.priority="Öncelik"
AdvSceneSwitcher.generalTab.priority.description="Geçiş yöntemleri önceliği (En yüksek öncelik en üsttedir)"
AdvSceneSwitcher.generalTab.priority.threadPriority="Konu önceliğini kullan"
AdvSceneSwitcher.generalTab.priority.threadPriorityNotice="(Önceliği \"Normal\" üzerine çıkarmak önerilmez)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings="Kayıt / Yükle Ayarları"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Dışa Aktar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="İçe Aktar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Gelişmiş Sahne Değiştirici ayarlarını dosyaya aktar ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Gelişmiş Sahne Değiştirici ayarlarını dosyadan içe aktar ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Text dosyası (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="Gelişmiş Sahne Değiştirici ayarları dosyadan içe aktarmakta başarısız oldu"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Gelişmiş Sahne Değiştirici ayarları başarılı bir şekilde dosyadan içe aktarıldı"
AdvSceneSwitcher.generalTab.priority.fileContent="Dosya İçeriği"
AdvSceneSwitcher.generalTab.priority.sceneSequence="Sahne Sıralama"
AdvSceneSwitcher.generalTab.priority.idleDetection="Boşta Algılama"
AdvSceneSwitcher.generalTab.priority.executable="Yürütülebilir"
AdvSceneSwitcher.generalTab.priority.screenRegion="Ekran Alanı"
AdvSceneSwitcher.generalTab.priority.windowTitle="Pencere Başlığı"
AdvSceneSwitcher.generalTab.priority.media="Medya"
AdvSceneSwitcher.generalTab.priority.time="Zaman"
AdvSceneSwitcher.generalTab.priority.audio="Ses"
AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Makro"
; Macro Tab
AdvSceneSwitcher.macroTab.title="Makro"
AdvSceneSwitcher.macroTab.macros="Makrolar"
AdvSceneSwitcher.macroTab.priorityWarning="Not: Makroları en yüksek öncelikli işlev olacak şekilde yapılandırmanız önerilir.\n Bu ayar, Genel sekmesinde değiştirilebilir."
AdvSceneSwitcher.macroTab.help="Makrolar, birden çok koşula bağlı olarak bir dizi eylemi yürütmenize izin verir.\n\n Yeni bir Makro eklemek için vurgulanan artı sembolüne tıklayın."
AdvSceneSwitcher.macroTab.editConditionHelp="Bu bölüm, Makro koşullarını tanımlamanızı sağlar.\n\n Sol taraftan mevcut bir Makro seçin veya yeni bir Makro ekleyin.\n Ardından yeni bir koşul eklemek için aşağıdaki artı düğmesini tıklayın."
AdvSceneSwitcher.macroTab.editActionHelp="Bu bölüm, Makro eylemlerini tanımlamanızı sağlar.\n\n Soldan mevcut bir Makro seçin veya yeni bir Makro ekleyin.\n Ardından yeni bir eylem eklemek için aşağıdaki artı düğmesini tıklayın."
AdvSceneSwitcher.macroTab.edit="Makro Düzenleme"
AdvSceneSwitcher.macroTab.edit.logic="Mantık türü:"
AdvSceneSwitcher.macroTab.edit.condition="Durum türü:"
AdvSceneSwitcher.macroTab.edit.action="Eylem türü:"
AdvSceneSwitcher.macroTab.add="Yeni Makro ekle"
AdvSceneSwitcher.macroTab.name="İsim:"
AdvSceneSwitcher.macroTab.run="Makro Çalıştırma"
AdvSceneSwitcher.macroTab.runFail="\"%1\" çalıştırılamadı!\n Eylemlerden biri başarısız oldu veya makro zaten çalışıyor."
AdvSceneSwitcher.macroTab.runInParallel="Makroyu diğer makrolara paralel olarak çalıştırın"
AdvSceneSwitcher.macroTab.onChange="Eylemleri yalnızca koşul değişikliğinde gerçekleştirin"
AdvSceneSwitcher.macroTab.defaultname="Makro %1"
AdvSceneSwitcher.macroTab.exists="Macro adı zaten mevcut"
AdvSceneSwitcher.macroTab.copy="Kopya oluştur"
AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet"
AdvSceneSwitcher.macroTab.collapseAll="Hepsini Küçült"
; Macro Logic
AdvSceneSwitcher.logic.none="Girişi yoksay"
AdvSceneSwitcher.logic.and="Ve"
AdvSceneSwitcher.logic.or="Ya da"
AdvSceneSwitcher.logic.andNot="ve değil"
AdvSceneSwitcher.logic.orNot="ya da değil"
AdvSceneSwitcher.logic.rootNone="Eğer"
AdvSceneSwitcher.logic.not="Eğer değilse"
; Macro Conditions
AdvSceneSwitcher.condition.audio="Ses"
AdvSceneSwitcher.condition.audio.state.below="altında"
AdvSceneSwitcher.condition.audio.state.above="üstünde"
AdvSceneSwitcher.condition.audio.state.mute="sessiz"
AdvSceneSwitcher.condition.audio.state.unmute="ses açıldı"
AdvSceneSwitcher.condition.cursor="İmleç"
AdvSceneSwitcher.condition.cursor.type.region="alanında"
AdvSceneSwitcher.condition.cursor.type.moving="hareketlidir"
AdvSceneSwitcher.condition.cursor.showFrame="Çerçeveyi Göster"
AdvSceneSwitcher.condition.cursor.hideFrame="Çerçeveyi Gizle"
AdvSceneSwitcher.condition.cursor.entry.line1="İmleç {{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line2="İmleç şuanda {{xPos}} x {{yPos}}"
AdvSceneSwitcher.condition.scene="Sahne"
AdvSceneSwitcher.condition.scene.type.current="Mevcut Sahne"
AdvSceneSwitcher.condition.scene.type.previous="Önceki Sahne"
AdvSceneSwitcher.condition.scene.type.changed="Sahne değişti"
AdvSceneSwitcher.condition.scene.type.notChanged="Sahne değişmedi"
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Geçiş hedefi sahnesi için geçiş kontrolü sırasında"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Pencere"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} Varolan ve ..."
AdvSceneSwitcher.condition.window.entry.line2="... {{fullscreen}} Tam ekran {{maximized}} büyütüldü {{focused}} odaklanıldı {{windowFocusChanged}} ön plan penceresi değişikliği"
AdvSceneSwitcher.condition.file="Dosya"
AdvSceneSwitcher.condition.file.entry.line1="İçerik {{fileType}} {{filePath}} eşleştirmeler:"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.media="Medya"
AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı"
AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları "
AdvSceneSwitcher.condition.media.matchOnChange="Yalnızca değişiklikle eşleştirin (Not: Bu seçenek gelecekteki bir sürümde kaldırılacaktır - lütfen bunun yerine zaman kısıtlamalarını kullanın)"
AdvSceneSwitcher.condition.media.entry="{{mediaSources}}{{scenes}} durumu {{states}} ve {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir"
AdvSceneSwitcher.condition.video.condition.differ="Eşleştirme"
AdvSceneSwitcher.condition.video.condition.hasChanged="Değişti"
AdvSceneSwitcher.condition.video.condition.hasNotChanged="Değişmedi"
AdvSceneSwitcher.condition.video.condition.noImage=ıktı yok"
AdvSceneSwitcher.condition.video.condition.pattern="Modelle eşleşir."
AdvSceneSwitcher.condition.video.condition.object="nesne içerir"
AdvSceneSwitcher.condition.video.askFileAction="Mevcut bir dosyayı kullanmak mı yoksa seçili olan kaynağın ekran görüntüsünü mü oluşturmak istiyorsunuz?"
AdvSceneSwitcher.condition.video.askFileAction.file="Mevcut dosyayı kullan"
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Ekran görüntüsü oluştur"
AdvSceneSwitcher.condition.video.usePatternForChangedCheck="Model eşleştirmeyi kullan"
AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="Bu, koşulun doğru olması için görüntünün ne kadar değişmesi gerektiğini kontrol etmenizi sağlar."
AdvSceneSwitcher.condition.video.patternThreshold="Eşik: "
AdvSceneSwitcher.condition.video.patternThresholdDescription="Daha yüksek bir eşik değeri, modelin video kaynağıyla daha yakından eşleşmesi gerektiği anlamına gelir."
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Desen için maske olarak alfa kanalını kullanın."
AdvSceneSwitcher.condition.video.objectScaleThreshold="Ölçek faktörü: "
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="Daha düşük bir ölçek faktörü, daha fazla eşleşmeye, ancak daha yüksek CPU yüküne yol açacaktır."
AdvSceneSwitcher.condition.video.minNeighborDescription="Daha yüksek bir minimum komşu değeri, daha az ancak daha kaliteli eşleşmelerle sonuçlanacaktır."
AdvSceneSwitcher.condition.video.showMatch="Eşleşmeyi Göster"
AdvSceneSwitcher.condition.video.screenshotFail="Kaynağın ekran görüntüsü alınamadı!"
AdvSceneSwitcher.condition.video.patternMatchFail="Desen bulunamadı!"
AdvSceneSwitcher.condition.video.objectMatchFail="Nesne bulunamadı!"
AdvSceneSwitcher.condition.video.modelLoadFail="Model verileri yüklenemedi!"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Model verileri (haar kademeli sınıflandırıcı):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum komşular: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}} Yalnızca her seferinde kontrol gerçekleştirerek CPU yükünü azaltın {{throttleCount}} millisaniyeler"
AdvSceneSwitcher.condition.stream="Yayınlama"
AdvSceneSwitcher.condition.stream.state.start="Yayın çalışıyor"
AdvSceneSwitcher.condition.stream.state.stop="Yayın durdu"
AdvSceneSwitcher.condition.stream.state.starting="Yayın başlıyor"
AdvSceneSwitcher.condition.stream.state.stopping="Yayın duruyor"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}"
AdvSceneSwitcher.condition.record="Kayıt"
AdvSceneSwitcher.condition.record.state.start="Kayıt Çalışıyor"
AdvSceneSwitcher.condition.record.state.pause="Kayıt durakladı"
AdvSceneSwitcher.condition.record.state.stop="Kayıt durdu"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="İşlem"
AdvSceneSwitcher.condition.process.entry="{{processes}} çalışıyor {{focused}} ve odaklandı"
AdvSceneSwitcher.condition.idle="Boşta"
AdvSceneSwitcher.condition.idle.entry="...için klavye veya fare girişi yok {{duration}}"
AdvSceneSwitcher.condition.pluginState="Eklenti durumu"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Bu aralıkta otomatik sahne değişikliği tetiklendi"
AdvSceneSwitcher.condition.pluginState.state.running="Gelişmiş sahne değiştirici çalışıyor"
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
AdvSceneSwitcher.condition.timer="Zamanlayıcı"
AdvSceneSwitcher.condition.timer.type.fixed="Sabit"
AdvSceneSwitcher.condition.timer.type.random="Rastgele"
AdvSceneSwitcher.condition.timer.pause="Duraklat"
AdvSceneSwitcher.condition.timer.continue="Devam etmek"
AdvSceneSwitcher.condition.timer.entry.line1.fixed="{{type}} süresi {{duration}} geçti"
AdvSceneSwitcher.condition.timer.entry.line1.random="{{type}} itibaren süre {{duration}} to {{duration2}} geçti"
AdvSceneSwitcher.condition.timer.entry.line2="Kalan Süre: {{remaining}} saniye"
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} Kalan zamanı kayıt {{autoReset}} Süreye ulaşıldıktan sonra zamanlayıcıyı otomatik olarak sıfırla"
AdvSceneSwitcher.condition.timer.reset="Sıfırlama"
AdvSceneSwitcher.condition.macro="Makro"
AdvSceneSwitcher.condition.macro.type.count="Saymak"
AdvSceneSwitcher.condition.macro.type.state="Belirtmek"
AdvSceneSwitcher.condition.macro.type.selection="Koşul türü: {{types}}"
AdvSceneSwitcher.condition.macro.count.type.below="Daha az"
AdvSceneSwitcher.condition.macro.count.type.above="Daha fazla"
AdvSceneSwitcher.condition.macro.count.type.equal="Aynen"
AdvSceneSwitcher.condition.macro.count.reset="Sıfırlama"
AdvSceneSwitcher.condition.macro.pausedWarning="Seçili makro şu anda duraklatıldı!"
AdvSceneSwitcher.condition.macro.state.entry="Koşulları {{macros}} doğru"
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} yürütüldü {{conditions}} {{count}} zaman"
AdvSceneSwitcher.condition.macro.count.entry.line2="Mevcut sayı: {{currentCount}} {{resetCount}}"
AdvSceneSwitcher.condition.source="Kaynak"
AdvSceneSwitcher.condition.source.type.active="Aktiftir"
AdvSceneSwitcher.condition.source.type.showing="gösteriyor"
AdvSceneSwitcher.condition.source.type.settings="Ayarlar eşleştirildi"
AdvSceneSwitcher.condition.source.regex="Normal ifadeler kullanın"
AdvSceneSwitcher.condition.source.getSettings="Mevcut ayarları al"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.source.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.virtualCamera="Sanal kamera"
AdvSceneSwitcher.condition.virtualCamera.state.start="Sanal kamera başladı"
AdvSceneSwitcher.condition.virtualCamera.state.stop="Sanal kamera durdu"
AdvSceneSwitcher.condition.virtualCamera.entry="{{states}}"
AdvSceneSwitcher.condition.filter="Filtrele"
AdvSceneSwitcher.condition.filter.type.active="Etkinleştirildi"
AdvSceneSwitcher.condition.filter.type.showing="Etkisizleştirildi"
AdvSceneSwitcher.condition.filter.type.settings="Ayarlar eşleşti"
AdvSceneSwitcher.condition.filter.regex="Normal ifadeler kullanın"
AdvSceneSwitcher.condition.filter.getSettings="Mevcut ayarları al"
AdvSceneSwitcher.condition.filter.entry.line1="Açık {{sources}} {{filters}} {{conditions}}"
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.filter.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.sceneOrder="Sahne öğesi sırası"
AdvSceneSwitcher.condition.sceneOrder.type.above="Üstünde"
AdvSceneSwitcher.condition.sceneOrder.type.below="Altında"
AdvSceneSwitcher.condition.sceneOrder.type.position="konumunda"
AdvSceneSwitcher.condition.sceneOrder.positionInfo="Konum değeri en altta 0 ile başlar ve sahne gruplarındakiler dahil her sahne öğesi için bir artar."
AdvSceneSwitcher.condition.sceneOrder.entry="Açık{{scenes}}{{sources}}{{conditions}}{{sources2}}{{position}}"
AdvSceneSwitcher.condition.hotkey="Kısayol tuşu"
AdvSceneSwitcher.condition.hotkey.name="Makro tetik kısayol tuşu"
AdvSceneSwitcher.condition.hotkey.tip="Not: Bu kısayol tuşu için tuş atamalarını OBS ayarları penceresinde yapılandırabilirsiniz."
AdvSceneSwitcher.condition.replay="Tekrar oynatma arabelleği"
AdvSceneSwitcher.condition.replay.state.stopped="Tekrar oynatma arabelleği durdu"
AdvSceneSwitcher.condition.replay.state.started="Tekrar oynatma arabelleği başladı"
AdvSceneSwitcher.condition.replay.state.saved="Tekrar oynatma arabelleği kaydedildi"
AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="Tarih"
AdvSceneSwitcher.condition.date.state.at="de"
AdvSceneSwitcher.condition.date.state.after="Sonra"
AdvSceneSwitcher.condition.date.state.before="Önce"
AdvSceneSwitcher.condition.date.state.between="Arasında"
AdvSceneSwitcher.condition.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.condition.sceneTransform.regex="Normal ifadeler kullanın"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Açık{{scenes}}{{sources}} dönüşümle eşleşir"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="Geçiş"
AdvSceneSwitcher.condition.transition.type.current="Geçerli geçiş türü"
AdvSceneSwitcher.condition.transition.type.duration="Mevcut geçiş süresi"
AdvSceneSwitcher.condition.transition.type.started="Geçiş başladı"
AdvSceneSwitcher.condition.transition.type.ended="Geçiş sona erdi"
AdvSceneSwitcher.condition.transition.type.transitionSource="İtibaren geçiş"
AdvSceneSwitcher.condition.transition.type.transitionTarget="Geçiş "
AdvSceneSwitcher.condition.transition.durationSuffix="saniyesi"
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{scenes}}{{duration}}{{durationSuffix}}"
AdvSceneSwitcher.condition.sceneVisibility="Sahne öğesi görünürlüğü"
AdvSceneSwitcher.condition.sceneVisibility.type.shown="Gösterilen"
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="Gizli"
AdvSceneSwitcher.condition.sceneVisibility.entry="Açık {{scenes}}{{sources}}is{{conditions}}"
AdvSceneSwitcher.condition.studioMode="Stüdyo ingilizce"
AdvSceneSwitcher.condition.studioMode.state.active="Studyo modu Etkinleştirildi"
AdvSceneSwitcher.condition.studioMode.state.notActive="Studyo modu aktif değil"
AdvSceneSwitcher.condition.studioMode.state.previewScene="Önizleme sahnesi"
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
AdvSceneSwitcher.condition.openvr="Aç VR"
AdvSceneSwitcher.condition.errorStatus="Aç VR hatası: "
AdvSceneSwitcher.condition.openvr.entry.line1="HMD içinde ..."
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
AdvSceneSwitcher.condition.openvr.entry.line3="HMD mevcut {{xPos}} x {{yPos}} x {{zPos}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Sahne Degistirici"
AdvSceneSwitcher.action.scene.entry="Sahneyi {{scenes}} kullanarak {{transitions}} süresi olan {{duration}} saniye"
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Hedef sahneye geçiş tamamlanana kadar bekleyin"
AdvSceneSwitcher.action.wait="Bekle"
AdvSceneSwitcher.action.wait.type.fixed="sabit"
AdvSceneSwitcher.action.wait.type.random="rastgele"
AdvSceneSwitcher.action.wait.entry.fixed="Şunu bekle {{waitType}} süre {{duration}}"
AdvSceneSwitcher.action.wait.entry.random="Şunu bekle {{waitType}} bu aralıktan {{duration}} buna {{duration2}}"
AdvSceneSwitcher.action.audio="Ses"
AdvSceneSwitcher.action.audio.type.mute="Sessiz"
AdvSceneSwitcher.action.audio.type.unmute="Ses açmak"
AdvSceneSwitcher.action.audio.type.sourceVolume="Kaynak ses seviyesini ayarla"
AdvSceneSwitcher.action.audio.type.masterVolume="Ana ses seviyesini ayarla"
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.recording="Kayıt"
AdvSceneSwitcher.action.recording.type.stop="Kayıt Durdur"
AdvSceneSwitcher.action.recording.type.start="Kayıt Başlat"
AdvSceneSwitcher.action.recording.type.pause="Kayıt Duraklat"
AdvSceneSwitcher.action.recording.type.unpause="Kayıt Duraklatma"
AdvSceneSwitcher.action.recording.pause.hint="Kayıt ayarlarınıza bağlı olarak kaydı duraklatamayabileceğinizi unutmayın."
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}"
AdvSceneSwitcher.action.replay="Tekrar arabelleği"
AdvSceneSwitcher.action.replay.type.stop="Tekrar arabelleğini durdur"
AdvSceneSwitcher.action.replay.type.start="Tekrar arabelleğini başlat"
AdvSceneSwitcher.action.replay.type.save="Tekrar arabelleğini kaydet"
AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Yayın"
AdvSceneSwitcher.action.streaming.type.stop="Yayın durdur"
AdvSceneSwitcher.action.streaming.type.start="Yayın başlat"
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
AdvSceneSwitcher.action.run="Çalıştır"
AdvSceneSwitcher.action.run.arguments="Argümanlar:"
AdvSceneSwitcher.action.run.addArgument="Argüman ekle"
AdvSceneSwitcher.action.run.addArgumentDescription="Yeni Argüman ekle:"
AdvSceneSwitcher.action.run.entry="Çalıştır {{filePath}}"
AdvSceneSwitcher.action.sceneVisibility="Sahne öğesi görünürlüğü"
AdvSceneSwitcher.action.sceneVisibility.type.show="Göster"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Gizle"
AdvSceneSwitcher.action.sceneVisibility.type.source="Kayıt"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Herhangi"
AdvSceneSwitcher.action.sceneVisibility.entry="Açık{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
AdvSceneSwitcher.action.filter="Filtrele"
AdvSceneSwitcher.action.filter.type.enable="Etkin"
AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
AdvSceneSwitcher.action.filter.type.settings="Ayarları yap"
AdvSceneSwitcher.action.filter.entry="Açık {{sources}} {{actions}} {{filters}}"
AdvSceneSwitcher.action.filter.getSettings="Mevcut ayarları al"
AdvSceneSwitcher.action.source="Kaynak"
AdvSceneSwitcher.action.source.type.enable="Etkin"
AdvSceneSwitcher.action.source.type.disable="Etkisiz"
AdvSceneSwitcher.action.source.type.settings="Ayarları yap"
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
AdvSceneSwitcher.action.source.warning="Uyarı: Kaynakların global olarak etkinleştirilmesi ve devre dışı bırakılması, OBS UI tarafından kontrol edilemez"
AdvSceneSwitcher.action.source.getSettings="Mevcut Ayarları yap"
AdvSceneSwitcher.action.media="Medya"
AdvSceneSwitcher.action.media.type.play="Oynat"
AdvSceneSwitcher.action.media.type.pause="Duraklat"
AdvSceneSwitcher.action.media.type.stop="Dur"
AdvSceneSwitcher.action.media.type.restart="Yeniden Başlat"
AdvSceneSwitcher.action.media.type.next="Sonraki"
AdvSceneSwitcher.action.media.type.previous="Önceki"
AdvSceneSwitcher.action.media.entry="{{actions}}{{duration}}{{mediaSources}}"
AdvSceneSwitcher.action.macro="Makro"
AdvSceneSwitcher.action.macro.type.pause="Duraklat"
AdvSceneSwitcher.action.macro.type.unpause="Duraklatma"
AdvSceneSwitcher.action.macro.type.resetCounter="Sayacı sıfırla"
AdvSceneSwitcher.action.macro.type.run="Çalıştır"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.pluginState="Eklenti durumu"
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcher eklentisini durdurun"
AdvSceneSwitcher.action.pluginState.type.noMatch="Eşleşmeme davranışını değiştirin:"
AdvSceneSwitcher.action.pluginState.type.import="Ayarları şuradan içe aktar:"
AdvSceneSwitcher.action.pluginState.importWarning="Not: Ayarlar penceresi açılırken eylem göz ardı edilecektir."
AdvSceneSwitcher.action.pluginState.entry="{{actions}}{{values}}{{scenes}}{{settings}}{{settingsWarning}}"
AdvSceneSwitcher.action.virtualCamera="Sanal Kamera"
AdvSceneSwitcher.action.virtualCamera.type.stop="Sanal Kamerayı Durdur"
AdvSceneSwitcher.action.virtualCamera.type.start="Sanal Kamerayı Başlat"
AdvSceneSwitcher.action.virtualCamera.entry="{{actions}}"
AdvSceneSwitcher.action.hotkey="Kısayol tuşu"
AdvSceneSwitcher.action.hotkey.leftShift="Sol shift"
AdvSceneSwitcher.action.hotkey.rightShift="Sağ Shift"
AdvSceneSwitcher.action.hotkey.leftCtrl="Sol Ctrl"
AdvSceneSwitcher.action.hotkey.rightCtrl="Sağ Ctrl"
AdvSceneSwitcher.action.hotkey.leftAlt="Sol Alt"
AdvSceneSwitcher.action.hotkey.rightAlt="Sağ Alt"
AdvSceneSwitcher.action.hotkey.leftMeta="Sol Meta"
AdvSceneSwitcher.action.hotkey.rightMeta="Sağ Meta"
AdvSceneSwitcher.action.hotkey.onlyOBS="Tuşa basımı yalnızca OBS'ye gönder"
AdvSceneSwitcher.action.hotkey.disabled="Tuşa basma simülasyonu yapılamıyor - işlevsellik devre dışı!"
AdvSceneSwitcher.action.hotkey.entry="Bas {{keys}} uygun {{duration}} millisaniye"
AdvSceneSwitcher.action.sceneOrder="Sahne öğesi sırası"
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Yukarı taşı"
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Aşağı indir"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="En üste taşı"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Aşağıya taşı"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı"
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}transform{{sources}}"
AdvSceneSwitcher.action.file="Dosya"
AdvSceneSwitcher.action.file.type.write="Yaz"
AdvSceneSwitcher.action.file.type.append="Ekle"
AdvSceneSwitcher.action.file.entry="{{actions}} {{filePath}}:"
AdvSceneSwitcher.action.previewScene="Önizleme sahnesini değiştir"
AdvSceneSwitcher.action.previewScene.entry="Önizleme sahnesini şu şekilde değiştir: {{scenes}}"
AdvSceneSwitcher.action.SceneSwap="Sahneyi değiştir (Studyo modu)"
AdvSceneSwitcher.action.SceneSwap.entry="Stüdyo modunda önizleme ve program sahnesini değiştir"
AdvSceneSwitcher.action.transition="Geçiş"
AdvSceneSwitcher.action.transition.entry.line1="{{setType}}Geçiş türünü ayarla {{transitions}}"
AdvSceneSwitcher.action.transition.entry.line2="{{setDuration}}Geçiş süresini şuna ayarla: {{duration}}saniyeler"
AdvSceneSwitcher.action.timer="Zamanlayıcı"
AdvSceneSwitcher.action.timer.type.pause="Duraklat"
AdvSceneSwitcher.action.timer.type.continue="Devam et"
AdvSceneSwitcher.action.timer.type.reset="Sıfırla"
AdvSceneSwitcher.action.timer.type.setTimeRemaining="Kalan süreyi ayarla"
AdvSceneSwitcher.action.timer.entry="{{timerAction}} zamanlayıcılar açık {{macros}} {{duration}}"
AdvSceneSwitcher.action.random="Rastgele"
AdvSceneSwitcher.action.random.entry="Aşağıdaki makrolardan herhangi birini rastgele çalıştırın (duraklatılmış makrolar yoksayılır)"
AdvSceneSwitcher.action.systray="Sistem tepsisi bildirimi"
AdvSceneSwitcher.action.systray.entry="Bildirimleri göster: {{message}}"
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
AdvSceneSwitcher.action.screenshot.entry="Ekran görüntüsü {{sources}}"
AdvSceneSwitcher.action.profile="Profil"
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"
AdvSceneSwitcher.action.sceneCollection.entry="Aktif sahne koleksiyonunu şu şekilde değiştir: {{sceneCollections}}"
AdvSceneSwitcher.action.sceneCollection.warning="Not: Değişen sahne koleksiyonu sahne değiştirici ayarlarını da yeniden yükleyeceğinden, bundan sonraki eylemler yürütülmeyecektir.\n Ayarlar penceresi açılırken sahne toplama eylemi yok sayılacaktır."
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Geçiş"
AdvSceneSwitcher.transitionTab.setTransitionBy="Geçişleri değiştirirken:"
AdvSceneSwitcher.transitionTab.transitionOverride="Geçiş geçersiz kılmaları ayarla"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Etkin geçiş türünü değiştir"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="En az bir seçenek etkinleştirilmelidir: \n\n - Geçiş geçersiz kılmalarını kullan \n\n - Etkin geçiş türünü değiştir"
AdvSceneSwitcher.transitionTab.transitionForAToB="Sahne A'dan sahne B'ye otomatik sahne geçişi için geçişi kullanın"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Bu ayarlar <span style=\"font-style:italic;\">yalnızca</span>, sahne değiştiricinin neden olduğu geçişleri etkiler - <a href=\ Göz atın "https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Geçiş Tablosu</span></ a> bunu manuel sahne değişiklikleri için yapılandırmak istiyorsanız.<br/>Burada tanımlanan ayarlar, sahne değiştiricide başka bir yerde yapılandırılmış geçiş ayarlarına göre önceliklidir.<br/><br/>Yeni bir giriş eklemek için aşağıdaki artı simgesini tıklayın. .</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Sahne aktifse geçişi değiştir"
AdvSceneSwitcher.transitionTab.entry="Şuradan geçiş yap {{scenes}} buna {{scenes2}} kullanarak {{transitions}} süresinde {{duration}}"
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="Ne zaman sahne {{scenes}} aktifse, varsayılan sahne geçişini değiştir{{transitions}}"
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Bir giriş eklemek için artı sembolüne tıklayın."
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Geçişi değiştirmek {{defTransitionDelay}} sahne değişikliğinden sonra."
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="Gecikme, bir geçiş devam ederken geçiş türü değiştirilirse meydana gelebilecek iptal edilen sahne geçişlerini önlemek için kullanılır."
; Pause Scenes Tab
AdvSceneSwitcher.pauseTab.title="Duraklat"
AdvSceneSwitcher.pauseTab.pauseOnScene="Sahne Değiştiriciyi sahnede duraklatın"
AdvSceneSwitcher.pauseTab.pauseInFocus1="Sahne Değiştiriciyi duraklat şu durumda: "
AdvSceneSwitcher.pauseTab.pauseInFocus2="odakta"
AdvSceneSwitcher.pauseTab.pauseTypeScene="sahne aktif"
AdvSceneSwitcher.pauseTab.pauseTypeWindow="pencere odakta"
AdvSceneSwitcher.pauseTab.pauseTargetAll="tümü"
AdvSceneSwitcher.pauseTab.pauseEntry="Duraklat {{pauseTargets}} kontrol edildiğinde {{pauseTypes}} {{scenes}} {{windows}}"
AdvSceneSwitcher.pauseTab.help="Bu sekmede, bir sahne etkinse veya pencere odaktaysa bireysel geçiş yöntemlerini duraklatmak için yapılandırabilirsiniz.\ve Devam etmek için vurgulanan artı sembolüne tıklayın."
; Window Title Tab
AdvSceneSwitcher.windowTitleTab.title="Başlık"
AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>Doğrudan pencere başlıklarını veya geçerli normal ifadeyi girin. <a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span'i kullanarak normal ifadeler için söz dizimini ve eşleşmeleri kontrol edebilirsiniz. ></a></p></body></html>"
AdvSceneSwitcher.windowTitleTab.stayInFocus1="Bu pencere adını yoksay"
AdvSceneSwitcher.windowTitleTab.stayInFocus2=" "
AdvSceneSwitcher.windowTitleTab.fullscreen="tam ekran ise"
AdvSceneSwitcher.windowTitleTab.maximized="maksimize edilirse"
AdvSceneSwitcher.windowTitleTab.focused="eğer odaklanmışsa"
AdvSceneSwitcher.windowTitleTab.entry="{{windows}} {{scenes}} {{transitions}} {{fullscreen}} {{maximized}} {{focused}}"
AdvSceneSwitcher.windowTitleTab.windowsHelp="Çalışan uygulamaların pencere başlığına göre sahneler arasında geçiş yapın.\nAşağıdaki ek koşullar seçilebilir:\nPencere Tam Ekrandır\nPencere büyütülür\nPencere odaklanılır\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="Bir pencere başlığı yoksayılırsa, sahne değiştirici, önceden seçilen pencere hala odaktaymış gibi davranacaktır.\nBu, sık sık farklı bir pencereye geçerseniz, sahne değişikliğini tetiklemeyecek olan sahne geçişlerinden kaçınmanıza olanak tanır.\ n\nBir pencere seçin veya yukarıya bir pencere başlığı girin ve listeye eklemek için aşağıdaki artı sembolüne tıklayın."
; Executable Tab
AdvSceneSwitcher.executableTab.title="yürütülebilir"
AdvSceneSwitcher.executableTab.implemented="dasOven tarafından uygulandı"
AdvSceneSwitcher.executableTab.requiresFocus="sadece odaklanmışsa"
AdvSceneSwitcher.executableTab.entry="Bu {{processes}} çalışıyorsa değiştir {{scenes}} kullanılır {{transitions}} {{requiresFocus}}"
AdvSceneSwitcher.executableTab.help="Bu sekme, bir işlem çalışıyorsa sahneleri otomatik olarak değiştirmenize olanak tanır.\nBu, pencere adının değişebileceği veya bilinmediği durumlarda faydalı olabilir.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; Screen Region Tab
AdvSceneSwitcher.screenRegionTab.title="Bölge"
AdvSceneSwitcher.screenRegionTab.currentPosition="İmleç şu anda şu konumda:"
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Kılavuz çerçeveleri göster"
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="Kılavuz çerçeveleri gizle"
AdvSceneSwitcher.screenRegionTab.excludeScenes.None="Seçim yok"
AdvSceneSwitcher.screenRegionTab.entry="Eğer imleç şuanda böyleyse {{minX}} {{minY}} x {{maxX}} {{maxY}} şuna çevir {{scenes}} kullanarak {{transitions}} sürece {{excludeScenes}}"
AdvSceneSwitcher.screenRegionTab.help="Bu sekme, fare imlecinizin geçerli konumuna göre sahneleri otomatik olarak değiştirmenize olanak tanır.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; Media Tab
AdvSceneSwitcher.mediaTab.title="Medya"
AdvSceneSwitcher.mediaTab.implemented="Exelero tarafından uygulandı"
AdvSceneSwitcher.mediaTab.states.none="Yok"
AdvSceneSwitcher.mediaTab.states.playing="Oynamak"
AdvSceneSwitcher.mediaTab.states.opening="Açmak"
AdvSceneSwitcher.mediaTab.states.buffering="Arabelleğe alma"
AdvSceneSwitcher.mediaTab.states.Paused="Duraklatıldı"
AdvSceneSwitcher.mediaTab.states.stopped="Durduruldu"
AdvSceneSwitcher.mediaTab.states.ended="Bitti"
AdvSceneSwitcher.mediaTab.states.error="Hata"
AdvSceneSwitcher.mediaTab.states.any="Herhangi"
AdvSceneSwitcher.mediaTab.timeRestriction.none="Yok"
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Zaman daha kısa"
AdvSceneSwitcher.mediaTab.timeRestriction.longer="Zaman daha uzun"
AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter="Kalan süre daha kısa"
AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="Kalan süre daha uzun"
AdvSceneSwitcher.mediaTab.entry="Kaynak {{mediaSources}} durumu {{states}} ve {{timeRestrictions}} {{time}} şuna çevir {{scenes}} kullanarak {{transitions}}"
AdvSceneSwitcher.mediaTab.help="Bu sekme, medya kaynaklarının durumuna göre sahneler arasında geçiş yapmanızı sağlar.\nÖrneğin, seçilen medya kaynaklı oynatmayı bitirdiğinde otomatik olarak önceki sahneye dönebilirsiniz.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; File Tab
AdvSceneSwitcher.fileTab.title="Dosya"
AdvSceneSwitcher.fileTab.readWriteSceneFile="Oku / Sahneden yazdır / Dosyaya"
AdvSceneSwitcher.fileTab.currentSceneOutputFile="Mevcut sahnenin adını bu dosyaya yazın:"
AdvSceneSwitcher.fileTab.switchSceneBaseOnFile="Dosya girişine göre sahnelerin değiştirilmesini etkinleştirin"
AdvSceneSwitcher.fileTab.switchSceneNameInputFile="Bu dosyadan geçiş yapılacak sahne adını okuyun:"
AdvSceneSwitcher.fileTab.switchSceneBaseOnFileContent="Dosya içeriğine göre sahneyi değiştir"
AdvSceneSwitcher.fileTab.remoteFileWarning="Uzak seçeneğini seçerseniz, sahne değiştiricinin Genel sekmesinde belirtildiği gibi her x ms'de bir uzak konuma erişmeye çalışacağını lütfen unutmayın!"
AdvSceneSwitcher.fileTab.remoteFileWarning1="Sahne değiştiricinin her seferinde uzak konuma erişmeye çalışacağını unutmayın."
AdvSceneSwitcher.fileTab.remoteFileWarning2="ms"
AdvSceneSwitcher.fileTab.libcurlWarning="libcurl yüklenemedi! Uzak dosyalara erişim mümkün olmayacak!"
AdvSceneSwitcher.fileTab.selectWrite="Yazmak için bir dosya seçin..."
AdvSceneSwitcher.fileTab.selectRead="Okumak için bir dosya seçin..."
AdvSceneSwitcher.fileTab.textFileType="Text dosyası (*.txt)"
AdvSceneSwitcher.fileTab.anyFileType="Herhangi bir dosya türü (*.*)"
AdvSceneSwitcher.fileTab.remote="uzak dosya"
AdvSceneSwitcher.fileTab.local="yerel dosya"
AdvSceneSwitcher.fileTab.useRegExp="normal ifadeler kullan (desen eşleştirme)"
AdvSceneSwitcher.fileTab.checkfileContentTime="değişiklik tarihi değiştiyse"
AdvSceneSwitcher.fileTab.checkfileContent="İçerik değiştiyse"
AdvSceneSwitcher.fileTab.entry="Suna dönüştür {{scenes}} kullanıp {{transitions}} içerik şunlarla {{fileType}} {{filePath}} {{browseButton}} eşleşirse:"
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.fileTab.help="Bu sekme, uzak veya yerel dosyaların içeriğine göre sahneleri otomatik olarak değiştirmenize olanak tanır.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; Random Tab
AdvSceneSwitcher.randomTab.title="Rastgele"
AdvSceneSwitcher.randomTab.randomDisabledWarning="İşlevsellik devre dışı - Etkinleştirmek için Genel sekmesinde \"Herhangi bir geçiş koşulu karşılanmazsa Rastgele sekmesinde herhangi bir sahneye geç\"i seçin"
AdvSceneSwitcher.randomTab.entry="Herhangi bir geçiş koşulu karşılanmıyorsa, geçiş yapın {{scenes}} kullan {{transitions}} için {{delay}}"
AdvSceneSwitcher.randomTab.help="Sahne değiştirici, yapılandırılan süre için geçiş yapmak için bu sekmede rastgele bir giriş seçecektir.\nAynı girişin arka arkaya iki kez seçilmeyeceğini unutmayın.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; Time Tab
AdvSceneSwitcher.timeTab.title="Zaman"
AdvSceneSwitcher.timeTab.anyDay="Herhangi bir günde"
AdvSceneSwitcher.timeTab.mondays="Pazartesi günleri"
AdvSceneSwitcher.timeTab.tuesdays="Salı günleri"
AdvSceneSwitcher.timeTab.wednesdays="Çarşamba günleri"
AdvSceneSwitcher.timeTab.thursdays="Perşembe günleri"
AdvSceneSwitcher.timeTab.fridays="Cuma günleri"
AdvSceneSwitcher.timeTab.saturdays="Cumartesi günleri"
AdvSceneSwitcher.timeTab.sundays="Pazar günleri"
AdvSceneSwitcher.timeTab.afterstart="Akış/kayıt başladıktan sonra"
AdvSceneSwitcher.timeTab.afterstart.tip="Akışın / kaydın başlangıcına göre zaman kullanılacaktır"
AdvSceneSwitcher.timeTab.entry="{{triggers}} da/de {{time}} Değiştir {{scenes}} kullan {{transitions}}"
AdvSceneSwitcher.timeTab.help="Bu sekme, geçerli yerel saate göre otomatik olarak farklı bir sahneye geçmenizi sağlar.\n\nSahne değiştiricinin sahneleri yalnızca tam olarak belirttiğiniz saatte değiştireceğini unutmayın.\nGenel'de öncelik ayarlarını yapılandırdığınızdan emin olun. sekmesini beğeninize göre seçin, böylece daha yüksek önceliğe sahip diğer geçiş yöntemleri nedeniyle seçilen zaman noktası kaçırılmaz.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; Idle Tab
AdvSceneSwitcher.idleTab.title="Boşta"
AdvSceneSwitcher.idleTab.enable="Boşta Algılamayı Etkinleştir"
AdvSceneSwitcher.idleTab.idleswitch="Sonra {{duration}} klavye veya fare girişi olmadan sahneye geçiş {{scenes}} kullan {{transitions}}"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="eğer geçiş yapmayın"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="odakta"
; Scene Sequence Tab
AdvSceneSwitcher.sceneSequenceTab.title="Sıra"
AdvSceneSwitcher.sceneSequenceTab.description="Otomatik sahne geçişleri dizisi, sahne değiştiriciyi duraklatarak/durdurarak veya manuel olarak farklı bir sahneye geçerek iptal edilebilir."
AdvSceneSwitcher.sceneSequenceTab.save="Sahne dizilerini dosyaya kaydet"
AdvSceneSwitcher.sceneSequenceTab.load="Dosyadan sahne dizilerini yükle"
AdvSceneSwitcher.sceneSequenceTab.saveTitle="Sahne Sırasını dosyaya kaydet..."
AdvSceneSwitcher.sceneSequenceTab.loadTitle="Sahne Sırasını okumak için bir dosya seçin ..."
AdvSceneSwitcher.sceneSequenceTab.loadFail="Advanced Scene Switcher ayarları içe aktaramadı!"
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Gelişmiş Sahne Değiştirici ayarları başarıyla içe aktarıldı!"
AdvSceneSwitcher.sceneSequenceTab.fileType="Text dosyaları (*.txt)"
AdvSceneSwitcher.sceneSequenceTab.interruptible="Kesilebilir"
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Diğer anahtarlama yöntemlerinin bu sahne dizisini kesmesine izin verilir"
AdvSceneSwitcher.sceneSequenceTab.entry=" {{startScenes}} aktif olduğunda değiştir {{scenes}} sonra {{delay}} kullan {{transitions}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Sırayı Uzat"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Sonra {{delay}} çevirmek {{scenes}} kullan {{transitions}}"
AdvSceneSwitcher.sceneSequenceTab.help="Bu sekme, bir sahne yapılandırılmış bir süre boyunca etkinse otomatik olarak farklı bir sahneye geçmenizi sağlar.\nÖrneğin, iki sahne arasında otomatik olarak ileri ve geri geçiş yapabilirsiniz.\n\nVurgulanan artı sembolüne tıklayarak devam et."
; Audio Tab
AdvSceneSwitcher.audioTab.title="Ses"
AdvSceneSwitcher.audioTab.condition.above="üstünde"
AdvSceneSwitcher.audioTab.condition.below="altında"
AdvSceneSwitcher.audioTab.ignoreInactiveSource="kaynak etkin olmadığı sürece"
AdvSceneSwitcher.audioTab.entry="Ses Hacmi{{audioSources}} durumu {{condition}} {{volumeWidget}} için {{duration}} saniye geçiş {{scenes}} kullanarak {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="Birden çok giriş eşleşirse..."
AdvSceneSwitcher.audioTab.multiMatchfallback="... için {{duration}} saniye geçiş {{scenes}} kullan {{transitions}}"
AdvSceneSwitcher.audioTab.help="Bu sekme, kaynakların ses düzeyine göre sahneler arasında geçiş yapmanızı sağlar.\nÖrneğin, mikrofonunuzun ses düzeyi belirli bir eşiğe ulaşırsa otomatik olarak farklı bir sahneye geçebilirsiniz.\n\nDevam etmek için vurgulanan artı simgesini tıklayın."
; Video Tab
AdvSceneSwitcher.videoTab.title="Video"
AdvSceneSwitcher.videoTab.getScreenshot="Seçilen giriş için ekran görüntüsü al"
AdvSceneSwitcher.videoTab.getScreenshotHelp="Seçili olan girişin video kaynağının Ekran Görüntüsünü alın ve otomatik olarak hedef görüntü olarak ayarlayın"
AdvSceneSwitcher.videoTab.condition.match="tam olarak eşleşir"
AdvSceneSwitcher.videoTab.condition.match.tooltip="Tam eşleşme, hedef ve kaynak görüntünün aynı çözünürlükte olmasını gerektirir.\nAyrıca her bir pikselin eşleşmesi gerekir, bu nedenle sıkıştırma kullanan görüntü biçimlerinin (ör. .JPG) kullanılması önerilmez!"
AdvSceneSwitcher.videoTab.condition.differ="eşleşmiyor"
AdvSceneSwitcher.videoTab.condition.hasNotChanged="değişmedi"
AdvSceneSwitcher.videoTab.condition.hasChanged="değişti"
AdvSceneSwitcher.videoTab.ignoreInactiveSource="Kaynak etkin olmadığı sürece"
AdvSceneSwitcher.videoTab.entry="Zaman {{videoSources}} {{condition}} {{filePath}} {{browseButton}} için {{duration}} şuna dönüştür {{scenes}} kullan {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Bu sekme, seçilen kaynakların mevcut video çıkışına göre sahneler arasında geçiş yapmanızı sağlar.<br/><a href=\"https:// adresini kontrol ettiğinizden emin olun. obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> bu işlevin daha da iyi bir şekilde uygulanması için.<br/><br/> Devam etmek için vurgulanan artı simgesini tıklayın.</p></body></html>"
; Network Tab
AdvSceneSwitcher.networkTab.title="Ağ"
AdvSceneSwitcher.networkTab.description="Bu sekme, başka bir OBS örneğinin etkin sahnesini uzaktan kontrol etmenizi sağlar.\nSahne adlarının tüm OBS örneklerinde tam olarak eşleşmesi gerektiğini lütfen unutmayın."
AdvSceneSwitcher.networkTab.warning="Sunucuyu yerel bir ağın dışında çalıştırmak, üçüncü tarafların aktif sahneyi okumasına izin verecektir."
AdvSceneSwitcher.networkTab.server="Sunucuyu başlat (Bağlı tüm istemcilere sahne değiştirme mesajları gönderir)"
AdvSceneSwitcher.networkTab.server.port="Port"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Sunucuyu yalnızca IPv4 kullanacak şekilde kilitleyin"
AdvSceneSwitcher.networkTab.server.sendSceneChange="Sahne değişiklikleri için mesaj gönder"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Yalnızca otomatik sahne anahtarları için mesaj gönder"
AdvSceneSwitcher.networkTab.server.sendPreview="Stüdyo modunda çalışırken önizleme sahnesi değişikliği için mesajlar gönderin"
AdvSceneSwitcher.networkTab.startFailed.message="WebSockets sunucusu başlatılamadı, bunun nedeni şunlar olabilir:\n - %1 TCP bağlantı noktası şu anda bu sistemde başka bir yerde, muhtemelen başka bir uygulama tarafından kullanılıyor olabilir. WebSocket sunucu ayarlarında farklı bir TCP bağlantı noktası ayarlamayı deneyin veya bu bağlantı noktasını kullanabilecek herhangi bir uygulamayı durdurun.\n - Hata mesajı: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Şu anki durum"
AdvSceneSwitcher.networkTab.server.status.notRunning="Çalışmıyor"
AdvSceneSwitcher.networkTab.server.status.starting="Başlangıç"
AdvSceneSwitcher.networkTab.server.status.running="Çalışıyor"
AdvSceneSwitcher.networkTab.server.restart="Sunucuyu yeniden başlat"
AdvSceneSwitcher.networkTab.client="İstemciyi başlat (Sahne değiştirme mesajlarını alır)"
AdvSceneSwitcher.networkTab.client.address="Ana makine adı veya IP adresi"
AdvSceneSwitcher.networkTab.client.port="Port"
AdvSceneSwitcher.networkTab.client.status.currentStatus="Şu anki durum"
AdvSceneSwitcher.networkTab.client.status.disconnected="Bağlantı kesildi"
AdvSceneSwitcher.networkTab.client.status.connecting="Bağlanıyor"
AdvSceneSwitcher.networkTab.client.status.connected="Bağlandı"
AdvSceneSwitcher.networkTab.client.reconnect="Yeniden bağlanmaya zorla"
; Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="Sahne Grubu"
AdvSceneSwitcher.sceneGroupTab.list="Sahne Grupları"
AdvSceneSwitcher.sceneGroupTab.edit="Sahne Gruplarını Düzenle"
AdvSceneSwitcher.sceneGroupTab.edit.name="İsim:"
AdvSceneSwitcher.sceneGroupTab.edit.type="Tip: {{type}}"
AdvSceneSwitcher.sceneGroupTab.type.count="Saymak"
AdvSceneSwitcher.sceneGroupTab.type.time="Zaman"
AdvSceneSwitcher.sceneGroupTab.type.random="Rastgele"
AdvSceneSwitcher.sceneGroupTab.edit.count="Sonra listedeki bir sonraki sahneye ilerleyin {{count}} karşılama"
AdvSceneSwitcher.sceneGroupTab.edit.time="Sonra listedeki bir sonraki sahneye ilerleyin {{time}} geçtiyse"
AdvSceneSwitcher.sceneGroupTab.edit.random="Listeden rastgele bir sonraki sahneyi seçin"
AdvSceneSwitcher.sceneGroupTab.edit.repeat="Sahne listesinin sonuna ulaşılırsa baştan başlayın"
AdvSceneSwitcher.sceneGroupTab.edit.addScene="Sahne ekle"
AdvSceneSwitcher.sceneGroupTab.add="Sahne Grubu Ekle"
AdvSceneSwitcher.sceneGroupTab.defaultname="Sahne Grubu %1"
AdvSceneSwitcher.sceneGroupTab.exists="Sahne Grubu veya Sahne adı zaten var"
AdvSceneSwitcher.sceneGroupTab.help="Sahne Grupları, normal bir sahne gibi bir hedef olarak seçilebilir.\n\nAdından da anlaşılacağı gibi, bir sahne grubu birden fazla sahneden oluşan bir koleksiyondur.\nSahne grubu, yapılandırılmış ayarlara bağlı olarak kendisine atanan sahneler listesinde ilerleyecektir. sağ tarafta bulunabilir.\n\nSahne grubunu listedeki bir sonraki sahneye geçecek şekilde yapılandırabilirsiniz:\nSahne grubu birkaç kez hedef olarak seçildikten sonra.\nBelirli bir süre sonra geçti.\nVeya rastgele.\n\nÖrneğin, sahneleri içeren bir sahne grubu ...\nSahne 1\nSahne 2\nSahne 3 \n... ilk seçildiğinde \"Sahne 1\"i etkinleştirecek bir hedef olarak.\nİkinci kez \"Sahne 2\"yi etkinleştirecek.\nKalan zamanlar \"Scene 3\" etkinleştirilecek.\n\nYeni bir sahne grubu eklemek için aşağıdaki vurgulanan artı sembolüne tıklayın."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Soldan değiştirmek istediğiniz sahne grubunu seçin.\n\nYukarıdaki sahneyi seçip aşağıdaki artı simgesini tıklayarak bu sahne grubuna eklemek için bir sahne seçin.\n\nAynı sahneye birden fazla sahne eklenebilir grup."
; Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="Sahne Tetikleyicileri"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--tetikleyiciyi seçin--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="aktif"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="aktif değil"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="uzaklaştı"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--eylemi seç--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="Kayda başla"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="Kaydı duraklat"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="kaydı duraklatma"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="Kaydetmeyi bırak"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="Akışı durdur"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="Akışı başlat"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="Yeniden oynatma arabelleğini başlat"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="Tekrar arabelleğini durdur"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Kaynağı sessize al"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="Kaynağı sesi aç"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="Sahne değiştiriciyi başlat"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="Sahne değiştiriciyi durdur"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera="Sanal kamerayı başlat"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="Sanal kamerayı durdur"
AdvSceneSwitcher.sceneTriggerTab.entry="Şundan {{scenes}} {{triggers}} {{actions}} {{audioSources}} sonra {{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="Bu sekme, kaydı veya akışı durdurma gibi sahne değişikliklerinde eylemleri tetiklemenize olanak tanır."
; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Advanced Scene Switcher'ı başlatın"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Gelişmiş Sahne Değiştiriciyi Durdurun"
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Gelişmiş Sahne Değiştirici için Başlat/Durdur arasında geçiş yapın"
AdvSceneSwitcher.hotkey.macro.pause="%1 makrosunu duraklat"
AdvSceneSwitcher.hotkey.macro.unpause="%1 makrosunu duraklatma"
AdvSceneSwitcher.hotkey.macro.togglePause="%1 makrosunun duraklatmasını aç/kapat"
AdvSceneSwitcher.askBackup="Advanced Scene Switcher'ın yeni bir sürümü algılandı.\nEski ayarların bir yedeği oluşturulsun mu?"
AdvSceneSwitcher.askForMacro="Makro seç {{macroSelection}}"
AdvSceneSwitcher.close="Kapat"
AdvSceneSwitcher.browse="Araştır"
AdvSceneSwitcher.selectScene="--sahne seç--"
AdvSceneSwitcher.selectPreviousScene="Önceki Sahne"
AdvSceneSwitcher.selectCurrentScene="Mevcut Sahne"
AdvSceneSwitcher.selectAnyScene="Herhangi bir Sahne"
AdvSceneSwitcher.currentTransition="Mevcut Geçiş"
AdvSceneSwitcher.anyTransition="Herhangi Bir Geçiş"
AdvSceneSwitcher.selectTransition="--geçişi seç--"
AdvSceneSwitcher.selectWindow="--pencereyi seç--"
AdvSceneSwitcher.selectSource="--kaynak Seç--"
AdvSceneSwitcher.selectAudioSource="--ses kaynağını seçin--"
AdvSceneSwitcher.selectVideoSource="--video kaynağını seçin--"
AdvSceneSwitcher.selectMediaSource="--medya kaynağını seçin--"
AdvSceneSwitcher.selectProcess="--işlemi seç--"
AdvSceneSwitcher.selectFilter="--filtre seç--"
AdvSceneSwitcher.selectMacro="--makroyu seçin--"
AdvSceneSwitcher.selectItem="--Öğeyi seçin--"
AdvSceneSwitcher.selectProfile="--profil seç--"
AdvSceneSwitcher.selectSceneCollection="--sahne koleksiyonunu seç--"
AdvSceneSwitcher.enterPath="--yolu girin--"
AdvSceneSwitcher.enterText="--metni girin--"
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="geçersiz girişler kaydedilmeyecek"
AdvSceneSwitcher.selectWindowTip="\"OBS\" OBS penceresini belirlemek için \nKullan \"Görev değiştirme\"için ALT + TAB"
AdvSceneSwitcher.sceneItemSelection.all="Hepsi"
AdvSceneSwitcher.sceneItemSelection.any="Herhangi"
AdvSceneSwitcher.status.active="Aktif"
AdvSceneSwitcher.status.inactive="İnaktif"
AdvSceneSwitcher.running="Eklenti çalışıyor"
AdvSceneSwitcher.stopped="Eklenti durdu"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Gelişmiş Sahne Değiştirici ilk kez başlatılıyor gibi görünüyor.<br>Lütfen <a href=\"https://github.com/ adresine bir göz atın. WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> için kılavuzlar ve örnekler listesi.<br>Yapmayın. eklentinin <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:# sayfasında soru sormaktan çekinmeyin OBS forumlarında 268bd2;\">konu</span></a>!</p></body></html>"
AdvSceneSwitcher.unit.milliseconds="millisaniye"
AdvSceneSwitcher.unit.secends="saniye"
AdvSceneSwitcher.unit.minutes="dakika"
AdvSceneSwitcher.unit.hours="saat"
AdvSceneSwitcher.duration.condition.none="Zaman kısıtlaması yok"
AdvSceneSwitcher.duration.condition.more="En azından"
AdvSceneSwitcher.duration.condition.equal="Tam olarak"
AdvSceneSwitcher.duration.condition.less="En çoğu"

View File

@@ -30,6 +30,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="详细日志输出"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="保存窗口位置和大小" AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="保存窗口位置和大小"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="显示系统托盘通知" AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="显示系统托盘通知"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="禁用UI提示" AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="禁用UI提示"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="隐藏可通过宏操作的分组栏"
AdvSceneSwitcher.generalTab.priority="优先级" AdvSceneSwitcher.generalTab.priority="优先级"
AdvSceneSwitcher.generalTab.priority.description="切换场景优先级 (最上方的项优先级最高)" AdvSceneSwitcher.generalTab.priority.description="切换场景优先级 (最上方的项优先级最高)"
AdvSceneSwitcher.generalTab.priority.threadPriority="使用线程优先级" AdvSceneSwitcher.generalTab.priority.threadPriority="使用线程优先级"
@@ -57,6 +58,7 @@ AdvSceneSwitcher.generalTab.priority.macro="宏"
; Macro Tab ; Macro Tab
AdvSceneSwitcher.macroTab.title="宏" AdvSceneSwitcher.macroTab.title="宏"
AdvSceneSwitcher.macroTab.macros="宏文件" AdvSceneSwitcher.macroTab.macros="宏文件"
AdvSceneSwitcher.macroTab.priorityWarning="注意:建议将宏配置为最高优先级的功能.\n可以在“常规”选项卡上更改此设置."
AdvSceneSwitcher.macroTab.help="宏允许您根据多种条件执行一系列操作。\n\n单击突出显示的加号可添加新宏." AdvSceneSwitcher.macroTab.help="宏允许您根据多种条件执行一系列操作。\n\n单击突出显示的加号可添加新宏."
AdvSceneSwitcher.macroTab.editConditionHelp="此部分允许您定义宏条件。\n\n在左侧选择现有宏或添加新宏。\n然后单击下面的加号按钮添加新条件。" AdvSceneSwitcher.macroTab.editConditionHelp="此部分允许您定义宏条件。\n\n在左侧选择现有宏或添加新宏。\n然后单击下面的加号按钮添加新条件。"
AdvSceneSwitcher.macroTab.editActionHelp="此部分允许您定义宏操作。\n\n在左侧选择现有宏或添加新宏。\n然后单击下面的加号按钮添加新操作." AdvSceneSwitcher.macroTab.editActionHelp="此部分允许您定义宏操作。\n\n在左侧选择现有宏或添加新宏。\n然后单击下面的加号按钮添加新操作."
@@ -66,11 +68,17 @@ AdvSceneSwitcher.macroTab.edit.condition="条件类型:"
AdvSceneSwitcher.macroTab.edit.action="动作类型:" AdvSceneSwitcher.macroTab.edit.action="动作类型:"
AdvSceneSwitcher.macroTab.add="添加新宏" AdvSceneSwitcher.macroTab.add="添加新宏"
AdvSceneSwitcher.macroTab.name="名称:" AdvSceneSwitcher.macroTab.name="名称:"
AdvSceneSwitcher.macroTab.run="运行宏"
AdvSceneSwitcher.macroTab.runFail="运行 \"%1\" 失败!\n其中一个操作失败或者宏已在运行."
AdvSceneSwitcher.macroTab.runInParallel="与其他宏并行运行宏"
AdvSceneSwitcher.macroTab.onChange="仅在条件更改时执行操作"
AdvSceneSwitcher.macroTab.defaultname="宏 %1" AdvSceneSwitcher.macroTab.defaultname="宏 %1"
AdvSceneSwitcher.macroTab.exists="宏名称已存在" AdvSceneSwitcher.macroTab.exists="宏名称已存在"
AdvSceneSwitcher.macroTab.copy="创建副本" AdvSceneSwitcher.macroTab.copy="创建副本"
AdvSceneSwitcher.macroTab.expandAll="全部展开" AdvSceneSwitcher.macroTab.expandAll="全部展开"
AdvSceneSwitcher.macroTab.collapseAll="全部收回" AdvSceneSwitcher.macroTab.collapseAll="全部收回"
AdvSceneSwitcher.macroTab.maximize="最大化"
AdvSceneSwitcher.macroTab.minimize="最小化"
; Macro Logic ; Macro Logic
AdvSceneSwitcher.logic.none="忽略条目" AdvSceneSwitcher.logic.none="忽略条目"
@@ -83,23 +91,27 @@ AdvSceneSwitcher.logic.not="如果 不"
; Macro Conditions ; Macro Conditions
AdvSceneSwitcher.condition.audio="音频" AdvSceneSwitcher.condition.audio="音频"
AdvSceneSwitcher.condition.audio.state.below="于" AdvSceneSwitcher.condition.audio.state.below="于"
AdvSceneSwitcher.condition.audio.state.exact="相等"
AdvSceneSwitcher.condition.audio.state.above="大于" AdvSceneSwitcher.condition.audio.state.above="大于"
AdvSceneSwitcher.condition.audio.entry="音量 来源 {{audioSources}} 是 {{condition}} {{volume}}" AdvSceneSwitcher.condition.audio.state.mute="静音"
AdvSceneSwitcher.condition.cursor="光标" AdvSceneSwitcher.condition.audio.state.unmute="取消静音"
AdvSceneSwitcher.condition.audio.type.output="输出音频"
AdvSceneSwitcher.condition.audio.type.volume="配置音量级别"
AdvSceneSwitcher.condition.cursor="屏幕区域"
AdvSceneSwitcher.condition.cursor.type.region="当前位置" AdvSceneSwitcher.condition.cursor.type.region="当前位置"
AdvSceneSwitcher.condition.cursor.type.moving="正在移动" AdvSceneSwitcher.condition.cursor.type.moving="正在移动"
AdvSceneSwitcher.condition.cursor.showFrame="显示帧率" AdvSceneSwitcher.condition.cursor.showFrame="显示帧率"
AdvSceneSwitcher.condition.cursor.hideFrame="隐藏帧率" AdvSceneSwitcher.condition.cursor.hideFrame="隐藏帧率"
AdvSceneSwitcher.condition.cursor.entry.line1="标 处于{{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}" AdvSceneSwitcher.condition.cursor.entry.line1="标 处于{{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line2="标当前位于 {{xPos}} x {{yPos}}" AdvSceneSwitcher.condition.cursor.entry.line2="标当前位于 {{xPos}} x {{yPos}}"
AdvSceneSwitcher.condition.scene="场景" AdvSceneSwitcher.condition.scene="场景"
AdvSceneSwitcher.condition.scene.type.current="当前场景是" AdvSceneSwitcher.condition.scene.type.current="当前场景是"
AdvSceneSwitcher.condition.scene.type.previous="上一个场景是" AdvSceneSwitcher.condition.scene.type.previous="上一个场景是"
AdvSceneSwitcher.condition.scene.type.changed="场景改变了" AdvSceneSwitcher.condition.scene.type.changed="场景改变了"
AdvSceneSwitcher.condition.scene.type.notChanged="场景没有改变" AdvSceneSwitcher.condition.scene.type.notChanged="场景没有改变"
AdvSceneSwitcher.condition.scene.waitForTransition="等待过渡完成" AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} {{scenes}} {{waitForTransition}}" AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="窗口" AdvSceneSwitcher.condition.window="窗口"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} 存在..." AdvSceneSwitcher.condition.window.entry.line1="{{windows}} 存在..."
AdvSceneSwitcher.condition.window.entry.line2="... 并且是 {{fullscreen}} 全屏 {{maximized}} 最大化 {{focused}} 获得焦点 {{windowFocusChanged}} 焦点窗口已更改" AdvSceneSwitcher.condition.window.entry.line2="... 并且是 {{fullscreen}} 全屏 {{maximized}} 最大化 {{focused}} 获得焦点 {{windowFocusChanged}} 焦点窗口已更改"
@@ -108,7 +120,9 @@ AdvSceneSwitcher.condition.file.entry.line1="内容 {{fileType}} {{filePath}} (
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}" AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.media="媒体" AdvSceneSwitcher.condition.media="媒体"
AdvSceneSwitcher.condition.media.entry="{{mediaSources}} 状态是 {{states}} 和 {{timeRestrictions}} {{time}}" AdvSceneSwitcher.condition.media.anyOnScene="任何媒体来源"
AdvSceneSwitcher.condition.media.allOnScene="所有媒体来源"
AdvSceneSwitcher.condition.media.matchOnChange="仅在更改时匹配(注意:此选项将在未来版本中删除-请改用时间限制)"
AdvSceneSwitcher.condition.video="视频" AdvSceneSwitcher.condition.video="视频"
AdvSceneSwitcher.condition.video.condition.match="完全匹配" AdvSceneSwitcher.condition.video.condition.match="完全匹配"
AdvSceneSwitcher.condition.video.condition.differ="不匹配" AdvSceneSwitcher.condition.video.condition.differ="不匹配"
@@ -129,15 +143,17 @@ AdvSceneSwitcher.condition.video.objectScaleThreshold="模型比例: "
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="较低的模比例将导致更多的匹配但会导致更高的CPU负载." AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="较低的模比例将导致更多的匹配但会导致更高的CPU负载."
AdvSceneSwitcher.condition.video.minNeighborDescription="较高的“最小区域”值将导致较少但质量较高的匹配." AdvSceneSwitcher.condition.video.minNeighborDescription="较高的“最小区域”值将导致较少但质量较高的匹配."
AdvSceneSwitcher.condition.video.showMatch="显示模式" AdvSceneSwitcher.condition.video.showMatch="显示模式"
AdvSceneSwitcher.condition.video.showMatch.loading="Checking for match"
AdvSceneSwitcher.condition.video.screenshotFail="无法获取源的屏幕截图!" AdvSceneSwitcher.condition.video.screenshotFail="无法获取源的屏幕截图!"
AdvSceneSwitcher.condition.video.screenshotEmpty="Screenshot is empty - Is the source visible?"
AdvSceneSwitcher.condition.video.patternMatchFail="没有找到模式!" AdvSceneSwitcher.condition.video.patternMatchFail="没有找到模式!"
AdvSceneSwitcher.condition.video.patternMatchSuccess="Pattern is highlighted in red"
AdvSceneSwitcher.condition.video.objectMatchFail="找不到对象!" AdvSceneSwitcher.condition.video.objectMatchFail="找不到对象!"
AdvSceneSwitcher.condition.video.objectMatchSuccess="Object is highlighted in red"
AdvSceneSwitcher.condition.video.modelLoadFail="无法加载模型数据!" AdvSceneSwitcher.condition.video.modelLoadFail="无法加载模型数据!"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}" AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="模型数据 (haar级联分类器): {{modelDataPath}}" AdvSceneSwitcher.condition.video.entry.modelPath="模型数据 (haar级联分类器): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="最小区域: {{minNeighbors}}" AdvSceneSwitcher.condition.video.entry.minNeighbor="最小区域: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.minSize="最小尺寸: {{minSizeX}} x {{minSizeY}}"
AdvSceneSwitcher.condition.video.entry.maxSize="最大尺寸: {{maxSizeX}} x {{maxSizeY}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}通过只执行每一次检查来减少CPU负载 {{throttleCount}} 毫秒" AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}通过只执行每一次检查来减少CPU负载 {{throttleCount}} 毫秒"
AdvSceneSwitcher.condition.stream="推流" AdvSceneSwitcher.condition.stream="推流"
AdvSceneSwitcher.condition.stream.state.start="推流启动" AdvSceneSwitcher.condition.stream.state.start="推流启动"
@@ -156,11 +172,14 @@ AdvSceneSwitcher.condition.idle="闲置检测"
AdvSceneSwitcher.condition.idle.entry="没有键盘或鼠标输入{{duration}}" AdvSceneSwitcher.condition.idle.entry="没有键盘或鼠标输入{{duration}}"
AdvSceneSwitcher.condition.pluginState="插件状态" AdvSceneSwitcher.condition.pluginState="插件状态"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="在此时间间隔内触发了自动场景更改" AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="在此时间间隔内触发了自动场景更改"
AdvSceneSwitcher.condition.pluginState.state.running="高级场景切换程序正在运行"
AdvSceneSwitcher.condition.pluginState.state.shutdown="OBS正在关闭"
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}" AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
AdvSceneSwitcher.condition.timer="计时器" AdvSceneSwitcher.condition.timer="计时器"
AdvSceneSwitcher.condition.timer.type.fixed="固定"
AdvSceneSwitcher.condition.timer.type.random="随机"
AdvSceneSwitcher.condition.timer.pause="暂停" AdvSceneSwitcher.condition.timer.pause="暂停"
AdvSceneSwitcher.condition.timer.continue="继续" AdvSceneSwitcher.condition.timer.continue="继续"
AdvSceneSwitcher.condition.timer.entry.line1="{{duration}} 时间倒计时"
AdvSceneSwitcher.condition.timer.entry.line2="剩余时间: {{remaining}} 秒" AdvSceneSwitcher.condition.timer.entry.line2="剩余时间: {{remaining}} 秒"
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} 保存剩余时间 {{autoReset}} 达到时间后自动重置计时器" AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} 保存剩余时间 {{autoReset}} 达到时间后自动重置计时器"
AdvSceneSwitcher.condition.timer.reset="重置" AdvSceneSwitcher.condition.timer.reset="重置"
@@ -177,8 +196,8 @@ AdvSceneSwitcher.condition.macro.state.entry="条件 {{macros}} 是真的"
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} 运行 {{conditions}} {{count}} 次(相对于下面计数匹配)" AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} 运行 {{conditions}} {{count}} 次(相对于下面计数匹配)"
AdvSceneSwitcher.condition.macro.count.entry.line2="当前计数: {{currentCount}} {{resetCount}}" AdvSceneSwitcher.condition.macro.count.entry.line2="当前计数: {{currentCount}} {{resetCount}}"
AdvSceneSwitcher.condition.source="源" AdvSceneSwitcher.condition.source="源"
AdvSceneSwitcher.condition.source.type.active="激活状态" AdvSceneSwitcher.condition.source.type.active="激活状态"
AdvSceneSwitcher.condition.source.type.showing="显示状态" AdvSceneSwitcher.condition.source.type.showing="显示状态"
AdvSceneSwitcher.condition.source.type.settings="设置完全匹配" AdvSceneSwitcher.condition.source.type.settings="设置完全匹配"
AdvSceneSwitcher.condition.source.regex="使用正则表达式" AdvSceneSwitcher.condition.source.regex="使用正则表达式"
AdvSceneSwitcher.condition.source.getSettings="获取当前设置" AdvSceneSwitcher.condition.source.getSettings="获取当前设置"
@@ -207,19 +226,34 @@ AdvSceneSwitcher.condition.sceneOrder.entry="在 {{scenes}} {{sources}} {{condit
AdvSceneSwitcher.condition.hotkey="热键" AdvSceneSwitcher.condition.hotkey="热键"
AdvSceneSwitcher.condition.hotkey.name="宏触发热键" AdvSceneSwitcher.condition.hotkey.name="宏触发热键"
AdvSceneSwitcher.condition.hotkey.tip="注意您可以在OBS设置窗口中为此热键配置按键绑定" AdvSceneSwitcher.condition.hotkey.tip="注意您可以在OBS设置窗口中为此热键配置按键绑定"
AdvSceneSwitcher.condition.hotkey.entry="名称: {{name}}" AdvSceneSwitcher.condition.hotkey.entry.line1="热键被按下"
AdvSceneSwitcher.condition.hotkey.entry.line2="名称: {{name}}"
AdvSceneSwitcher.condition.replay="回放缓存" AdvSceneSwitcher.condition.replay="回放缓存"
AdvSceneSwitcher.condition.replay.state.stopped="回放缓存已停止" AdvSceneSwitcher.condition.replay.state.stopped="回放缓存已停止"
AdvSceneSwitcher.condition.replay.state.started="回放缓存已启动" AdvSceneSwitcher.condition.replay.state.started="回放缓存已启动"
AdvSceneSwitcher.condition.replay.state.saved="已保存回放缓存" AdvSceneSwitcher.condition.replay.state.saved="已保存回放缓存"
AdvSceneSwitcher.condition.replay.entry="{{state}}" AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="日期" AdvSceneSwitcher.condition.date="日期"
AdvSceneSwitcher.condition.date.anyDay="每一天"
AdvSceneSwitcher.condition.date.monday="周一"
AdvSceneSwitcher.condition.date.tuesday="周二"
AdvSceneSwitcher.condition.date.wednesday="周三"
AdvSceneSwitcher.condition.date.thursday="周四"
AdvSceneSwitcher.condition.date.friday="周五"
AdvSceneSwitcher.condition.date.saturday="周六"
AdvSceneSwitcher.condition.date.sunday="周日"
AdvSceneSwitcher.condition.date.state.at="现在" AdvSceneSwitcher.condition.date.state.at="现在"
AdvSceneSwitcher.condition.date.state.after="之后" AdvSceneSwitcher.condition.date.state.after="之后"
AdvSceneSwitcher.condition.date.state.before="之前" AdvSceneSwitcher.condition.date.state.before="之前"
AdvSceneSwitcher.condition.date.state.between="之间" AdvSceneSwitcher.condition.date.state.between="之间"
AdvSceneSwitcher.condition.date.entry.line1="{{condition}} {{dateTime}} {{dateTime2}} {{ignoreDate}} 忽略日期 {{ignoreTime}} 忽略时间" AdvSceneSwitcher.condition.date.separator="和"
AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} 重复的每一次 {{duration}} 匹配的时间" AdvSceneSwitcher.condition.date.ignoreDate="如果未选中,日期组件将被忽略"
AdvSceneSwitcher.condition.date.ignoreTime="如果未选中,时间组件将被忽略"
AdvSceneSwitcher.condition.date.showAdvancedSettings="显示高级设置"
AdvSceneSwitcher.condition.date.showSimpleSettings="显示简单设置"
AdvSceneSwitcher.condition.date.entry.simple="在 {{dayOfWeek}} 的 {{weekTime}}"
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} 在匹配到日期时间后,每隔 {{duration}} 重复一次"
AdvSceneSwitcher.condition.sceneTransform="场景项目被改变" AdvSceneSwitcher.condition.sceneTransform="场景项目被改变"
AdvSceneSwitcher.condition.sceneTransform.getTransform="内容或者设置被改变了" AdvSceneSwitcher.condition.sceneTransform.getTransform="内容或者设置被改变了"
AdvSceneSwitcher.condition.sceneTransform.regex="使用正则表达式" AdvSceneSwitcher.condition.sceneTransform.regex="使用正则表达式"
@@ -231,16 +265,48 @@ AdvSceneSwitcher.condition.transition.type.current="当前转场特效类型为"
AdvSceneSwitcher.condition.transition.type.duration="当前转场特效持续时间为" AdvSceneSwitcher.condition.transition.type.duration="当前转场特效持续时间为"
AdvSceneSwitcher.condition.transition.type.started="转场特效开始" AdvSceneSwitcher.condition.transition.type.started="转场特效开始"
AdvSceneSwitcher.condition.transition.type.ended="转场特效结束" AdvSceneSwitcher.condition.transition.type.ended="转场特效结束"
AdvSceneSwitcher.condition.transition.type.transitionSource="转场特效来自"
AdvSceneSwitcher.condition.transition.type.transitionTarget="转场特效到"
AdvSceneSwitcher.condition.transition.durationSuffix="秒" AdvSceneSwitcher.condition.transition.durationSuffix="秒"
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{duration}}{{durationSuffix}}" AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{scenes}}{{duration}}{{durationSuffix}}"
AdvSceneSwitcher.condition.sceneVisibility="场景项目可见" AdvSceneSwitcher.condition.sceneVisibility="场景项目可见"
AdvSceneSwitcher.condition.sceneVisibility.type.shown="显示的" AdvSceneSwitcher.condition.sceneVisibility.type.shown="显示的"
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="隐藏的" AdvSceneSwitcher.condition.sceneVisibility.type.hidden="隐藏的"
AdvSceneSwitcher.condition.sceneVisibility.entry="在 {{scenes}} {{sources}} 是 {{conditions}} " AdvSceneSwitcher.condition.sceneVisibility.entry="在 {{scenes}} {{sources}} 是 {{conditions}} "
AdvSceneSwitcher.condition.studioMode="工作室模式"
AdvSceneSwitcher.condition.studioMode.state.active="工作室模式处于活动状态"
AdvSceneSwitcher.condition.studioMode.state.notActive="工作室模式未处于活动状态"
AdvSceneSwitcher.condition.studioMode.state.previewScene="预览场景是"
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
AdvSceneSwitcher.condition.openvr="OpenVR"
AdvSceneSwitcher.condition.errorStatus="OpenVR 错误: "
AdvSceneSwitcher.condition.openvr.entry.line1="HMD在 ..."
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
AdvSceneSwitcher.condition.openvr.entry.line3="HMD目前处于 {{xPos}} x {{yPos}} x {{zPos}}"
AdvSceneSwitcher.condition.stats="OBS统计数据"
AdvSceneSwitcher.condition.stats.type.fps="FPS"
AdvSceneSwitcher.condition.stats.type.CPUUsage="CPU使用率"
AdvSceneSwitcher.condition.stats.type.HDDSpaceAvailable="可用磁盘空间"
AdvSceneSwitcher.condition.stats.type.memoryUsage="内存使用"
AdvSceneSwitcher.condition.stats.type.averageTimeToRender="渲染帧的平均时间"
AdvSceneSwitcher.condition.stats.type.skippedFrames="由于编码延迟跳过的帧"
AdvSceneSwitcher.condition.stats.type.missedFrames="由于渲染延迟错过的帧"
AdvSceneSwitcher.condition.stats.type.droppedFrames.stream="串流丢弃的帧"
AdvSceneSwitcher.condition.stats.type.megabytesSent.stream="串流总数据输出"
AdvSceneSwitcher.condition.stats.type.bitrate.stream="串流比特率"
AdvSceneSwitcher.condition.stats.type.droppedFrames.recording="录制丢弃的帧"
AdvSceneSwitcher.condition.stats.type.megabytesSent.recording="录制总数据输出"
AdvSceneSwitcher.condition.stats.type.bitrate.recording="录制比特率"
AdvSceneSwitcher.condition.stats.condition.above="大于"
AdvSceneSwitcher.condition.stats.condition.equals="等于"
AdvSceneSwitcher.condition.stats.condition.below="少于"
AdvSceneSwitcher.condition.stats.dockHint="你可以打开“统计”窗口查看当前状态"
AdvSceneSwitcher.condition.stats.entry="{{stats}} is {{condition}} {{value}}"
; Macro Actions ; Macro Actions
AdvSceneSwitcher.action.switchScene="切换场景" AdvSceneSwitcher.action.switchScene="切换场景"
AdvSceneSwitcher.action.scene.entry="切换场景 {{scenes}} 使用 {{transitions}} 时长 {{duration}} 秒" AdvSceneSwitcher.action.scene.entry="切换场景 {{scenes}} 使用 {{transitions}} 时长 {{duration}} 秒"
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="等待目标场景的过渡完成"
AdvSceneSwitcher.action.wait="等待" AdvSceneSwitcher.action.wait="等待"
AdvSceneSwitcher.action.wait.type.fixed="固定数值" AdvSceneSwitcher.action.wait.type.fixed="固定数值"
AdvSceneSwitcher.action.wait.type.random="随机数值" AdvSceneSwitcher.action.wait.type.random="随机数值"
@@ -251,7 +317,6 @@ AdvSceneSwitcher.action.audio.type.mute="静音"
AdvSceneSwitcher.action.audio.type.unmute="取消静音" AdvSceneSwitcher.action.audio.type.unmute="取消静音"
AdvSceneSwitcher.action.audio.type.sourceVolume="来源音量" AdvSceneSwitcher.action.audio.type.sourceVolume="来源音量"
AdvSceneSwitcher.action.audio.type.masterVolume="主音量" AdvSceneSwitcher.action.audio.type.masterVolume="主音量"
AdvSceneSwitcher.action.audio.fade="{{fade}}淡出 {{duration}} 秒"
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}" AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.recording="录制" AdvSceneSwitcher.action.recording="录制"
AdvSceneSwitcher.action.recording.type.stop="停止录制" AdvSceneSwitcher.action.recording.type.stop="停止录制"
@@ -279,7 +344,6 @@ AdvSceneSwitcher.action.sceneVisibility.type.show="显示"
AdvSceneSwitcher.action.sceneVisibility.type.hide="隐藏" AdvSceneSwitcher.action.sceneVisibility.type.hide="隐藏"
AdvSceneSwitcher.action.sceneVisibility.type.source="来源" AdvSceneSwitcher.action.sceneVisibility.type.source="来源"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="全部" AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="全部"
AdvSceneSwitcher.action.sceneVisibility.entry="在 {{scenes}} {{actions}} {{sourceTypes}} {{sources}}"
AdvSceneSwitcher.action.filter="滤镜" AdvSceneSwitcher.action.filter="滤镜"
AdvSceneSwitcher.action.filter.type.enable="开启" AdvSceneSwitcher.action.filter.type.enable="开启"
AdvSceneSwitcher.action.filter.type.disable="关闭" AdvSceneSwitcher.action.filter.type.disable="关闭"
@@ -300,16 +364,19 @@ AdvSceneSwitcher.action.media.type.stop="停止"
AdvSceneSwitcher.action.media.type.restart="重新启动" AdvSceneSwitcher.action.media.type.restart="重新启动"
AdvSceneSwitcher.action.media.type.next="下一个" AdvSceneSwitcher.action.media.type.next="下一个"
AdvSceneSwitcher.action.media.type.previous="上一个" AdvSceneSwitcher.action.media.type.previous="上一个"
AdvSceneSwitcher.action.media.entry="{{actions}} {{mediaSources}}" AdvSceneSwitcher.action.media.entry="{{actions}}{{duration}}{{mediaSources}}"
AdvSceneSwitcher.action.macro="宏" AdvSceneSwitcher.action.macro="宏"
AdvSceneSwitcher.action.macro.type.pause="暂停" AdvSceneSwitcher.action.macro.type.pause="暂停"
AdvSceneSwitcher.action.macro.type.unpause="取消暂停" AdvSceneSwitcher.action.macro.type.unpause="取消暂停"
AdvSceneSwitcher.action.macro.type.resetCounter="复位计数器" AdvSceneSwitcher.action.macro.type.resetCounter="复位计数器"
AdvSceneSwitcher.action.macro.type.run="运行"
AdvSceneSwitcher.action.macro.type.stop="停止"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}" AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.pluginState="插件状态" AdvSceneSwitcher.action.pluginState="插件状态"
AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件" AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件"
AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项:" AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项:"
AdvSceneSwitcher.action.pluginState.entry="{{actions}} {{values}} {{scenes}}" AdvSceneSwitcher.action.pluginState.type.import="从设置导入"
AdvSceneSwitcher.action.pluginState.importWarning="注意:当“设置”窗口打开时,操作将被忽略。"
AdvSceneSwitcher.action.virtualCamera="虚拟摄像机" AdvSceneSwitcher.action.virtualCamera="虚拟摄像机"
AdvSceneSwitcher.action.virtualCamera.type.stop="停止虚拟摄像机" AdvSceneSwitcher.action.virtualCamera.type.stop="停止虚拟摄像机"
AdvSceneSwitcher.action.virtualCamera.type.start="启动虚拟摄像机" AdvSceneSwitcher.action.virtualCamera.type.start="启动虚拟摄像机"
@@ -333,13 +400,13 @@ AdvSceneSwitcher.action.sceneOrder.type.moveTop="顶部"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="底部" AdvSceneSwitcher.action.sceneOrder.type.moveBottom="底部"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="移动到数值位置" AdvSceneSwitcher.action.sceneOrder.type.movePosition="移动到数值位置"
AdvSceneSwitcher.action.sceneOrder.entry="在 {{scenes}} {{actions}} {{sources}} {{position}}" AdvSceneSwitcher.action.sceneOrder.entry="在 {{scenes}} {{actions}} {{sources}} {{position}}"
AdvSceneSwitcher.action.sceneTransform="场景项变换" AdvSceneSwitcher.action.sceneTransform="场景项变换"
AdvSceneSwitcher.action.sceneTransform.getTransform="得到变换" AdvSceneSwitcher.action.sceneTransform.getTransform="得到变换"
AdvSceneSwitcher.action.sceneTransform.entry="On {{scenes}} transform {{sources}}" AdvSceneSwitcher.action.sceneTransform.entry=" {{scenes}} 变换 {{sources}}"
AdvSceneSwitcher.action.file="文件" AdvSceneSwitcher.action.file="文件"
AdvSceneSwitcher.action.file.type.write="写入" AdvSceneSwitcher.action.file.type.write="写入"
AdvSceneSwitcher.action.file.type.append="追加写入" AdvSceneSwitcher.action.file.type.append="追加写入"
AdvSceneSwitcher.action.file.entry="{{actions}} to {{filePath}}:" AdvSceneSwitcher.action.file.entry="{{actions}} {{filePath}}:"
AdvSceneSwitcher.action.previewScene="切换预览场景" AdvSceneSwitcher.action.previewScene="切换预览场景"
AdvSceneSwitcher.action.previewScene.entry="将预览场景切换到 {{scenes}}" AdvSceneSwitcher.action.previewScene.entry="将预览场景切换到 {{scenes}}"
AdvSceneSwitcher.action.SceneSwap="交换场景 (Studio mode)" AdvSceneSwitcher.action.SceneSwap="交换场景 (Studio mode)"
@@ -354,9 +421,22 @@ AdvSceneSwitcher.action.timer.type.reset="重置"
AdvSceneSwitcher.action.timer.type.setTimeRemaining="设置剩余时间" AdvSceneSwitcher.action.timer.type.setTimeRemaining="设置剩余时间"
AdvSceneSwitcher.action.timer.entry="{{timerAction}} 定时在 {{macros}} {{duration}}" AdvSceneSwitcher.action.timer.entry="{{timerAction}} 定时在 {{macros}} {{duration}}"
AdvSceneSwitcher.action.random="随机" AdvSceneSwitcher.action.random="随机"
AdvSceneSwitcher.action.random.arguments="宏:" AdvSceneSwitcher.action.systray="系统托盘通知"
AdvSceneSwitcher.action.random.addArgument="增加 宏" AdvSceneSwitcher.action.systray.entry="显示通知: {{message}}"
AdvSceneSwitcher.action.random.entry="运行 {{macroSelection}}" AdvSceneSwitcher.action.screenshot="截图"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS 预览画面"
AdvSceneSwitcher.action.screenshot.entry="截图 {{sources}}"
AdvSceneSwitcher.action.profile="配置文件"
AdvSceneSwitcher.action.profile.entry="将活动配置文件切换到 {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="场景集合"
AdvSceneSwitcher.action.sceneCollection.entry="将活动场景集合切换到 {{sceneCollections}}"
AdvSceneSwitcher.action.sceneCollection.warning="注意:在此之后的任何操作都不会执行,因为更改场景集合也会重新加载场景切换器设置。\n打开“设置”窗口时场景采集操作将被忽略."
AdvSceneSwitcher.action.sequence="场景序列"
AdvSceneSwitcher.action.sequence.entry="每次执行此操作时,运行列表中的下一个宏(暂停的宏将被忽略)"
AdvSceneSwitcher.action.sequence.status="上次执行的宏:%1 - 要执行的下一个宏: %2"
AdvSceneSwitcher.action.sequence.status.none="无"
AdvSceneSwitcher.action.sequence.restart="到达列表末尾后,从开头重新启动"
AdvSceneSwitcher.action.sequence.continueFrom="继续所选项目"
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="转场特效" AdvSceneSwitcher.transitionTab.title="转场特效"
@@ -366,8 +446,6 @@ AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="更改激活转场特
AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效" AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>这里的设定<span style=\"font-style:bold;\">只影响由场景切换器引起的转场有效</span>,不影响你手动的引起的转场。<br/>在这里设定的转场特效优先级高于场景切换器其他地方设置的<br/><br/>单击加号添加项目.</p></body></html>" AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>这里的设定<span style=\"font-style:bold;\">只影响由场景切换器引起的转场有效</span>,不影响你手动的引起的转场。<br/>在这里设定的转场特效优先级高于场景切换器其他地方设置的<br/><br/>单击加号添加项目.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="当切换到这个场景时修改默认转场特效" AdvSceneSwitcher.transitionTab.defaultTransition="当切换到这个场景时修改默认转场特效"
AdvSceneSwitcher.transitionTab.switchDefaultLabel="活跃时切换默认转场特效为"
AdvSceneSwitcher.transitionTab.entry="从场景 {{scenes}} 切换到场景 {{scenes2}} 时使用转场特效 {{transitions}}"
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="当场景 {{scenes}} 被激活时更改默认转场特效为 {{transitions}}" AdvSceneSwitcher.transitionTab.defaultTransitionEntry="当场景 {{scenes}} 被激活时更改默认转场特效为 {{transitions}}"
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="单击加号添加项目." AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="单击加号添加项目."
AdvSceneSwitcher.transitionTab.defaultTransition.delay="场景更改 {{defTransitionDelay}} 后切换转换特效." AdvSceneSwitcher.transitionTab.defaultTransition.delay="场景更改 {{defTransitionDelay}} 后切换转换特效."
@@ -423,7 +501,6 @@ AdvSceneSwitcher.mediaTab.states.Paused="暂停"
AdvSceneSwitcher.mediaTab.states.stopped="停止" AdvSceneSwitcher.mediaTab.states.stopped="停止"
AdvSceneSwitcher.mediaTab.states.ended="结束" AdvSceneSwitcher.mediaTab.states.ended="结束"
AdvSceneSwitcher.mediaTab.states.error="错误" AdvSceneSwitcher.mediaTab.states.error="错误"
AdvSceneSwitcher.mediaTab.states.playedToEnd="播放结束"
AdvSceneSwitcher.mediaTab.states.any="任意" AdvSceneSwitcher.mediaTab.states.any="任意"
AdvSceneSwitcher.mediaTab.timeRestriction.none="无" AdvSceneSwitcher.mediaTab.timeRestriction.none="无"
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="播放时间小于" AdvSceneSwitcher.mediaTab.timeRestriction.shorter="播放时间小于"
@@ -475,7 +552,7 @@ AdvSceneSwitcher.timeTab.fridays="每周五"
AdvSceneSwitcher.timeTab.saturdays="每周六" AdvSceneSwitcher.timeTab.saturdays="每周六"
AdvSceneSwitcher.timeTab.sundays="每周日" AdvSceneSwitcher.timeTab.sundays="每周日"
AdvSceneSwitcher.timeTab.afterstart="推流或录制开始后" AdvSceneSwitcher.timeTab.afterstart="推流或录制开始后"
AdvSceneSwitcher.timeTab.afterstart.tip="相对于直播或录制开始之后的时间点" AdvSceneSwitcher.timeTab.afterstart.tip="相对于串流或录制开始之后的时间点"
AdvSceneSwitcher.timeTab.entry="{{triggers}} 的 {{time}} 使用转场特效 {{transitions}} 切换到场景 {{scenes}}" AdvSceneSwitcher.timeTab.entry="{{triggers}} 的 {{time}} 使用转场特效 {{transitions}} 切换到场景 {{scenes}}"
AdvSceneSwitcher.timeTab.help="此选项卡将允许您根据当前本地时间自动切换到其他场景。\n\n请注意场景切换程序仅在您指定的确切时间切换场景。\n请确保您已根据自己的喜好在“常规”选项卡上配置了优先级设置以便不会因其他切换而错过选定的时间点具有较高优先级的方法。\n\n单击突出显示的加号继续." AdvSceneSwitcher.timeTab.help="此选项卡将允许您根据当前本地时间自动切换到其他场景。\n\n请注意场景切换程序仅在您指定的确切时间切换场景。\n请确保您已根据自己的喜好在“常规”选项卡上配置了优先级设置以便不会因其他切换而错过选定的时间点具有较高优先级的方法。\n\n单击突出显示的加号继续."
@@ -508,9 +585,8 @@ AdvSceneSwitcher.audioTab.title="音频"
AdvSceneSwitcher.audioTab.condition.above="高于" AdvSceneSwitcher.audioTab.condition.above="高于"
AdvSceneSwitcher.audioTab.condition.below="低于" AdvSceneSwitcher.audioTab.condition.below="低于"
AdvSceneSwitcher.audioTab.ignoreInactiveSource="除非源处于非活动状态" AdvSceneSwitcher.audioTab.ignoreInactiveSource="除非源处于非活动状态"
AdvSceneSwitcher.audioTab.entry="当{{audioSources}} 在 {{condition}} {{volumeWidget}} for {{duration}} 使用转场特效 {{transitions}} 切换到场景 {{scenes}}"
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="如果多个条目匹配 ..." AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="如果多个条目匹配 ..."
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{duration}} 秒切换到 {{scenes}} 使用 {{transitions}}" AdvSceneSwitcher.audioTab.multiMatchfallback="... {{duration}} 秒切换到 {{scenes}} 使用 {{transitions}}"
AdvSceneSwitcher.audioTab.help="此选项卡允许您根据源音量切换场景。\n例如如果麦克风音量达到某个阈值您可以自动切换到其他场景。\n\n单击高亮显示的加号继续." AdvSceneSwitcher.audioTab.help="此选项卡允许您根据源音量切换场景。\n例如如果麦克风音量达到某个阈值您可以自动切换到其他场景。\n\n单击高亮显示的加号继续."
; Video Tab ; Video Tab
@@ -530,7 +606,6 @@ AdvSceneSwitcher.videoTab.help="<html><head/><body><p>此选项卡将允许您
AdvSceneSwitcher.networkTab.title="网络" AdvSceneSwitcher.networkTab.title="网络"
AdvSceneSwitcher.networkTab.description="此选项卡将允许您远程控制另一个OBS实例的活动场景。\n请注意所有OBS实例上的场景名称必须完全匹配." AdvSceneSwitcher.networkTab.description="此选项卡将允许您远程控制另一个OBS实例的活动场景。\n请注意所有OBS实例上的场景名称必须完全匹配."
AdvSceneSwitcher.networkTab.warning="在本地网络之外运行服务器将允许第三方读取活动场景." AdvSceneSwitcher.networkTab.warning="在本地网络之外运行服务器将允许第三方读取活动场景."
AdvSceneSwitcher.networkTab.DisabledWarning="不幸的是在并行运行obs websocket插件时由于库不兼容不得不在macOS上禁用此功能."
AdvSceneSwitcher.networkTab.server="启动服务器(向所有连接的客户端发送场景切换消息)" AdvSceneSwitcher.networkTab.server="启动服务器(向所有连接的客户端发送场景切换消息)"
AdvSceneSwitcher.networkTab.server.port="端口" AdvSceneSwitcher.networkTab.server.port="端口"
AdvSceneSwitcher.networkTab.server.lockToIPv4="锁定为仅使用IPv4将服务器" AdvSceneSwitcher.networkTab.server.lockToIPv4="锁定为仅使用IPv4将服务器"
@@ -602,8 +677,11 @@ AdvSceneSwitcher.hotkey.stopSwitcherHotkey="停止高级场景切换器"
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="切换(启动/停止)高级场景切换器" AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="切换(启动/停止)高级场景切换器"
AdvSceneSwitcher.hotkey.macro.pause="暂停宏 %1" AdvSceneSwitcher.hotkey.macro.pause="暂停宏 %1"
AdvSceneSwitcher.hotkey.macro.unpause="取消暂停宏 %1" AdvSceneSwitcher.hotkey.macro.unpause="取消暂停宏 %1"
AdvSceneSwitcher.hotkey.macro.togglePause="切换暂停宏 %1"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="删除选定的宏"
AdvSceneSwitcher.askBackup="检测到新版本的高级场景切换程序。\n是否要创建旧设置的备份?" AdvSceneSwitcher.askBackup="检测到新版本的高级场景切换程序。\n是否要创建旧设置的备份?"
AdvSceneSwitcher.askForMacro="选择宏 {{macroSelection}}"
AdvSceneSwitcher.close="关闭" AdvSceneSwitcher.close="关闭"
AdvSceneSwitcher.browse="浏览文件" AdvSceneSwitcher.browse="浏览文件"
@@ -621,19 +699,27 @@ AdvSceneSwitcher.selectAudioSource="--选择音频源--"
AdvSceneSwitcher.selectVideoSource="--选择视频源--" AdvSceneSwitcher.selectVideoSource="--选择视频源--"
AdvSceneSwitcher.selectMediaSource="--选择媒体源--" AdvSceneSwitcher.selectMediaSource="--选择媒体源--"
AdvSceneSwitcher.selectProcess="--选择进程--" AdvSceneSwitcher.selectProcess="--选择进程--"
AdvSceneSwitcher.selectFilter="--select filter--" AdvSceneSwitcher.selectFilter="--选择滤镜--"
AdvSceneSwitcher.selectMacro="--选择宏--" AdvSceneSwitcher.selectMacro="--选择宏--"
AdvSceneSwitcher.selectItem="--选择项目--" AdvSceneSwitcher.selectItem="--选择项目--"
AdvSceneSwitcher.selectProfile="--选择配置文件--"
AdvSceneSwitcher.selectSceneCollection="--选择场景集合--"
AdvSceneSwitcher.enterPath="--输入路径--" AdvSceneSwitcher.enterPath="--输入路径--"
AdvSceneSwitcher.enterText="--输入文本--" AdvSceneSwitcher.enterText="--输入文本--"
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="无效项将不会被保存" AdvSceneSwitcher.invaildEntriesWillNotBeSaved="无效项将不会被保存"
AdvSceneSwitcher.selectWindowTip="使用 \"OBS\" 来指定OBS窗口\n使用 \"任务切换\" 来指定 Alt + Tab" AdvSceneSwitcher.selectWindowTip="使用 \"OBS\" 来指定OBS窗口\n使用 \"任务切换\" 来指定 Alt + Tab"
AdvSceneSwitcher.sceneItemSelection.all="全部"
AdvSceneSwitcher.sceneItemSelection.any="任何"
AdvSceneSwitcher.status.active="运行中" AdvSceneSwitcher.status.active="运行中"
AdvSceneSwitcher.status.inactive="已停止" AdvSceneSwitcher.status.inactive="已停止"
AdvSceneSwitcher.running="插件运行" AdvSceneSwitcher.running="插件运行"
AdvSceneSwitcher.stopped="插件停止" AdvSceneSwitcher.stopped="插件停止"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>这似乎是第一次启动高级场景切换程序.<br>请看一下 <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> 查看指南和示例列表.<br>如果有问题在在OBS论坛插件帖子内提问 <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">thread</span></a></p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="此选项卡的开发已停止!请考虑转换为使用宏来代替。\n可以在“常规”选项卡上禁用此提示."
AdvSceneSwitcher.unit.milliseconds="毫秒" AdvSceneSwitcher.unit.milliseconds="毫秒"
AdvSceneSwitcher.unit.secends="秒" AdvSceneSwitcher.unit.secends="秒"
AdvSceneSwitcher.unit.minutes="分钟" AdvSceneSwitcher.unit.minutes="分钟"

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/status-dock.hpp" #include "headers/status-control.hpp"
#include "headers/curl-helper.hpp" #include "headers/curl-helper.hpp"
#include "headers/utility.hpp" #include "headers/utility.hpp"
#include "headers/version.h" #include "headers/version.h"
@@ -20,6 +20,8 @@ SwitcherData *GetSwitcher()
return switcher; return switcher;
} }
AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr;
/****************************************************************************** /******************************************************************************
* Create the Advanced Scene Switcher settings window * Create the Advanced Scene Switcher settings window
******************************************************************************/ ******************************************************************************/
@@ -39,6 +41,7 @@ AdvSceneSwitcher::~AdvSceneSwitcher()
{ {
if (switcher) { if (switcher) {
switcher->settingsWindowOpened = false; switcher->settingsWindowOpened = false;
switcher->lastOpenedTab = ui->tabWidget->currentIndex();
} }
} }
@@ -85,7 +88,9 @@ void AdvSceneSwitcher::loadUI()
setupTriggerTab(); setupTriggerTab();
setupMacroTab(); setupMacroTab();
setDeprecationWarnings();
setTabOrder(); setTabOrder();
setCurrentTab();
restoreWindowGeo(); restoreWindowGeo();
checkFirstTimeSetup(); checkFirstTimeSetup();
@@ -340,7 +345,7 @@ bool SwitcherData::checkForMatch(OBSWeakSource &scene,
return match; return match;
} }
void switchScene(const sceneSwitchInfo &sceneSwitch) void switchScene(const sceneSwitchInfo &sceneSwitch, bool force)
{ {
if (!sceneSwitch.scene && switcher->verbose) { if (!sceneSwitch.scene && switcher->verbose) {
blog(LOG_INFO, "nothing to switch to"); blog(LOG_INFO, "nothing to switch to");
@@ -350,7 +355,7 @@ void switchScene(const sceneSwitchInfo &sceneSwitch)
obs_source_t *source = obs_weak_source_get_source(sceneSwitch.scene); obs_source_t *source = obs_weak_source_get_source(sceneSwitch.scene);
obs_source_t *currentSource = obs_frontend_get_current_scene(); obs_source_t *currentSource = obs_frontend_get_current_scene();
if (source && source != currentSource) { if (source && (source != currentSource || force)) {
transitionData currentTransitionData; transitionData currentTransitionData;
setNextTransition(sceneSwitch, currentSource, setNextTransition(sceneSwitch, currentSource,
currentTransitionData); currentTransitionData);
@@ -432,13 +437,6 @@ void SwitcherData::Stop()
server.stop(); server.stop();
client.disconnect(); client.disconnect();
for (auto &t : audioHelperThreads) {
if (t.joinable()) {
t.join();
}
}
audioHelperThreads.clear();
if (showSystemTrayNotifications) { if (showSystemTrayNotifications) {
DisplayTrayMessage( DisplayTrayMessage(
obs_module_text("AdvSceneSwitcher.pluginName"), obs_module_text("AdvSceneSwitcher.pluginName"),
@@ -574,6 +572,18 @@ void setStreamStopping()
std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::now();
} }
void handleExit()
{
switcher->obsIsShuttingDown = true;
if (switcher && switcher->obsIsShuttingDown &&
switcher->shutdownConditionCount) {
switcher->Stop();
switcher->checkMacros();
switcher->runMacros();
}
FreeSceneSwitcher();
}
// Note to future self: // Note to future self:
// be careful using switcher->m here as there is potential for deadlocks when using // be careful using switcher->m here as there is potential for deadlocks when using
// frontend functions such as obs_frontend_set_current_scene() // frontend functions such as obs_frontend_set_current_scene()
@@ -585,7 +595,7 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
switch (event) { switch (event) {
case OBS_FRONTEND_EVENT_EXIT: case OBS_FRONTEND_EVENT_EXIT:
FreeSceneSwitcher(); handleExit();
break; break;
case OBS_FRONTEND_EVENT_SCENE_CHANGED: case OBS_FRONTEND_EVENT_SCENE_CHANGED:
handleSceneChange(); handleSceneChange();
@@ -626,26 +636,32 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
void LoadPlugins() void LoadPlugins()
{ {
QFileInfo fi(obs_get_module_binary_path(obs_current_module())); QFileInfo libPath(
QDirIterator it(fi.absolutePath(), QStringList() << "*.so", QString(obs_get_module_binary_path(obs_current_module())));
QDir::Files); QString pluginDir(libPath.absolutePath() + "/adv-ss-plugins");
#ifdef _WIN32
QString libPattern = "*.dll";
SetDllDirectory(pluginDir.toStdWString().c_str());
#else
QString libPattern = "*.so";
#endif
QDirIterator it(pluginDir, QStringList() << libPattern, QDir::Files);
while (it.hasNext()) { while (it.hasNext()) {
auto file = it.next(); auto file = it.next();
if (it.fileName() == "advanced-scene-switcher.so") {
continue;
}
blog(LOG_INFO, "attempting to load \"%s\"", blog(LOG_INFO, "attempting to load \"%s\"",
file.toStdString().c_str()); file.toStdString().c_str());
auto lib = new QLibrary(file, nullptr); auto lib = new QLibrary(file, nullptr);
if (lib->load()) { if (lib->load()) {
blog(LOG_INFO, "successfully loaded \"%s\"", blog(LOG_INFO, "successfully loaded \"%s\"",
file.toStdString().c_str()); file.toStdString().c_str());
} else {
blog(LOG_WARNING, "failed to load \"%s\": %s",
file.toStdString().c_str(),
lib->errorString().toStdString().c_str());
} }
} }
} }
AdvSceneSwitcher *ssWindow;
extern "C" void InitSceneSwitcher() extern "C" void InitSceneSwitcher()
{ {
blog(LOG_INFO, "version: %s", g_GIT_TAG); blog(LOG_INFO, "version: %s", g_GIT_TAG);
@@ -658,26 +674,21 @@ extern "C" void InitSceneSwitcher()
} }
PlatformInit(); PlatformInit();
#if !defined(_WIN32)
// Windows does not require the plugins to be loaded manually
LoadPlugins(); LoadPlugins();
#endif SetupDock();
dock = new StatusDock(
static_cast<QMainWindow *>(obs_frontend_get_main_window()));
obs_frontend_add_dock(dock);
auto cb = []() { auto cb = []() {
if (switcher->settingsWindowOpened) { if (switcher->settingsWindowOpened) {
ssWindow->show(); AdvSceneSwitcher::window->show();
ssWindow->raise(); AdvSceneSwitcher::window->raise();
ssWindow->activateWindow(); AdvSceneSwitcher::window->activateWindow();
} else { } else {
ssWindow = AdvSceneSwitcher::window =
new AdvSceneSwitcher(static_cast<QMainWindow *>( new AdvSceneSwitcher(static_cast<QMainWindow *>(
obs_frontend_get_main_window())); obs_frontend_get_main_window()));
ssWindow->setAttribute(Qt::WA_DeleteOnClose); AdvSceneSwitcher::window->setAttribute(
ssWindow->show(); Qt::WA_DeleteOnClose);
AdvSceneSwitcher::window->show();
} }
}; };

View File

@@ -1,3 +1,57 @@
# Helper function to install plugins to correct location
function(install_advss_plugin target)
set(plugin_folder "adv-ss-plugins")
if(APPLE)
set(_bit_suffix "")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_bit_suffix "64bit/")
else()
set(_bit_suffix "32bit/")
endif()
set_target_properties(${target} PROPERTIES PREFIX "")
install(
TARGETS ${target}
LIBRARY DESTINATION "${OBS_PLUGIN_DESTINATION}/${plugin_folder}"
RUNTIME DESTINATION "${OBS_PLUGIN_DESTINATION}/${plugin_folder}")
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND
"${CMAKE_COMMAND}" -E copy "$<TARGET_FILE:${target}>"
"${OBS_OUTPUT_DIR}/$<CONFIGURATION>/obs-plugins/${_bit_suffix}/${plugin_folder}/$<TARGET_FILE_NAME:${target}>"
VERBATIM)
if(DEFINED ENV{obsInstallerTempDir})
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND
"${CMAKE_COMMAND}" -E copy "$<TARGET_FILE:${target}>"
"$ENV{obsInstallerTempDir}/${OBS_PLUGIN_DESTINATION}/${plugin_folder}/$<TARGET_FILE_NAME:${target}>"
VERBATIM)
endif()
if(MSVC)
obs_debug_copy_helper(
${target}
"${OBS_OUTPUT_DIR}/$<CONFIGURATION>/obs-plugins/${_bit_suffix}/${plugin_folder}"
)
if(DEFINED ENV{obsInstallerTempDir})
obs_debug_copy_helper(
${target}
"$ENV{obsInstallerTempDir}/${OBS_PLUGIN_DESTINATION}/${plugin_folder}")
endif()
install(
DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/pdbs/"
DESTINATION "${OBS_PLUGIN_DESTINATION}/${plugin_folder}"
CONFIGURATIONS Debug RelWithDebInfo)
endif()
endfunction()
# Add macro conditions or actions which have dependencies to external libraries # Add macro conditions or actions which have dependencies to external libraries
# or other components which might potentially not be fulfilled by the user and # or other components which might potentially not be fulfilled by the user and
# thus cause issues. # thus cause issues.

View File

@@ -16,7 +16,19 @@ else()
endif() endif()
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../headers") include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../headers")
set(module_SOURCES macro-condition-video.cpp macro-condition-video.hpp) set(module_SOURCES
area-selection.cpp
area-selection.hpp
macro-condition-video.cpp
macro-condition-video.hpp
opencv-helpers.cpp
opencv-helpers.hpp
preview-dialog.cpp
preview-dialog.hpp
threshold-slider.cpp
threshold-slider.hpp
video-selection.cpp
video-selection.hpp)
add_library(advanced-scene-switcher-opencv MODULE ${module_SOURCES}) add_library(advanced-scene-switcher-opencv MODULE ${module_SOURCES})
if(BUILD_OUT_OF_TREE) if(BUILD_OUT_OF_TREE)
@@ -34,8 +46,9 @@ if(BUILD_OUT_OF_TREE)
set(LIB_OUT_DIR "/lib/obs-plugins") set(LIB_OUT_DIR "/lib/obs-plugins")
endif() endif()
set_target_properties(advanced-scene-switcher-opencv PROPERTIES PREFIX "") set_target_properties(advanced-scene-switcher-opencv PROPERTIES PREFIX "")
install(TARGETS advanced-scene-switcher-opencv install(
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR}) TARGETS advanced-scene-switcher-opencv
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR}/adv-ss-plugins)
endif() endif()
else() else()
target_link_libraries( target_link_libraries(
@@ -46,5 +59,5 @@ else()
Qt5::Core Qt5::Core
Qt5::Widgets Qt5::Widgets
libobs) libobs)
install_obs_plugin(advanced-scene-switcher-opencv) install_advss_plugin(advanced-scene-switcher-opencv)
endif() endif()

View File

@@ -0,0 +1,130 @@
#include "area-selection.hpp"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
#include <obs-module.h>
void advss::Size::Save(obs_data_t *obj, const char *name)
{
auto data = obs_data_create();
obs_data_set_int(data, "width", width);
obs_data_set_int(data, "height", height);
obs_data_set_obj(obj, name, data);
obs_data_release(data);
}
void advss::Size::Load(obs_data_t *obj, const char *name)
{
auto data = obs_data_get_obj(obj, name);
width = obs_data_get_int(data, "width");
height = obs_data_get_int(data, "height");
obs_data_release(data);
}
cv::Size advss::Size::CV()
{
return {width, height};
}
void advss::Area::Save(obs_data_t *obj, const char *name)
{
auto data = obs_data_create();
obs_data_set_int(data, "x", x);
obs_data_set_int(data, "y", y);
obs_data_set_int(data, "width", width);
obs_data_set_int(data, "height", height);
obs_data_set_obj(obj, name, data);
obs_data_release(data);
}
void advss::Area::Load(obs_data_t *obj, const char *name)
{
auto data = obs_data_get_obj(obj, name);
x = obs_data_get_int(data, "x");
y = obs_data_get_int(data, "y");
width = obs_data_get_int(data, "width");
height = obs_data_get_int(data, "height");
obs_data_release(data);
}
SizeSelection::SizeSelection(int min, int max, QWidget *parent)
: QWidget(parent), _x(new QSpinBox), _y(new QSpinBox)
{
_x->setMinimum(min);
_y->setMinimum(min);
_x->setMaximum(max);
_y->setMaximum(max);
connect(_x, SIGNAL(valueChanged(int)), this, SLOT(XChanged(int)));
connect(_y, SIGNAL(valueChanged(int)), this, SLOT(YChanged(int)));
auto layout = new QHBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(_x);
layout->addWidget(new QLabel(" x "));
layout->addWidget(_y);
setLayout(layout);
}
void SizeSelection::SetSize(const advss::Size &s)
{
_x->setValue(s.width);
_y->setValue(s.height);
}
advss::Size SizeSelection::Size()
{
return advss::Size{_x->value(), _y->value()};
}
void SizeSelection::XChanged(int value)
{
emit SizeChanged(advss::Size{value, _y->value()});
}
void SizeSelection::YChanged(int value)
{
emit SizeChanged(advss::Size{_x->value(), value});
}
AreaSelection::AreaSelection(int min, int max, QWidget *parent)
: QWidget(parent),
_x(new SizeSelection(min, max)),
_y(new SizeSelection(min, max))
{
_x->_x->setToolTip("X");
_x->_y->setToolTip("Y");
_y->_x->setToolTip(
obs_module_text("AdvSceneSwitcher.condition.video.width"));
_y->_y->setToolTip(
obs_module_text("AdvSceneSwitcher.condition.video.height"));
connect(_x, SIGNAL(SizeChanged(advss::Size)), this,
SLOT(XSizeChanged(advss::Size)));
connect(_y, SIGNAL(SizeChanged(advss::Size)), this,
SLOT(YSizeChanged(advss::Size)));
auto layout = new QVBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(_x);
layout->addWidget(_y);
setLayout(layout);
}
void AreaSelection::SetArea(const advss::Area &value)
{
_x->SetSize({value.x, value.y});
_y->SetSize({value.width, value.height});
}
void AreaSelection::XSizeChanged(advss::Size value)
{
emit AreaChanged(advss::Area{value.width, value.height,
_y->Size().width, _y->Size().height});
}
void AreaSelection::YSizeChanged(advss::Size value)
{
emit AreaChanged(advss::Area{_x->Size().width, _x->Size().height,
value.width, value.height});
}

View File

@@ -0,0 +1,67 @@
#pragma once
#include <QWidget>
#include <QSpinBox>
#include <obs-data.h>
#include <opencv2/opencv.hpp>
namespace advss {
struct Size {
void Save(obs_data_t *obj, const char *name);
void Load(obs_data_t *obj, const char *name);
cv::Size CV();
int width;
int height;
};
struct Area {
void Save(obs_data_t *obj, const char *name);
void Load(obs_data_t *obj, const char *name);
int x;
int y;
int width;
int height;
};
}
class SizeSelection : public QWidget {
Q_OBJECT
public:
SizeSelection(int min, int max, QWidget *parent = 0);
void SetSize(const advss::Size &);
advss::Size Size();
private slots:
void XChanged(int);
void YChanged(int);
signals:
void SizeChanged(advss::Size value);
private:
QSpinBox *_x;
QSpinBox *_y;
friend class AreaSelection;
};
class AreaSelection : public QWidget {
Q_OBJECT
public:
AreaSelection(int min, int max, QWidget *parent = 0);
void SetArea(const advss::Area &);
private slots:
void XSizeChanged(advss::Size value);
void YSizeChanged(advss::Size value);
signals:
void AreaChanged(advss::Area value);
private:
SizeSelection *_x;
SizeSelection *_y;
};

View File

@@ -72,33 +72,37 @@ bool MacroConditionVideo::CheckShouldBeSkipped()
bool MacroConditionVideo::CheckCondition() bool MacroConditionVideo::CheckCondition()
{ {
bool match = false; if (!_video.ValidSelection()) {
return false;
}
bool match = false;
if (CheckShouldBeSkipped()) { if (CheckShouldBeSkipped()) {
return _lastMatchResult; return _lastMatchResult;
} }
if (_screenshotData && _screenshotData->done) { if (_screenshotData.done) {
match = Compare(); match = Compare();
_lastMatchResult = match; _lastMatchResult = match;
if (!requiresFileInput(_condition)) { if (!requiresFileInput(_condition)) {
_matchImage = std::move(_screenshotData->image); _matchImage = std::move(_screenshotData.image);
} }
_screenshotData.reset(nullptr); _getNextScreenshot = true;
} else { } else {
match = _lastMatchResult; match = _lastMatchResult;
} }
GetScreenshot(); if (_getNextScreenshot) {
GetScreenshot();
}
return match; return match;
} }
bool MacroConditionVideo::Save(obs_data_t *obj) bool MacroConditionVideo::Save(obs_data_t *obj)
{ {
MacroCondition::Save(obj); MacroCondition::Save(obj);
obs_data_set_string(obj, "videoSource", _video.Save(obj);
GetWeakSourceName(_videoSource).c_str());
obs_data_set_int(obj, "condition", static_cast<int>(_condition)); obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_string(obj, "filePath", _file.c_str()); obs_data_set_string(obj, "filePath", _file.c_str());
obs_data_set_bool(obj, "usePatternForChangedCheck", obs_data_set_bool(obj, "usePatternForChangedCheck",
@@ -108,12 +112,12 @@ bool MacroConditionVideo::Save(obs_data_t *obj)
obs_data_set_string(obj, "modelDataPath", _modelDataPath.c_str()); obs_data_set_string(obj, "modelDataPath", _modelDataPath.c_str());
obs_data_set_double(obj, "scaleFactor", _scaleFactor); obs_data_set_double(obj, "scaleFactor", _scaleFactor);
obs_data_set_int(obj, "minNeighbors", _minNeighbors); obs_data_set_int(obj, "minNeighbors", _minNeighbors);
obs_data_set_int(obj, "minSizeX", _minSizeX); _minSize.Save(obj, "minSize");
obs_data_set_int(obj, "minSizeY", _minSizeY); _maxSize.Save(obj, "maxSize");
obs_data_set_int(obj, "maxSizeX", _maxSizeX);
obs_data_set_int(obj, "maxSizeY", _maxSizeY);
obs_data_set_bool(obj, "throttleEnabled", _throttleEnabled); obs_data_set_bool(obj, "throttleEnabled", _throttleEnabled);
obs_data_set_int(obj, "throttleCount", _throttleCount); obs_data_set_int(obj, "throttleCount", _throttleCount);
obs_data_set_bool(obj, "checkAreaEnabled", _checkAreaEnable);
_checkArea.Save(obj, "checkArea");
return true; return true;
} }
@@ -131,8 +135,10 @@ bool isMinNeighborsValid(int minNeighbors)
bool MacroConditionVideo::Load(obs_data_t *obj) bool MacroConditionVideo::Load(obs_data_t *obj)
{ {
MacroCondition::Load(obj); MacroCondition::Load(obj);
const char *videoSourceName = obs_data_get_string(obj, "videoSource"); _video.Load(obj);
_videoSource = GetWeakSourceByName(videoSourceName); if (obs_data_has_user_value(obj, "videoSource")) {
_video.Load(obj, "videoSource");
}
_condition = _condition =
static_cast<VideoCondition>(obs_data_get_int(obj, "condition")); static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
_file = obs_data_get_string(obj, "filePath"); _file = obs_data_get_string(obj, "filePath");
@@ -149,13 +155,20 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
if (!isMinNeighborsValid(_minNeighbors)) { if (!isMinNeighborsValid(_minNeighbors)) {
_minNeighbors = minMinNeighbors; _minNeighbors = minMinNeighbors;
} }
_minSizeX = obs_data_get_int(obj, "minSizeX"); // TODO: Remove this fallback in future version
_minSizeY = obs_data_get_int(obj, "minSizeY"); if (obs_data_has_user_value(obj, "minSizeX")) {
_maxSizeX = obs_data_get_int(obj, "maxSizeX"); _minSize.width = obs_data_get_int(obj, "minSizeX");
_maxSizeY = obs_data_get_int(obj, "maxSizeY"); _minSize.height = obs_data_get_int(obj, "minSizeY");
_maxSize.width = obs_data_get_int(obj, "maxSizeX");
_maxSize.height = obs_data_get_int(obj, "maxSizeY");
} else {
_minSize.Load(obj, "minSize");
_maxSize.Load(obj, "maxSize");
}
_throttleEnabled = obs_data_get_bool(obj, "throttleEnabled"); _throttleEnabled = obs_data_get_bool(obj, "throttleEnabled");
_throttleCount = obs_data_get_int(obj, "throttleCount"); _throttleCount = obs_data_get_int(obj, "throttleCount");
_checkAreaEnable = obs_data_get_bool(obj, "checkAreaEnabled");
_checkArea.Load(obj, "checkArea");
if (requiresFileInput(_condition)) { if (requiresFileInput(_condition)) {
(void)LoadImageFromFile(); (void)LoadImageFromFile();
} }
@@ -169,55 +182,16 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
std::string MacroConditionVideo::GetShortDesc() std::string MacroConditionVideo::GetShortDesc()
{ {
if (_videoSource) { return _video.ToString();
return GetWeakSourceName(_videoSource);
}
return "";
} }
void MacroConditionVideo::GetScreenshot() void MacroConditionVideo::GetScreenshot()
{ {
auto source = obs_weak_source_get_source(_videoSource); auto source = obs_weak_source_get_source(_video.GetVideo());
_screenshotData = std::make_unique<AdvSSScreenshotObj>(source); _screenshotData.~ScreenshotHelper();
new (&_screenshotData) ScreenshotHelper(source);
obs_source_release(source); obs_source_release(source);
} _getNextScreenshot = false;
// Assumption is that QImage uses Format_RGBA8888.
// Conversion from: https://github.com/dbzhang800/QtOpenCV
cv::Mat QImageToMat(const QImage &img)
{
if (img.isNull()) {
return cv::Mat();
}
return cv::Mat(img.height(), img.width(), CV_8UC(img.depth() / 8),
(uchar *)img.bits(), img.bytesPerLine());
}
QImage MatToQImage(const cv::Mat &mat)
{
if (mat.empty()) {
return QImage();
}
return QImage(mat.data, mat.cols, mat.rows, mat.step,
QImage::Format::Format_RGBA8888);
}
PatternMatchData createPatternData(QImage &pattern)
{
PatternMatchData data;
if (pattern.isNull()) {
return data;
}
data.rgbaPattern = QImageToMat(pattern);
std::vector<cv::Mat1b> rgbaChannelsPattern;
cv::split(data.rgbaPattern, rgbaChannelsPattern);
std::vector<cv::Mat1b> rgbChanlesPattern(
rgbaChannelsPattern.begin(), rgbaChannelsPattern.begin() + 3);
cv::merge(rgbChanlesPattern, data.rgbPattern);
cv::threshold(rgbaChannelsPattern[3], data.mask, 0, 255,
cv::THRESH_BINARY);
return data;
} }
bool MacroConditionVideo::LoadImageFromFile() bool MacroConditionVideo::LoadImageFromFile()
@@ -241,51 +215,10 @@ bool MacroConditionVideo::LoadModelData(std::string &path)
return !_objectCascade.empty(); return !_objectCascade.empty();
} }
void matchPattern(QImage &img, PatternMatchData &patternData, double threshold,
cv::Mat &result, bool useAlphaAsMask = true)
{
if (img.isNull() || patternData.rgbaPattern.empty()) {
return;
}
if (img.height() < patternData.rgbaPattern.rows ||
img.width() < patternData.rgbaPattern.cols) {
return;
}
auto i = QImageToMat(img);
if (useAlphaAsMask) {
std::vector<cv::Mat1b> rgbaChannelsImage;
cv::split(i, rgbaChannelsImage);
std::vector<cv::Mat1b> rgbChanlesImage(
rgbaChannelsImage.begin(),
rgbaChannelsImage.begin() + 3);
cv::Mat3b rgbImage;
cv::merge(rgbChanlesImage, rgbImage);
cv::matchTemplate(rgbImage, patternData.rgbPattern, result,
cv::TM_CCORR_NORMED, patternData.mask);
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
} else {
cv::matchTemplate(i, patternData.rgbaPattern, result,
cv::TM_CCOEFF_NORMED);
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
}
}
void matchPattern(QImage &img, QImage &pattern, double threshold,
cv::Mat &result, bool useAlphaAsMask)
{
auto data = createPatternData(pattern);
matchPattern(img, data, threshold, result, useAlphaAsMask);
}
bool MacroConditionVideo::ScreenshotContainsPattern() bool MacroConditionVideo::ScreenshotContainsPattern()
{ {
cv::Mat result; cv::Mat result;
matchPattern(_screenshotData->image, _patternData, _patternThreshold, matchPattern(_screenshotData.image, _patternData, _patternThreshold,
result, _useAlphaAsMask); result, _useAlphaAsMask);
return countNonZero(result) > 0; return countNonZero(result) > 0;
} }
@@ -295,53 +228,40 @@ bool MacroConditionVideo::OutputChanged()
if (_usePatternForChangedCheck) { if (_usePatternForChangedCheck) {
cv::Mat result; cv::Mat result;
_patternData = createPatternData(_matchImage); _patternData = createPatternData(_matchImage);
matchPattern(_screenshotData->image, _patternData, matchPattern(_screenshotData.image, _patternData,
_patternThreshold, result, _useAlphaAsMask); _patternThreshold, result, _useAlphaAsMask);
return countNonZero(result) == 0; return countNonZero(result) == 0;
} }
return _screenshotData->image != _matchImage; return _screenshotData.image != _matchImage;
}
std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
double scaleFactor, int minNeighbors,
cv::Size minSize, cv::Size maxSize)
{
if (img.isNull() || cascade.empty()) {
return {};
}
auto i = QImageToMat(img);
cv::Mat frameGray;
cv::cvtColor(i, frameGray, cv::COLOR_BGR2GRAY);
equalizeHist(frameGray, frameGray);
std::vector<cv::Rect> objects;
cascade.detectMultiScale(frameGray, objects, scaleFactor, minNeighbors,
0, minSize, maxSize);
return objects;
} }
bool MacroConditionVideo::ScreenshotContainsObject() bool MacroConditionVideo::ScreenshotContainsObject()
{ {
auto objects = matchObject(_screenshotData->image, _objectCascade, auto objects = matchObject(_screenshotData.image, _objectCascade,
_scaleFactor, _minNeighbors, _scaleFactor, _minNeighbors, _minSize.CV(),
{_minSizeX, _minSizeY}, _maxSize.CV());
{_maxSizeX, _maxSizeY});
return objects.size() > 0; return objects.size() > 0;
} }
bool MacroConditionVideo::Compare() bool MacroConditionVideo::Compare()
{ {
if (_checkAreaEnable && _condition != VideoCondition::NO_IMAGE) {
_screenshotData.image = _screenshotData.image.copy(
_checkArea.x, _checkArea.y, _checkArea.width,
_checkArea.height);
}
switch (_condition) { switch (_condition) {
case VideoCondition::MATCH: case VideoCondition::MATCH:
return _screenshotData->image == _matchImage; return _screenshotData.image == _matchImage;
case VideoCondition::DIFFER: case VideoCondition::DIFFER:
return _screenshotData->image != _matchImage; return _screenshotData.image != _matchImage;
case VideoCondition::HAS_CHANGED: case VideoCondition::HAS_CHANGED:
return OutputChanged(); return OutputChanged();
case VideoCondition::HAS_NOT_CHANGED: case VideoCondition::HAS_NOT_CHANGED:
return !OutputChanged(); return !OutputChanged();
case VideoCondition::NO_IMAGE: case VideoCondition::NO_IMAGE:
return _screenshotData->image.isNull(); return _screenshotData.image.isNull();
case VideoCondition::PATTERN: case VideoCondition::PATTERN:
return ScreenshotContainsPattern(); return ScreenshotContainsPattern();
case VideoCondition::OBJECT: case VideoCondition::OBJECT:
@@ -359,108 +279,62 @@ static inline void populateConditionSelection(QComboBox *list)
} }
} }
ThresholdSlider::ThresholdSlider(double min, double max, const QString &label,
const QString &description, QWidget *parent)
: QWidget(parent)
{
_slider = new QSlider();
_slider->setOrientation(Qt::Horizontal);
_slider->setRange(min * _scale, max * _scale);
_value = new QLabel();
QString labelText = label + QString("0.");
for (int i = 0; i < _precision; i++) {
labelText.append(QString("0"));
}
_value->setText(labelText);
connect(_slider, SIGNAL(valueChanged(int)), this,
SLOT(NotifyValueChanged(int)));
QVBoxLayout *mainLayout = new QVBoxLayout();
QHBoxLayout *sliderLayout = new QHBoxLayout();
sliderLayout->addWidget(_value);
sliderLayout->addWidget(_slider);
mainLayout->addLayout(sliderLayout);
if (!description.isEmpty()) {
mainLayout->addWidget(new QLabel(description));
}
mainLayout->setContentsMargins(0, 0, 0, 0);
setLayout(mainLayout);
}
void ThresholdSlider::SetDoubleValue(double value)
{
_slider->setValue(value * _scale);
SetDoubleValueText(value);
}
void ThresholdSlider::NotifyValueChanged(int value)
{
double doubleValue = value / _scale;
SetDoubleValueText(doubleValue);
emit DoubleValueChanged(doubleValue);
}
void ThresholdSlider::SetDoubleValueText(double value)
{
QString labelText = _value->text();
labelText.chop(_precision + 2); // 2 for the part left of the "."
labelText.append(QString::number(value, 'f', _precision));
_value->setText(labelText);
}
MacroConditionVideoEdit::MacroConditionVideoEdit( MacroConditionVideoEdit::MacroConditionVideoEdit(
QWidget *parent, std::shared_ptr<MacroConditionVideo> entryData) QWidget *parent, std::shared_ptr<MacroConditionVideo> entryData)
: QWidget(parent) : QWidget(parent),
_videoSelection(new VideoSelectionWidget(this)),
_condition(new QComboBox()),
_usePatternForChangedCheck(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck"))),
_imagePath(new FileSelection()),
_patternThreshold(new ThresholdSlider(
0., 1.,
obs_module_text(
"AdvSceneSwitcher.condition.video.patternThreshold"),
obs_module_text(
"AdvSceneSwitcher.condition.video.patternThresholdDescription"))),
_useAlphaAsMask(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask"))),
_modelDataPath(new FileSelection()),
_modelPathLayout(new QHBoxLayout),
_objectScaleThreshold(new ThresholdSlider(
1.1, 5.,
obs_module_text(
"AdvSceneSwitcher.condition.video.objectScaleThreshold"),
obs_module_text(
"AdvSceneSwitcher.condition.video.objectScaleThresholdDescription"))),
_neighborsControlLayout(new QHBoxLayout),
_minNeighbors(new QSpinBox()),
_minNeighborsDescription(new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.video.minNeighborDescription"))),
_sizeLayout(new QHBoxLayout()),
_minSize(new SizeSelection(0, 1024)),
_maxSize(new SizeSelection(0, 4096)),
_checkAreaControlLayout(new QHBoxLayout),
_checkAreaEnable(new QCheckBox()),
_checkArea(new AreaSelection(0, 99999)),
_selectArea(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.video.selectArea"))),
_throttleControlLayout(new QHBoxLayout),
_throttleEnable(new QCheckBox()),
_throttleCount(new QSpinBox()),
_showMatch(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.video.showMatch"))),
_previewDialog(this, entryData.get(), &GetSwitcher()->m)
{ {
_videoSelection = new QComboBox();
_condition = new QComboBox();
_imagePath = new FileSelection();
_imagePath->Button()->disconnect(); _imagePath->Button()->disconnect();
_usePatternForChangedCheck = new QCheckBox(obs_module_text(
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck"));
_usePatternForChangedCheck->setToolTip(obs_module_text( _usePatternForChangedCheck->setToolTip(obs_module_text(
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip")); "AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip"));
_patternThreshold = new ThresholdSlider(
0., 1.,
obs_module_text(
"AdvSceneSwitcher.condition.video.patternThreshold"),
obs_module_text(
"AdvSceneSwitcher.condition.video.patternThresholdDescription"));
_useAlphaAsMask = new QCheckBox(obs_module_text(
"AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask"));
_modelDataPath = new FileSelection();
_objectScaleThreshold = new ThresholdSlider(
1.1, 5.,
obs_module_text(
"AdvSceneSwitcher.condition.video.objectScaleThreshold"),
obs_module_text(
"AdvSceneSwitcher.condition.video.objectScaleThresholdDescription"));
_minNeighbors = new QSpinBox();
_minNeighbors->setMinimum(minMinNeighbors); _minNeighbors->setMinimum(minMinNeighbors);
_minNeighbors->setMaximum(maxMinNeighbors); _minNeighbors->setMaximum(maxMinNeighbors);
_minNeighborsDescription = new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.video.minNeighborDescription"));
_minSizeX = new QSpinBox();
_minSizeY = new QSpinBox();
_minSizeX->setMaximum(1024);
_minSizeY->setMaximum(1024);
_maxSizeX = new QSpinBox();
_maxSizeY = new QSpinBox();
_maxSizeX->setMaximum(4096);
_maxSizeY->setMaximum(4096);
_throttleEnable = new QCheckBox();
_throttleCount = new QSpinBox();
_throttleCount->setMinimum(1 * GetSwitcher()->interval); _throttleCount->setMinimum(1 * GetSwitcher()->interval);
_throttleCount->setMaximum(10 * GetSwitcher()->interval); _throttleCount->setMaximum(10 * GetSwitcher()->interval);
_throttleCount->setSingleStep(GetSwitcher()->interval); _throttleCount->setSingleStep(GetSwitcher()->interval);
_showMatch = new QPushButton(
obs_module_text("AdvSceneSwitcher.condition.video.showMatch"));
QWidget::connect(_videoSelection, QWidget::connect(_videoSelection,
SIGNAL(currentTextChanged(const QString &)), this, SIGNAL(VideoSelectionChange(const VideoSelection &)),
SLOT(SourceChanged(const QString &))); this,
SLOT(VideoSelectionChanged(const VideoSelection &)));
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int))); SLOT(ConditionChanged(int)));
QWidget::connect(_imagePath, SIGNAL(PathChanged(const QString &)), this, QWidget::connect(_imagePath, SIGNAL(PathChanged(const QString &)), this,
@@ -478,14 +352,14 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
SLOT(ObjectScaleThresholdChanged(double))); SLOT(ObjectScaleThresholdChanged(double)));
QWidget::connect(_minNeighbors, SIGNAL(valueChanged(int)), this, QWidget::connect(_minNeighbors, SIGNAL(valueChanged(int)), this,
SLOT(MinNeighborsChanged(int))); SLOT(MinNeighborsChanged(int)));
QWidget::connect(_minSizeX, SIGNAL(valueChanged(int)), this, QWidget::connect(_minSize, SIGNAL(SizeChanged(advss::Size)), this,
SLOT(MinSizeXChanged(int))); SLOT(MinSizeChanged(advss::Size)));
QWidget::connect(_minSizeY, SIGNAL(valueChanged(int)), this, QWidget::connect(_maxSize, SIGNAL(SizeChanged(advss::Size)), this,
SLOT(MinSizeYChanged(int))); SLOT(MaxSizeChanged(advss::Size)));
QWidget::connect(_maxSizeX, SIGNAL(valueChanged(int)), this, QWidget::connect(_checkAreaEnable, SIGNAL(stateChanged(int)), this,
SLOT(MaxSizeXChanged(int))); SLOT(CheckAreaEnableChanged(int)));
QWidget::connect(_maxSizeY, SIGNAL(valueChanged(int)), this, QWidget::connect(_checkArea, SIGNAL(AreaChanged(advss::Area)), this,
SLOT(MaxSizeYChanged(int))); SLOT(CheckAreaChanged(advss::Area)));
QWidget::connect(_modelDataPath, SIGNAL(PathChanged(const QString &)), QWidget::connect(_modelDataPath, SIGNAL(PathChanged(const QString &)),
this, SLOT(ModelPathChanged(const QString &))); this, SLOT(ModelPathChanged(const QString &)));
QWidget::connect(_throttleEnable, SIGNAL(stateChanged(int)), this, QWidget::connect(_throttleEnable, SIGNAL(stateChanged(int)), this,
@@ -494,8 +368,11 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
SLOT(ThrottleCountChanged(int))); SLOT(ThrottleCountChanged(int)));
QWidget::connect(_showMatch, SIGNAL(clicked()), this, QWidget::connect(_showMatch, SIGNAL(clicked()), this,
SLOT(ShowMatchClicked())); SLOT(ShowMatchClicked()));
QWidget::connect(&_previewDialog, SIGNAL(SelectionAreaChanged(QRect)),
this, SLOT(CheckAreaChanged(QRect)));
QWidget::connect(_selectArea, SIGNAL(clicked()), this,
SLOT(SelectAreaClicked()));
populateVideoSelection(_videoSelection);
populateConditionSelection(_condition); populateConditionSelection(_condition);
QHBoxLayout *entryLine1Layout = new QHBoxLayout; QHBoxLayout *entryLine1Layout = new QHBoxLayout;
@@ -504,43 +381,47 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
{"{{condition}}", _condition}, {"{{condition}}", _condition},
{"{{imagePath}}", _imagePath}, {"{{imagePath}}", _imagePath},
{"{{minNeighbors}}", _minNeighbors}, {"{{minNeighbors}}", _minNeighbors},
{"{{minSizeX}}", _minSizeX}, {"{{minSize}}", _minSize},
{"{{minSizeY}}", _minSizeY}, {"{{maxSize}}", _maxSize},
{"{{maxSizeX}}", _maxSizeX},
{"{{maxSizeY}}", _maxSizeY},
{"{{modelDataPath}}", _modelDataPath}, {"{{modelDataPath}}", _modelDataPath},
{"{{throttleEnable}}", _throttleEnable}, {"{{throttleEnable}}", _throttleEnable},
{"{{throttleCount}}", _throttleCount}, {"{{throttleCount}}", _throttleCount},
{"{{checkAreaEnable}}", _checkAreaEnable},
{"{{checkArea}}", _checkArea},
{"{{selectArea}}", _selectArea},
}; };
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.video.entry"), placeWidgets(obs_module_text("AdvSceneSwitcher.condition.video.entry"),
entryLine1Layout, widgetPlaceholders); entryLine1Layout, widgetPlaceholders);
_modelPathLayout = new QHBoxLayout;
placeWidgets( placeWidgets(
obs_module_text( obs_module_text(
"AdvSceneSwitcher.condition.video.entry.modelPath"), "AdvSceneSwitcher.condition.video.entry.modelPath"),
_modelPathLayout, widgetPlaceholders); _modelPathLayout, widgetPlaceholders);
_neighborsControlLayout = new QHBoxLayout;
placeWidgets( placeWidgets(
obs_module_text( obs_module_text(
"AdvSceneSwitcher.condition.video.entry.minNeighbor"), "AdvSceneSwitcher.condition.video.entry.minNeighbor"),
_neighborsControlLayout, widgetPlaceholders); _neighborsControlLayout, widgetPlaceholders);
_minSizeControlLayout = new QHBoxLayout;
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.video.entry.minSize"),
_minSizeControlLayout, widgetPlaceholders);
_maxSizeControlLayout = new QHBoxLayout;
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.video.entry.maxSize"),
_maxSizeControlLayout, widgetPlaceholders);
_throttleControlLayout = new QHBoxLayout;
placeWidgets(obs_module_text( placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.video.entry.throttle"), "AdvSceneSwitcher.condition.video.entry.throttle"),
_throttleControlLayout, widgetPlaceholders); _throttleControlLayout, widgetPlaceholders);
placeWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.video.entry.checkArea"),
_checkAreaControlLayout, widgetPlaceholders);
QGridLayout *sizeGrid = new QGridLayout;
sizeGrid->addWidget(
new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.video.minSize")),
0, 0);
sizeGrid->addWidget(_minSize, 0, 1);
sizeGrid->addWidget(
new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.video.maxSize")),
1, 0);
sizeGrid->addWidget(_maxSize, 1, 1);
_sizeLayout->addLayout(sizeGrid);
_sizeLayout->addStretch();
_sizeLayout->setContentsMargins(0, 0, 0, 0);
QHBoxLayout *showMatchLayout = new QHBoxLayout; QHBoxLayout *showMatchLayout = new QHBoxLayout;
showMatchLayout->addWidget(_showMatch); showMatchLayout->addWidget(_showMatch);
@@ -554,10 +435,10 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
mainLayout->addWidget(_objectScaleThreshold); mainLayout->addWidget(_objectScaleThreshold);
mainLayout->addLayout(_neighborsControlLayout); mainLayout->addLayout(_neighborsControlLayout);
mainLayout->addWidget(_minNeighborsDescription); mainLayout->addWidget(_minNeighborsDescription);
mainLayout->addLayout(_minSizeControlLayout); mainLayout->addLayout(_sizeLayout);
mainLayout->addLayout(_maxSizeControlLayout);
mainLayout->addLayout(showMatchLayout); mainLayout->addLayout(showMatchLayout);
mainLayout->addLayout(_throttleControlLayout); mainLayout->addLayout(_throttleControlLayout);
mainLayout->addLayout(_checkAreaControlLayout);
setLayout(mainLayout); setLayout(mainLayout);
_entryData = entryData; _entryData = entryData;
@@ -591,14 +472,14 @@ void MacroConditionVideoEdit::UpdatePreviewTooltip()
this->setToolTip(html); this->setToolTip(html);
} }
void MacroConditionVideoEdit::SourceChanged(const QString &text) void MacroConditionVideoEdit::VideoSelectionChanged(const VideoSelection &v)
{ {
if (_loading || !_entryData) { if (_loading || !_entryData) {
return; return;
} }
std::lock_guard<std::mutex> lock(GetSwitcher()->m); std::lock_guard<std::mutex> lock(GetSwitcher()->m);
_entryData->_videoSource = GetWeakSourceByQString(text); _entryData->_video = v;
_entryData->ResetLastMatch(); _entryData->ResetLastMatch();
emit HeaderInfoChanged( emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc())); QString::fromStdString(_entryData->GetShortDesc()));
@@ -653,7 +534,7 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
QString path; QString path;
bool useExistingFile = false; bool useExistingFile = false;
// Ask whether to create screenshot or to select existing file // Ask whether to create screenshot or to select existing file
if (_entryData->_videoSource) { if (_entryData->_video.ValidSelection()) {
QMessageBox msgBox( QMessageBox msgBox(
QMessageBox::Question, QMessageBox::Question,
obs_module_text("AdvSceneSwitcher.windowTitle"), obs_module_text("AdvSceneSwitcher.windowTitle"),
@@ -680,9 +561,9 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
} }
} else { } else {
auto source = auto source = obs_weak_source_get_source(
obs_weak_source_get_source(_entryData->_videoSource); _entryData->_video.GetVideo());
auto screenshot = std::make_unique<AdvSSScreenshotObj>(source); ScreenshotHelper screenshot(source);
obs_source_release(source); obs_source_release(source);
path = QFileDialog::getSaveFileName(this); path = QFileDialog::getSaveFileName(this);
@@ -693,16 +574,22 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return; return;
} }
if (!screenshot->done) { // Screenshot usually completed by now if (!screenshot.done) { // Screenshot usually completed by now
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
} }
if (!screenshot->done) { if (!screenshot.done) {
DisplayMessage(obs_module_text( DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotFail")); "AdvSceneSwitcher.condition.video.screenshotFail"));
return; return;
} }
if (_entryData->_checkAreaEnable) {
screenshot->image.save(path); screenshot.image = screenshot.image.copy(
_entryData->_checkArea.x,
_entryData->_checkArea.y,
_entryData->_checkArea.width,
_entryData->_checkArea.height);
}
screenshot.image.save(path);
} }
_imagePath->SetPath(path); _imagePath->SetPath(path);
ImagePathChanged(path); ImagePathChanged(path);
@@ -761,44 +648,53 @@ void MacroConditionVideoEdit::MinNeighborsChanged(int value)
_entryData->_minNeighbors = value; _entryData->_minNeighbors = value;
} }
void MacroConditionVideoEdit::MinSizeXChanged(int value) void MacroConditionVideoEdit::MinSizeChanged(advss::Size value)
{ {
if (_loading || !_entryData) { if (_loading || !_entryData) {
return; return;
} }
std::lock_guard<std::mutex> lock(GetSwitcher()->m); std::lock_guard<std::mutex> lock(GetSwitcher()->m);
_entryData->_minSizeX = value; _entryData->_minSize = value;
} }
void MacroConditionVideoEdit::MinSizeYChanged(int value) void MacroConditionVideoEdit::MaxSizeChanged(advss::Size value)
{ {
if (_loading || !_entryData) { if (_loading || !_entryData) {
return; return;
} }
std::lock_guard<std::mutex> lock(GetSwitcher()->m); std::lock_guard<std::mutex> lock(GetSwitcher()->m);
_entryData->_minSizeY = value; _entryData->_maxSize = value;
} }
void MacroConditionVideoEdit::MaxSizeXChanged(int value) void MacroConditionVideoEdit::CheckAreaEnableChanged(int value)
{ {
if (_loading || !_entryData) { if (_loading || !_entryData) {
return; return;
} }
std::lock_guard<std::mutex> lock(GetSwitcher()->m); std::lock_guard<std::mutex> lock(GetSwitcher()->m);
_entryData->_maxSizeX = value; _entryData->_checkAreaEnable = value;
_checkArea->setEnabled(value);
_selectArea->setEnabled(value);
} }
void MacroConditionVideoEdit::MaxSizeYChanged(int value) void MacroConditionVideoEdit::CheckAreaChanged(advss::Area value)
{ {
if (_loading || !_entryData) { if (_loading || !_entryData) {
return; return;
} }
std::lock_guard<std::mutex> lock(GetSwitcher()->m); std::lock_guard<std::mutex> lock(GetSwitcher()->m);
_entryData->_maxSizeY = value; _entryData->_checkArea = value;
}
void MacroConditionVideoEdit::CheckAreaChanged(QRect rect)
{
advss::Area area{rect.topLeft().x(), rect.y(), rect.width(),
rect.height()};
_checkArea->SetArea(area);
} }
void MacroConditionVideoEdit::ThrottleEnableChanged(int value) void MacroConditionVideoEdit::ThrottleEnableChanged(int value)
@@ -822,83 +718,20 @@ void MacroConditionVideoEdit::ThrottleCountChanged(int value)
_entryData->_throttleCount = value / GetSwitcher()->interval; _entryData->_throttleCount = value / GetSwitcher()->interval;
} }
QImage markPatterns(cv::Mat &matchResult, QImage &image, QImage &pattern)
{
auto matchImg = QImageToMat(image);
for (int row = 0; row < matchResult.rows - 1; row++) {
for (int col = 0; col < matchResult.cols - 1; col++) {
if (matchResult.at<float>(row, col) != 0.0) {
rectangle(matchImg, {col, row},
cv::Point(col + pattern.width(),
row + pattern.height()),
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
}
}
}
return MatToQImage(matchImg);
}
QImage markObjects(QImage &image, std::vector<cv::Rect> &objects)
{
auto frame = QImageToMat(image);
for (size_t i = 0; i < objects.size(); i++) {
rectangle(frame, cv::Point(objects[i].x, objects[i].y),
cv::Point(objects[i].x + objects[i].width,
objects[i].y + objects[i].height),
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
}
return MatToQImage(frame);
}
void MacroConditionVideoEdit::ShowMatchClicked() void MacroConditionVideoEdit::ShowMatchClicked()
{ {
auto source = obs_weak_source_get_source(_entryData->_videoSource); _previewDialog.show();
auto screenshot = std::make_unique<AdvSSScreenshotObj>(source); _previewDialog.raise();
obs_source_release(source); _previewDialog.activateWindow();
if (!screenshot->done) { _previewDialog.ShowMatch();
std::this_thread::sleep_for(std::chrono::seconds(1)); }
}
if (!screenshot->done) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotFail"));
return;
}
QImage markedIamge; void MacroConditionVideoEdit::SelectAreaClicked()
if (_entryData->_condition == VideoCondition::PATTERN) { {
cv::Mat result; _previewDialog.show();
QImage pattern = _entryData->GetMatchImage(); _previewDialog.raise();
matchPattern(screenshot->image, pattern, _previewDialog.activateWindow();
_entryData->_patternThreshold, result, _previewDialog.SelectArea();
_entryData->_useAlphaAsMask);
if (countNonZero(result) == 0) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.patternMatchFail"));
return;
}
markedIamge = markPatterns(result, screenshot->image, pattern);
} else if (_entryData->_condition == VideoCondition::OBJECT) {
auto objects = matchObject(
screenshot->image, _entryData->_objectCascade,
_entryData->_scaleFactor, _entryData->_minNeighbors,
{_entryData->_minSizeX, _entryData->_minSizeY},
{_entryData->_maxSizeX, _entryData->_maxSizeY});
if (objects.empty()) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.objectMatchFail"));
return;
}
markedIamge = markObjects(screenshot->image, objects);
}
QLabel *label = new QLabel;
label->setPixmap(QPixmap::fromImage(markedIamge));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(label);
QDialog dialog;
dialog.setLayout(layout);
dialog.setWindowTitle("Advanced Scene Switcher");
dialog.exec();
} }
void MacroConditionVideoEdit::ModelPathChanged(const QString &text) void MacroConditionVideoEdit::ModelPathChanged(const QString &text)
@@ -949,6 +782,11 @@ bool patternControlIsOptional(VideoCondition cond)
cond == VideoCondition::HAS_NOT_CHANGED; cond == VideoCondition::HAS_NOT_CHANGED;
} }
bool needsAreaControls(VideoCondition cond)
{
return cond != VideoCondition::NO_IMAGE;
}
void MacroConditionVideoEdit::SetWidgetVisibility() void MacroConditionVideoEdit::SetWidgetVisibility()
{ {
_imagePath->setVisible(requiresFileInput(_entryData->_condition)); _imagePath->setVisible(requiresFileInput(_entryData->_condition));
@@ -964,14 +802,14 @@ void MacroConditionVideoEdit::SetWidgetVisibility()
needsObjectControls(_entryData->_condition)); needsObjectControls(_entryData->_condition));
_minNeighborsDescription->setVisible( _minNeighborsDescription->setVisible(
needsObjectControls(_entryData->_condition)); needsObjectControls(_entryData->_condition));
setLayoutVisible(_minSizeControlLayout, setLayoutVisible(_sizeLayout,
needsObjectControls(_entryData->_condition));
setLayoutVisible(_maxSizeControlLayout,
needsObjectControls(_entryData->_condition)); needsObjectControls(_entryData->_condition));
setLayoutVisible(_modelPathLayout, setLayoutVisible(_modelPathLayout,
needsObjectControls(_entryData->_condition)); needsObjectControls(_entryData->_condition));
setLayoutVisible(_throttleControlLayout, setLayoutVisible(_throttleControlLayout,
needsThrottleControls(_entryData->_condition)); needsThrottleControls(_entryData->_condition));
setLayoutVisible(_checkAreaControlLayout,
needsAreaControls(_entryData->_condition));
if (_entryData->_condition == VideoCondition::HAS_CHANGED || if (_entryData->_condition == VideoCondition::HAS_CHANGED ||
_entryData->_condition == VideoCondition::HAS_NOT_CHANGED) { _entryData->_condition == VideoCondition::HAS_NOT_CHANGED) {
@@ -988,8 +826,7 @@ void MacroConditionVideoEdit::UpdateEntryData()
return; return;
} }
_videoSelection->setCurrentText( _videoSelection->SetVideoSelection(_entryData->_video);
GetWeakSourceName(_entryData->_videoSource).c_str());
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition)); _condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
_imagePath->SetPath(QString::fromStdString(_entryData->_file)); _imagePath->SetPath(QString::fromStdString(_entryData->_file));
_usePatternForChangedCheck->setChecked( _usePatternForChangedCheck->setChecked(
@@ -999,12 +836,13 @@ void MacroConditionVideoEdit::UpdateEntryData()
_modelDataPath->SetPath(_entryData->GetModelDataPath().c_str()); _modelDataPath->SetPath(_entryData->GetModelDataPath().c_str());
_objectScaleThreshold->SetDoubleValue(_entryData->_scaleFactor); _objectScaleThreshold->SetDoubleValue(_entryData->_scaleFactor);
_minNeighbors->setValue(_entryData->_minNeighbors); _minNeighbors->setValue(_entryData->_minNeighbors);
_minSizeX->setValue(_entryData->_minSizeX); _minSize->SetSize(_entryData->_minSize);
_minSizeY->setValue(_entryData->_minSizeY); _maxSize->SetSize(_entryData->_maxSize);
_maxSizeX->setValue(_entryData->_maxSizeX);
_maxSizeY->setValue(_entryData->_maxSizeY);
_throttleEnable->setChecked(_entryData->_throttleEnabled); _throttleEnable->setChecked(_entryData->_throttleEnabled);
_throttleCount->setValue(_entryData->_throttleCount * _throttleCount->setValue(_entryData->_throttleCount *
GetSwitcher()->interval); GetSwitcher()->interval);
_checkAreaEnable->setChecked(_entryData->_checkAreaEnable);
_checkArea->SetArea(_entryData->_checkArea);
UpdatePreviewTooltip();
SetWidgetVisibility(); SetWidgetVisibility();
} }

View File

@@ -1,15 +1,21 @@
#pragma once #pragma once
#include "opencv-helpers.hpp"
#include "threshold-slider.hpp"
#include "preview-dialog.hpp"
#include "area-selection.hpp"
#include "video-selection.hpp"
#include <macro.hpp> #include <macro.hpp>
#include <screenshot-helper.hpp>
#include <file-selection.hpp> #include <file-selection.hpp>
#include <screenshot-helper.hpp>
#include <QWidget> #include <QWidget>
#include <QComboBox> #include <QComboBox>
#include <QCheckBox> #include <QCheckBox>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <chrono> #include <QGridLayout>
#undef NO // MacOS macro that can conflict with OpenCV #include <QLabel>
#include <opencv2/opencv.hpp> #include <QRect>
enum class VideoCondition { enum class VideoCondition {
MATCH, MATCH,
@@ -21,26 +27,18 @@ enum class VideoCondition {
OBJECT, OBJECT,
}; };
struct PatternMatchData {
cv::Mat4b rgbaPattern;
cv::Mat3b rgbPattern;
cv::Mat1b mask;
};
constexpr int minMinNeighbors = 3;
constexpr int maxMinNeighbors = 6;
class MacroConditionVideo : public MacroCondition { class MacroConditionVideo : public MacroCondition {
public: public:
MacroConditionVideo(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
QImage GetMatchImage() { return _matchImage; }; QImage GetMatchImage() { return _matchImage; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionVideo>(); return std::make_shared<MacroConditionVideo>(m);
} }
void GetScreenshot(); void GetScreenshot();
bool LoadImageFromFile(); bool LoadImageFromFile();
@@ -48,7 +46,7 @@ public:
std::string GetModelDataPath() { return _modelDataPath; } std::string GetModelDataPath() { return _modelDataPath; }
void ResetLastMatch() { _lastMatchResult = false; } void ResetLastMatch() { _lastMatchResult = false; }
OBSWeakSource _videoSource; VideoSelection _video;
VideoCondition _condition = VideoCondition::MATCH; VideoCondition _condition = VideoCondition::MATCH;
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath"); std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
bool _useAlphaAsMask = false; bool _useAlphaAsMask = false;
@@ -58,10 +56,11 @@ public:
cv::CascadeClassifier _objectCascade; cv::CascadeClassifier _objectCascade;
double _scaleFactor = 1.1; double _scaleFactor = 1.1;
int _minNeighbors = minMinNeighbors; int _minNeighbors = minMinNeighbors;
int _minSizeX = 0; advss::Size _minSize{0, 0};
int _minSizeY = 0; advss::Size _maxSize{0, 0};
int _maxSizeX = 0;
int _maxSizeY = 0; bool _checkAreaEnable = false;
advss::Area _checkArea{0, 0, 0, 0};
bool _throttleEnabled = false; bool _throttleEnabled = false;
int _throttleCount = 3; int _throttleCount = 3;
@@ -73,7 +72,8 @@ private:
bool Compare(); bool Compare();
bool CheckShouldBeSkipped(); bool CheckShouldBeSkipped();
std::unique_ptr<AdvSSScreenshotObj> _screenshotData = nullptr; bool _getNextScreenshot = true;
ScreenshotHelper _screenshotData;
QImage _matchImage; QImage _matchImage;
std::string _modelDataPath = std::string _modelDataPath =
obs_get_module_data_path(obs_current_module()) + obs_get_module_data_path(obs_current_module()) +
@@ -86,27 +86,6 @@ private:
static const std::string id; static const std::string id;
}; };
class ThresholdSlider : public QWidget {
Q_OBJECT
public:
ThresholdSlider(double min = 0., double max = 1.,
const QString &label = "threshold",
const QString &description = "", QWidget *parent = 0);
void SetDoubleValue(double);
public slots:
void NotifyValueChanged(int value);
signals:
void DoubleValueChanged(double value);
private:
void SetDoubleValueText(double);
QLabel *_value;
QSlider *_slider;
double _scale = 100.0;
int _precision = 2;
};
class MacroConditionVideoEdit : public QWidget { class MacroConditionVideoEdit : public QWidget {
Q_OBJECT Q_OBJECT
@@ -126,7 +105,7 @@ public:
void UpdatePreviewTooltip(); void UpdatePreviewTooltip();
private slots: private slots:
void SourceChanged(const QString &text); void VideoSelectionChanged(const VideoSelection &);
void ConditionChanged(int cond); void ConditionChanged(int cond);
void ImagePathChanged(const QString &text); void ImagePathChanged(const QString &text);
void ImageBrowseButtonClicked(); void ImageBrowseButtonClicked();
@@ -137,10 +116,13 @@ private slots:
void ModelPathChanged(const QString &text); void ModelPathChanged(const QString &text);
void ObjectScaleThresholdChanged(double); void ObjectScaleThresholdChanged(double);
void MinNeighborsChanged(int value); void MinNeighborsChanged(int value);
void MinSizeXChanged(int value); void MinSizeChanged(advss::Size value);
void MinSizeYChanged(int value); void MaxSizeChanged(advss::Size value);
void MaxSizeXChanged(int value);
void MaxSizeYChanged(int value); void CheckAreaEnableChanged(int value);
void CheckAreaChanged(advss::Area);
void CheckAreaChanged(QRect area);
void SelectAreaClicked();
void ThrottleEnableChanged(int value); void ThrottleEnableChanged(int value);
void ThrottleCountChanged(int value); void ThrottleCountChanged(int value);
@@ -149,7 +131,7 @@ signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
protected: protected:
QComboBox *_videoSelection; VideoSelectionWidget *_videoSelection;
QComboBox *_condition; QComboBox *_condition;
QCheckBox *_usePatternForChangedCheck; QCheckBox *_usePatternForChangedCheck;
@@ -163,17 +145,21 @@ protected:
QHBoxLayout *_neighborsControlLayout; QHBoxLayout *_neighborsControlLayout;
QSpinBox *_minNeighbors; QSpinBox *_minNeighbors;
QLabel *_minNeighborsDescription; QLabel *_minNeighborsDescription;
QHBoxLayout *_minSizeControlLayout; QHBoxLayout *_sizeLayout;
QSpinBox *_minSizeX; SizeSelection *_minSize;
QSpinBox *_minSizeY; SizeSelection *_maxSize;
QHBoxLayout *_maxSizeControlLayout;
QSpinBox *_maxSizeX; QHBoxLayout *_checkAreaControlLayout;
QSpinBox *_maxSizeY; QCheckBox *_checkAreaEnable;
AreaSelection *_checkArea;
QPushButton *_selectArea;
QHBoxLayout *_throttleControlLayout; QHBoxLayout *_throttleControlLayout;
QCheckBox *_throttleEnable; QCheckBox *_throttleEnable;
QSpinBox *_throttleCount; QSpinBox *_throttleCount;
QPushButton *_showMatch; QPushButton *_showMatch;
PreviewDialog _previewDialog;
std::shared_ptr<MacroConditionVideo> _entryData; std::shared_ptr<MacroConditionVideo> _entryData;

View File

@@ -0,0 +1,98 @@
#include "opencv-helpers.hpp"
PatternMatchData createPatternData(QImage &pattern)
{
PatternMatchData data;
if (pattern.isNull()) {
return data;
}
data.rgbaPattern = QImageToMat(pattern);
std::vector<cv::Mat1b> rgbaChannelsPattern;
cv::split(data.rgbaPattern, rgbaChannelsPattern);
std::vector<cv::Mat1b> rgbChanlesPattern(
rgbaChannelsPattern.begin(), rgbaChannelsPattern.begin() + 3);
cv::merge(rgbChanlesPattern, data.rgbPattern);
cv::threshold(rgbaChannelsPattern[3], data.mask, 0, 255,
cv::THRESH_BINARY);
return data;
}
void matchPattern(QImage &img, PatternMatchData &patternData, double threshold,
cv::Mat &result, bool useAlphaAsMask)
{
if (img.isNull() || patternData.rgbaPattern.empty()) {
return;
}
if (img.height() < patternData.rgbaPattern.rows ||
img.width() < patternData.rgbaPattern.cols) {
return;
}
auto i = QImageToMat(img);
if (useAlphaAsMask) {
std::vector<cv::Mat1b> rgbaChannelsImage;
cv::split(i, rgbaChannelsImage);
std::vector<cv::Mat1b> rgbChanlesImage(
rgbaChannelsImage.begin(),
rgbaChannelsImage.begin() + 3);
cv::Mat3b rgbImage;
cv::merge(rgbChanlesImage, rgbImage);
cv::matchTemplate(rgbImage, patternData.rgbPattern, result,
cv::TM_CCORR_NORMED, patternData.mask);
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
} else {
cv::matchTemplate(i, patternData.rgbaPattern, result,
cv::TM_CCOEFF_NORMED);
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
}
}
void matchPattern(QImage &img, QImage &pattern, double threshold,
cv::Mat &result, bool useAlphaAsMask)
{
auto data = createPatternData(pattern);
matchPattern(img, data, threshold, result, useAlphaAsMask);
}
std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
double scaleFactor, int minNeighbors,
cv::Size minSize, cv::Size maxSize)
{
if (img.isNull() || cascade.empty()) {
return {};
}
auto i = QImageToMat(img);
cv::Mat frameGray;
cv::cvtColor(i, frameGray, cv::COLOR_BGR2GRAY);
equalizeHist(frameGray, frameGray);
std::vector<cv::Rect> objects;
cascade.detectMultiScale(frameGray, objects, scaleFactor, minNeighbors,
0, minSize, maxSize);
return objects;
}
// Assumption is that QImage uses Format_RGBA8888.
// Conversion from: https://github.com/dbzhang800/QtOpenCV
cv::Mat QImageToMat(const QImage &img)
{
if (img.isNull()) {
return cv::Mat();
}
return cv::Mat(img.height(), img.width(), CV_8UC(img.depth() / 8),
(uchar *)img.bits(), img.bytesPerLine());
}
QImage MatToQImage(const cv::Mat &mat)
{
if (mat.empty()) {
return QImage();
}
return QImage(mat.data, mat.cols, mat.rows,
QImage::Format::Format_RGBA8888);
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <QImage>
#undef NO // MacOS macro that can conflict with OpenCV
#include <opencv2/opencv.hpp>
constexpr int minMinNeighbors = 3;
constexpr int maxMinNeighbors = 6;
struct PatternMatchData {
cv::Mat4b rgbaPattern;
cv::Mat3b rgbPattern;
cv::Mat1b mask;
};
PatternMatchData createPatternData(QImage &pattern);
void matchPattern(QImage &img, PatternMatchData &patternData, double threshold,
cv::Mat &result, bool useAlphaAsMask = true);
void matchPattern(QImage &img, QImage &pattern, double threshold,
cv::Mat &result, bool useAlphaAsMask);
std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
double scaleFactor, int minNeighbors,
cv::Size minSize, cv::Size maxSize);
cv::Mat QImageToMat(const QImage &img);
QImage MatToQImage(const cv::Mat &mat);

View File

@@ -0,0 +1,249 @@
#include "preview-dialog.hpp"
#include "macro-condition-video.hpp"
#include "opencv-helpers.hpp"
#include "utility.hpp"
#include <condition_variable>
PreviewDialog::PreviewDialog(QWidget *parent,
MacroConditionVideo *conditionData,
std::mutex *mutex)
: QDialog(parent),
_conditionData(conditionData),
_scrollArea(new QScrollArea),
_imageLabel(new QLabel(this)),
_rubberBand(new QRubberBand(QRubberBand::Rectangle, this)),
_mtx(mutex)
{
setWindowTitle("Advanced Scene Switcher");
_statusLabel = new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.video.showMatch.loading"));
resize(640, 480);
_scrollArea->setBackgroundRole(QPalette::Dark);
_scrollArea->setWidget(_imageLabel);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(_statusLabel);
layout->addWidget(_scrollArea);
setLayout(layout);
// This is a workaround to handle random segfaults triggered when using:
// QMetaObject::invokeMethod(this, "RedrawImage", Qt::QueuedConnection,
// Q_ARG(QImage, image));
// from within CheckForMatchLoop().
// Even using BlockingQueuedConnection causes deadlocks
_timer.setInterval(500);
QWidget::connect(&_timer, &QTimer::timeout, this,
&PreviewDialog::Resize);
_timer.start();
}
void PreviewDialog::mousePressEvent(QMouseEvent *event)
{
_selectingArea = true;
if (_type == Type::SHOW_MATCH) {
return;
}
_origin = event->pos();
_rubberBand->setGeometry(QRect(_origin, QSize()));
_rubberBand->show();
}
void PreviewDialog::mouseMoveEvent(QMouseEvent *event)
{
if (_type == Type::SHOW_MATCH) {
return;
}
_rubberBand->setGeometry(QRect(_origin, event->pos()).normalized());
}
void PreviewDialog::mouseReleaseEvent(QMouseEvent *)
{
if (_type == Type::SHOW_MATCH) {
return;
}
auto selectionStart =
_rubberBand->mapToGlobal(_rubberBand->rect().topLeft());
QRect selectionArea(selectionStart, _rubberBand->size());
auto imageStart =
_imageLabel->mapToGlobal(_imageLabel->rect().topLeft());
QRect imageArea(imageStart, _imageLabel->size());
auto intersected = imageArea.intersected(selectionArea);
QRect checksize(QPoint(intersected.topLeft() - imageStart),
intersected.size());
if (checksize.x() >= 0 && checksize.y() >= 0 && checksize.width() > 0 &&
checksize.height() > 0) {
emit SelectionAreaChanged(checksize);
}
_selectingArea = false;
}
PreviewDialog::~PreviewDialog()
{
_stop = true;
if (_thread.joinable()) {
_thread.join();
}
}
void PreviewDialog::ShowMatch()
{
Start();
_rubberBand->hide();
_type = Type::SHOW_MATCH;
}
void PreviewDialog::SelectArea()
{
_selectingArea = false;
Start();
_type = Type::SELECT_AREA;
DrawFrame();
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.selectArea.status"));
}
void PreviewDialog::Resize()
{
_imageLabel->adjustSize();
if (_type == Type::SELECT_AREA && !_selectingArea) {
DrawFrame();
}
}
void PreviewDialog::Start()
{
if (_thread.joinable()) {
return;
}
if (!_conditionData) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotFail"));
return;
}
_thread = std::thread(&PreviewDialog::CheckForMatchLoop, this);
}
void PreviewDialog::CheckForMatchLoop()
{
std::condition_variable cv;
while (!_stop) {
std::unique_lock<std::mutex> lock(*_mtx);
auto source = obs_weak_source_get_source(
_conditionData->_video.GetVideo());
ScreenshotHelper screenshot(source);
obs_source_release(source);
cv.wait_for(lock, std::chrono::seconds(1));
if (_stop) {
return;
}
if (isHidden()) {
continue;
}
if (!screenshot.done ||
!_conditionData->_video.ValidSelection()) {
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotFail"));
_imageLabel->setPixmap(QPixmap());
continue;
}
if (screenshot.image.width() == 0 ||
screenshot.image.height() == 0) {
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotEmpty"));
_imageLabel->setPixmap(QPixmap());
continue;
}
if (_type == Type::SHOW_MATCH) {
if (_conditionData->_checkAreaEnable) {
screenshot.image = screenshot.image.copy(
_conditionData->_checkArea.x,
_conditionData->_checkArea.y,
_conditionData->_checkArea.width,
_conditionData->_checkArea.height);
}
MarkMatch(screenshot.image);
}
_imageLabel->setPixmap(QPixmap::fromImage(screenshot.image));
}
}
void markPatterns(cv::Mat &matchResult, QImage &image, QImage &pattern)
{
auto matchImg = QImageToMat(image);
for (int row = 0; row < matchResult.rows - 1; row++) {
for (int col = 0; col < matchResult.cols - 1; col++) {
if (matchResult.at<float>(row, col) != 0.0) {
rectangle(matchImg, {col, row},
cv::Point(col + pattern.width(),
row + pattern.height()),
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
}
}
}
}
void markObjects(QImage &image, std::vector<cv::Rect> &objects)
{
auto frame = QImageToMat(image);
for (size_t i = 0; i < objects.size(); i++) {
rectangle(frame, cv::Point(objects[i].x, objects[i].y),
cv::Point(objects[i].x + objects[i].width,
objects[i].y + objects[i].height),
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
}
}
void PreviewDialog::MarkMatch(QImage &screenshot)
{
if (_conditionData->_condition == VideoCondition::PATTERN) {
cv::Mat result;
QImage pattern = _conditionData->GetMatchImage();
matchPattern(screenshot, pattern,
_conditionData->_patternThreshold, result,
_conditionData->_useAlphaAsMask);
if (countNonZero(result) == 0) {
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.patternMatchFail"));
} else {
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.patternMatchSuccess"));
markPatterns(result, screenshot, pattern);
}
} else if (_conditionData->_condition == VideoCondition::OBJECT) {
auto objects = matchObject(screenshot,
_conditionData->_objectCascade,
_conditionData->_scaleFactor,
_conditionData->_minNeighbors,
_conditionData->_minSize.CV(),
_conditionData->_maxSize.CV());
if (objects.empty()) {
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.objectMatchFail"));
} else {
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.objectMatchSuccess"));
markObjects(screenshot, objects);
}
}
}
void PreviewDialog::DrawFrame()
{
if (!_conditionData) {
return;
}
auto imageStart =
_imageLabel->mapToGlobal(_imageLabel->rect().topLeft());
auto windowStart = mapToGlobal(rect().topLeft());
_rubberBand->resize(_conditionData->_checkArea.width,
_conditionData->_checkArea.height);
_rubberBand->move(_conditionData->_checkArea.x +
(imageStart.x() - windowStart.x()),
_conditionData->_checkArea.y +
(imageStart.y() - windowStart.y()));
_rubberBand->show();
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include <QDialog>
#include <QLabel>
#include <QScrollArea>
#include <QTimer>
#include <QMouseEvent>
#include <QRubberBand>
#include <QPoint>
#include <thread>
#include <mutex>
class MacroConditionVideo;
class PreviewDialog : public QDialog {
Q_OBJECT
public:
PreviewDialog(QWidget *parent, MacroConditionVideo *_conditionData,
std::mutex *mutex);
virtual ~PreviewDialog();
void ShowMatch();
void SelectArea();
private slots:
void Resize();
signals:
void SelectionAreaChanged(QRect area);
private:
void Start();
void CheckForMatchLoop();
void MarkMatch(QImage &screenshot);
void DrawFrame();
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
MacroConditionVideo *_conditionData;
QScrollArea *_scrollArea;
QLabel *_statusLabel;
QLabel *_imageLabel;
QTimer _timer;
QPoint _origin;
QRubberBand *_rubberBand = nullptr;
std::atomic_bool _selectingArea = {false};
std::mutex *_mtx;
std::thread _thread;
std::atomic_bool _stop = {false};
enum class Type {
SHOW_MATCH,
SELECT_AREA,
};
Type _type = Type::SHOW_MATCH;
};

View File

@@ -0,0 +1,50 @@
#include "threshold-slider.hpp"
#include <QHBoxLayout>
ThresholdSlider::ThresholdSlider(double min, double max, const QString &label,
const QString &description, QWidget *parent)
: QWidget(parent)
{
_slider = new QSlider();
_slider->setOrientation(Qt::Horizontal);
_slider->setRange(min * _scale, max * _scale);
_value = new QLabel();
QString labelText = label + QString("0.");
for (int i = 0; i < _precision; i++) {
labelText.append(QString("0"));
}
_value->setText(labelText);
connect(_slider, SIGNAL(valueChanged(int)), this,
SLOT(NotifyValueChanged(int)));
QVBoxLayout *mainLayout = new QVBoxLayout();
QHBoxLayout *sliderLayout = new QHBoxLayout();
sliderLayout->addWidget(_value);
sliderLayout->addWidget(_slider);
mainLayout->addLayout(sliderLayout);
if (!description.isEmpty()) {
mainLayout->addWidget(new QLabel(description));
}
mainLayout->setContentsMargins(0, 0, 0, 0);
setLayout(mainLayout);
}
void ThresholdSlider::SetDoubleValue(double value)
{
_slider->setValue(value * _scale);
SetDoubleValueText(value);
}
void ThresholdSlider::NotifyValueChanged(int value)
{
double doubleValue = value / _scale;
SetDoubleValueText(doubleValue);
emit DoubleValueChanged(doubleValue);
}
void ThresholdSlider::SetDoubleValueText(double value)
{
QString labelText = _value->text();
labelText.chop(_precision + 2); // 2 for the part left of the "."
labelText.append(QString::number(value, 'f', _precision));
_value->setText(labelText);
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <QWidget>
#include <QSlider>
#include <QLabel>
class ThresholdSlider : public QWidget {
Q_OBJECT
public:
ThresholdSlider(double min = 0., double max = 1.,
const QString &label = "threshold",
const QString &description = "", QWidget *parent = 0);
void SetDoubleValue(double);
public slots:
void NotifyValueChanged(int value);
signals:
void DoubleValueChanged(double value);
private:
void SetDoubleValueText(double);
QLabel *_value;
QSlider *_slider;
double _scale = 100.0;
int _precision = 2;
};

View File

@@ -0,0 +1,122 @@
#include "video-selection.hpp"
#include "utility.hpp"
void VideoSelection::Save(obs_data_t *obj, const char *name,
const char *typeName)
{
obs_data_set_int(obj, typeName, static_cast<int>(_type));
switch (_type) {
case VideoSelectionType::SOURCE:
obs_data_set_string(obj, name,
GetWeakSourceName(_source).c_str());
break;
default:
break;
}
}
void VideoSelection::Load(obs_data_t *obj, const char *name,
const char *typeName)
{
_type = static_cast<VideoSelectionType>(
obs_data_get_int(obj, typeName));
auto target = obs_data_get_string(obj, name);
switch (_type) {
case VideoSelectionType::SOURCE:
_source = GetWeakSourceByName(target);
break;
case VideoSelectionType::OBS_MAIN:
_source = nullptr;
break;
default:
break;
}
}
OBSWeakSource VideoSelection::GetVideo()
{
if (_type == VideoSelectionType::SOURCE) {
return _source;
}
return nullptr;
}
std::string VideoSelection::ToString()
{
switch (_type) {
case VideoSelectionType::SOURCE:
return GetWeakSourceName(_source);
case VideoSelectionType::OBS_MAIN:
return obs_module_text("AdvSceneSwitcher.OBSVideoOutput");
default:
break;
}
return "";
}
bool VideoSelection::ValidSelection()
{
return _type == VideoSelectionType::OBS_MAIN || !!_source;
}
VideoSelectionWidget::VideoSelectionWidget(QWidget *parent, bool addOBSVideoOut)
: QComboBox(parent)
{
setDuplicatesEnabled(true);
populateVideoSelection(this, addOBSVideoOut);
QWidget::connect(this, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SelectionChanged(const QString &)));
}
void VideoSelectionWidget::SetVideoSelection(VideoSelection &t)
{
int idx;
switch (t.GetType()) {
case VideoSelectionType::SOURCE:
setCurrentText(QString::fromStdString(t.ToString()));
break;
case VideoSelectionType::OBS_MAIN:
idx = findText(QString::fromStdString(obs_module_text(
obs_module_text("AdvSceneSwitcher.OBSVideoOutput"))));
if (idx != -1) {
setCurrentIndex(idx);
}
break;
default:
setCurrentIndex(0);
break;
}
}
static bool isFirstEntry(QComboBox *l, QString name, int idx)
{
for (int i = 0; i < l->count(); i++) {
if (l->itemText(i) == name) {
return idx == i;
}
}
return false;
}
bool VideoSelectionWidget::IsOBSVideoOutSelected(const QString &name)
{
if (name == QString::fromStdString(obs_module_text(
"AdvSceneSwitcher.OBSVideoOutput"))) {
return isFirstEntry(this, name, currentIndex());
}
return false;
}
void VideoSelectionWidget::SelectionChanged(const QString &name)
{
VideoSelection t;
if (IsOBSVideoOutSelected(name)) {
t._type = VideoSelectionType::OBS_MAIN;
} else {
auto source = GetWeakSourceByQString(name);
t._type = VideoSelectionType::SOURCE;
t._source = source;
}
emit VideoSelectionChange(t);
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include <QComboBox>
#include <obs-module.h>
#include <obs.hpp>
enum class VideoSelectionType {
SOURCE,
OBS_MAIN,
};
class VideoSelection {
public:
void Save(obs_data_t *obj, const char *name = "video",
const char *typeName = "videoType");
void Load(obs_data_t *obj, const char *name = "video",
const char *typeName = "videoType");
VideoSelectionType GetType() { return _type; }
OBSWeakSource GetVideo();
std::string ToString();
bool ValidSelection();
private:
OBSWeakSource _source;
VideoSelectionType _type = VideoSelectionType::SOURCE;
friend class VideoSelectionWidget;
};
class VideoSelectionWidget : public QComboBox {
Q_OBJECT
public:
VideoSelectionWidget(QWidget *parent, bool addOBSVideoOut = true);
void SetVideoSelection(VideoSelection &);
signals:
void VideoSelectionChange(const VideoSelection &);
private slots:
void SelectionChanged(const QString &name);
private:
bool IsOBSVideoOutSelected(const QString &name);
};

View File

@@ -73,8 +73,9 @@ if(BUILD_OUT_OF_TREE)
set(LIB_OUT_DIR "/lib/obs-plugins") set(LIB_OUT_DIR "/lib/obs-plugins")
endif() endif()
set_target_properties(advanced-scene-switcher-openvr PROPERTIES PREFIX "") set_target_properties(advanced-scene-switcher-openvr PROPERTIES PREFIX "")
install(TARGETS advanced-scene-switcher-openvr install(
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR}) TARGETS advanced-scene-switcher-openvr
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR}/adv-ss-plugins)
endif() endif()
else() else()
target_link_libraries( target_link_libraries(
@@ -85,5 +86,5 @@ else()
Qt5::Core Qt5::Core
Qt5::Widgets Qt5::Widgets
libobs) libobs)
install_obs_plugin(advanced-scene-switcher-openvr) install_advss_plugin(advanced-scene-switcher-openvr)
endif() endif()

View File

@@ -166,6 +166,9 @@ MacroConditionOpenVREdit::MacroConditionOpenVREdit(
controlsLayout->addWidget(_maxY, 1, 1); controlsLayout->addWidget(_maxY, 1, 1);
controlsLayout->addWidget(_maxZ, 1, 2); controlsLayout->addWidget(_maxZ, 1, 2);
QWidget *controls = new QWidget; QWidget *controls = new QWidget;
controls->setObjectName("openVRControls");
controls->setStyleSheet(
"#openVRControls { background-color: rgba(0,0,0,0); }");
controls->setLayout(controlsLayout); controls->setLayout(controlsLayout);
controls->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); controls->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

View File

@@ -8,13 +8,14 @@
class MacroConditionOpenVR : public MacroCondition { class MacroConditionOpenVR : public MacroCondition {
public: public:
MacroConditionOpenVR(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionOpenVR>(); return std::make_shared<MacroConditionOpenVR>(m);
} }
double _minX = 0, _minY = 0, _minZ = 0, _maxX = 0, _maxY = 0, _maxZ = 0; double _minX = 0, _minY = 0, _minZ = 0, _maxX = 0, _maxY = 0, _maxZ = 0;

View File

@@ -1,13 +1,11 @@
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/status-dock.hpp" #include "headers/status-control.hpp"
#include "headers/utility.hpp" #include "headers/utility.hpp"
#include "headers/version.h" #include "headers/version.h"
#include <QFileDialog> #include <QFileDialog>
#include <QStandardPaths> #include <QStandardPaths>
QMetaObject::Connection inactivePluse;
void AdvSceneSwitcher::on_close_clicked() void AdvSceneSwitcher::on_close_clicked()
{ {
close(); close();
@@ -147,41 +145,6 @@ void AdvSceneSwitcher::on_checkInterval_valueChanged(int value)
switcher->interval = value; switcher->interval = value;
} }
void AdvSceneSwitcher::SetStarted()
{
ui->toggleStartButton->setText(
obs_module_text("AdvSceneSwitcher.generalTab.status.stop"));
ui->pluginRunningText->setText(
obs_module_text("AdvSceneSwitcher.status.active"));
ui->pluginRunningText->disconnect(inactivePluse);
currentStatusActive = true;
}
void AdvSceneSwitcher::SetStopped()
{
ui->toggleStartButton->setText(
obs_module_text("AdvSceneSwitcher.generalTab.status.start"));
ui->pluginRunningText->setText(
obs_module_text("AdvSceneSwitcher.status.inactive"));
if (!switcher->disableHints) {
inactivePluse = PulseWidget(ui->pluginRunningText,
QColor(Qt::red), QColor(0, 0, 0, 0),
"QLabel ");
}
currentStatusActive = false;
}
void AdvSceneSwitcher::on_toggleStartButton_clicked()
{
if (switcher->th && switcher->th->isRunning()) {
switcher->Stop();
SetStopped();
} else {
switcher->Start();
SetStarted();
}
}
void AdvSceneSwitcher::closeEvent(QCloseEvent *) void AdvSceneSwitcher::closeEvent(QCloseEvent *)
{ {
if (!switcher) { if (!switcher) {
@@ -229,6 +192,62 @@ void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
switcher->disableHints = state; switcher->disableHints = state;
} }
bool isLegacyTab(const QString &name)
{
return name == obs_module_text(
"AdvSceneSwitcher.transitionTab.title") ||
name == obs_module_text(
"AdvSceneSwitcher.windowTitleTab.title") ||
name == obs_module_text(
"AdvSceneSwitcher.executableTab.title") ||
name == obs_module_text(
"AdvSceneSwitcher.screenRegionTab.title") ||
name == obs_module_text("AdvSceneSwitcher.mediaTab.title") ||
name == obs_module_text("AdvSceneSwitcher.fileTab.title") ||
name == obs_module_text("AdvSceneSwitcher.randomTab.title") ||
name == obs_module_text("AdvSceneSwitcher.timeTab.title") ||
name == obs_module_text("AdvSceneSwitcher.idleTab.title") ||
name == obs_module_text(
"AdvSceneSwitcher.sceneSequenceTab.title") ||
name == obs_module_text("AdvSceneSwitcher.audioTab.title") ||
name == obs_module_text("AdvSceneSwitcher.videoTab.title") ||
name == obs_module_text("AdvSceneSwitcher.pauseTab.title") ||
name == obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.title");
}
void AdvSceneSwitcher::on_hideLegacyTabs_stateChanged(int state)
{
switcher->hideLegacyTabs = state;
for (int idx = 0; idx < ui->tabWidget->count(); idx++) {
if (isLegacyTab(ui->tabWidget->tabText(idx))) {
#if defined(_WIN32) || defined(__APPLE__)
ui->tabWidget->setTabVisible(idx, !state);
#else
// TODO: Switch to setTabVisible() once QT 5.15 is more wide spread
ui->tabWidget->setTabEnabled(idx, !state);
ui->tabWidget->setStyleSheet(
"QTabBar::tab::disabled {width: 0; height: 0; margin: 0; padding: 0; border: none;} ");
#endif
}
}
}
void AdvSceneSwitcher::setDeprecationWarnings()
{
QString toolTip =
switcher->disableHints
? ""
: obs_module_text(
"AdvSceneSwitcher.deprecatedTabWarning");
for (int idx = 0; idx < ui->tabWidget->count(); idx++) {
if (isLegacyTab(ui->tabWidget->tabText(idx))) {
ui->tabWidget->widget(idx)->setToolTip(toolTip);
}
}
}
QString getDefaultSaveLocation() QString getDefaultSaveLocation()
{ {
QString desktopPath = QStandardPaths::writableLocation( QString desktopPath = QStandardPaths::writableLocation(
@@ -436,6 +455,13 @@ void AdvSceneSwitcher::setTabOrder()
connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved); connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
} }
void AdvSceneSwitcher::setCurrentTab()
{
if (switcher->lastOpenedTab >= 0) {
ui->tabWidget->setCurrentIndex(switcher->lastOpenedTab);
}
}
void AdvSceneSwitcher::restoreWindowGeo() void AdvSceneSwitcher::restoreWindowGeo()
{ {
if (switcher->saveWindowGeo && windowPosValid(switcher->windowPos)) { if (switcher->saveWindowGeo && windowPosValid(switcher->windowPos)) {
@@ -447,6 +473,7 @@ void AdvSceneSwitcher::restoreWindowGeo()
void AdvSceneSwitcher::checkFirstTimeSetup() void AdvSceneSwitcher::checkFirstTimeSetup()
{ {
if (switcher->firstBoot && !switcher->disableHints) { if (switcher->firstBoot && !switcher->disableHints) {
switcher->firstBoot = false;
DisplayMessage( DisplayMessage(
obs_module_text("AdvSceneSwitcher.firstBootMessage")); obs_module_text("AdvSceneSwitcher.firstBootMessage"));
} }
@@ -493,7 +520,9 @@ void SwitcherData::loadSettings(obs_data_t *obj)
loadSceneTriggers(obj); loadSceneTriggers(obj);
loadGeneralSettings(obj); loadGeneralSettings(obj);
loadHotkeys(obj); loadHotkeys(obj);
loadDock(obj);
// Reset on startup and scene collection change
switcher->lastOpenedTab = -1;
} }
void SwitcherData::saveSettings(obs_data_t *obj) void SwitcherData::saveSettings(obs_data_t *obj)
@@ -524,7 +553,6 @@ void SwitcherData::saveSettings(obs_data_t *obj)
saveSceneTriggers(obj); saveSceneTriggers(obj);
saveGeneralSettings(obj); saveGeneralSettings(obj);
saveHotkeys(obj); saveHotkeys(obj);
saveDock(obj);
saveVersion(obj, g_GIT_SHA1); saveVersion(obj, g_GIT_SHA1);
} }
@@ -550,6 +578,7 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
obs_data_set_bool(obj, "showSystemTrayNotifications", obs_data_set_bool(obj, "showSystemTrayNotifications",
showSystemTrayNotifications); showSystemTrayNotifications);
obs_data_set_bool(obj, "disableHints", disableHints); obs_data_set_bool(obj, "disableHints", disableHints);
obs_data_set_bool(obj, "hideLegacyTabs", hideLegacyTabs);
obs_data_set_int(obj, "priority0", functionNamesByPriority[0]); obs_data_set_int(obj, "priority0", functionNamesByPriority[0]);
obs_data_set_int(obj, "priority1", functionNamesByPriority[1]); obs_data_set_int(obj, "priority1", functionNamesByPriority[1]);
@@ -623,6 +652,8 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
showSystemTrayNotifications = showSystemTrayNotifications =
obs_data_get_bool(obj, "showSystemTrayNotifications"); obs_data_get_bool(obj, "showSystemTrayNotifications");
disableHints = obs_data_get_bool(obj, "disableHints"); disableHints = obs_data_get_bool(obj, "disableHints");
obs_data_set_default_bool(obj, "hideLegacyTabs", true);
hideLegacyTabs = obs_data_get_bool(obj, "hideLegacyTabs");
obs_data_set_default_int(obj, "priority0", default_priority_0); 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, "priority1", default_priority_1);
@@ -665,21 +696,21 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
obs_data_set_default_int(obj, "generalTabPos", 0); obs_data_set_default_int(obj, "generalTabPos", 0);
obs_data_set_default_int(obj, "macroTabPos", 1); obs_data_set_default_int(obj, "macroTabPos", 1);
obs_data_set_default_int(obj, "transitionTabPos", 2); obs_data_set_default_int(obj, "networkTabPos", 13);
obs_data_set_default_int(obj, "pauseTabPos", 3); obs_data_set_default_int(obj, "sceneGroupTabPos", 14);
obs_data_set_default_int(obj, "titleTabPos", 4); obs_data_set_default_int(obj, "transitionTabPos", 15);
obs_data_set_default_int(obj, "exeTabPos", 5); obs_data_set_default_int(obj, "pauseTabPos", 16);
obs_data_set_default_int(obj, "regionTabPos", 6); obs_data_set_default_int(obj, "titleTabPos", 2);
obs_data_set_default_int(obj, "mediaTabPos", 7); obs_data_set_default_int(obj, "exeTabPos", 3);
obs_data_set_default_int(obj, "fileTabPos", 8); obs_data_set_default_int(obj, "regionTabPos", 4);
obs_data_set_default_int(obj, "randomTabPos", 9); obs_data_set_default_int(obj, "mediaTabPos", 5);
obs_data_set_default_int(obj, "timeTabPos", 10); obs_data_set_default_int(obj, "fileTabPos", 6);
obs_data_set_default_int(obj, "idleTabPos", 11); obs_data_set_default_int(obj, "randomTabPos", 7);
obs_data_set_default_int(obj, "sequenceTabPos", 12); obs_data_set_default_int(obj, "timeTabPos", 8);
obs_data_set_default_int(obj, "audioTabPos", 13); obs_data_set_default_int(obj, "idleTabPos", 9);
obs_data_set_default_int(obj, "videoTabPos", 14); obs_data_set_default_int(obj, "sequenceTabPos", 10);
obs_data_set_default_int(obj, "networkTabPos", 15); obs_data_set_default_int(obj, "audioTabPos", 11);
obs_data_set_default_int(obj, "sceneGroupTabPos", 16); obs_data_set_default_int(obj, "videoTabPos", 12);
obs_data_set_default_int(obj, "triggerTabPos", 17); obs_data_set_default_int(obj, "triggerTabPos", 17);
tabOrder.clear(); tabOrder.clear();
@@ -791,21 +822,6 @@ void populateAutoStartEventSelection(QComboBox *cb)
"AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming")); "AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming"));
} }
void AdvSceneSwitcher::updateStatus()
{
if (switcher->th && switcher->th->isRunning()) {
if (currentStatusActive) {
return;
}
SetStarted();
} else {
if (!currentStatusActive) {
return;
}
SetStopped();
}
}
void AdvSceneSwitcher::setupGeneralTab() void AdvSceneSwitcher::setupGeneralTab()
{ {
populateSceneSelection(ui->noMatchSwitchScene, false); populateSceneSelection(ui->noMatchSwitchScene, false);
@@ -851,6 +867,7 @@ void AdvSceneSwitcher::setupGeneralTab()
ui->showTrayNotifications->setChecked( ui->showTrayNotifications->setChecked(
switcher->showSystemTrayNotifications); switcher->showSystemTrayNotifications);
ui->uiHintsDisable->setChecked(switcher->disableHints); ui->uiHintsDisable->setChecked(switcher->disableHints);
ui->hideLegacyTabs->setChecked(switcher->hideLegacyTabs);
for (int p : switcher->functionNamesByPriority) { for (int p : switcher->functionNamesByPriority) {
std::string s = ""; std::string s = "";
@@ -926,17 +943,17 @@ void AdvSceneSwitcher::setupGeneralTab()
ui->autoStartEvent->setCurrentIndex( ui->autoStartEvent->setCurrentIndex(
static_cast<int>(switcher->autoStartEvent)); static_cast<int>(switcher->autoStartEvent));
if (switcher->th && switcher->th->isRunning()) { // Set up status control
SetStarted(); auto statusControl = new StatusControl(this, true);
} else { ui->statusLayout->addWidget(statusControl->StatusPrefixLabel(), 1, 0);
SetStopped(); auto tmp = new QHBoxLayout;
} tmp->addWidget(statusControl->StatusLabel());
tmp->addStretch();
// Updates the UI status element if the status changed externally ui->statusLayout->addLayout(tmp, 1, 1);
// (e.g. via hotkeys) ui->statusLayout->addWidget(statusControl->Button(), 2, 1);
QTimer *statusTimer = new QTimer(this); // Hide the now empty invisible shell of the statusControl widget
connect(statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); // as otherwise it could block user input
statusTimer->start(1000); statusControl->hide();
setWindowTitle(windowTitle() + " - " + g_GIT_TAG); setWindowTitle(windowTitle() + " - " + g_GIT_TAG);
} }

View File

@@ -6,6 +6,7 @@
#else #else
#include "ui_advanced-scene-switcher.h" #include "ui_advanced-scene-switcher.h"
#endif #endif
#include "macro-segment-list.hpp"
#include "switcher-data-structs.hpp" #include "switcher-data-structs.hpp"
#include "platform-funcs.hpp" #include "platform-funcs.hpp"
@@ -28,17 +29,15 @@ class AdvSceneSwitcher : public QDialog {
public: public:
std::unique_ptr<Ui_AdvSceneSwitcher> ui; std::unique_ptr<Ui_AdvSceneSwitcher> ui;
bool loading = true; bool loading = true;
bool currentStatusActive = false;
AdvSceneSwitcher(QWidget *parent); AdvSceneSwitcher(QWidget *parent);
~AdvSceneSwitcher(); ~AdvSceneSwitcher();
static AdvSceneSwitcher *window;
void reject() override; void reject() override;
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
void SetStarted();
void SetStopped();
void SetShowFrames(); void SetShowFrames();
void SetHideFrames(); void SetHideFrames();
void clearFrames(QListWidget *list); void clearFrames(QListWidget *list);
@@ -50,15 +49,16 @@ public:
void OpenSequenceExtendEdit(SequenceWidget *sw); void OpenSequenceExtendEdit(SequenceWidget *sw);
void SetEditSceneGroup(SceneGroup &sg); void SetEditSceneGroup(SceneGroup &sg);
bool addNewMacro(std::string &name); bool addNewMacro(std::string &name, std::string format = "");
Macro *getSelectedMacro(); Macro *getSelectedMacro();
void SetEditMacro(Macro &m); void SetEditMacro(Macro &m);
void SetMacroEditAreaDisabled(bool);
void HighlightAction(int idx); void HighlightAction(int idx);
void HighlightCondition(int idx); void HighlightCondition(int idx);
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0); void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0); void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
void ConnectControlSignals(MacroActionEdit *); void SetActionData(Macro &m);
void ConnectControlSignals(MacroConditionEdit *); void SetConditionData(Macro &m);
void SwapActions(Macro *m, int pos1, int pos2); void SwapActions(Macro *m, int pos1, int pos2);
void SwapConditions(Macro *m, int pos1, int pos2); void SwapConditions(Macro *m, int pos1, int pos2);
@@ -81,7 +81,9 @@ public:
void setupVideoTab(); void setupVideoTab();
void setupNetworkTab(); void setupNetworkTab();
void setupMacroTab(); void setupMacroTab();
void setDeprecationWarnings();
void setTabOrder(); void setTabOrder();
void setCurrentTab();
void restoreWindowGeo(); void restoreWindowGeo();
void checkFirstTimeSetup(); void checkFirstTimeSetup();
@@ -89,6 +91,9 @@ signals:
void MacroAdded(const QString &name); void MacroAdded(const QString &name);
void MacroRemoved(const QString &name); void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString newName); void MacroRenamed(const QString &oldName, const QString newName);
void HighlightMacrosChanged(bool value);
void HighlightActionsChanged(bool value);
void HighlightConditionsChanged(bool value);
void SceneGroupAdded(const QString &name); void SceneGroupAdded(const QString &name);
void SceneGroupRemoved(const QString &name); void SceneGroupRemoved(const QString &name);
void SceneGroupRenamed(const QString &oldName, const QString newName); void SceneGroupRenamed(const QString &oldName, const QString newName);
@@ -109,7 +114,6 @@ public slots:
void on_autoStartEvent_currentIndexChanged(int index); void on_autoStartEvent_currentIndexChanged(int index);
void on_noMatchSwitchScene_currentTextChanged(const QString &text); void on_noMatchSwitchScene_currentTextChanged(const QString &text);
void on_checkInterval_valueChanged(int value); void on_checkInterval_valueChanged(int value);
void on_toggleStartButton_clicked();
void on_tabMoved(int from, int to); void on_tabMoved(int from, int to);
void on_tabWidget_currentChanged(int index); void on_tabWidget_currentChanged(int index);
@@ -122,11 +126,17 @@ public slots:
void on_runMacroInParallel_stateChanged(int value); void on_runMacroInParallel_stateChanged(int value);
void on_runMacroOnChange_stateChanged(int value); void on_runMacroOnChange_stateChanged(int value);
void on_macros_currentRowChanged(int idx); void on_macros_currentRowChanged(int idx);
void on_macros_itemChanged(QListWidgetItem *);
void on_conditionAdd_clicked(); void on_conditionAdd_clicked();
void on_conditionRemove_clicked(); void on_conditionRemove_clicked();
void on_conditionUp_clicked();
void on_conditionDown_clicked();
void on_actionAdd_clicked(); void on_actionAdd_clicked();
void on_actionRemove_clicked(); void on_actionRemove_clicked();
void on_actionUp_clicked();
void on_actionDown_clicked();
void UpMacroSegementHotkey();
void DownMacroSegementHotkey();
void DeleteMacroSegementHotkey();
void ShowMacroContextMenu(const QPoint &); void ShowMacroContextMenu(const QPoint &);
void ShowMacroActionsContextMenu(const QPoint &); void ShowMacroActionsContextMenu(const QPoint &);
void ShowMacroConditionsContextMenu(const QPoint &); void ShowMacroConditionsContextMenu(const QPoint &);
@@ -135,14 +145,28 @@ public slots:
void ExpandAllConditions(); void ExpandAllConditions();
void CollapseAllActions(); void CollapseAllActions();
void CollapseAllConditions(); void CollapseAllConditions();
void MinimizeActions();
void MinimizeConditions();
void MacroActionSelectionChanged(int idx);
void MacroActionReorder(int to, int target);
void AddMacroAction(int idx); void AddMacroAction(int idx);
void RemoveMacroAction(int idx); void RemoveMacroAction(int idx);
void MoveMacroActionUp(int idx); void MoveMacroActionUp(int idx);
void MoveMacroActionDown(int idx); void MoveMacroActionDown(int idx);
void MacroConditionSelectionChanged(int idx);
void MacroConditionReorder(int to, int target);
void AddMacroCondition(int idx); void AddMacroCondition(int idx);
void RemoveMacroCondition(int idx); void RemoveMacroCondition(int idx);
void MoveMacroConditionUp(int idx); void MoveMacroConditionUp(int idx);
void MoveMacroConditionDown(int idx); void MoveMacroConditionDown(int idx);
void FadeOutActionControls();
void FadeOutConditionControls();
void ResetOpacityActionControls();
void ResetOpacityConditionControls();
void HighlightControls();
void MacroDragDropReorder(QModelIndex, int, int, QModelIndex, int);
void HighlightOnChange();
void on_macroProperties_clicked();
void on_screenRegionSwitches_currentRowChanged(int idx); void on_screenRegionSwitches_currentRowChanged(int idx);
void on_showFrame_clicked(); void on_showFrame_clicked();
@@ -173,6 +197,7 @@ public slots:
void on_saveWindowGeo_stateChanged(int state); void on_saveWindowGeo_stateChanged(int state);
void on_showTrayNotifications_stateChanged(int state); void on_showTrayNotifications_stateChanged(int state);
void on_uiHintsDisable_stateChanged(int state); void on_uiHintsDisable_stateChanged(int state);
void on_hideLegacyTabs_stateChanged(int state);
void on_exportSettings_clicked(); void on_exportSettings_clicked();
void on_importSettings_clicked(); void on_importSettings_clicked();
@@ -272,11 +297,22 @@ public slots:
void on_threadPriority_currentTextChanged(const QString &text); void on_threadPriority_currentTextChanged(const QString &text);
void updateScreenRegionCursorPos(); void updateScreenRegionCursorPos();
void updateStatus();
void on_close_clicked(); void on_close_clicked();
private: private:
bool MacroTabIsInFocus();
MacroSegmentList *conditionsList = nullptr;
MacroSegmentList *actionsList = nullptr;
enum class MacroSection {
CONDITIONS,
ACTIONS,
};
MacroSection lastInteracted = MacroSection::CONDITIONS;
int currentConditionIdx = -1;
int currentActionIdx = -1;
}; };
/****************************************************************************** /******************************************************************************
@@ -288,7 +324,7 @@ void setNextTransition(const sceneSwitchInfo &ssi, obs_source_t *currentSource,
void overwriteTransitionOverride(const sceneSwitchInfo &ssi, void overwriteTransitionOverride(const sceneSwitchInfo &ssi,
transitionData &td); transitionData &td);
void restoreTransitionOverride(obs_source_t *scene, const transitionData &td); void restoreTransitionOverride(obs_source_t *scene, const transitionData &td);
void switchScene(const sceneSwitchInfo &ssi); void switchScene(const sceneSwitchInfo &ssi, bool force = false);
void switchPreviewScene(const OBSWeakSource &ws); void switchPreviewScene(const OBSWeakSource &ws);
/****************************************************************************** /******************************************************************************

View File

@@ -42,6 +42,7 @@ public:
void SetValue(double value); void SetValue(double value);
void SetUnit(DurationUnit u); void SetUnit(DurationUnit u);
void SetDuration(Duration d); void SetDuration(Duration d);
QDoubleSpinBox *SpinBox() { return _duration; }
private slots: private slots:
void _DurationChanged(double value); void _DurationChanged(double value);

View File

@@ -3,6 +3,7 @@
#include "duration-control.hpp" #include "duration-control.hpp"
#include <QSpinBox> #include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox> #include <QCheckBox>
#include <QHBoxLayout> #include <QHBoxLayout>
@@ -13,26 +14,44 @@ enum class AudioAction {
MASTER_VOLUME, MASTER_VOLUME,
}; };
enum class FadeType {
DURATION,
RATE,
};
class MacroActionAudio : public MacroAction { class MacroActionAudio : public MacroAction {
public: public:
MacroActionAudio(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionAudio>(); return std::make_shared<MacroActionAudio>(m);
} }
OBSWeakSource _audioSource; OBSWeakSource _audioSource;
AudioAction _action = AudioAction::MUTE; AudioAction _action = AudioAction::MUTE;
FadeType _fadeType = FadeType::DURATION;
int _volume = 0; int _volume = 0;
bool _fade = false; bool _fade = false;
Duration _duration; Duration _duration;
double _rate = 100.;
bool _wait = false;
bool _abortActiveFade = false;
private: private:
void StartFade();
void FadeVolume();
void SetVolume(float vol);
float GetVolume();
void SetFadeActive(bool value);
bool FadeActive();
std::atomic_int *GetFadeIdPtr();
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
}; };
@@ -44,7 +63,6 @@ public:
MacroActionAudioEdit( MacroActionAudioEdit(
QWidget *parent, QWidget *parent,
std::shared_ptr<MacroActionAudio> entryData = nullptr); std::shared_ptr<MacroActionAudio> entryData = nullptr);
void SetWidgetVisibility();
void UpdateEntryData(); void UpdateEntryData();
static QWidget *Create(QWidget *parent, static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action) std::shared_ptr<MacroAction> action)
@@ -60,18 +78,29 @@ private slots:
void VolumeChanged(int value); void VolumeChanged(int value);
void FadeChanged(int value); void FadeChanged(int value);
void DurationChanged(double seconds); void DurationChanged(double seconds);
void RateChanged(double value);
void WaitChanged(int value);
void AbortActiveFadeChanged(int value);
void FadeTypeChanged(int value);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
protected: protected:
QComboBox *_audioSources; QComboBox *_audioSources;
QComboBox *_actions; QComboBox *_actions;
QComboBox *_fadeTypes;
QSpinBox *_volumePercent; QSpinBox *_volumePercent;
QCheckBox *_fade; QCheckBox *_fade;
DurationSelection *_duration; DurationSelection *_duration;
QHBoxLayout *_fadeLayout; QDoubleSpinBox *_rate;
QCheckBox *_wait;
QCheckBox *_abortActiveFade;
QHBoxLayout *_fadeTypeLayout;
QVBoxLayout *_fadeOptionsLayout;
std::shared_ptr<MacroActionAudio> _entryData; std::shared_ptr<MacroActionAudio> _entryData;
private: private:
void SetWidgetVisibility();
bool _loading = true; bool _loading = true;
}; };

View File

@@ -2,7 +2,7 @@
#include "macro.hpp" #include "macro.hpp"
struct MacroActionInfo { struct MacroActionInfo {
using TCreateMethod = std::shared_ptr<MacroAction> (*)(); using TCreateMethod = std::shared_ptr<MacroAction> (*)(Macro *m);
using TCreateWidgetMethod = QWidget *(*)(QWidget *parent, using TCreateWidgetMethod = QWidget *(*)(QWidget *parent,
std::shared_ptr<MacroAction>); std::shared_ptr<MacroAction>);
TCreateMethod _createFunc = nullptr; TCreateMethod _createFunc = nullptr;
@@ -14,7 +14,8 @@ class MacroActionFactory {
public: public:
MacroActionFactory() = delete; MacroActionFactory() = delete;
static bool Register(const std::string &id, MacroActionInfo); static bool Register(const std::string &id, MacroActionInfo);
static std::shared_ptr<MacroAction> Create(const std::string &id); static std::shared_ptr<MacroAction> Create(const std::string &id,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent, static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroAction> action); std::shared_ptr<MacroAction> action);
static auto GetActionTypes() { return _methods; } static auto GetActionTypes() { return _methods; }
@@ -33,6 +34,7 @@ public:
std::shared_ptr<MacroAction> * = nullptr, std::shared_ptr<MacroAction> * = nullptr,
const std::string &id = "scene_switch"); const std::string &id = "scene_switch");
void UpdateEntryData(const std::string &id); void UpdateEntryData(const std::string &id);
void SetEntryData(std::shared_ptr<MacroAction> *);
private slots: private slots:
void ActionSelectionChanged(const QString &text); void ActionSelectionChanged(const QString &text);

View File

@@ -1,8 +1,9 @@
#pragma once #pragma once
#include <QSpinBox>
#include <QPlainTextEdit>
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "file-selection.hpp" #include "file-selection.hpp"
#include "resizing-text-edit.hpp"
#include <QSpinBox>
enum class FileAction { enum class FileAction {
WRITE, WRITE,
@@ -11,15 +12,16 @@ enum class FileAction {
class MacroActionFile : public MacroAction { class MacroActionFile : public MacroAction {
public: public:
MacroActionFile(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionFile>(); return std::make_shared<MacroActionFile>(m);
} }
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath"); std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
@@ -56,7 +58,7 @@ signals:
protected: protected:
FileSelection *_filePath; FileSelection *_filePath;
QPlainTextEdit *_text; ResizingPlainTextEdit *_text;
QComboBox *_actions; QComboBox *_actions;
std::shared_ptr<MacroActionFile> _entryData; std::shared_ptr<MacroActionFile> _entryData;

View File

@@ -1,7 +1,8 @@
#pragma once #pragma once
#include <QSpinBox>
#include <QPlainTextEdit>
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "resizing-text-edit.hpp"
#include <QSpinBox>
enum class FilterAction { enum class FilterAction {
ENABLE, ENABLE,
@@ -11,15 +12,16 @@ enum class FilterAction {
class MacroActionFilter : public MacroAction { class MacroActionFilter : public MacroAction {
public: public:
MacroActionFilter(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionFilter>(); return std::make_shared<MacroActionFilter>(m);
} }
OBSWeakSource _source; OBSWeakSource _source;
@@ -62,7 +64,7 @@ protected:
QComboBox *_filters; QComboBox *_filters;
QComboBox *_actions; QComboBox *_actions;
QPushButton *_getSettings; QPushButton *_getSettings;
QPlainTextEdit *_settings; ResizingPlainTextEdit *_settings;
std::shared_ptr<MacroActionFilter> _entryData; std::shared_ptr<MacroActionFilter> _entryData;
private: private:

View File

@@ -8,14 +8,15 @@
class MacroActionHotkey : public MacroAction { class MacroActionHotkey : public MacroAction {
public: public:
MacroActionHotkey(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionHotkey>(); return std::make_shared<MacroActionHotkey>(m);
} }
OBSWeakSource _HotkeySource; OBSWeakSource _HotkeySource;

View File

@@ -9,19 +9,21 @@ enum class PerformMacroAction {
UNPAUSE, UNPAUSE,
RESET_COUNTER, RESET_COUNTER,
RUN, RUN,
STOP,
}; };
class MacroActionMacro : public MacroRefAction { class MacroActionMacro : public MacroRefAction {
public: public:
MacroActionMacro(Macro *m) : MacroRefAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionMacro>(); return std::make_shared<MacroActionMacro>(m);
} }
PerformMacroAction _action = PerformMacroAction::PAUSE; PerformMacroAction _action = PerformMacroAction::PAUSE;

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "duration-control.hpp"
#include <QSpinBox> #include <QSpinBox>
#include <QHBoxLayout> #include <QHBoxLayout>
@@ -11,23 +12,26 @@ enum class MediaAction {
RESTART, RESTART,
NEXT, NEXT,
PREVIOUS, PREVIOUS,
SEEK,
}; };
class MacroActionMedia : public MacroAction { class MacroActionMedia : public MacroAction {
public: public:
MacroActionMedia(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionMedia>(); return std::make_shared<MacroActionMedia>(m);
} }
OBSWeakSource _mediaSource; OBSWeakSource _mediaSource;
MediaAction _action = MediaAction::PLAY; MediaAction _action = MediaAction::PLAY;
Duration _seek;
private: private:
static bool _registered; static bool _registered;
@@ -53,15 +57,20 @@ public:
private slots: private slots:
void SourceChanged(const QString &text); void SourceChanged(const QString &text);
void ActionChanged(int value); void ActionChanged(int value);
void DurationChanged(double value);
void DurationUnitChanged(DurationUnit unit);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
protected: protected:
QComboBox *_mediaSources; QComboBox *_mediaSources;
QComboBox *_actions; QComboBox *_actions;
DurationSelection *_seek;
std::shared_ptr<MacroActionMedia> _entryData; std::shared_ptr<MacroActionMedia> _entryData;
private: private:
void SetWidgetVisibility();
QHBoxLayout *_mainLayout; QHBoxLayout *_mainLayout;
bool _loading = true; bool _loading = true;
}; };

View File

@@ -13,14 +13,15 @@ enum class PluginStateAction {
class MacroActionPluginState : public MacroAction { class MacroActionPluginState : public MacroAction {
public: public:
MacroActionPluginState(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionPluginState>(); return std::make_shared<MacroActionPluginState>(m);
} }
PluginStateAction _action = PluginStateAction::STOP; PluginStateAction _action = PluginStateAction::STOP;

View File

@@ -4,15 +4,16 @@
class MacroActionPreviewScene : public MacroAction { class MacroActionPreviewScene : public MacroAction {
public: public:
MacroActionPreviewScene(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionPreviewScene>(); return std::make_shared<MacroActionPreviewScene>(m);
} }
SceneSelection _scene; SceneSelection _scene;

View File

@@ -3,15 +3,16 @@
class MacroActionProfile : public MacroAction { class MacroActionProfile : public MacroAction {
public: public:
MacroActionProfile(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionProfile>(); return std::make_shared<MacroActionProfile>(m);
} }
std::string _profile; std::string _profile;

View File

@@ -4,19 +4,19 @@
#include <QPushButton> #include <QPushButton>
#include <QListWidget> #include <QListWidget>
#include <QDialog>
#include <unordered_map> #include <unordered_map>
class MacroActionRandom : public MultiMacroRefAction { class MacroActionRandom : public MultiMacroRefAction {
public: public:
MacroActionRandom(Macro *m) : MultiMacroRefAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionRandom>(); return std::make_shared<MacroActionRandom>(m);
} }
// TODO: add weights to each macro ... // TODO: add weights to each macro ...
@@ -28,17 +28,6 @@ private:
static const std::string id; static const std::string id;
}; };
class MacroDialog : public QDialog {
Q_OBJECT
public:
MacroDialog(QWidget *parent);
static bool AskForMacro(QWidget *parent, std::string &macroName);
private:
MacroSelection *_macroSelection;
};
class MacroActionRandomEdit : public QWidget { class MacroActionRandomEdit : public QWidget {
Q_OBJECT Q_OBJECT

View File

@@ -13,14 +13,15 @@ enum class RecordAction {
class MacroActionRecord : public MacroAction { class MacroActionRecord : public MacroAction {
public: public:
MacroActionRecord(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionRecord>(); return std::make_shared<MacroActionRecord>(m);
} }
RecordAction _action = RecordAction::STOP; RecordAction _action = RecordAction::STOP;

View File

@@ -14,14 +14,15 @@ enum class ReplayBufferAction {
class MacroActionReplayBuffer : public MacroAction { class MacroActionReplayBuffer : public MacroAction {
public: public:
MacroActionReplayBuffer(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionReplayBuffer>(); return std::make_shared<MacroActionReplayBuffer>(m);
} }
ReplayBufferAction _action = ReplayBufferAction::STOP; ReplayBufferAction _action = ReplayBufferAction::STOP;

View File

@@ -9,15 +9,16 @@
class MacroActionRun : public MacroAction { class MacroActionRun : public MacroAction {
public: public:
MacroActionRun(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionRun>(); return std::make_shared<MacroActionRun>(m);
} }
std::string _path = obs_module_text("AdvSceneSwitcher.enterPath"); std::string _path = obs_module_text("AdvSceneSwitcher.enterPath");
@@ -49,6 +50,7 @@ private slots:
void RemoveArg(); void RemoveArg();
void ArgUp(); void ArgUp();
void ArgDown(); void ArgDown();
void ArgItemClicked(QListWidgetItem *);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);

View File

@@ -3,15 +3,16 @@
class MacroActionSceneCollection : public MacroAction { class MacroActionSceneCollection : public MacroAction {
public: public:
MacroActionSceneCollection(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionSceneCollection>(); return std::make_shared<MacroActionSceneCollection>(m);
} }
std::string _sceneCollection; std::string _sceneCollection;

View File

@@ -15,15 +15,16 @@ enum class SceneOrderAction {
class MacroActionSceneOrder : public MacroAction { class MacroActionSceneOrder : public MacroAction {
public: public:
MacroActionSceneOrder(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionSceneOrder>(); return std::make_shared<MacroActionSceneOrder>(m);
} }
SceneSelection _scene; SceneSelection _scene;

View File

@@ -3,14 +3,15 @@
class MacroActionSceneSwap : public MacroAction { class MacroActionSceneSwap : public MacroAction {
public: public:
MacroActionSceneSwap(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionSceneSwap>(); return std::make_shared<MacroActionSceneSwap>(m);
} }
private: private:

View File

@@ -8,6 +8,7 @@
class MacroActionSwitchScene : public MacroAction { class MacroActionSwitchScene : public MacroAction {
public: public:
MacroActionSwitchScene(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
@@ -15,9 +16,9 @@ public:
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionSwitchScene>(); return std::make_shared<MacroActionSwitchScene>(m);
} }
SceneSelection _scene; SceneSelection _scene;
TransitionSelection _transition; TransitionSelection _transition;

View File

@@ -2,21 +2,22 @@
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "scene-selection.hpp" #include "scene-selection.hpp"
#include "scene-item-selection.hpp" #include "scene-item-selection.hpp"
#include "resizing-text-edit.hpp"
#include <QSpinBox> #include <QSpinBox>
#include <QPlainTextEdit>
class MacroActionSceneTransform : public MacroAction { class MacroActionSceneTransform : public MacroAction {
public: public:
MacroActionSceneTransform(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionSceneTransform>(); return std::make_shared<MacroActionSceneTransform>(m);
} }
std::string GetSettings(); std::string GetSettings();
void SetSettings(std::string &); void SetSettings(std::string &);
@@ -60,7 +61,7 @@ protected:
SceneSelectionWidget *_scenes; SceneSelectionWidget *_scenes;
SceneItemSelectionWidget *_sources; SceneItemSelectionWidget *_sources;
QPushButton *_getSettings; QPushButton *_getSettings;
QPlainTextEdit *_settings; ResizingPlainTextEdit *_settings;
std::shared_ptr<MacroActionSceneTransform> _entryData; std::shared_ptr<MacroActionSceneTransform> _entryData;
private: private:

View File

@@ -17,15 +17,16 @@ enum class SceneItemSourceType {
class MacroActionSceneVisibility : public MacroAction { class MacroActionSceneVisibility : public MacroAction {
public: public:
MacroActionSceneVisibility(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionSceneVisibility>(); return std::make_shared<MacroActionSceneVisibility>(m);
} }
SceneSelection _scene; SceneSelection _scene;

View File

@@ -3,15 +3,16 @@
class MacroActionScreenshot : public MacroAction { class MacroActionScreenshot : public MacroAction {
public: public:
MacroActionScreenshot(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionScreenshot>(); return std::make_shared<MacroActionScreenshot>(m);
} }
OBSWeakSource _source; OBSWeakSource _source;

View File

@@ -0,0 +1,78 @@
#pragma once
#include "macro-action-edit.hpp"
#include "macro-selection.hpp"
#include <QPushButton>
#include <QListWidget>
#include <QCheckBox>
#include <QTimer>
class MacroActionSequence : public MultiMacroRefAction {
public:
MacroActionSequence(Macro *m) : MultiMacroRefAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
MacroRef GetNextMacro(bool advance = true);
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionSequence>(m);
}
bool _restart = true;
MacroRef _lastSequenceMacro;
int _lastIdx = -1;
private:
static bool _registered;
static const std::string id;
};
class MacroActionSequenceEdit : public QWidget {
Q_OBJECT
public:
MacroActionSequenceEdit(
QWidget *parent,
std::shared_ptr<MacroActionSequence> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionSequenceEdit(
parent,
std::dynamic_pointer_cast<MacroActionSequence>(action));
}
private slots:
void MacroRemove(const QString &name);
void MacroRename(const QString &oldName, const QString &newName);
void Add();
void Remove();
void Up();
void Down();
void MacroItemClicked(QListWidgetItem *);
void ContinueFromClicked();
void RestartChanged(int state);
void UpdateStatusLine();
protected:
std::shared_ptr<MacroActionSequence> _entryData;
private:
int FindEntry(const std::string &macro);
void SetMacroListSize();
QListWidget *_macroList;
QPushButton *_add;
QPushButton *_remove;
QPushButton *_up;
QPushButton *_down;
QPushButton *_continueFrom;
QCheckBox *_restart;
QLabel *_statusLine;
QTimer _statusTimer;
bool _loading = true;
};

View File

@@ -1,10 +1,10 @@
#pragma once #pragma once
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "resizing-text-edit.hpp"
#include <QSpinBox> #include <QSpinBox>
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QPushButton>
#include <QPlainTextEdit>
enum class SourceAction { enum class SourceAction {
ENABLE, ENABLE,
@@ -14,15 +14,16 @@ enum class SourceAction {
class MacroActionSource : public MacroAction { class MacroActionSource : public MacroAction {
public: public:
MacroActionSource(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionSource>(); return std::make_shared<MacroActionSource>(m);
} }
OBSWeakSource _source; OBSWeakSource _source;
@@ -62,7 +63,7 @@ protected:
QComboBox *_sources; QComboBox *_sources;
QComboBox *_actions; QComboBox *_actions;
QPushButton *_getSettings; QPushButton *_getSettings;
QPlainTextEdit *_settings; ResizingPlainTextEdit *_settings;
QLabel *_warning; QLabel *_warning;
std::shared_ptr<MacroActionSource> _entryData; std::shared_ptr<MacroActionSource> _entryData;

View File

@@ -1,10 +1,10 @@
#pragma once #pragma once
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "duration-control.hpp"
#include <QDoubleSpinBox> #include <QDoubleSpinBox>
#include <QComboBox> #include <QComboBox>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <chrono>
enum class StreamAction { enum class StreamAction {
STOP, STOP,
@@ -13,22 +13,22 @@ enum class StreamAction {
class MacroActionStream : public MacroAction { class MacroActionStream : public MacroAction {
public: public:
MacroActionStream(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionStream>(); return std::make_shared<MacroActionStream>(m);
} }
StreamAction _action = StreamAction::STOP; StreamAction _action = StreamAction::STOP;
private: private:
// Acts as a safeguard for misconfigured streaming setups leading to an bool CooldownDurationReached();
// endless error spam. static std::chrono::high_resolution_clock::time_point s_lastAttempt;
Duration _retryCooldown;
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;

View File

@@ -5,14 +5,15 @@
class MacroActionSystray : public MacroAction { class MacroActionSystray : public MacroAction {
public: public:
MacroActionSystray(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionSystray>(); return std::make_shared<MacroActionSystray>(m);
} }
std::string _msg = ""; std::string _msg = "";

View File

@@ -15,15 +15,16 @@ enum class TimerAction {
class MacroActionTimer : public MacroRefAction { class MacroActionTimer : public MacroRefAction {
public: public:
MacroActionTimer(Macro *m) : MacroRefAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionTimer>(); return std::make_shared<MacroActionTimer>(m);
} }
Duration _duration; Duration _duration;
TimerAction _actionType = TimerAction::PAUSE; TimerAction _actionType = TimerAction::PAUSE;

View File

@@ -8,15 +8,16 @@
class MacroActionTransition : public MacroAction { class MacroActionTransition : public MacroAction {
public: public:
MacroActionTransition(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionTransition>(); return std::make_shared<MacroActionTransition>(m);
} }
bool _setDuration = true; bool _setDuration = true;

View File

@@ -13,14 +13,15 @@ enum class VCamAction {
class MacroActionVCam : public MacroAction { class MacroActionVCam : public MacroAction {
public: public:
MacroActionVCam(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
void LogAction(); void LogAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionVCam>(); return std::make_shared<MacroActionVCam>(m);
} }
VCamAction _action = VCamAction::STOP; VCamAction _action = VCamAction::STOP;

View File

@@ -12,13 +12,14 @@ enum class WaitType {
class MacroActionWait : public MacroAction { class MacroActionWait : public MacroAction {
public: public:
MacroActionWait(Macro *m) : MacroAction(m) {}
bool PerformAction(); bool PerformAction();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create() static std::shared_ptr<MacroAction> Create(Macro *m)
{ {
return std::make_shared<MacroActionWait>(); return std::make_shared<MacroActionWait>(m);
} }
Duration _duration; Duration _duration;
Duration _duration2; Duration _duration2;

View File

@@ -6,24 +6,39 @@
#include <QComboBox> #include <QComboBox>
#include <chrono> #include <chrono>
enum class AudioCondition { enum class AudioConditionCheckType {
OUTPUT_VOLUME,
CONFIGURED_VOLUME,
};
enum class AudioOutputCondition {
ABOVE, ABOVE,
BELOW, BELOW,
// TODO: To be removed
MUTE,
UNMUTE,
};
enum class AudioVolumeCondition {
ABOVE,
EXACT,
BELOW,
MUTE, MUTE,
UNMUTE, UNMUTE,
}; };
class MacroConditionAudio : public MacroCondition { class MacroConditionAudio : public MacroCondition {
public: public:
MacroConditionAudio(Macro *m) : MacroCondition(m) {}
~MacroConditionAudio(); ~MacroConditionAudio();
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionAudio>(); return std::make_shared<MacroConditionAudio>(m);
} }
static void SetVolumeLevel(void *data, static void SetVolumeLevel(void *data,
const float magnitude[MAX_AUDIO_CHANNELS], const float magnitude[MAX_AUDIO_CHANNELS],
@@ -33,10 +48,16 @@ public:
OBSWeakSource _audioSource; OBSWeakSource _audioSource;
int _volume = 0; int _volume = 0;
AudioCondition _condition = AudioCondition::ABOVE; AudioConditionCheckType _checkType =
AudioConditionCheckType::OUTPUT_VOLUME;
AudioOutputCondition _outputCondition = AudioOutputCondition::ABOVE;
AudioVolumeCondition _volumeCondition = AudioVolumeCondition::ABOVE;
obs_volmeter_t *_volmeter = nullptr; obs_volmeter_t *_volmeter = nullptr;
private: private:
bool CheckOutputCondition();
bool CheckVolumeCondition();
float _peak = -std::numeric_limits<float>::infinity(); float _peak = -std::numeric_limits<float>::infinity();
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
@@ -63,10 +84,13 @@ private slots:
void SourceChanged(const QString &text); void SourceChanged(const QString &text);
void VolumeThresholdChanged(int vol); void VolumeThresholdChanged(int vol);
void ConditionChanged(int cond); void ConditionChanged(int cond);
void CheckTypeChanged(int cond);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
protected: protected:
QComboBox *_checkTypes;
QComboBox *_audioSources; QComboBox *_audioSources;
QComboBox *_condition; QComboBox *_condition;
QSpinBox *_volume; QSpinBox *_volume;

View File

@@ -14,13 +14,14 @@ enum class CursorCondition {
class MacroConditionCursor : public MacroCondition { class MacroConditionCursor : public MacroCondition {
public: public:
MacroConditionCursor(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionCursor>(); return std::make_shared<MacroConditionCursor>(m);
} }
CursorCondition _condition = CursorCondition::REGION; CursorCondition _condition = CursorCondition::REGION;

View File

@@ -13,27 +13,44 @@ enum class DateCondition {
BETWEEN, BETWEEN,
}; };
enum class DayOfWeekSelection {
ANY = 0,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY,
};
class MacroConditionDate : public MacroCondition { class MacroConditionDate : public MacroCondition {
public: public:
MacroConditionDate(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionDate>(); return std::make_shared<MacroConditionDate>(m);
} }
QDateTime _dateTime; DayOfWeekSelection _dayOfWeek = DayOfWeekSelection::ANY;
QDateTime _dateTime2; QDateTime _dateTime = QDateTime::currentDateTime();
QDateTime _dateTime2 = QDateTime::currentDateTime();
bool _ignoreDate = false; bool _ignoreDate = false;
bool _ignoreTime = false; bool _ignoreTime = false;
bool _repeat = false; bool _repeat = false;
Duration _duration; Duration _duration;
DateCondition _condition = DateCondition::AT; DateCondition _condition = DateCondition::AT;
bool _dayOfWeekCheck = false;
private: private:
bool CheckDayOfWeek(int64_t);
bool CheckRegularDate(int64_t);
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
}; };
@@ -55,28 +72,45 @@ public:
} }
private slots: private slots:
void DayOfWeekChanged(int day);
void ConditionChanged(int cond); void ConditionChanged(int cond);
void DateTimeChanged(const QDateTime &datetime); void DateChanged(const QDate &date);
void DateTime2Changed(const QDateTime &datetime); void TimeChanged(const QTime &time);
void Date2Changed(const QDate &date);
void Time2Changed(const QTime &time);
void IgnoreDateChanged(int state); void IgnoreDateChanged(int state);
void IgnoreTimeChanged(int state); void IgnoreTimeChanged(int state);
void RepeatChanged(int state); void RepeatChanged(int state);
void DurationChanged(double seconds); void DurationChanged(double seconds);
void DurationUnitChanged(DurationUnit unit); void DurationUnitChanged(DurationUnit unit);
void AdvancedSettingsToggleClicked();
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
protected: protected:
QComboBox *_dayOfWeek;
QTimeEdit *_weekTime;
QComboBox *_condition; QComboBox *_condition;
QDateTimeEdit *_dateTime; QDateEdit *_date;
QDateTimeEdit *_dateTime2; QTimeEdit *_time;
QLabel *_separator;
QDateEdit *_date2;
QTimeEdit *_time2;
QCheckBox *_ignoreDate; QCheckBox *_ignoreDate;
QCheckBox *_ignoreTime; QCheckBox *_ignoreTime;
QCheckBox *_repeat; QCheckBox *_repeat;
DurationSelection *_duration; DurationSelection *_duration;
QPushButton *_advancedSettingsTooggle;
QHBoxLayout *_simpleLayout;
QHBoxLayout *_advancedLayout;
QHBoxLayout *_repeatLayout;
std::shared_ptr<MacroConditionDate> _entryData; std::shared_ptr<MacroConditionDate> _entryData;
private: private:
void SetWidgetStatus();
void ShowSecondDateSelection(bool visible); void ShowSecondDateSelection(bool visible);
bool _loading = true; bool _loading = true;
}; };

View File

@@ -2,7 +2,7 @@
#include "macro.hpp" #include "macro.hpp"
struct MacroConditionInfo { struct MacroConditionInfo {
using TCreateMethod = std::shared_ptr<MacroCondition> (*)(); using TCreateMethod = std::shared_ptr<MacroCondition> (*)(Macro *m);
using TCreateWidgetMethod = using TCreateWidgetMethod =
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>); QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
TCreateMethod _createFunc = nullptr; TCreateMethod _createFunc = nullptr;
@@ -15,7 +15,8 @@ class MacroConditionFactory {
public: public:
MacroConditionFactory() = delete; MacroConditionFactory() = delete;
static bool Register(const std::string &, MacroConditionInfo); static bool Register(const std::string &, MacroConditionInfo);
static std::shared_ptr<MacroCondition> Create(const std::string &); static std::shared_ptr<MacroCondition> Create(const std::string &,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent, static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition>); std::shared_ptr<MacroCondition>);
static auto GetConditionTypes() { return _methods; } static auto GetConditionTypes() { return _methods; }
@@ -37,6 +38,7 @@ public:
bool IsRootNode(); bool IsRootNode();
void SetRootNode(bool); void SetRootNode(bool);
void UpdateEntryData(const std::string &id); void UpdateEntryData(const std::string &id);
void SetEntryData(std::shared_ptr<MacroCondition> *);
private slots: private slots:
void LogicSelectionChanged(int idx); void LogicSelectionChanged(int idx);
@@ -46,6 +48,7 @@ private slots:
void DurationUnitChanged(DurationUnit unit); void DurationUnitChanged(DurationUnit unit);
private: private:
void SetLogicSelection();
MacroSegment *Data(); MacroSegment *Data();
QComboBox *_logicSelection; QComboBox *_logicSelection;

View File

@@ -1,13 +1,13 @@
#pragma once #pragma once
#include "macro.hpp" #include "macro.hpp"
#include "file-selection.hpp" #include "file-selection.hpp"
#include "resizing-text-edit.hpp"
#include <QWidget> #include <QWidget>
#include <QComboBox> #include <QComboBox>
#include <QDateTime> #include <QDateTime>
#include <QLineEdit> #include <QLineEdit>
#include <QPushButton> #include <QPushButton>
#include <QPlainTextEdit>
#include <QCheckBox> #include <QCheckBox>
enum class FileType { enum class FileType {
@@ -17,14 +17,15 @@ enum class FileType {
class MacroConditionFile : public MacroCondition { class MacroConditionFile : public MacroCondition {
public: public:
MacroConditionFile(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionFile>(); return std::make_shared<MacroConditionFile>(m);
} }
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath"); std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
@@ -74,7 +75,7 @@ signals:
protected: protected:
QComboBox *_fileType; QComboBox *_fileType;
FileSelection *_filePath; FileSelection *_filePath;
QPlainTextEdit *_matchText; ResizingPlainTextEdit *_matchText;
QCheckBox *_useRegex; QCheckBox *_useRegex;
QCheckBox *_checkModificationDate; QCheckBox *_checkModificationDate;
QCheckBox *_checkFileContent; QCheckBox *_checkFileContent;

View File

@@ -1,9 +1,9 @@
#pragma once #pragma once
#include "macro.hpp" #include "macro.hpp"
#include "resizing-text-edit.hpp"
#include <QComboBox> #include <QComboBox>
#include <QPushButton> #include <QPushButton>
#include <QPlainTextEdit>
#include <QCheckBox> #include <QCheckBox>
enum class FilterCondition { enum class FilterCondition {
@@ -14,14 +14,15 @@ enum class FilterCondition {
class MacroConditionFilter : public MacroCondition { class MacroConditionFilter : public MacroCondition {
public: public:
MacroConditionFilter(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionFilter>(); return std::make_shared<MacroConditionFilter>(m);
} }
OBSWeakSource _source; OBSWeakSource _source;
@@ -66,7 +67,7 @@ protected:
QComboBox *_filters; QComboBox *_filters;
QComboBox *_conditions; QComboBox *_conditions;
QPushButton *_getSettings; QPushButton *_getSettings;
QPlainTextEdit *_settings; ResizingPlainTextEdit *_settings;
QCheckBox *_regex; QCheckBox *_regex;
std::shared_ptr<MacroConditionFilter> _entryData; std::shared_ptr<MacroConditionFilter> _entryData;

View File

@@ -5,15 +5,15 @@
class MacroConditionHotkey : public MacroCondition { class MacroConditionHotkey : public MacroCondition {
public: public:
MacroConditionHotkey(); MacroConditionHotkey(Macro *m);
~MacroConditionHotkey(); ~MacroConditionHotkey();
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionHotkey>(); return std::make_shared<MacroConditionHotkey>(m);
} }
void SetPressed() { _pressed = true; } void SetPressed() { _pressed = true; }

View File

@@ -7,13 +7,14 @@
class MacroConditionIdle : public MacroCondition { class MacroConditionIdle : public MacroCondition {
public: public:
MacroConditionIdle(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionIdle>(); return std::make_shared<MacroConditionIdle>(m);
} }
Duration _duration; Duration _duration;

View File

@@ -21,14 +21,15 @@ enum class CounterCondition {
class MacroConditionMacro : public MacroRefCondition { class MacroConditionMacro : public MacroRefCondition {
public: public:
MacroConditionMacro(Macro *m) : MacroRefCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionMacro>(); return std::make_shared<MacroConditionMacro>(m);
} }
MacroConditionMacroType _type = MacroConditionMacroType::STATE; MacroConditionMacroType _type = MacroConditionMacroType::STATE;

View File

@@ -31,7 +31,7 @@ enum class MediaState {
// Just a marker // Just a marker
LAST_OBS_MEDIA_STATE, LAST_OBS_MEDIA_STATE,
// states added for use in the plugin // states added for use in the plugin
PLAYED_TO_END = custom_media_states_offset, PLAYLIST_ENDED = custom_media_states_offset,
ANY, ANY,
}; };
@@ -43,20 +43,22 @@ enum class MediaSourceType {
class MacroConditionMedia : public MacroCondition { class MacroConditionMedia : public MacroCondition {
public: public:
MacroConditionMedia(Macro *m) : MacroCondition(m) {}
~MacroConditionMedia(); ~MacroConditionMedia();
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionMedia>(); return std::make_shared<MacroConditionMedia>(m);
} }
void ClearSignalHandler(); void ClearSignalHandler();
void ResetSignalHandler(); void ResetSignalHandler();
static void MediaStopped(void *data, calldata_t *); static void MediaStopped(void *data, calldata_t *);
static void MediaEnded(void *data, calldata_t *); static void MediaEnded(void *data, calldata_t *);
static void MediaNext(void *data, calldata_t *);
MediaSourceType _sourceType = MediaSourceType::SOURCE; MediaSourceType _sourceType = MediaSourceType::SOURCE;
SceneSelection _scene; SceneSelection _scene;
@@ -69,17 +71,19 @@ public:
bool _onlyMatchonChagne = false; bool _onlyMatchonChagne = false;
private: private:
bool CheckTime();
bool CheckState();
bool CheckPlaylistEnd(const obs_media_state);
bool CheckMediaMatch(); bool CheckMediaMatch();
bool _stopped = false; bool _stopped = false;
bool _ended = false; bool _ended = false;
bool _next = false;
// TODO: Remove _alreadyMatched as it does not make much sense when // TODO: Remove _alreadyMatched as it does not make much sense when
// time restrictions for macro conditions are available. // time restrictions for macro conditions are available.
// Trigger scene change only once even if media state might trigger repeatedly
bool _alreadyMatched = false; bool _alreadyMatched = false;
// Workaround to enable use of "ended" to specify end of VLC playlist // Workaround to enable use of "ended" to specify end of VLC playlist
bool _previousStateEnded = false; bool _previousStateEnded = false;
bool _playedToEnd = false;
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;

View File

@@ -0,0 +1,118 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include <QDoubleSpinBox>
#include <util/platform.h>
enum class StatsType {
FPS,
CPU_USAGE,
DISK_USAGE, // not implemented
MEM_USAGE,
AVG_FRAMETIME,
RENDER_LAG,
ENCODE_LAG,
STREAM_DROPPED_FRAMES,
STREAM_BITRATE,
STREAM_MB_SENT,
RECORDING_DROPPED_FRAMES, // not sure if this makes sense
RECORDING_BITRATE,
RECORDING_MB_SENT,
};
enum class StatsCondition {
ABOVE,
EQUALS,
BELOW,
};
class MacroConditionStats : public MacroCondition {
public:
MacroConditionStats(Macro *m);
~MacroConditionStats();
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionStats>(m);
}
double _value = 0;
StatsType _type = StatsType::FPS;
StatsCondition _condition = StatsCondition::ABOVE;
private:
bool CheckFPS();
bool CheckCPU();
bool CheckMemory();
bool CheckAvgFrametime();
bool CheckRenderLag();
bool CheckEncodeLag();
bool CheckStreamDroppedFrames();
bool CheckStreamBitrate();
bool CheckStreamMBSent();
bool CheckRecordingDroppedFrames();
bool CheckRecordingBitrate();
bool CheckRecordingMBSent();
os_cpu_usage_info_t *_cpu_info = nullptr;
uint32_t _first_encoded = 0xFFFFFFFF;
uint32_t _first_skipped = 0xFFFFFFFF;
uint32_t _first_rendered = 0xFFFFFFFF;
uint32_t _first_lagged = 0xFFFFFFFF;
struct OutputInfo {
void Update(obs_output_t *output);
uint64_t lastBytesSent = 0;
uint64_t lastBytesSentTime = 0;
int first_total = 0;
int first_dropped = 0;
double dropped_relative = 0.0;
long double kbps = 0.0l;
};
OutputInfo _streamInfo;
OutputInfo _recordingInfo;
static bool _registered;
static const std::string id;
};
class MacroConditionStatsEdit : public QWidget {
Q_OBJECT
public:
MacroConditionStatsEdit(
QWidget *parent,
std::shared_ptr<MacroConditionStats> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionStatsEdit(
parent,
std::dynamic_pointer_cast<MacroConditionStats>(cond));
}
private slots:
void ValueChanged(double value);
void StatsTypeChanged(int type);
void ConditionChanged(int cond);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_stats;
QComboBox *_condition;
QDoubleSpinBox *_value;
std::shared_ptr<MacroConditionStats> _entryData;
private:
void SetWidgetVisibility();
bool _loading = true;
};

View File

@@ -4,22 +4,25 @@
#include <QComboBox> #include <QComboBox>
enum class PluginStateCondition { enum class PluginStateCondition {
SCENESWITCHED, SCENE_SWITCHED,
RUNNING, RUNNING,
SHUTDOWN,
}; };
class MacroConditionPluginState : public MacroCondition { class MacroConditionPluginState : public MacroCondition {
public: public:
MacroConditionPluginState(Macro *m) : MacroCondition(m) {}
~MacroConditionPluginState();
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionPluginState>(); return std::make_shared<MacroConditionPluginState>(m);
} }
PluginStateCondition _condition = PluginStateCondition::SCENESWITCHED; PluginStateCondition _condition = PluginStateCondition::SCENE_SWITCHED;
private: private:
static bool _registered; static bool _registered;

View File

@@ -6,14 +6,15 @@
class MacroConditionProcess : public MacroCondition { class MacroConditionProcess : public MacroCondition {
public: public:
MacroConditionProcess(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionProcess>(); return std::make_shared<MacroConditionProcess>(m);
} }
std::string _process; std::string _process;

View File

@@ -11,13 +11,14 @@ enum class RecordState {
class MacroConditionRecord : public MacroCondition { class MacroConditionRecord : public MacroCondition {
public: public:
MacroConditionRecord(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionRecord>(); return std::make_shared<MacroConditionRecord>(m);
} }
RecordState _recordState = RecordState::STOP; RecordState _recordState = RecordState::STOP;

View File

@@ -13,13 +13,14 @@ enum class ReplayBufferState {
class MacroConditionReplayBuffer : public MacroCondition { class MacroConditionReplayBuffer : public MacroCondition {
public: public:
MacroConditionReplayBuffer(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionReplayBuffer>(); return std::make_shared<MacroConditionReplayBuffer>(m);
} }
ReplayBufferState _state = ReplayBufferState::STOP; ReplayBufferState _state = ReplayBufferState::STOP;

View File

@@ -14,14 +14,15 @@ enum class SceneOrderCondition {
class MacroConditionSceneOrder : public MacroCondition { class MacroConditionSceneOrder : public MacroCondition {
public: public:
MacroConditionSceneOrder(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionSceneOrder>(); return std::make_shared<MacroConditionSceneOrder>(m);
} }
SceneSelection _scene; SceneSelection _scene;

View File

@@ -2,21 +2,22 @@
#include "macro.hpp" #include "macro.hpp"
#include "scene-selection.hpp" #include "scene-selection.hpp"
#include "scene-item-selection.hpp" #include "scene-item-selection.hpp"
#include "resizing-text-edit.hpp"
#include <QSpinBox> #include <QSpinBox>
#include <QPlainTextEdit>
#include <QCheckBox> #include <QCheckBox>
class MacroConditionSceneTransform : public MacroCondition { class MacroConditionSceneTransform : public MacroCondition {
public: public:
MacroConditionSceneTransform(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionSceneTransform>(); return std::make_shared<MacroConditionSceneTransform>(m);
} }
SceneSelection _scene; SceneSelection _scene;
@@ -60,7 +61,7 @@ protected:
SceneSelectionWidget *_scenes; SceneSelectionWidget *_scenes;
SceneItemSelectionWidget *_sources; SceneItemSelectionWidget *_sources;
QPushButton *_getSettings; QPushButton *_getSettings;
QPlainTextEdit *_settings; ResizingPlainTextEdit *_settings;
QCheckBox *_regex; QCheckBox *_regex;
std::shared_ptr<MacroConditionSceneTransform> _entryData; std::shared_ptr<MacroConditionSceneTransform> _entryData;

View File

@@ -12,14 +12,15 @@ enum class SceneVisibilityCondition {
class MacroConditionSceneVisibility : public MacroCondition { class MacroConditionSceneVisibility : public MacroCondition {
public: public:
MacroConditionSceneVisibility(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionSceneVisibility>(); return std::make_shared<MacroConditionSceneVisibility>(m);
} }
SceneSelection _scene; SceneSelection _scene;

View File

@@ -15,14 +15,15 @@ enum class SceneType {
class MacroConditionScene : public MacroCondition { class MacroConditionScene : public MacroCondition {
public: public:
MacroConditionScene(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionScene>(); return std::make_shared<MacroConditionScene>(m);
} }
SceneSelection _scene; SceneSelection _scene;

View File

@@ -1,9 +1,9 @@
#pragma once #pragma once
#include "macro.hpp" #include "macro.hpp"
#include "resizing-text-edit.hpp"
#include <QComboBox> #include <QComboBox>
#include <QPushButton> #include <QPushButton>
#include <QPlainTextEdit>
#include <QCheckBox> #include <QCheckBox>
enum class SourceCondition { enum class SourceCondition {
@@ -14,14 +14,15 @@ enum class SourceCondition {
class MacroConditionSource : public MacroCondition { class MacroConditionSource : public MacroCondition {
public: public:
MacroConditionSource(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionSource>(); return std::make_shared<MacroConditionSource>(m);
} }
OBSWeakSource _source = nullptr; OBSWeakSource _source = nullptr;
@@ -63,7 +64,7 @@ protected:
QComboBox *_sources; QComboBox *_sources;
QComboBox *_conditions; QComboBox *_conditions;
QPushButton *_getSettings; QPushButton *_getSettings;
QPlainTextEdit *_settings; ResizingPlainTextEdit *_settings;
QCheckBox *_regex; QCheckBox *_regex;
std::shared_ptr<MacroConditionSource> _entryData; std::shared_ptr<MacroConditionSource> _entryData;

View File

@@ -12,13 +12,14 @@ enum class StreamState {
class MacroConditionStream : public MacroCondition { class MacroConditionStream : public MacroCondition {
public: public:
MacroConditionStream(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionStream>(); return std::make_shared<MacroConditionStream>(m);
} }
StreamState _streamState = StreamState::STOP; StreamState _streamState = StreamState::STOP;

View File

@@ -13,14 +13,15 @@ enum class StudioModeCondition {
class MacroConditionStudioMode : public MacroCondition { class MacroConditionStudioMode : public MacroCondition {
public: public:
MacroConditionStudioMode(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionStudioMode>(); return std::make_shared<MacroConditionStudioMode>(m);
} }
StudioModeCondition _condition = StudioModeCondition _condition =

View File

@@ -17,13 +17,14 @@ enum class TimerType {
class MacroConditionTimer : public MacroCondition { class MacroConditionTimer : public MacroCondition {
public: public:
MacroConditionTimer(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionTimer>(); return std::make_shared<MacroConditionTimer>(m);
} }
void Pause(); void Pause();
void Continue(); void Continue();

View File

@@ -18,14 +18,15 @@ enum class TransitionCondition {
class MacroConditionTransition : public MacroCondition { class MacroConditionTransition : public MacroCondition {
public: public:
MacroConditionTransition(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionTransition>(); return std::make_shared<MacroConditionTransition>(m);
} }
void ConnectToTransitionSignals(); void ConnectToTransitionSignals();
void DisconnectTransitionSignals(); void DisconnectTransitionSignals();

View File

@@ -12,13 +12,14 @@ enum class VCamState {
class MacroConditionVCam : public MacroCondition { class MacroConditionVCam : public MacroCondition {
public: public:
MacroConditionVCam(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionVCam>(); return std::make_shared<MacroConditionVCam>(m);
} }
VCamState _state = VCamState::STOP; VCamState _state = VCamState::STOP;

View File

@@ -6,14 +6,15 @@
class MacroConditionWindow : public MacroCondition { class MacroConditionWindow : public MacroCondition {
public: public:
MacroConditionWindow(Macro *m) : MacroCondition(m) {}
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj); bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string GetShortDesc(); std::string GetShortDesc();
std::string GetId() { return id; }; std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create() static std::shared_ptr<MacroCondition> Create(Macro *m)
{ {
return std::make_shared<MacroConditionWindow>(); return std::make_shared<MacroConditionWindow>(m);
} }
private: private:

View File

@@ -1,31 +0,0 @@
#pragma once
#include <QFrame>
#include <QPropertyAnimation>
#include <QScrollArea>
#include <QPushButton>
#include <QLayout>
#include <QWidget>
class MacroEntryControls : public QWidget {
Q_OBJECT
public:
explicit MacroEntryControls(const int animationDuration = 300,
QWidget *parent = 0);
void Show(bool visible = true);
signals:
void Add();
void Remove();
void Up();
void Down();
private:
QPushButton *_add;
QPushButton *_remove;
QPushButton *_up;
QPushButton *_down;
QPropertyAnimation *_animation = nullptr;
};

View File

@@ -0,0 +1,32 @@
#pragma once
#include <QLabel>
#include <QCheckBox>
#include <QTimer>
#include <memory>
class Macro;
class MacroListEntryWidget : public QWidget {
Q_OBJECT
public:
MacroListEntryWidget(std::shared_ptr<Macro>, bool highlight,
QWidget *parent);
void SetName(const QString &);
void SetMacro(std::shared_ptr<Macro> &);
private slots:
void PauseChanged(int);
void HighlightExecuted();
void UpdatePaused();
void EnableHighlight(bool);
private:
QTimer _timer;
QLabel *_name;
QCheckBox *_running;
std::shared_ptr<Macro> _macro;
bool _highlightExecutedMacros = false;
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <QWidget>
#include <QDialog>
#include <QCheckBox>
#include <obs-data.h>
class MacroProperties {
public:
void Save(obs_data_t *obj);
void Load(obs_data_t *obj);
bool _highlightExecuted = false;
bool _highlightConditions = false;
bool _highlightActions = false;
};
class MacroPropertiesDialog : public QDialog {
Q_OBJECT
public:
MacroPropertiesDialog(QWidget *parent, const MacroProperties &);
static bool AskForSettings(QWidget *parent, MacroProperties &userInput);
private:
QCheckBox *_executed;
QCheckBox *_conditions;
QCheckBox *_actions;
};

View File

@@ -0,0 +1,61 @@
#pragma once
#include "macro-segment.hpp"
#include <QWidget>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QLabel>
#include <thread>
class MacroSegmentList : public QScrollArea {
Q_OBJECT
public:
MacroSegmentList(QWidget *parent = nullptr);
virtual ~MacroSegmentList();
void SetHelpMsg(const QString &msg);
void SetHelpMsgVisible(bool visible);
MacroSegmentEdit *WidgetAt(int idx);
void Insert(int idx, QWidget *widget);
void Add(QWidget *widget);
void Remove(int idx);
void Clear(int idx = 0); // Clear all elements >= idx
void Highlight(int idx);
void SetCollapsed(bool);
void SetSelection(int idx);
QVBoxLayout *ContentLayout() { return _contentLayout; }
signals:
void SelectionChagned(int idx);
void Reorder(int source, int target);
protected:
bool eventFilter(QObject *object, QEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
private:
int GetDragIndex(const QPoint &);
int GetDropIndex(const QPoint &);
int GetWidgetIdx(const QPoint &);
void CheckScroll();
void CheckDropLine(const QPoint &);
bool IsInListArea(const QPoint &);
QRect GetContentItemRectWithPadding(int idx);
void HideLastDropLine();
int _dragPosition = -1;
int _dropLineIdx = -1;
QPoint _dragCursorPos;
std::thread _autoScrollThread;
std::atomic_bool _autoScroll{false};
QVBoxLayout *_layout;
QVBoxLayout *_contentLayout;
QLabel *_helpMsg;
};

View File

@@ -1,9 +1,16 @@
#pragma once #pragma once
#include <QWidget> #include <QWidget>
#include <QFrame>
#include <QVBoxLayout>
#include <QTimer>
#include <obs.hpp> #include <obs.hpp>
class Macro;
class MacroSegment { class MacroSegment {
public: public:
MacroSegment(Macro *m) : _macro(m) {}
Macro *GetMacro() { return _macro; }
void SetIndex(int idx) { _idx = idx; } void SetIndex(int idx) { _idx = idx; }
int GetIndex() { return _idx; } int GetIndex() { return _idx; }
void SetCollapsed(bool collapsed) { _collapsed = collapsed; } void SetCollapsed(bool collapsed) { _collapsed = collapsed; }
@@ -12,33 +19,38 @@ public:
virtual bool Load(obs_data_t *obj) = 0; virtual bool Load(obs_data_t *obj) = 0;
virtual std::string GetShortDesc(); virtual std::string GetShortDesc();
virtual std::string GetId() = 0; virtual std::string GetId() = 0;
void SetHighlight();
bool Highlight();
protected: protected:
int _idx = 0; int _idx = 0;
bool _collapsed = false; bool _collapsed = false;
// UI helper
bool _highlight = false;
private:
Macro *_macro = nullptr;
}; };
class Section; class Section;
class QLabel; class QLabel;
class MacroEntryControls;
class MacroSegmentEdit : public QWidget { class MacroSegmentEdit : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
MacroSegmentEdit(QWidget *parent = nullptr); MacroSegmentEdit(bool highlight, QWidget *parent = nullptr);
// Use this function to avoid accidental edits when scrolling through // Use this function to avoid accidental edits when scrolling through
// list of actions and conditions // list of actions and conditions
void SetFocusPolicyOfWidgets(); void SetFocusPolicyOfWidgets();
void SetCollapsed(bool collapsed); void SetCollapsed(bool collapsed);
void SetSelected(bool);
protected slots: protected slots:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
void Add();
void Remove();
void Up();
void Down();
void Collapsed(bool); void Collapsed(bool);
void Highlight();
void EnableHighlight(bool);
signals: signals:
void MacroAdded(const QString &name); void MacroAdded(const QString &name);
void MacroRemoved(const QString &name); void MacroRemoved(const QString &name);
@@ -46,21 +58,43 @@ signals:
void SceneGroupAdded(const QString &name); void SceneGroupAdded(const QString &name);
void SceneGroupRemoved(const QString &name); void SceneGroupRemoved(const QString &name);
void SceneGroupRenamed(const QString &oldName, const QString newName); void SceneGroupRenamed(const QString &oldName, const QString newName);
void AddAt(int idx);
void RemoveAt(int idx);
void UpAt(int idx);
void DownAt(int idx);
protected: protected:
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
Section *_section; Section *_section;
QLabel *_headerInfo; QLabel *_headerInfo;
MacroEntryControls *_controls; QWidget *_frame;
QVBoxLayout *_contentLayout;
private: private:
enum class DropLineState {
NONE,
ABOVE,
BELOW,
};
virtual MacroSegment *Data() = 0; virtual MacroSegment *Data() = 0;
void ShowDropLine(DropLineState);
// The reason for using two separate frame widget each with their own
// stylesheet and changing their visibility vs. using a single frame
// and changing the stylesheet at runtime is that the operation of
// adjusting the stylesheet is very expensive and can take multiple
// hundred milliseconds per widget.
// This performance impact would hurt in areas like drag and drop or
// emitting the "SelectionChanged" signal.
QFrame *_noBorderframe;
QFrame *_borderFrame;
// In most cases the line above the widget will be used.
// The lower one will only be used if the segment is the last one in
// the list.
QFrame *_dropLineAbove;
QFrame *_dropLineBelow;
bool _showHighlight;
QTimer _timer;
friend class MacroSegmentList;
}; };
class MouseWheelWidgetAdjustmentGuard : public QObject { class MouseWheelWidgetAdjustmentGuard : public QObject {

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <QComboBox> #include <QComboBox>
#include <QDialog>
class Macro; class Macro;
@@ -9,9 +10,22 @@ class MacroSelection : public QComboBox {
public: public:
MacroSelection(QWidget *parent); MacroSelection(QWidget *parent);
void SetCurrentMacro(Macro *); void SetCurrentMacro(Macro *);
void HideSelectedMacro(); // Macro currently being edited
void ShowAllMacros();
private slots: private slots:
void MacroAdd(const QString &name); void MacroAdd(const QString &name);
void MacroRemove(const QString &name); void MacroRemove(const QString &name);
void MacroRename(const QString &oldName, const QString &newName); void MacroRename(const QString &oldName, const QString &newName);
}; };
class MacroSelectionDialog : public QDialog {
Q_OBJECT
public:
MacroSelectionDialog(QWidget *parent);
static bool AskForMacro(QWidget *parent, std::string &macroName);
private:
MacroSelection *_macroSelection;
};

View File

@@ -39,6 +39,7 @@ struct LogicTypeInfo {
class MacroCondition : public MacroSegment { class MacroCondition : public MacroSegment {
public: public:
MacroCondition(Macro *m) : MacroSegment(m) {}
virtual bool CheckCondition() = 0; virtual bool CheckCondition() = 0;
virtual bool Save(obs_data_t *obj) = 0; virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0; virtual bool Load(obs_data_t *obj) = 0;
@@ -61,6 +62,7 @@ private:
class MacroAction : public MacroSegment { class MacroAction : public MacroSegment {
public: public:
MacroAction(Macro *m) : MacroSegment(m) {}
virtual bool PerformAction() = 0; virtual bool PerformAction() = 0;
virtual bool Save(obs_data_t *obj) = 0; virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0; virtual bool Load(obs_data_t *obj) = 0;
@@ -73,9 +75,10 @@ public:
virtual ~Macro(); virtual ~Macro();
bool CeckMatch(); bool CeckMatch();
bool PerformAction(bool forceParallel = false, bool PerformActions(bool forceParallel = false,
bool ignorePause = false); bool ignorePause = false);
bool Matched() { return _matched; } bool Matched() { return _matched; }
int64_t MsSinceLastCheck();
std::string Name() { return _name; } std::string Name() { return _name; }
void SetName(const std::string &name); void SetName(const std::string &name);
void SetRunInParallel(bool parallel) { _runInParallel = parallel; } void SetRunInParallel(bool parallel) { _runInParallel = parallel; }
@@ -86,6 +89,9 @@ public:
bool MatchOnChange() { return _matchOnChange; } bool MatchOnChange() { return _matchOnChange; }
int GetCount() { return _count; }; int GetCount() { return _count; };
void ResetCount() { _count = 0; }; void ResetCount() { _count = 0; };
void AddHelperThread(std::thread &&);
bool GetStop() { return _stop; }
void Stop();
std::deque<std::shared_ptr<MacroCondition>> &Conditions() std::deque<std::shared_ptr<MacroCondition>> &Conditions()
{ {
return _conditions; return _conditions;
@@ -103,6 +109,11 @@ public:
// Helper function for plugin state condition regarding scene change // Helper function for plugin state condition regarding scene change
bool SwitchesScene(); bool SwitchesScene();
// UI helpers for the macro tab
bool WasExecutedRecently();
bool OnChangePreventedActionsRecently();
void ResetUIHelpers();
private: private:
void SetupHotkeys(); void SetupHotkeys();
void ClearHotkeys(); void ClearHotkeys();
@@ -110,6 +121,7 @@ private:
void ResetTimers(); void ResetTimers();
void RunActions(bool &ret, bool ignorePause); void RunActions(bool &ret, bool ignorePause);
void RunActions(bool ignorePause); void RunActions(bool ignorePause);
void SetOnChangeHighlight();
std::string _name = ""; std::string _name = "";
std::deque<std::shared_ptr<MacroCondition>> _conditions; std::deque<std::shared_ptr<MacroCondition>> _conditions;
@@ -124,9 +136,17 @@ private:
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID; obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID; obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID;
// UI helpers for the macro tab
bool _wasExecutedRecently = false;
bool _onChangeTriggered = false;
std::chrono::high_resolution_clock::time_point _lastCheckTime{};
bool _die = false;
bool _stop = false; bool _stop = false;
bool _done = true; bool _done = true;
std::thread _thread; std::thread _backgroundThread;
std::vector<std::thread> _helperThreads;
}; };
Macro *GetMacroByName(const char *name); Macro *GetMacroByName(const char *name);
@@ -151,18 +171,21 @@ private:
class MacroRefCondition : public MacroCondition { class MacroRefCondition : public MacroCondition {
public: public:
MacroRefCondition(Macro *m) : MacroCondition(m) {}
void ResolveMacroRef(); void ResolveMacroRef();
MacroRef _macro; MacroRef _macro;
}; };
class MacroRefAction : public MacroAction { class MacroRefAction : public MacroAction {
public: public:
MacroRefAction(Macro *m) : MacroAction(m) {}
void ResolveMacroRef(); void ResolveMacroRef();
MacroRef _macro; MacroRef _macro;
}; };
class MultiMacroRefAction : public MacroAction { class MultiMacroRefAction : public MacroAction {
public: public:
MultiMacroRefAction(Macro *m) : MacroAction(m) {}
void ResolveMacroRef(); void ResolveMacroRef();
std::vector<MacroRef> _macros; std::vector<MacroRef> _macros;
}; };

View File

@@ -0,0 +1,17 @@
#pragma once
#include <QPlainTextEdit>
class ResizingPlainTextEdit : public QPlainTextEdit {
Q_OBJECT
public:
ResizingPlainTextEdit(QWidget *parent, const int scrollAt = 10,
const int minLines = 3,
const int paddingLines = 2);
private slots:
void ResizeTexteditArea();
private:
const int _scrollAt;
const int _minLines;
const int _paddingLines;
};

Some files were not shown because too many files have changed in this diff Show More