Compare commits

...

237 Commits

Author SHA1 Message Date
WarmUpTill
20488afdd1 Fix tempvars being reset across macros for cached widgets
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-06-12 21:29:07 +02:00
WarmUpTill
d9d387ad47 Fix crash when deleting cached macro widgets 2025-06-12 21:29:07 +02:00
WarmUpTill
0736d673e1 Add option to check if chat was cleared / message was removed 2025-06-12 21:29:07 +02:00
WarmUpTill
34151e4bc6 Fix "Get user info" only allowing small values for user ids 2025-06-12 21:29:07 +02:00
WarmUpTill
1346c19bec Fix crash when invalid token is used to connect to Twitch chat 2025-06-12 21:29:07 +02:00
WarmUpTill
be6bc48231 Fix macro selection resetting when any macro was removed 2025-06-12 18:33:24 +02:00
WarmUpTill
79a8ad57af Rework HTTP action URL input
The URL to be accessed can now be passed as a single URL instead of it
being split into a host and path component
2025-06-12 18:33:24 +02:00
WarmUpTill
daeb9275a3 Add support for inline scripts
* Script can be defined in the macro segment or loaded from a file
* Supports both LUA and Python
* Can be used for actions and conditions
* obs_script_create and obs_script_destroy are resolved at runtime
  (Let's hope the API remains stable)
2025-06-09 14:20:44 +02:00
WarmUpTill
73b542a4db Move scripting support to seperate project 2025-06-09 14:20:44 +02:00
WarmUpTill
e3471066e9 Export symbols 2025-06-09 14:20:44 +02:00
WarmUpTill
ec41c06b4d Update feature request description 2025-06-09 14:20:44 +02:00
WarmUpTill
201e45d058 Fix macro tree not resizing properly with OBS 31.1.0 2025-06-07 18:56:42 +02:00
WarmUpTill
b17aa30432 Fix attempting to cache widgets of about to be deleted macros 2025-06-06 19:14:46 +02:00
WarmUpTill
93703c80bc Hide option to verify timestamps when functionality is disabled 2025-06-01 11:57:26 +02:00
WarmUpTill
d7951a7179 Cleanup constness 2025-06-01 11:57:26 +02:00
WarmUpTill
d42a3b584a Rework resource tab hotkey handling
Added hotkey to add new entry
2025-06-01 11:57:26 +02:00
WarmUpTill
becd1bd02a Set default hotkey for new macro to CTRL + N 2025-06-01 11:57:26 +02:00
WarmUpTill
30422aecf3 Fix crash when deleting macro 2025-06-01 11:57:26 +02:00
WarmUpTill
c567e6ef7f Allow import of json files
Automatic backups are stored as json files
2025-06-01 11:57:26 +02:00
WarmUpTill
1ca61f3ed4 Add option to not link date-tz even if the source code exists 2025-05-28 22:11:07 +02:00
WarmUpTill
1affe9dce3 Fix macro segment list entries not resizing properly 2025-05-27 22:37:46 +02:00
WarmUpTill
406e3c1855 Cleanup
* Add variable support to process condition
* Display unresolved variable value in Window action
* Add tooltips to indicate that variables are supported
2025-05-27 22:37:46 +02:00
WarmUpTill
c05dd40c4c Add option to change various OBS video settings 2025-05-27 22:37:46 +02:00
WarmUpTill
9a86ecac42 Add option to disable widget caching 2025-05-27 22:37:46 +02:00
WarmUpTill
721a786e79 Rework preview dialog to improve performance
Object detection and OCR models were constantly being re-initialized
each frame due to copy in the signal / slot handling
2025-05-27 22:37:46 +02:00
WarmUpTill
32d29875ed Move responsibility of cascade init to ObjDetectParameters 2025-05-27 22:37:46 +02:00
warmuptill
0e5f56b562 Add tesseract config file support 2025-05-27 22:37:46 +02:00
WarmUpTill
5490fabf92 Add hotkey to create new macro 2025-05-27 22:37:46 +02:00
WarmUpTill
5e3ab19940 Add ProfileSelectionWidget
Refreshes list of profiles when widget becomes visible to support widget
caching.
2025-05-27 22:37:46 +02:00
WarmUpTill
1c94a1ab44 Add X and Y position tempvars to cursor condition 2025-05-27 22:37:46 +02:00
WarmUpTill
34baa56134 Update process list on visibility change
This is done to support widget caching
2025-05-27 22:37:46 +02:00
WarmUpTill
5ce4171773 Add MonitorSelectionWidget
Refreshes list of monitors when widget becomes visible to support widget
caching
2025-05-27 22:37:46 +02:00
WarmUpTill
c281c6db83 Add WindowSelectionWidget
Refreshes list of windows when widget becomes visible to support widget
caching
2025-05-27 22:37:46 +02:00
WarmUpTill
c43439ee64 Add option to keep selection when FilterComboBox entry doesn't exist 2025-05-27 22:37:46 +02:00
WarmUpTill
a84731b8fe Rework TransitionSelectionWidget to support widget caching 2025-05-27 22:37:46 +02:00
WarmUpTill
61fbff5821 Fix SetSourceSetting() not changing settings if the default value is set
This won't help in scenarios in which even a default value does not
exist, e.g. "text" for the text sources, as here it is unclear which
value type to use
2025-05-27 22:37:46 +02:00
WarmUpTill
1381654fed Add Add support for widget caching to FilterSelectionWidget 2025-05-27 22:37:46 +02:00
WarmUpTill
9805601c07 Add support for widget caching to SceneItemSelectionWidget 2025-05-27 22:37:46 +02:00
WarmUpTill
fcf57ee031 Add support for widget caching to SceneSelectionWidget 2025-05-27 22:37:46 +02:00
WarmUpTill
ba3e87a761 Rework SourceSelectionWidget to support widget caching 2025-05-27 22:37:46 +02:00
WarmUpTill
3dd3f576c3 Add hint that parallel condition check feature is experimental 2025-05-27 22:37:46 +02:00
WarmUpTill
7403a18e96 Cleanup 2025-05-27 22:37:46 +02:00
warmuptill
56494480ba Cache macro segment widgets for each macro 2025-05-27 22:37:46 +02:00
warmuptill
cdc5d16e95 Allow changing Tesseract model directory
This will prevent custom models being deleted when installing a new
version of the plugin, as the plugin's data directory might get wiped on
some operating systems.
2025-05-27 22:37:46 +02:00
warmuptill
27aed79305 Enable building with OCR support in Ubuntu environment 2025-05-27 22:37:46 +02:00
WarmUpTill
7ec95e33eb Hide outdated video condition options
The "throttle" and "reduce matching latency" optiohns were introduced
before "short circuit evaluation" was available and are now outdated.

Both don't behave as expected with this option enabled.
The throttling effect can be better achieved with an additional "Timer"
condition.
2025-05-27 22:37:46 +02:00
WarmUpTill
98260b25a1 Add option to toggle the pause state of a macro 2025-05-27 22:37:46 +02:00
WarmUpTill
45b37de9f7 Add MQTT connection tab 2025-05-27 20:23:07 +02:00
WarmUpTill
027a3e9074 Add MQTT condition and action 2025-05-27 20:23:07 +02:00
WarmUpTill
cab50e0922 Add MQTT helpers 2025-05-27 20:23:07 +02:00
WarmUpTill
88514e209d CI: build paho.mqtt.cpp libs 2025-05-27 20:23:07 +02:00
WarmUpTill
4ed7f13b72 Add paho.mqtt.cpp dependency 2025-05-27 20:23:07 +02:00
WarmUpTill
0fb11ac274 Add copy / move to MessageDispatcher 2025-05-27 20:23:07 +02:00
WarmUpTill
942933290a Remove unnecessary include 2025-05-27 20:23:07 +02:00
WarmUpTill
f13beaead2 Reduce log spam in case of invalid token
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The caching mechanism for the token validity checks did not have the
desired effect, and the Twitch channel to user id mapping function was
attempting to resolve names even with an invalid token.
2025-05-08 21:10:13 +02:00
WarmUpTill
13dba6527d Exclude jsoncons files from release folder 2025-05-08 21:10:13 +02:00
WarmUpTill
b78a6510a9 Start the plugin on first boot 2025-05-08 21:10:13 +02:00
WarmUpTill
6c22f438b6 Disable macro add button highlight when importing macros 2025-05-08 21:10:13 +02:00
WarmUpTill
347abe6c84 Enable macro highlighting by default 2025-05-08 21:10:13 +02:00
WarmUpTill
230863adda Lazy initialize macro segment widgets
This should make it less time consuming to accidentally switch between
macros with a large amount of macro segments
2025-05-08 21:10:13 +02:00
WarmUpTill
f59478fac7 Cleanup 2025-05-08 21:10:13 +02:00
WarmUpTill
8adac79cd8 Rework option to run macro actions in parallel to other macros 2025-05-08 20:44:56 +02:00
WarmUpTill
ea93c44db7 Add option to check macro conditions in parallel to other macros 2025-05-08 20:44:56 +02:00
WarmUpTill
9e20b341d8 Rework Macro constructors to support passing GlobalMacroSettings 2025-05-08 20:44:56 +02:00
WarmUpTill
232cbb06f6 Disable Twitch event timestamp verification by default
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
It brakes too often to be worth it as an option to be enabled by
default.
2025-05-07 18:33:04 +02:00
WarmUpTill
e9d57a0f4a Refactor log helpers and add option to disable logging
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-05-01 19:55:28 +02:00
WarmUpTill
9633a61a65 Add option to disable Twitch event timestamp verification
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2025-05-01 12:46:20 +02:00
WarmUpTill
0643b250e1 Fix typos 2025-05-01 12:46:20 +02:00
WarmUpTill
b52738881f Fix "HTTP" action failing to send data on MacOS
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2025-04-30 21:25:44 +02:00
WarmUpTill
470d5ba3d7 Log HTTP errors when verbose logging is enabled 2025-04-30 21:25:44 +02:00
WarmUpTill
8326e72047 Add link to installation wiki page
Some checks failed
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-04-23 11:48:17 +02:00
WarmUpTill
12ab4d8cf9 Add option to query JSON strings and access JSON array elements
Some checks failed
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
2025-04-22 15:35:45 +02:00
WarmUpTill
295ec9eb81 Add more JSON tests 2025-04-22 15:35:45 +02:00
WarmUpTill
4c5dbd4b7c Add more JSON helpers 2025-04-22 15:35:45 +02:00
WarmUpTill
5568f92ad0 Move JSON helpers to lib 2025-04-22 15:35:45 +02:00
WarmUpTill
7c4c0056ce Remove libremidi patch 2025-04-22 15:35:45 +02:00
WarmUpTill
d2b4b1cc07 Add jsoncons 2025-04-22 15:35:45 +02:00
WarmUpTill
a3ca22d238 Cleanup CMakeLists.txt 2025-04-22 15:35:45 +02:00
WarmUpTill
c73542a3cc Fix variables not being re-resolved on Load()
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-04-17 19:22:52 +02:00
WarmUpTill
70bbc7cdac Implement proper timestamp validation for Twitch messages
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-04-11 19:02:37 +02:00
WarmUpTill
d892298995 Add missing "[adv-ss]" log tag 2025-04-11 19:02:37 +02:00
WarmUpTill
0fe31432be Add "previous scene" to the "scene has (not) changed" checks 2025-04-11 18:57:58 +02:00
WarmUpTill
aaa0113ccb Ignore Xerrors 2025-04-11 18:57:25 +02:00
WarmUpTill
b908954b46 CI: Add cmake setup step to Linux build
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-04-02 13:48:23 +02:00
WarmUpTill
b0eede8a85 Add "disable" effect to macro conditions using "ignore" logic selection 2025-04-02 13:48:23 +02:00
WarmUpTill
aa87911b71 Use cpp-httplib based HTTP action type
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The goal is to remove the older, more limited version of the HTTP action
at some point in the future.
2025-03-30 14:06:19 +02:00
WarmUpTill
1b05019acc Add option to hide entries from action selection
Intended to be used to hide legacy version of actions types (e.g. HTTP)
2025-03-30 14:01:41 +02:00
WarmUpTill
78a5a2629d Hide the "remote" file check option
This option will be removed at some point in the future.
The http action should be used instead.
2025-03-30 14:01:41 +02:00
WarmUpTill
634270a978 Cleanup includes 2025-03-30 14:01:41 +02:00
WarmUpTill
78ba22e1e4 Hide "get settings" button when setting macro property value 2025-03-30 14:01:41 +02:00
WarmUpTill
aba5737a60 Update and clean up locale (qwe1154323937) 2025-03-30 14:01:41 +02:00
WarmUpTill
4315f7f621 Exclude unwanted files from sources archive 2025-03-30 14:01:41 +02:00
WarmUpTill
53c535962f Use tab key to switch to dialog controls and set default focus to input 2025-03-30 14:01:41 +02:00
WarmUpTill
1e718b78c7 Fall back to project version if git tag cannot be queried 2025-03-30 14:01:41 +02:00
WarmUpTill
b1d2156228 Update libremidi to v4.3.0 2025-03-30 14:01:41 +02:00
WarmUpTill
9c3c953c6b Ignore deprecation warnings for Qt 6.7 and above
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-22 18:09:51 +01:00
WarmUpTill
ae74f68db7 Add option to close projector windows 2025-03-22 18:09:51 +01:00
WarmUpTill
213f1bba36 Add "Any" entry of the reward selection only for Twitch condition 2025-03-22 18:09:51 +01:00
WarmUpTill
5a2cb0bd68 Add more scripting signals / procedures 2025-03-22 18:09:51 +01:00
WarmUpTill
23b461828b Add start start / stop callbacks 2025-03-22 18:09:51 +01:00
WarmUpTill
9944a1b03b Move interval reset handling 2025-03-22 18:09:51 +01:00
WarmUpTill
eaad4d1bbd Fix Twitch helper caches misbehaving
API calls with different sets of arguments could map to the same key
value pair, which resulted in unexpected behavior for functions using
those caches
2025-03-22 18:09:51 +01:00
WarmUpTill
17d9b73b9a Fix thread flood when Twitch event sub connection is lost
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-15 12:31:13 +01:00
WarmUpTill
d6185a6099 Add handling for Twitch API throttling 2025-03-15 12:31:13 +01:00
WarmUpTill
41c06b9edb Refactor Twitch condition event handling 2025-03-15 12:31:13 +01:00
WarmUpTill
d8807077da Add option to query reward information 2025-03-15 12:31:13 +01:00
WarmUpTill
34e125f82d Rework chache handling
* No longer provide multiple HTTP helper functions with the same name
* Default to cache not being used
* Explicitly use the cache for certain Twitch actions and conditions
* Clear caches on shutdown to avoid leaks being reported
2025-03-15 12:31:13 +01:00
WarmUpTill
d670b6d07e Move JSON temp var heper functions 2025-03-15 12:31:13 +01:00
WarmUpTill
7b0f985f18 Add macro properties for 'reward' object in redemption events 2025-03-15 12:31:13 +01:00
WarmUpTill
212be923f6 Add option to query user information 2025-03-15 12:31:13 +01:00
WarmUpTill
88fcb57e9f Cleanup 2025-03-15 12:31:13 +01:00
WarmUpTill
9b609c118f Fix disabling chat emote only mode attempting to send chat messages 2025-03-15 12:31:13 +01:00
WarmUpTill
37c398d37a Indicate that "Set to condition/action value" should be avoided 2025-03-15 12:31:13 +01:00
WarmUpTill
a51b7f6b13 Fix UI theming issues
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-11 22:12:05 +01:00
WarmUpTill
808fd84b83 Remove Qt5 support 2025-03-11 22:12:05 +01:00
WarmUpTill
57bcea15f5 cmake: Replace find_qt with find_package 2025-03-10 19:48:01 +01:00
WarmUpTill
4e51b56b9b Add game capture condition 2025-03-10 18:49:07 +01:00
WarmUpTill
ef5cf41d34 Add OBS 31 support 2025-03-10 18:49:07 +01:00
WarmUpTill
911e79ea6a Fix OCR controls showing in place of color controls
This happened when OCR support was disabled at build time
2025-03-10 18:33:13 +01:00
WarmUpTill
d586177de0 Add screenshot condition
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2025-03-09 20:57:51 +01:00
WarmUpTill
202c36646c Fix UI issues when opening ItemSettingsDialogs
Some widgets were displayed incorrectly if the parent of the dialog
window is not the main settings window
2025-03-09 20:05:07 +01:00
WarmUpTill
ab5102f5ca Fix typo 2025-03-09 20:05:07 +01:00
WarmUpTill
e612fb99f6 Cleanup
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
* Reorder action type selection
* Refactor
2025-03-06 21:30:03 +01:00
WarmUpTill
691e77a69a Add option to generate random number 2025-03-06 21:30:03 +01:00
Kaito Udagawa
930118c61f Call Setup() after Load()
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Tesseract API depends on the languageCode and this must be reinitialized after Load
2025-03-06 08:43:54 +01:00
Kaito Udagawa
1e05f2f2ce Fix languageCode
LanguageCode is not respected by OCRParameters and settings properly.
2025-03-06 08:43:54 +01:00
WarmUpTill
ae25b4d023 Improve setting selection handling
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Will now try to keep the current selection if possible.
This can be useful when switching between source of the same time.
2025-03-05 14:16:04 +01:00
WarmUpTill
af055a12a1 Cleanup 2025-03-05 14:16:04 +01:00
WarmUpTill
7de28eedd3 Improve settings button selection handling
Will now try to keep the current button selection if possible.
This can be useful when switching between source of the same time.
2025-03-05 14:16:04 +01:00
WarmUpTill
8dfe81f522 Display failure if Twitch token account name cannot be fetched
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-02-27 19:52:41 +01:00
WarmUpTill
a1d8ae291d Add option to hide empty name warning in ItemSettingsDialog 2025-02-27 19:52:41 +01:00
WarmUpTill
eb6989527d Add support for libproc2 version 4.0.5
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-02-10 18:12:08 +01:00
WarmUpTill
2986a5dd96 Fix crash when actions were copied into a action queue
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The steps added via AddPostLoadStep() from the copy were never executed.
Thus they might be invalid when RunPostLoadSteps() is next called.

This could result in a crash when a new action or condition is added
after e.g. a "Filter" action's TempVariableRef was copied, which adds a
PostLoadStep to resolve the macro segment reference.
2025-02-04 08:26:04 +01:00
WarmUpTill
816ee9b244 Fix crash when changing Twitch condition type 2025-02-04 08:26:04 +01:00
WarmUpTill
51b53bf948 Fix script actions / conditions not working after stopping macro
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-30 09:39:50 +01:00
WarmUpTill
2c5121ee94 Prevent crash when macro contains invalid action or condition 2025-01-30 09:39:50 +01:00
WarmUpTill
6a58684854 Fix import of incomplete settings not resetting duration to zero
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-23 19:00:51 +01:00
WarmUpTill
0b622fdbed Fix crash when importing settings 2025-01-23 19:00:51 +01:00
WarmUpTill
3bce8e075f CI: Switch to and adapt to Ubuntu 24 runner
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-15 21:36:54 +01:00
WarmUpTill
f598d84ea1 Fix freeze due to ResizingPlainTextEdit constantly resizing incorrectly 2025-01-15 21:36:35 +01:00
WarmUpTill
d7a244e80e Fix transition condition not considering transition overrides
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-12-31 12:47:20 +01:00
WarmUpTill
408002e96a Add support for OBS 31 in-tree builds
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-12-12 21:41:21 +01:00
WarmUpTill
50da3d3210 Fix "Media" condition showing the incorrect macro property selection
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-11-30 12:11:14 +01:00
WarmUpTill
9e13067769 Fix typo
Some checks failed
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-11-23 17:11:05 +01:00
WarmUpTill
821e768e76 Reduce screenshot selection area to area to be checked
Some checks failed
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
debian-build / build (push) Has been cancelled
2024-11-22 20:52:15 +01:00
WarmUpTill
4d2179448b Rename files 2024-11-22 20:52:15 +01:00
WarmUpTill
40d9002470 Update scripting examples
* Add more comments
* Add example showcasing how to register and set temp vars
2024-11-22 20:36:36 +01:00
WarmUpTill
7f1e310ca6 Add support to register, deregister, and set temp vars from scripts 2024-11-22 20:36:36 +01:00
WarmUpTill
66d02f4683 Fix typos 2024-11-22 20:36:36 +01:00
Arash Partow
0aae1b9978 Update ExprTk to 0.0.3
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-11-22 19:56:02 +01:00
WarmUpTill
db50822b05 Prepare to fix deprecation warning when using OBS 31
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The following log message will be present in OBS 31 when
CheckIfTrayIsDisabled() is called:

DEPRECATION: obs_frontend_get_global_config is deprecated. Read from
global or user configuration explicitly instead.

It will be resolved when the minimum OBS version is bumped to OBS 31.
2024-11-02 08:28:52 +01:00
WarmUpTill
63a545b293 Fix "Date" condition returning true unexpectedly
This could happen if the plugin was stopped on the General tab and
restarted or the first time the plugin was started.
2024-11-02 08:28:52 +01:00
WarmUpTill
bc0497d2c9 Add MacroWasCheckedSinceLastStart() 2024-11-02 08:28:52 +01:00
WarmUpTill
37226a3a4c Fix macro group icon not being displayed correctly in OBS 31
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-31 19:39:38 +01:00
WarmUpTill
9f15fbe47c Recursively search for source button in property groups
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-10-30 18:29:23 +01:00
WarmUpTill
0ee6feb529 Add option to check the current stream key
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-26 23:42:59 +02:00
WarmUpTill
3b82b55090 Fix Queue condition not displaying the correct size selection 2024-10-26 23:42:59 +02:00
WarmUpTill
071c3309c2 Add option to check current streaming service
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-10-26 11:09:06 +02:00
WarmUpTill
ee7ee7a846 Move AutoUpdateTooltipLabel definition 2024-10-26 11:09:06 +02:00
WarmUpTill
d10300bd4b Fix typo 2024-10-26 11:09:06 +02:00
WarmUpTill
e262672876 Add "Last empty" row to Queue tab 2024-10-26 11:08:51 +02:00
WarmUpTill
392e775c7b Add temp var support for size and running state 2024-10-26 11:08:51 +02:00
WarmUpTill
f641c20564 Add SetTempVarValue() overload to support boolean values 2024-10-26 11:08:51 +02:00
WarmUpTill
39327be88e Add option to move scene item above / below specified scene item
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-21 20:46:27 +02:00
WarmUpTill
ed0963799c Fix "Screenshot" action variable selection not being loaded
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-18 18:30:49 +02:00
WarmUpTill
f9e73a2b8f Fix crash when clearing Twitch chat buffer 2024-10-18 18:29:46 +02:00
Przemek Pawlas
dfaa704b71 Remove some unused includes 2024-10-12 17:09:33 +02:00
Przemek Pawlas
d54edddde4 Enable use of variables as input for "Variable" action 2024-10-12 17:09:33 +02:00
Przemek Pawlas
37cc289204 Rename function 2024-10-12 17:09:33 +02:00
WarmUpTill
8d9519c849 Adapt to OBS switch from "themeID" to "class" properties
OBS commit: cb026964b00c366943a3c16dfb1511eafc24c035
2024-10-10 20:02:35 +02:00
WarmUpTill
0fd5f47982 Add option to check if users leave or join Twitch chat 2024-10-08 19:57:50 +02:00
Przemek Pawlas
62ce83a7f6 Add one more Twitch chat tag temp var 2024-10-05 23:19:53 +02:00
Przemek Pawlas
8ecee0070d Add more Twitch chat tag temp vars and properties 2024-10-05 16:01:32 +02:00
WarmUpTill
a4970b1b07 Match any Twitch chat message by default 2024-10-05 14:30:30 +02:00
WarmUpTill
140dc9c6f4 Add option to set enabled state for partial match regex config 2024-10-05 14:30:30 +02:00
Przemek Pawlas
9a4d2935f8 Disable custom macro interval by default 2024-10-04 18:56:57 +02:00
WarmUpTill
335c964419 Fix connecting Twitch account leading to OBS freeze
In rare cases the close() call for the Twitch chat connection might fail
due to a race condition I have not fully figured out yet.
As a workaround the connection to the chat server will be force closed
for now.
2024-10-04 12:33:17 +02:00
WarmUpTill
72b2a7a07a Add option to assign screenshot to variable 2024-10-03 19:10:14 +02:00
WarmUpTill
f863909f34 Fix incorrect websocket request callback being called 2024-10-02 19:52:07 +02:00
WarmUpTill
621ca201ef Cleanup 2024-10-02 19:52:07 +02:00
WarmUpTill
563ea1a5e7 Add workaround for obs_source_set_sync_offset()
Calling obs_source_set_sync_offset() once with the desired value has no
effect.
The function needs to be called multiple times with different values in
hopes of the sync offset being changed successfully.

See https://github.com/obsproject/obs-studio/issues/7912
2024-10-01 22:19:27 +02:00
WarmUpTill
2af6ee883b Fix process condition not respecting regex options 2024-10-01 22:18:29 +02:00
Przemek Pawlas
0e9875e6b5 Allow using regex capture groups in replacement 2024-10-01 21:11:16 +02:00
WarmUpTill
c2d2e883c6 Add option to configure macro pause state on startup 2024-09-29 12:54:35 +02:00
WarmUpTill
a196e56078 Fix macro action paste happening in the wrong section
Instead of checking the cursor position at the time of the paste, it
needed to be checked at the time of the context menu creation to ensure
the segment gets pasted into the correct section
2024-09-24 23:00:47 +02:00
WarmUpTill
0536e4a60b Add option to trim screenshot created in Video condition 2024-09-24 23:00:47 +02:00
WarmUpTill
55ab096e49 Cleanup screenshot helper 2024-09-24 23:00:47 +02:00
WarmUpTill
c39ea0854a Fix crash when changing position of scene item transform 2024-09-24 23:00:47 +02:00
WarmUpTill
aef080984e Add new macros after current selection 2024-09-24 23:00:47 +02:00
WarmUpTill
1aa2425e81 Cleanup locale 2024-09-24 23:00:47 +02:00
WarmUpTill
f5eadfa39c Add option to disable saving when switching macros 2024-09-24 23:00:47 +02:00
WarmUpTill
faa65facfe Add Stream Deck condition type
The Stream Deck plugin to enable communication with the Advanced Scene
Switcher can be found here:
https://github.com/WarmUpTill/advanced-scene-switcher-streamdeck-plugin
2024-09-24 23:00:47 +02:00
WarmUpTill
b60235d964 Export VariableSpinbox symbols 2024-09-24 23:00:47 +02:00
WarmUpTill
718b18248e Add context menu entry to remove selected macro segment 2024-09-24 23:00:47 +02:00
WarmUpTill
4316e53039 Cleanup constness of MacroSegmentList 2024-09-24 23:00:47 +02:00
WarmUpTill
790ac50385 Add warning if temp vars are not available for "Run" action 2024-09-24 23:00:47 +02:00
WarmUpTill
3a87a8d155 Hide priority settings when legacy tabs are hidden 2024-09-24 23:00:47 +02:00
WarmUpTill
91912f2aa5 Add/Move helpers to register OBS websocket vendor requests
Adds websocket vendor requests to start and stop the plugin.
Adds vendor events to indicate when the plugin is started or stopped.

These are ntended to be used for the StreamDeck plugin support.
2024-09-24 23:00:47 +02:00
WarmUpTill
624b841ca1 Add option for custom macro condition check interval 2024-09-24 23:00:47 +02:00
WarmUpTill
9eb4e90291 Rename and refactor macro class functions
LastConditionCheckTime() will be required to support custom condition
check intervals per macro
2024-09-24 23:00:47 +02:00
WarmUpTill
94263cc7a0 Work around crash in std::filesystem due to unexpected char encoding 2024-09-24 23:00:47 +02:00
WarmUpTill
2d035758f4 Fix build issues with macos-14 runner
Default runner switched from X86_64 based to arm64 based starting with
the macos-14 runner images
2024-09-24 23:00:47 +02:00
WarmUpTill
86697002f7 Add option to wait for media playback to end 2024-09-24 23:00:47 +02:00
WarmUpTill
2247152aa8 Remove legacy tab "Scene Triggers" 2024-09-24 23:00:47 +02:00
WarmUpTill
2528449c84 Cleanup 2024-09-24 23:00:47 +02:00
WarmUpTill
f2df3d18be Rename locale key to better reflect its purpose 2024-09-24 23:00:47 +02:00
WarmUpTill
c7c48d03e9 Add option for short circuit evaluation of conditions 2024-09-24 23:00:47 +02:00
WarmUpTill
0906c6bb3f Add regex settings option to scene name pattern check 2024-09-24 23:00:47 +02:00
WarmUpTill
9f2daf1a15 Add option to select all items in a source group 2024-09-24 23:00:47 +02:00
WarmUpTill
6fb8619671 Clear outdated highlight triggers when enabling macro segment highlights 2024-09-24 23:00:47 +02:00
WarmUpTill
36fb83246b Add log level to log executed macros 2024-09-24 23:00:47 +02:00
WarmUpTill
e729d15e97 Remove legacy tab "Network" 2024-09-24 23:00:47 +02:00
WarmUpTill
7836298076 buildspec: Update OBS Studio dependency to 30.1.2
This is necessary to support OBS 31, which removes a bunch of deprecates
functions
2024-09-24 23:00:47 +02:00
WarmUpTill
354880ad52 Prepare for deprecations in OBS 31
See https://github.com/obsproject/obs-studio/pull/11164
2024-09-24 23:00:47 +02:00
WarmUpTill
c1ac9bb890 Resolve QCssParser::parseColorValue warning 2024-09-24 23:00:47 +02:00
WarmUpTill
49bf1ad379 Fix transition condition timing issues
The condition type was able to miss transition starts of very fast
conditions (e.g. Cut) when the selection "Any transition" was made.

In that case no signal handler was installed (as the condition type
might change) and only the transition scenes were compared at the time
of the condition check.
If that moment where the transition scenes differ was missed no
transition start was recognized.

Additionally, the "Transitioning to" and "Transitioning from" checks
faced a similar issue, if the scene transition was happening so fast,
that the condition check was not performed during the time of the
transition.
2024-09-24 23:00:47 +02:00
WarmUpTill
a6e30a1e20 Add temp var support to media condition 2024-09-24 20:27:11 +02:00
WarmUpTill
2f54c2ee62 Increase volume meter scale from -100 to 0 dB
This was done to enable users to to use higher precision values in the
"Audio" condition type.
Previously, the UI would reset the selected values to the minimum value
possible to display in the volume meter widget, which was -60 dB.
2024-09-20 23:05:45 +02:00
Przemek Pawlas
6ccbaad41c Add enhanced relative time to variable tab cells 2024-09-18 17:38:53 +02:00
Przemek Pawlas
57d78a996a Add variable trim and case change actions 2024-09-16 19:42:41 +02:00
炭酸コーラ
2d3dada30e Updated Japanese translation to the latest version 2024-09-11 17:59:20 +02:00
WarmUpTill
5bf20d155f Ignore comments 2024-09-11 17:59:20 +02:00
WarmUpTill
2b95598958 CI: Replace deprecated actions/upload-artifact action 2024-09-11 17:59:20 +02:00
WarmUpTill
b561a20dde Rename issue template file 2024-08-24 00:28:23 +02:00
WarmUpTill
835722af34 Update issue templates and links 2024-08-24 00:24:09 +02:00
WarmUpTill
93efc7cab3 Add option to check for addition chat message properties 2024-08-24 00:23:54 +02:00
WarmUpTill
d69364ec19 Fix crash on IRC disconnect due to maintenance
Reworked the (dis)connect handling
2024-08-24 00:23:54 +02:00
WarmUpTill
d75066df5f Fix crash when adding new entries to the legacy tabs 2024-08-24 00:23:54 +02:00
WarmUpTill
863c84026e Move GetSettingsWindow() to ui-helpers 2024-08-24 00:23:54 +02:00
WarmUpTill
a523081d77 Move GetIndexOfSignal() to ListEditor 2024-08-24 00:23:54 +02:00
WarmUpTill
66534519d6 Export complete RegexConfigWidget class 2024-08-24 00:23:54 +02:00
WarmUpTill
b3b2114c45 Fix SetHeightToContentHeight() not respecting diffrently sized rows 2024-08-24 00:23:54 +02:00
WarmUpTill
510f83246e Only emit signals when value changed
* Fixes crash when deleting macro in case macro selection is in focus
* Prevent unnecessary create operation when switching macro segment type
2024-08-16 06:49:27 +02:00
WarmUpTill
c969b21f93 Fix MacroSelection not properly unhiding first macro in selection 2024-08-16 06:49:27 +02:00
WarmUpTill
6ec40ef8e9 Cleanup 2024-08-16 06:49:27 +02:00
WarmUpTill
27859e83b3 Fix crash when creating macro which uses macro segment factories 2024-08-16 06:49:27 +02:00
WarmUpTill
263565700a Fix crashes related to widget highlighting 2024-08-16 06:49:27 +02:00
349 changed files with 27203 additions and 11370 deletions

View File

@@ -6,15 +6,6 @@
"autosort": true
},
"additional_commands": {
"find_qt": {
"flags": [],
"kwargs": {
"COMPONENTS": "+",
"COMPONENTS_WIN": "+",
"COMPONENTS_MACOS": "+",
"COMPONENTS_LINUX": "+"
}
},
"set_target_properties_obs": {
"pargs": 1,
"flags": [],

View File

@@ -1,36 +0,0 @@
---
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).
In case of a crash, please also include the corresponding crash log.
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.

112
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

@@ -0,0 +1,112 @@
# Based on the OBS issue templates
# https://github.com/obsproject/.github/tree/master/.github/ISSUE_TEMPLATE
name: Bug Report
description: Report a bug or crash
body:
- type: markdown
id: md_welcome
attributes:
value: This form is for reporting bugs for Advanced Scene Switcher!
- type: dropdown
id: os_info
attributes:
label: Operating System Info
description: What Operating System are you running?
options:
- Windows 11
- Windows 10
- macOS 14
- macOS 13
- macOS 12
- macOS 11
- Ubuntu 24.04
- Ubuntu 23.10
- Ubuntu 22.04
- Other
validations:
required: true
- type: input
id: os_info_other
attributes:
label: Other OS
description: "If \"Other\" was selected above, what OS are you using?"
placeholder: "e.g., Arch Linux, FreeBSD"
validations:
required: false
- type: input
id: obs_version
attributes:
label: OBS Studio Version
description: What version of OBS Studio are you using?
placeholder: 30.2.2
validations:
required: true
- type: input
id: advss_version
attributes:
label: Advanced Scene Switcher Version
description: What version of the Advanced Scene Switcher are you using?
placeholder: 1.27.2
validations:
required: true
- type: input
id: settings
attributes:
label: Plugin settings
description: |
Please provide the plugin settings used.
Either only [export the macros](https://github.com/WarmUpTill/SceneSwitcher/wiki/Exporting-and-importing-individual-macros) relevant to this issue or [export all settings](https://github.com/WarmUpTill/SceneSwitcher/wiki/Saving-and-loading-settings#how-to-create-and-import-a-backup-of-your-settings) on the General tab of the plugin.
validations:
required: false
- type: input
id: obs_log_url
attributes:
label: OBS Studio Log URL
description: Please provide the obsproject.com URL (from Help menu > Log Files > Upload Current/Previous Log File) to the OBS log file where this issue occurred.
validations:
required: false
- type: input
id: obs_crash_log_url
attributes:
label: OBS Studio Crash Log URL
description: If this is a crash report, please provide the obsproject.com URL to the OBS crash log file where this issue occurred.
validations:
required: false
- type: textarea
id: expected_behavior
attributes:
label: Expected Behavior
description: "What did you expect to happen?"
validations:
required: true
- type: textarea
id: current_behavior
attributes:
label: Current Behavior
description: "What actually happened?"
validations:
required: true
- type: textarea
id: steps_to_reproduce
attributes:
label: Steps to Reproduce
description: "How do you trigger this bug? Please walk us through it step by step."
placeholder: |
1.
2.
3.
...
value: |
1.
2.
3.
...
validations:
required: true
- type: textarea
id: additional_notes
attributes:
label: Anything else we should know?
validations:
required: false

View File

@@ -1,5 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: Help/Support
- name: 📚 Wiki
url: https://github.com/WarmUpTill/SceneSwitcher/wiki
about: For general questions about how to use and configure the plugin please have a look at the wiki (https://github.com/WarmUpTill/SceneSwitcher/wiki) or ask questions in the OBS forum (https://obsproject.com/forum/threads/advanced-scene-switcher.48264/)
about: For explanations on how the plugin works or to see example guides, check out the wiki.
- name: 💬 OBS Forum Thread
url: https://obsproject.com/forum/threads/advanced-scene-switcher.48264
about: To discuss the plugin or get assistance, feel free to use the OBS forum thread or the GitHub discussions page.

View File

@@ -1,20 +0,0 @@
---
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

@@ -0,0 +1,37 @@
# Based on the OBS issue templates
# https://github.com/obsproject/.github/tree/master/.github/ISSUE_TEMPLATE
name: Feature request
description: Suggest an idea for this project
body:
- type: markdown
id: md_welcome
attributes:
value: This form is for requesting new features for Advanced Scene Switcher!
- type: textarea
id: background
attributes:
label: Background
description: "Is your feature request related to a problem? Please describe."
validations:
required: false
- type: textarea
id: solution
attributes:
label: Solution
description: "Describe the solution you'd like."
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives
description: "Describe alternatives you've considered."
validations:
required: false
- type: textarea
id: additional_notes
attributes:
label: Anything else we should know?
validations:
required: false

View File

@@ -9,5 +9,7 @@ package 'libxtst-dev'
package 'libxss-dev'
package 'libopencv-dev'
package 'libtesseract-dev'
package 'libprocps-dev'
package 'libusb-1.0-0-dev'
package 'libproc2-dev'
package 'libusb-1.0-0-dev'
package 'libpaho-mqttpp-dev'
package 'libpaho-mqtt-dev'

View File

@@ -4,3 +4,4 @@ brew "cmake"
brew "jq"
brew "xcbeautify"
brew "automake"
brew "libtool"

View File

@@ -306,7 +306,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
log_info "Building OpenCV ..."
cmake --build ${opencv_build_dir} --config Release
log_info "Installing OpenCV..."
log_info "Installing OpenCV ..."
cmake --install ${opencv_build_dir} --prefix "${advss_dep_path}" --config Release || true
popd
@@ -335,17 +335,22 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
log_info "Building Leptonica ..."
cmake --build ${leptonica_build_dir} --config Release
log_info "Installing Leptonica..."
log_info "Installing Leptonica ..."
# Workaround for "unknown file attribute: H" errors when running install
cmake --install ${leptonica_build_dir} --prefix "${advss_dep_path}" --config Release || :
popd
local tesseract_dir="${project_root}/deps/tesseract"
local tesseract_build_dir="${tesseract_dir}/build_${target##*-}"
local tesseract_build_dir_x86_64="${tesseract_dir}/build_x86_64"
pushd ${tesseract_dir}
log_info "Configure Tesseract (x86_64) ..."
local -a tesseract_cmake_args=(
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
-DCMAKE_SYSTEM_NAME="Darwin"
-DCMAKE_OSX_ARCHITECTURES=x86_64
-DCMAKE_SYSTEM_PROCESSOR=x86_64
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
-DSW_BUILD=OFF
-DBUILD_TRAINING_TOOLS=OFF
@@ -353,28 +358,40 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
-DCMAKE_INSTALL_PREFIX="${advss_dep_path}"
)
if [ "${target}" != "macos-x86_64" ]; then
tesseract_cmake_args+=(
-DCMAKE_SYSTEM_PROCESSOR=aarch64
-DHAVE_AVX=FALSE
-DHAVE_AVX2=FALSE
-DHAVE_AVX512F=FALSE
-DHAVE_FMA=FALSE
-DHAVE_SSE4_1=FALSE
-DHAVE_NEON=TRUE
)
sed -i'.original' 's/HAVE_NEON FALSE/HAVE_NEON TRUE/g' "${tesseract_dir}/CMakeLists.txt"
fi
cmake -S . -B ${tesseract_build_dir_x86_64} ${tesseract_cmake_args}
pushd ${tesseract_dir}
log_info "Configure Tesseract ..."
cmake -S . -B ${tesseract_build_dir} ${tesseract_cmake_args}
log_info "Building Tesseract (x86_64) ..."
cmake --build ${tesseract_build_dir_x86_64} --config Release
log_info "Building Tesseract ..."
cmake --build ${tesseract_build_dir} --config Release
log_info "Configure Tesseract (arm64) ..."
git checkout .
local tesseract_build_dir_arm64="${tesseract_dir}/build_arm64"
local -a tesseract_cmake_args=(
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_SYSTEM_NAME="Darwin"
-DCMAKE_OSX_ARCHITECTURES=arm64
-DCMAKE_SYSTEM_PROCESSOR=arm64
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
-DSW_BUILD=OFF
-DBUILD_TRAINING_TOOLS=OFF
-DCMAKE_PREFIX_PATH="${advss_dep_path};${_plugin_deps}"
-DCMAKE_INSTALL_PREFIX="${advss_dep_path}"
)
cmake -S . -B ${tesseract_build_dir_arm64} ${tesseract_cmake_args}
log_info "Building Tesseract (arm64) ..."
cmake --build ${tesseract_build_dir_arm64} --config Release
log_info "Combine arm and x86 libtesseract binaries ..."
mv ${tesseract_build_dir_arm64}/libtesseract.a ${tesseract_build_dir_arm64}/libtesseract_arm.a
lipo -create ${tesseract_build_dir_x86_64}/libtesseract.a ${tesseract_build_dir_arm64}/libtesseract_arm.a -output ${tesseract_build_dir_arm64}/libtesseract.a
log_info "Installing Tesseract..."
cmake --install ${tesseract_build_dir} --prefix "${advss_dep_path}" --config Release
cmake --install ${tesseract_build_dir_arm64} --prefix "${advss_dep_path}" --config Release
popd
pushd ${advss_dep_path}
@@ -402,22 +419,29 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
lipo -create openssl_x86/libcrypto.a openssl_arm/libcrypto.a -output openssl-combined/libcrypto.a
lipo -create openssl_x86/libssl.a openssl_arm/libssl.a -output openssl-combined/libssl.a
log_info "Clean up openssl dir..."
log_info "Clean up openssl dir ..."
mv openssl_x86 openssl
rm -rf openssl_arm
popd
pushd ${project_root}/deps/libusb
log_info "Prepare libusb ..."
log_info "Building libusb x86 (deps) ..."
export MACOSX_DEPLOYMENT_TARGET=10.9
log_info "Configure libusb x86 ..."
export SDKROOT=$(xcrun --sdk macosx --show-sdk-path)
export CC=$(xcrun --sdk macosx --find clang)
export CXX=$(xcrun --sdk macosx --find clang++)
export CFLAGS="-arch x86_64 -isysroot $SDKROOT"
export CXXFLAGS="-arch x86_64 -isysroot $SDKROOT"
export LDFLAGS="-arch x86_64 -isysroot $SDKROOT"
export MACOSX_DEPLOYMENT_TARGET=10.15
log_info "Building libusb x86 ..."
mkdir ${project_root}/deps/libusb/out_x86
./autogen.sh
./configure --host=x86_64-apple-darwin --prefix=${advss_dep_path}
make && make install
log_info "Building libusb x86 ..."
log_info "Configure libusb arm ..."
make clean
rm -r ${project_root}/deps/libusb/out_x86
mkdir ${project_root}/deps/libusb/out_x86
@@ -454,14 +478,36 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
unset MACOSX_DEPLOYMENT_TARGET
popd
local mqtt_dir="${project_root}/deps/paho.mqtt.cpp"
local mqtt_build_dir="${mqtt_dir}/build_${target##*-}"
local -a mqtt_cmake_args=(
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
-DCMAKE_PREFIX_PATH="${advss_dep_path};${_plugin_deps}"
-DCMAKE_INSTALL_PREFIX="${advss_dep_path}"
-DPAHO_BUILD_SHARED=OFF
-DPAHO_BUILD_STATIC=ON
-DPAHO_WITH_MQTT_C=ON
-DOPENSSL_ROOT_DIR="${advss_dep_path}"
-DPAHO_WITH_SSL=OFF # TODO: figure out linking issues with openssl
)
pushd ${mqtt_dir}
log_info "Configure paho.mqtt.cpp ..."
cmake -S . -B ${mqtt_build_dir} ${mqtt_cmake_args}
log_info "Building paho.mqtt.cpp ..."
cmake --build ${mqtt_build_dir} --config Release
log_info "Installing paho.mqtt.cpp ..."
cmake --install ${mqtt_build_dir} --prefix "${advss_dep_path}" --config Release
popd
;;
linux)
# Hacky workaround to support libproc2 with Ubuntu 22 build environment
local lsb_version=$(lsb_release -r | cut -f 2 || true)
if [[ "${lsb_version}" == '22.04' ]] {
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-0_4.0.2-3_amd64.deb
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-dev_4.0.2-3_amd64.deb
}
# Nothing to do for now
;;
}
}

View File

@@ -219,6 +219,37 @@ function Build {
} else {
Log-Information "Failed to locate msbuild.exe - skipping libusb build"
}
Push-Location -Stack BuildMqttTemp
Ensure-Location $ProjectRoot
$MqttPath = "${ProjectRoot}/deps/paho.mqtt.cpp"
$MqttBuildPath = "${MqttPath}/build"
# Explicitly disable PkgConfig and tiff as it will lead build errors
$MqttCmakeArgs = @(
"-DCMAKE_BUILD_TYPE=${Configuration}"
"-DCMAKE_PREFIX_PATH:PATH=${OBSDepPath}"
"-DCMAKE_INSTALL_PREFIX:PATH=${ADVSSDepPath}"
"-DPAHO_WITH_MQTT_C=ON"
)
Log-Information "Configuring paho.mqtt.cpp..."
Invoke-External cmake -S ${MqttPath} -B ${MqttBuildPath} @MqttCmakeArgs
$MqttCmakeArgs = @(
'--config', "${Configuration}"
)
if ( $VerbosePreference -eq 'Continue' ) {
$MqttCmakeArgs += ('--verbose')
}
Log-Information "Building paho.mqtt.cpp..."
Invoke-External cmake --build "${MqttBuildPath}" @MqttCmakeArgs
Log-Information "Install paho.mqtt.cpp..."
Invoke-External cmake --install "${MqttBuildPath}" --prefix "${ADVSSDepPath}" @MqttCmakeArgs
}
Build

0
.github/test vendored
View File

View File

@@ -43,14 +43,14 @@ jobs:
# devscripts and libobs-dev are needed but they were already installed
# from check_libobs_revision and install_frontend_header sections.
sudo apt update
sudo apt install cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qtbase5-dev libopencv-dev libprocps-dev
sudo apt install build-essential cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qt6-base-dev libopencv-dev libproc2-dev
- name: build
run: |
debuild --no-lintian --no-sign
mv ../*.deb .
- name: Publish
if: success()
uses: actions/upload-artifact@v2.2.1
uses: actions/upload-artifact@v4
with:
name: "obs-scene-switcher.deb"
path: ${{ github.workspace }}/*.deb

View File

@@ -10,7 +10,7 @@ env:
jobs:
check-event:
name: Check GitHub Event Data 🔎
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
defaults:
run:
shell: bash
@@ -75,7 +75,7 @@ jobs:
macos-build:
name: Build for macOS 🍏
runs-on: macos-13
runs-on: macos-14
needs: check-event
defaults:
run:
@@ -107,8 +107,8 @@ jobs:
print "pluginName=${product_name}" >> $GITHUB_OUTPUT
print "pluginVersion=${git_tag}" >> $GITHUB_OUTPUT
print '::group::Select Xcode version'
sudo xcode-select --switch /Applications/Xcode_14.3.1.app
print '::group::Enable Xcode 15.2'
sudo xcode-select --switch /Applications/Xcode_15.2.app/Contents/Developer
print '::endgroup::'
- uses: actions/cache@v4
@@ -177,7 +177,7 @@ jobs:
ubuntu-build:
name: Build for Ubuntu 🐧
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: check-event
defaults:
run:
@@ -210,6 +210,11 @@ jobs:
restore-keys: |
${{ runner.os }}-ccache-x86_64-
- name: Set up CMake 🏗️
uses: jwlawson/actions-setup-cmake@v1.13
with:
cmake-version: '3.24.x'
- name: Set up Homebrew 🍺
uses: Homebrew/actions/setup-homebrew@master
@@ -241,14 +246,14 @@ jobs:
- name: Upload Artifacts 📡
uses: actions/upload-artifact@v4
with:
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-24.04-x86_64-${{ needs.check-event.outputs.commitHash }}
path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*.*
- name: Upload debug symbol artifacts 🪲
uses: actions/upload-artifact@v4
if: ${{ fromJSON(needs.check-event.outputs.package) }}
with:
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-24.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym
path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*-dbgsym.ddeb
windows-build:

View File

@@ -3,7 +3,7 @@ on:
workflow_call:
jobs:
clang-format:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
@@ -15,7 +15,7 @@ jobs:
failCondition: error
cmake-format:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:

View File

@@ -31,7 +31,7 @@ jobs:
create-release:
name: Create Release 🛫
if: github.ref_type == 'tag'
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: build-project
defaults:
run:
@@ -77,7 +77,7 @@ jobs:
variants=(
'windows-x64;zip|exe'
'macos-universal;tar.xz|pkg'
'ubuntu-22.04-x86_64;tar.xz|deb|ddeb'
'ubuntu-24.04-x86_64;tar.xz|deb|ddeb'
'sources;tar.xz'
)

9
.gitmodules vendored
View File

@@ -28,3 +28,12 @@
[submodule "deps/libusb"]
path = deps/libusb
url = https://github.com/libusb/libusb.git
[submodule "deps/date"]
path = deps/date
url = https://github.com/HowardHinnant/date.git
[submodule "deps/jsoncons"]
path = deps/jsoncons
url = https://github.com/danielaparker/jsoncons.git
[submodule "deps/paho.mqtt.cpp"]
path = deps/paho.mqtt.cpp
url = https://github.com/eclipse-paho/paho.mqtt.cpp.git

View File

@@ -20,6 +20,12 @@ if(BUILD_OUT_OF_TREE)
include(helpers)
endif()
# OBS 31 no longer defines find_qt so check if we need to include it for in-tree
# builds
if(NOT COMMAND find_qt)
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/helpers_common.cmake")
endif()
set(LIB_NAME "${PROJECT_NAME}-lib")
add_library(${PROJECT_NAME} MODULE)
add_library(${LIB_NAME} SHARED)
@@ -27,6 +33,11 @@ add_library(${LIB_NAME} SHARED)
include(cmake/common/get_git_revision_description.cmake)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
git_describe(GIT_TAG)
if(${GIT_TAG} STREQUAL "GIT-NOTFOUND")
set(GIT_TAG ${PROJECT_VERSION})
endif()
message(STATUS "${PROJECT_NAME} version: ${GIT_TAG}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/version.cpp.in"
"${CMAKE_CURRENT_BINARY_DIR}/lib/version.cpp" @ONLY)
@@ -52,8 +63,6 @@ target_sources(
${LIB_NAME}
PRIVATE lib/legacy/scene-group.cpp
lib/legacy/scene-group.hpp
lib/legacy/scene-trigger.cpp
lib/legacy/scene-trigger.hpp
lib/legacy/switch-audio.cpp
lib/legacy/switch-audio.hpp
lib/legacy/switch-executable.cpp
@@ -66,8 +75,6 @@ target_sources(
lib/legacy/switch-idle.hpp
lib/legacy/switch-media.cpp
lib/legacy/switch-media.hpp
lib/legacy/switch-network.cpp
lib/legacy/switch-network.hpp
lib/legacy/switch-pause.cpp
lib/legacy/switch-pause.hpp
lib/legacy/switch-random.cpp
@@ -96,8 +103,6 @@ target_sources(
lib/macro/macro-action-macro.hpp
lib/macro/macro-action-queue.cpp
lib/macro/macro-action-queue.hpp
lib/macro/macro-action-script.cpp
lib/macro/macro-action-script.hpp
lib/macro/macro-action-variable.cpp
lib/macro/macro-action-variable.hpp
lib/macro/macro-action.cpp
@@ -110,8 +115,6 @@ target_sources(
lib/macro/macro-condition-macro.hpp
lib/macro/macro-condition-queue.cpp
lib/macro/macro-condition-queue.hpp
lib/macro/macro-condition-script.cpp
lib/macro/macro-condition-script.hpp
lib/macro/macro-condition-tempvar.cpp
lib/macro/macro-condition-tempvar.hpp
lib/macro/macro-condition-variable.cpp
@@ -132,14 +135,10 @@ target_sources(
lib/macro/macro-ref.hpp
lib/macro/macro-run-button.cpp
lib/macro/macro-run-button.hpp
lib/macro/macro-script-handler.cpp
lib/macro/macro-script-handler.hpp
lib/macro/macro-segment-copy-paste.cpp
lib/macro/macro-segment-copy-paste.hpp
lib/macro/macro-segment-list.cpp
lib/macro/macro-segment-list.hpp
lib/macro/macro-segment-script.cpp
lib/macro/macro-segment-script.hpp
lib/macro/macro-segment-selection.cpp
lib/macro/macro-segment-selection.hpp
lib/macro/macro-segment.cpp
@@ -161,6 +160,8 @@ target_sources(
lib/queue/action-queue.hpp
lib/queue/action-queue-tab.cpp
lib/queue/action-queue-tab.hpp
lib/utils/auto-update-tooltip-label.cpp
lib/utils/auto-update-tooltip-label.hpp
lib/utils/backup.cpp
lib/utils/backup.hpp
lib/utils/condition-logic.cpp
@@ -186,6 +187,8 @@ target_sources(
lib/utils/help-icon.cpp
lib/utils/item-selection-helpers.cpp
lib/utils/item-selection-helpers.hpp
lib/utils/json-helpers.cpp
lib/utils/json-helpers.hpp
lib/utils/layout-helpers.cpp
lib/utils/layout-helpers.hpp
lib/utils/list-controls.cpp
@@ -212,17 +215,12 @@ target_sources(
lib/utils/plugin-state-helpers.hpp
lib/utils/priority-helper.cpp
lib/utils/priority-helper.hpp
lib/utils/properties-view.cpp
lib/utils/properties-view.hpp
lib/utils/properties-view.moc.hpp
lib/utils/regex-config.cpp
lib/utils/regex-config.hpp
lib/utils/resizing-text-edit.cpp
lib/utils/resizing-text-edit.hpp
lib/utils/resource-table.cpp
lib/utils/resource-table.hpp
lib/utils/resource-table-hotkey-handler.cpp
lib/utils/resource-table-hotkey-handler.hpp
lib/utils/scene-selection.cpp
lib/utils/scene-selection.hpp
lib/utils/scene-switch-helpers.cpp
@@ -255,12 +253,16 @@ target_sources(
lib/utils/tab-helpers.hpp
lib/utils/temp-variable.cpp
lib/utils/temp-variable.hpp
lib/utils/time-helpers.cpp
lib/utils/time-helpers.hpp
lib/utils/ui-helpers.cpp
lib/utils/ui-helpers.hpp
lib/utils/utility.cpp
lib/utils/utility.hpp
lib/utils/volume-control.cpp
lib/utils/volume-control.hpp
lib/utils/websocket-api.cpp
lib/utils/websocket-api.hpp
lib/variables/variable-line-edit.cpp
lib/variables/variable-line-edit.hpp
lib/variables/variable-number.hpp
@@ -287,9 +289,16 @@ include(cmake/common/advss_helpers.cmake)
setup_obs_lib_dependency(${LIB_NAME})
setup_obs_lib_dependency(${PROJECT_NAME})
find_qt(COMPONENTS Widgets Core)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt::Core Qt::Widgets)
target_link_libraries(${LIB_NAME} PRIVATE Qt::Core Qt::Widgets)
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core Qt6::Widgets)
target_link_libraries(${LIB_NAME} PRIVATE Qt6::Core Qt6::Widgets)
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported Qt
# version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
if(Qt6_VERSION VERSION_GREATER "6.0.0")
target_compile_definitions(${LIB_NAME} PRIVATE QT_NO_DEPRECATED_WARNINGS)
endif()
target_compile_options(
${PROJECT_NAME}
PRIVATE
@@ -326,13 +335,9 @@ set_target_properties(${LIB_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden)
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17)
target_compile_features(${LIB_NAME} PUBLIC cxx_std_17)
add_definitions(-DASIO_STANDALONE)
target_include_directories(
${LIB_NAME}
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include"
"${CMAKE_CURRENT_SOURCE_DIR}/deps/websocketpp"
"${CMAKE_CURRENT_SOURCE_DIR}/deps/obs-websocket/lib"
"${CMAKE_CURRENT_SOURCE_DIR}/deps/exprtk")
${LIB_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/deps/obs-websocket/lib"
"${CMAKE_CURRENT_SOURCE_DIR}/deps/exprtk")
if(NOT nlohmann_json_DIR
AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/deps/json/CMakeLists.txt")
@@ -342,6 +347,21 @@ else()
endif()
target_link_libraries(${LIB_NAME} PUBLIC nlohmann_json::nlohmann_json)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/deps/jsoncons/CMakeLists.txt")
# Don't build jsoncons unit tests as they are causing compilation issues and
# won't be executed either way
if(OS_MACOS)
cmake_policy(SET CMP0077 NEW)
endif()
set(JSONCONS_BUILD_TESTS
OFF
CACHE BOOL "" FORCE)
add_subdirectory(deps/jsoncons EXCLUDE_FROM_ALL)
target_link_libraries(${LIB_NAME} PRIVATE jsoncons)
target_compile_definitions(${LIB_NAME} PRIVATE JSONPATH_SUPPORT=1)
endif()
find_package(CURL QUIET)
find_package(Libcurl QUIET)
if(CURL_FOUND)
@@ -409,7 +429,12 @@ else()
set(PROCESS_CONDITION_SUPPORTED 1)
endif()
if(PROCPS2_INCLUDE_DIR)
message(STATUS "${PROJECT_NAME} using libproc2")
find_package(PkgConfig REQUIRED)
pkg_check_modules(libproc2 IMPORTED_TARGET libproc2<4.0.5 QUIET)
if(libproc2_FOUND)
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_USE_INFO)
endif()
pkg_check_modules(libproc2 REQUIRED IMPORTED_TARGET libproc2)
set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}")
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE)
set(PROCESS_CONDITION_SUPPORTED 1)
@@ -422,14 +447,16 @@ else()
endif()
target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}")
target_sources(${LIB_NAME} PRIVATE lib/linux/advanced-scene-switcher-nix.cpp)
# Don't include irrelevant folders into sources archive
list(APPEND CPACK_SOURCE_IGNORE_FILES "\\.deps/.*")
endif()
if(NOT OS_WINDOWS)
target_compile_options(
${LIB_NAME}
PUBLIC -Wno-error=unused-parameter -Wno-error=conversion -Wno-error=shadow
-Wno-error=float-conversion -Wno-error=enum-conversion
-Wno-error=deprecated-declarations)
-Wno-error=float-conversion -Wno-error=enum-conversion)
endif()
# --- End of section ---

View File

@@ -19,6 +19,8 @@ The **Snap** package manager offers an OBS Studio installation which is bundled
sudo snap install obs-studio
```
More information can be found [here](https://github.com/WarmUpTill/SceneSwitcher/wiki/Installation).
## Contributing
- If you wish to contribute code to the project, have a look at this [section](BUILDING.md) describing how to compile the plugin.

View File

@@ -6,6 +6,7 @@ import re
import sys
defaultLocaleFile = "en-US.ini"
commentChars = ";#"
class localeEntry:
@@ -21,6 +22,13 @@ class localeEntry:
self.lineNum = lineNum
def isCommentLine(line):
for char in commentChars:
if line.startswith(char):
return True
return False
def getNonDefaultLocales(dir):
files = []
for filename in os.listdir(dir):
@@ -38,7 +46,7 @@ def getAllLocaleEntries(file):
widgetPlaceholders = []
qStringArgs = []
if line.startswith(";"):
if isCommentLine(line):
continue
for word in line.split("{{"):

View File

@@ -7,7 +7,7 @@ Build-Depends: cmake,
libcurl4-openssl-dev,
libobs-dev,
libxtst-dev,
qtbase5-dev,
qt6-base-dev,
libxss-dev,
libopencv-dev
Standards-Version: 4.6.0

View File

@@ -3,7 +3,7 @@
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export QT_SELECT = qt5
export QT_SELECT = qt6
%:
dh $@

View File

@@ -1,33 +1,33 @@
{
"dependencies": {
"obs-studio": {
"version": "30.0.2",
"version": "30.1.2",
"baseUrl": "https://github.com/obsproject/obs-studio/archive/refs/tags",
"label": "OBS sources",
"hashes": {
"macos": "be12c3ad0a85713750d8325e4b1db75086223402d7080d0e3c2833d7c5e83c27",
"windows-x64": "970058c49322cfa9cd6d620abb393fed89743ba7e74bd9dbb6ebe0ea8141d9c7"
"macos": "490bae1c392b3b344b0270afd8cb887da4bc50bd92c0c426e96713c1ccb9701a",
"windows-x64": "c2dd03fa7fd01fad5beafce8f7156da11f9ed9a588373fd40b44a06f4c03b867"
}
},
"prebuilt": {
"version": "2023-11-03",
"version": "2024-03-19",
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
"label": "Pre-Built obs-deps",
"hashes": {
"macos": "90c2fc069847ec2768dcc867c1c63b112c615ed845a907dc44acab7a97181974",
"windows-x64": "d0825a6fb65822c993a3059edfba70d72d2e632ef74893588cf12b1f0d329ce6"
"macos": "2e9bfb55a5e0e4c1086fa1fda4cf268debfead473089df2aaea80e1c7a3ca7ff",
"windows-x64": "6e86068371526a967e805f6f9903f9407adb683c21820db5f07da8f30d11e998"
}
},
"qt6": {
"version": "2023-11-03",
"version": "2024-03-19",
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
"label": "Pre-Built Qt6",
"hashes": {
"macos": "ba4a7152848da0053f63427a2a2cb0a199af3992997c0db08564df6f48c9db98",
"windows-x64": "bc57dedf76b47119a6dce0435a2f21b35b08c8f2948b1cb34a157320f77732d1"
"macos": "694f1e639c017e3b1f456f735330dc5afae287cbea85757101af1368de3142c8",
"windows-x64": "72d1df34a0ef7413a681d5fcc88cae81da60adc03dcd23ef17862ab170bcc0dd"
},
"debugSymbols": {
"windows-x64": "fd8ecd1d8cd2ef049d9f4d7fb5c134f784836d6020758094855dfa98bd025036"
"windows-x64": "fbddd1f659c360f2291911ac5709b67b6f8182e6bca519d24712e4f6fd3cc865"
}
}
},

View File

@@ -283,8 +283,14 @@ endfunction()
function(setup_advss_plugin target)
setup_obs_lib_dependency(${target})
find_qt(COMPONENTS Widgets Core)
target_link_libraries(${target} PRIVATE Qt::Core Qt::Widgets)
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
target_link_libraries(${target} PRIVATE Qt6::Core Qt6::Widgets)
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported
# Qt version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
if(Qt6_VERSION VERSION_GREATER "6.7.0")
target_compile_definitions(${target} PRIVATE QT_NO_DEPRECATED_WARNINGS)
endif()
set_target_properties(
${target}

View File

@@ -17,85 +17,6 @@ if(NOT QT_VERSION)
set_property(CACHE QT_VERSION PROPERTY STRINGS AUTO 5 6)
endif()
# find_qt: Macro to find best possible Qt version for use with the project:
macro(find_qt)
set(multiValueArgs COMPONENTS COMPONENTS_WIN COMPONENTS_MAC COMPONENTS_LINUX)
cmake_parse_arguments(find_qt "" "${oneValueArgs}" "${multiValueArgs}"
${ARGN})
# Do not use versionless targets in the first step to avoid Qt::Core being
# clobbered by later opportunistic find_package runs
set(QT_NO_CREATE_VERSIONLESS_TARGETS TRUE)
message(DEBUG "Start Qt version discovery...")
# Loop until _QT_VERSION is set or FATAL_ERROR aborts script execution early
while(NOT _QT_VERSION)
message(DEBUG "QT_VERSION set to ${QT_VERSION}")
if(QT_VERSION STREQUAL AUTO AND NOT qt_test_version)
set(qt_test_version 6)
elseif(NOT QT_VERSION STREQUAL AUTO)
set(qt_test_version ${QT_VERSION})
endif()
message(DEBUG "Attempting to find Qt${qt_test_version}")
find_package(
Qt${qt_test_version}
COMPONENTS Core
QUIET)
if(TARGET Qt${qt_test_version}::Core)
set(_QT_VERSION
${qt_test_version}
CACHE INTERNAL "")
message(STATUS "Qt version found: ${_QT_VERSION}")
unset(qt_test_version)
break()
elseif(QT_VERSION STREQUAL AUTO)
if(qt_test_version EQUAL 6)
message(WARNING "Qt6 was not found, falling back to Qt5")
set(qt_test_version 5)
continue()
endif()
endif()
message(FATAL_ERROR "Neither Qt6 nor Qt5 found.")
endwhile()
# Enable versionless targets for the remaining Qt components
set(QT_NO_CREATE_VERSIONLESS_TARGETS FALSE)
set(qt_components ${find_qt_COMPONENTS})
if(OS_WINDOWS)
list(APPEND qt_components ${find_qt_COMPONENTS_WIN})
elseif(OS_MACOS)
list(APPEND qt_components ${find_qt_COMPONENTS_MAC})
else()
list(APPEND qt_components ${find_qt_COMPONENTS_LINUX})
endif()
message(DEBUG "Trying to find Qt components ${qt_components}...")
find_package(Qt${_QT_VERSION} REQUIRED ${qt_components})
list(APPEND qt_components Core)
if("Gui" IN_LIST find_qt_COMPONENTS_LINUX)
list(APPEND qt_components "GuiPrivate")
endif()
# Check for versionless targets of each requested component and create if
# necessary
foreach(component IN LISTS qt_components)
message(DEBUG "Checking for target Qt::${component}")
if(NOT TARGET Qt::${component} AND TARGET Qt${_QT_VERSION}::${component})
add_library(Qt::${component} INTERFACE IMPORTED)
set_target_properties(
Qt::${component} PROPERTIES INTERFACE_LINK_LIBRARIES
Qt${_QT_VERSION}::${component})
endif()
set_property(TARGET Qt::${component} PROPERTY INTERFACE_COMPILE_FEATURES "")
endforeach()
endmacro()
# check_uuid: Helper function to check for valid UUID
function(check_uuid uuid_string return_value)
set(valid_uuid TRUE)

View File

@@ -19,14 +19,13 @@ AdvSceneSwitcher.generalTab.status.autoStart.streaming="Streamen"
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Aufnehmen oder Streamen"
AdvSceneSwitcher.generalTab.status.checkInterval="Teste Bedingungen alle"
AdvSceneSwitcher.generalTab.generalBehavior="Allgemeines Verhalten"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="Wenn keine Bedingung erfüllt ist für "
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Kann nur so genau sein wie das eingestellte Interval zum Testen der Bedingungen."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Nicht wechseln"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Wechsle zu einer Szene auf dem Zufall-Tab"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Wechsle zu:"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch="Wenn keine Bedingung erfüllt ist für "
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatchDelay.tooltip="Kann nur so genau sein wie das eingestellte Interval zum Testen der Bedingungen."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="Nicht wechseln"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Wechsle zu einer Szene auf dem Zufall-Tab"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Wechsle zu:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Nach Ausführen von Aktionen überspringe das Ausführen von Aktionen für"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="In diesem Zeitraum werden potentielle erfüllte Bedingungen ignoriert!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ausführliches Logging"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Fensterposition und -größe speichern"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Benachrichtigungen im System-Tray anzeigen"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deaktiviere UI Tipps"
@@ -41,7 +40,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Exportieren"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Importieren"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Exportiere Erweiterter Automatischer Szenenwechsler Einstellungen zu Datei ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Exportiere Erweiterter Automatischer Szenenwechsler Einstellungen von Datei ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Text Dateien (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Text Dateien (*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="Importieren der Einstellungen ist fehlgeschlagen"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Einstellungen wurden erfolgreich importiert"
AdvSceneSwitcher.generalTab.priority.fileContent="Datei Inhalt"
@@ -94,7 +93,7 @@ AdvSceneSwitcher.macroTab.highlightExecutedMacros="Kürzlich ausgeführte Makros
AdvSceneSwitcher.macroTab.highlightTrueConditions="Bedingungen des aktuell ausgewählten Makros hervorheben, die kürzlich als wahr bewertet wurden"
AdvSceneSwitcher.macroTab.highlightPerformedActions="Zuletzt ausgeführte Aktionen des aktuell ausgewählten Makros hervorheben"
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Hotkeys zur Steuerung des Pausen-Status neuer Makros registrieren"
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Hotkeys zur Steuerung des Pausen-Status ausgewählter Makros registrieren"
AdvSceneSwitcher.macroTab.currentRegisterHotkeys="Hotkeys zur Steuerung des Pausen-Status ausgewählter Makros registrieren"
; Macro List
AdvSceneSwitcher.macroList.deleted="gelöscht"
@@ -142,8 +141,6 @@ AdvSceneSwitcher.condition.scene.type.currentPattern="Aktuelle Szene entspricht"
AdvSceneSwitcher.condition.scene.type.previousPattern="Vorherige Szene entspricht"
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Während des Übergangs die Ziel-Szene überprüfen"
AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="Während des Übergangs die Quell-Szene überprüfen"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Fenster"
AdvSceneSwitcher.condition.file="Datei"
AdvSceneSwitcher.condition.file.type.match="entspricht"
@@ -197,12 +194,12 @@ AdvSceneSwitcher.condition.video.modelLoadFail="Modelldaten konnten nicht gelade
AdvSceneSwitcher.condition.video.type.main="OBS's Haupt-Ausgabe"
AdvSceneSwitcher.condition.video.type.source="Quelle"
AdvSceneSwitcher.condition.video.type.scene="Szene"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Modelldaten (Haar-Kaskaden-Klassifikator): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimale Anzahl von Nachbarn: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduzieren Sie die CPU-Belastung, indem Sie die Prüfung nur alle {{throttleCount}} Millisekunden"
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Kontrolle nur im Bereich durchführen"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.layout="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.layout.modelPath="Modelldaten (Haar-Kaskaden-Klassifikator): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="Minimale Anzahl von Nachbarn: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.throttle="{{throttleEnable}}Reduzieren Sie die CPU-Belastung, indem Sie die Prüfung nur alle {{throttleCount}} Millisekunden"
AdvSceneSwitcher.condition.video.layout.checkAreaEnable="Kontrolle nur im Bereich durchführen"
AdvSceneSwitcher.condition.video.layout.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.minSize="Minimale Größe:"
AdvSceneSwitcher.condition.video.maxSize="Maximale Größe:"
AdvSceneSwitcher.condition.video.selectArea="Bereich auswählen"
@@ -214,7 +211,6 @@ AdvSceneSwitcher.condition.stream.state.start="Stream läuft"
AdvSceneSwitcher.condition.stream.state.stop="Stream gestoppt"
AdvSceneSwitcher.condition.stream.state.starting="Stream wird gestartet"
AdvSceneSwitcher.condition.stream.state.stopping="Stream wird gestoppt"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Aufnahme"
AdvSceneSwitcher.condition.record.state.start="Aufnahme läuft"
AdvSceneSwitcher.condition.record.state.pause="Aufnahme pausiert"
@@ -492,7 +488,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Nach unten verschieben"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="An erste Stelle verschieben"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="An letzte Stelle verschieben"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="An Position verschieben"
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren"
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -529,9 +525,8 @@ AdvSceneSwitcher.action.screenshot.save.default="Standard"
AdvSceneSwitcher.action.screenshot.save.custom="Benutzerdefiniert"
AdvSceneSwitcher.action.screenshot.type.source="Quelle"
AdvSceneSwitcher.action.screenshot.type.scene="Szene"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's Haupt-Ausgabe"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Quellen oder Szenen, die nicht immer gerendert werden, können dazu führen, dass einige Teile der Screenshots leer bleiben."
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}und speichere in{{saveType}}Pfad"
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}und speichere in{{saveType}}{{variables}}Pfad"
AdvSceneSwitcher.action.profile="Profil"
AdvSceneSwitcher.action.profile.entry="Aktives Profil umschalten auf {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Szenensammlung"
@@ -730,31 +725,6 @@ AdvSceneSwitcher.videoTab.ignoreInactiveSource="außer Video Quelle ist inaktiv"
AdvSceneSwitcher.videoTab.entry="Wenn {{videoSources}} {{condition}} {{filePath}} {{browseButton}} für {{duration}} wechsle zu {{scenes}} mit {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Dieser Tab ermöglicht es basierend auf der Videoausgabe von Quellen scenen zu wechseln.<br/>Für eine noch bessere Implementierung dieser Funktionalität siehe <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a>.<br/><br/>Klicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen..</p></body></html>"
; Network Tab
AdvSceneSwitcher.networkTab.title="Netzwerk"
AdvSceneSwitcher.networkTab.description="Über diesen Tab kann die aktive Szene einer anderen OBS-Instanz ferngesteuert werden.\nBitte beachten, dass die Szenennamen auf allen OBS-Instanzen exakt übereinstimmen müssen."
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.server="Server starten (Sendet Szenenwechselnachrichten zu allen verbundenen Clients)"
AdvSceneSwitcher.networkTab.server.port="Port"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Nur IPv4 verwenden (deaktiviert IPv6)"
AdvSceneSwitcher.networkTab.server.sendSceneChange="Sende Nachrichten für Szenenwechsel"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Sende nur Szenenwechselnachrichten für automatisierte Szenenwechsel"
AdvSceneSwitcher.networkTab.server.sendPreview="Sende Nachrichten für Vorschau Szenenwechsel im Studio-Modus"
AdvSceneSwitcher.networkTab.startFailed.message="Der WebSocket-Server konnte nicht gestartet werden, mögliche Gründe:\n - Der TCP-Port %1 wird möglicherweise gerade von einem anderen Programm verwendet. Versuchen Sie einen anderen Port in den Websocket-Servereinstellungen zu setzen oder alle Programme zu beenden, die den Port möglicherweise verwenden.\n - Fehler: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Aktueller Status"
AdvSceneSwitcher.networkTab.server.status.notRunning="Nicht gestartet"
AdvSceneSwitcher.networkTab.server.status.starting="Startet"
AdvSceneSwitcher.networkTab.server.status.running="Server läuft"
AdvSceneSwitcher.networkTab.server.restart="Server neu starten"
AdvSceneSwitcher.networkTab.client="Client starten (Empfängt Szenenwechselnachrichten )"
AdvSceneSwitcher.networkTab.client.address="Hostname oder IP-Adresse"
AdvSceneSwitcher.networkTab.client.port="Port"
AdvSceneSwitcher.networkTab.client.status.currentStatus="Aktueller status"
AdvSceneSwitcher.networkTab.client.status.disconnected="Verbindung getrennt"
AdvSceneSwitcher.networkTab.client.status.connecting="Verbinde"
AdvSceneSwitcher.networkTab.client.status.connected="Verbunden"
AdvSceneSwitcher.networkTab.client.reconnect="Erneutes Verbinden erzwingen"
; Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="Szenengruppe"
AdvSceneSwitcher.sceneGroupTab.list="Szenengruppen"
@@ -775,30 +745,6 @@ AdvSceneSwitcher.sceneGroupTab.exists="Szenengruppen- oder Szenenname existiert
AdvSceneSwitcher.sceneGroupTab.help="Szenengruppen können, genau wie reguläre Szenen, als Ziel eines Szenenwechsler Eintrags ausgewählt werden.\n\nSzenengruppen bestehen aus einer Liste von Szenen.\nDie aktive Szene der Szenengruppe schreitet abhängig von den konfigurierten Einstellungen durch die Liste der zugewiesenen Szenen fort.\n\nMögliche Einstellungen zum Fortschreiten der aktiven Szene sind:\nAnzahl der Szenenwechsel.\nZeit seit dem letzten Szenenwechsel in der Szenengruppe.\nOder zufällig.\n\nSo kann zum Beispiel eine Szenengruppe welche die folgenden Szenen enthält ...\nSzene 1\nSzene 2\nSzene 3 \n... beim erstem Mal zu \"Szene 1\" wechseln.\nBeim zweiten Mal zu \"Szene 2\".\nDie restlichem Male wechselt die Szenengruppe zu \"Szene 3\".\n\nKlicke auf das markierte Plus Symbol, um eine neuen Szenengruppe hinzuzufügen."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Wähle die Szenengruppe, die du bearbeiten möchtest, auf der linken Seite aus.\n\nWähle oben eine Szene aus, die du zur Szenengruppe hinzufügen möchtest, und klicke das Plus Symbol.\n\nDie gleiche Szene kann mehrfach in derselben Szenengruppe auftauchen."
; Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="Szenen Trigger"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--Trigger auswählen--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="aktiv ist"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="nicht aktiv ist"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="verlassen wird"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--Aktion auswählen--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="Aufnahme starten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="Aufnahme pausieren"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="Aufnahme fortsetzen"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="Aufnahme stoppen"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="Streamen stoppen"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="Streamen starten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="Replay Buffer starten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="Replay Buffer stoppen"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Audio Quelle stumm schalten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="Audio Quelle aktiv schalten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="Szenenwechsler starten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="Szenenwechsler stoppen"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera="Virtuelle Kamera starten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="Virtuelle Kamera stoppen"
AdvSceneSwitcher.sceneTriggerTab.entry="Wenn {{scenes}} {{triggers}} {{actions}} {{audioSources}} nach {{duration}} "
AdvSceneSwitcher.sceneTriggerTab.help="Dieser Tab ermöglicht es automatisiert Handlungen, wie etwa das Stoppen von einer Aufnahme oder des Streamens, bei einem Szenenwechsel auszuführen."
; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Starte den Erweiterten Szenenwechsler"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stoppe den Erweiterten Szenenwechsler"
@@ -806,9 +752,9 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Starte/Stoppe den Erweite
AdvSceneSwitcher.hotkey.macro.pause="Pausiere Makro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Makro %1 nicht mehr pausieren"
AdvSceneSwitcher.hotkey.macro.togglePause="Pause von Makro %1 togglen"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Makro-Segmentauswahl nach oben verschieben"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Makro-Segmentauswahl nach unten verschieben"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Ausgewähltes Makro-Segment entfernen"
AdvSceneSwitcher.hotkey.macro.segment.up="Makro-Segmentauswahl nach oben verschieben"
AdvSceneSwitcher.hotkey.macro.segment.down="Makro-Segmentauswahl nach unten verschieben"
AdvSceneSwitcher.hotkey.macro.segment.remove="Ausgewähltes Makro-Segment entfernen"
AdvSceneSwitcher.askBackup="Neue Version des Erweiterten Automatischen Szenenwechslers wurde erkannt.\nSoll ein Backup der alten Einstellungen angelegt werden?"
AdvSceneSwitcher.askForMacro="Makro auswählen {{macroSelection}}"
@@ -895,7 +841,7 @@ AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Dies scheint das erste
AdvSceneSwitcher.deprecatedTabWarning="Die Entwicklung für dieses Tab wurde gestoppt!\nBitte stattdessen zur Verwendung des Makros-Tab übergehen.\nDieser Hinweis kann im Allgemein-Tab deaktiviert werden."
AdvSceneSwitcher.unit.milliseconds="Millisekunden"
AdvSceneSwitcher.unit.secends="Sekunden"
AdvSceneSwitcher.unit.seconds="Sekunden"
AdvSceneSwitcher.unit.minutes="Minuten"
AdvSceneSwitcher.unit.hours="Stunden"
AdvSceneSwitcher.duration.condition.none="Kein Dauer-Modifikator"

File diff suppressed because it is too large Load Diff

View File

@@ -19,13 +19,12 @@ AdvSceneSwitcher.generalTab.status.autoStart.streaming="Emitiendo"
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Grabando o emitiendo"
AdvSceneSwitcher.generalTab.status.checkInterval="Comprobar las condiciones de cambio cada"
AdvSceneSwitcher.generalTab.generalBehavior="Comportamiento general"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="Si no se cumple ninguna condición por"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Solo será tan preciso como el intervalo de comprobación configurado."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="No cambiar"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Cambiar a una escena aleatoria"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Cambiar a:"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch="Si no se cumple ninguna condición por"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatchDelay.tooltip="Solo será tan preciso como el intervalo de comprobación configurado."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="No cambiar"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Cambiar a una escena aleatoria"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Cambiar a:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="¡Durante este tiempo, se ignorarán las posibles coincidencias!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Habilitar el registro detallado"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Guardar la posición y el tamaño de la ventana"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Mostrar notificaciones del sistema"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deshabilitar sugerencias de interfaz de usuario"
@@ -39,7 +38,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Exportar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Importar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Exportar los ajustes para Advanced Scene Switcher ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Importar los ajustes para Advanced Scene Switcher ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Archivos de texto (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Archivos de texto (*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="La importacion la configuracion de escenas fallo"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Configuración de escenas importada correctamente"
AdvSceneSwitcher.generalTab.priority.fileContent="Contenido del archivo"
@@ -117,8 +116,6 @@ AdvSceneSwitcher.condition.scene.type.changed="Escena cambiada"
AdvSceneSwitcher.condition.scene.type.notChanged="La escena no ha cambiado"
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Durante la transición, verifique la escena de destino de la transición"
AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="Durante la transición, verifique la escena de origen de la transición"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Ventana"
AdvSceneSwitcher.condition.file="Archivo"
AdvSceneSwitcher.condition.file.entry.line1="Contenido de{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
@@ -157,12 +154,12 @@ AdvSceneSwitcher.condition.video.patternMatchSuccess="El patrón está resaltado
AdvSceneSwitcher.condition.video.objectMatchFail="¡No se encontró el objeto!"
AdvSceneSwitcher.condition.video.objectMatchSuccess="El objeto está resaltado en rojo"
AdvSceneSwitcher.condition.video.modelLoadFail="¡No se pudieron cargar los datos del modelo!"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Datos del modelo (haar cascade classifier): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Mínimo de vecinos: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduzca la carga de la CPU realizando una comprobación solo cada {{throttleCount}} milisegundos"
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Realizar comprobación solo en el área"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}Realizar comprobación solo en el área {{checkArea}} {{selectArea}}"
AdvSceneSwitcher.condition.video.layout="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.layout.modelPath="Datos del modelo (haar cascade classifier): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="Mínimo de vecinos: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.throttle="{{throttleEnable}}Reduzca la carga de la CPU realizando una comprobación solo cada {{throttleCount}} milisegundos"
AdvSceneSwitcher.condition.video.layout.checkAreaEnable="Realizar comprobación solo en el área"
AdvSceneSwitcher.condition.video.layout.checkArea="{{checkAreaEnable}}Realizar comprobación solo en el área {{checkArea}} {{selectArea}}"
AdvSceneSwitcher.condition.video.minSize="Tamaño mínimo:"
AdvSceneSwitcher.condition.video.maxSize="Tamaño máximo:"
AdvSceneSwitcher.condition.video.selectArea="Seleccionar área"
@@ -174,7 +171,6 @@ AdvSceneSwitcher.condition.stream.state.start="Transmisión en ejecución"
AdvSceneSwitcher.condition.stream.state.stop="Transmisión detenida"
AdvSceneSwitcher.condition.stream.state.starting="Inicio de transmisión"
AdvSceneSwitcher.condition.stream.state.stopping="Detener transmisión"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Grabación"
AdvSceneSwitcher.condition.record.state.start="Grabación en ejecución"
AdvSceneSwitcher.condition.record.state.pause="Grabación en pausa"
@@ -409,7 +405,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover hacia abajo"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover al principio"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover al final"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -441,7 +437,6 @@ AdvSceneSwitcher.action.random="Aleatorio"
AdvSceneSwitcher.action.random.entry="Ejecute aleatoriamente cualquiera de las siguientes macros (las macros en pausa se ignoran)"
AdvSceneSwitcher.action.systray="Notificación de la bandeja del sistema"
AdvSceneSwitcher.action.screenshot="Captura de pantalla"
AdvSceneSwitcher.action.screenshot.mainOutput="Salida principal de OBS"
AdvSceneSwitcher.action.profile="Perfil"
AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
@@ -618,31 +613,6 @@ AdvSceneSwitcher.videoTab.ignoreInactiveSource="a menos que la fuente esté inac
AdvSceneSwitcher.videoTab.entry="Cuando {{videoSources}} {{condition}} {{filePath}} {{browseButton}} durante {{duration}} cambiar a {{scenes}} usando {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Esta pestaña te permitirá cambiar escenas según la salida de vídeo actual de las fuentes seleccionadas.<br/>Asegúrate de revisar <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> para una implementación aún mejor de esta funcionalidad.<br/><br/> Haz clic en el símbolo más resaltado para continuar.</p></body></html>"
; Network Tab
AdvSceneSwitcher.networkTab.title="Red"
AdvSceneSwitcher.networkTab.description="Esta pestaña le permitirá controlar de forma remota la escena activa de otra instancia de OBS.\nTenga en cuenta que los nombres de las escenas deben coincidir exactamente en todas las instancias de OBS".
AdvSceneSwitcher.networkTab.warning="Ejecutar el servidor fuera de una red local permitirá que terceros lean la escena activa".
AdvSceneSwitcher.networkTab.server="Iniciar servidor (envía mensajes de cambio de escena a todos los clientes conectados)"
AdvSceneSwitcher.networkTab.server.port="Puerto"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Bloquear servidor para usar solo IPv4"
AdvSceneSwitcher.networkTab.server.sendSceneChange="Enviar mensajes para cambios de escena"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Solo enviar mensajes para cambios de escena automatizados"
AdvSceneSwitcher.networkTab.server.sendPreview="Enviar mensajes para obtener una vista previa del cambio de escena cuando se ejecuta en Modo Estudio"
AdvSceneSwitcher.networkTab.startFailed.message="El servidor WebSockets no pudo iniciarse, tal vez porque:\n - El puerto TCP %1 puede estar actualmente en uso en otro lugar de este sistema, posiblemente por otra aplicación. Intente configurar un puerto TCP diferente en el WebSocket configuración del servidor o detenga cualquier aplicación que pueda estar usando este puerto.\n - Mensaje de error: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Estado actual"
AdvSceneSwitcher.networkTab.server.status.notRunning="Desconectado"
AdvSceneSwitcher.networkTab.server.status.starting="Iniciando"
AdvSceneSwitcher.networkTab.server.status.running="En ejecución"
AdvSceneSwitcher.networkTab.server.restart="Reiniciar servidor"
AdvSceneSwitcher.networkTab.client="Iniciar cliente (Recibe mensajes de cambios de escena)"
AdvSceneSwitcher.networkTab.client.address="Nombre de host o dirección IP"
AdvSceneSwitcher.networkTab.client.port="Puerto"
AdvSceneSwitcher.networkTab.client.status.currentStatus="Estado actual"
AdvSceneSwitcher.networkTab.client.status.disconnected="Desconectado"
AdvSceneSwitcher.networkTab.client.status.connecting="Conectando"
AdvSceneSwitcher.networkTab.client.status.connected="Conectado"
AdvSceneSwitcher.networkTab.client.reconnect="Forzar reconexión"
; Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="Grupo de escenas"
AdvSceneSwitcher.sceneGroupTab.list="Grupos de escenas"
@@ -663,30 +633,6 @@ AdvSceneSwitcher.sceneGroupTab.exists="El grupo de escenas o el nombre de la esc
AdvSceneSwitcher.sceneGroupTab.help="Los grupos de escenas se pueden seleccionar como un objetivo al igual que una escena normal.\n\nComo sugiere el nombre, un grupo de escenas es una colección de varias escenas.\nEl grupo de escenas avanzará a través de la lista de sus escenas asignadas según los ajustes configurados, que se puede encontrar en el lado derecho.\n\nPuedes configurar el grupo de escenas para avanzar a la siguiente escena en la lista:\nDespués de varias veces, el grupo de escenas se selecciona como objetivo.\nDespués de que un cierto período de tiempo haya pasado.\nO al azar.\n\nPor ejemplo, un grupo de escenas que contiene las escenas ... \nEscena 1 \nEscena 2 \nEscena 3 \n ... activará la \"Escena 1 \" la primera vez que se seleccione como objetivo. \nLa segunda vez se activará \"Escena 2 \". \nLas veces restantes \"Escena 3 \" se activarán. \n\nHaz clic en el símbolo más resaltado a continuación para agregar un nuevo grupo de escenas."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Selecciona el grupo de escenas que deseas modificar a la izquierda. \n\nSelecciona una escena para agregar a este grupo de escenas seleccionando la escena de arriba y haciendo clic en el símbolo más a continuación. \n\nSe puede agregar una escena varias veces al mismo grupo de escenas."
; Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="Activadores de escena"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--selecciona el activador--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="esté activo"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="no esté activo"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="cambie a otra escena"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--Selecciona la acción--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="empezar a grabar"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="pausar la grabación"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="reanudar grabación"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="detener grabación"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="detener transmisión"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="iniciar transmisión"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="iniciar búfer de repetición"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="detener búfer de repetición"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="silenciar fuente"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="dejar de silenciar fuente"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="iniciar el selector de escenas"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="detener el selector de escenas"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera="Iniciar Camara Virtual"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="Detener Camara Virtual"
AdvSceneSwitcher.sceneTriggerTab.entry="Cuando {{scenes}} {{triggers}} {{actions}} {{audioSources}} después de {{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="Esta pestaña te permite activar acciones sobre cambios de escena, como detener la grabación o la transmisión."
; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Iniciar Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Detener Advanced Scene Switcher"
@@ -694,9 +640,9 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Alternar inicio / detenci
AdvSceneSwitcher.hotkey.macro.pause="Pausar macro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Despausar macro %1"
AdvSceneSwitcher.hotkey.macro.togglePause="Alternar pausa de macro %1"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Mover selección de segmento de macro hacia arriba"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Mover selección de segmento de macro hacia abajo"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Eliminar segmento de macro seleccionado"
AdvSceneSwitcher.hotkey.macro.segment.up="Mover selección de segmento de macro hacia arriba"
AdvSceneSwitcher.hotkey.macro.segment.down="Mover selección de segmento de macro hacia abajo"
AdvSceneSwitcher.hotkey.macro.segment.remove="Eliminar segmento de macro seleccionado"
AdvSceneSwitcher.askBackup="Se detectó una nueva versión de Advanced Scene Switcher.\n¿Crear una copia de seguridad de la configuración anterior?"
AdvSceneSwitcher.askForMacro="Select macro {{macroSelection}}"
@@ -740,7 +686,7 @@ AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Esta parece ser la prim
AdvSceneSwitcher.deprecatedTabWarning="¡Se detuvo el desarrollo de esta pestaña!\nConsidere cambiar a macros en su lugar.\nEsta sugerencia se puede desactivar en la pestaña General".
AdvSceneSwitcher.unit.milliseconds="milisegundos"
AdvSceneSwitcher.unit.secends="segundos"
AdvSceneSwitcher.unit.seconds="segundos"
AdvSceneSwitcher.unit.minutes="minutos"
AdvSceneSwitcher.unit.hours="horas"
AdvSceneSwitcher.duration.condition.none="Sin límite de tiempo"

View File

@@ -19,14 +19,13 @@ AdvSceneSwitcher.generalTab.status.autoStart.streaming="Diffusion en continu"
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Enregistrement ou Diffusion en continu"
AdvSceneSwitcher.generalTab.status.checkInterval="Vérifier les conditions toutes les"
AdvSceneSwitcher.generalTab.generalBehavior="Comportement général"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="Si aucune action n'est effectuée pendant"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Sera aussi précis que l'intervalle de vérification configuré."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Ne pas basculer"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Basculer vers n'importe quelle scène dans l'onglet Aléatoire"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Basculer vers :"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch="Si aucune action n'est effectuée pendant"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatchDelay.tooltip="Sera aussi précis que l'intervalle de vérification configuré."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="Ne pas basculer"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Basculer vers n'importe quelle scène dans l'onglet Aléatoire"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Basculer vers :"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Après avoir effectué des actions, sauter l'exécution d'actions pendant"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="Pendant cette période, les correspondances potentielles seront ignorées !"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Activer les journaux détaillés"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Enregistrer la position et la taille de la fenêtre"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Afficher les notifications dans la zone de notification système"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Désactiver les indications de l'interface utilisateur"
@@ -44,7 +43,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Exporter"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Importer"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Exporter les paramètres d'Advanced Scene Switcher vers un fichier ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Importer les paramètres d'Advanced Scene Switcher depuis un fichier ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Fichiers texte (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Fichiers texte (*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="L'importation des paramètres d'Advanced Scene Switcher a échoué"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Les paramètres d'Advanced Scene Switcher ont été importés avec succès"
AdvSceneSwitcher.generalTab.priority.fileContent="Contenu du fichier"
@@ -109,7 +108,7 @@ AdvSceneSwitcher.macroTab.highlightExecutedMacros="Mettre en surbrillance les ma
AdvSceneSwitcher.macroTab.highlightTrueConditions="Mettre en surbrillance les conditions de la macro actuellement sélectionnée qui ont évalué à vrai récemment"
AdvSceneSwitcher.macroTab.highlightPerformedActions="Mettre en surbrillance les actions récemment effectuées de la macro actuellement sélectionnée"
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Enregistrer des raccourcis clavier pour contrôler l'état de pause des nouvelles macros"
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Enregistrer des raccourcis clavier pour contrôler l'état de pause de la macro sélectionnée"
AdvSceneSwitcher.macroTab.currentRegisterHotkeys="Enregistrer des raccourcis clavier pour contrôler l'état de pause de la macro sélectionnée"
AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="Ignorer l'exécution des actions de la macro actuelle au démarrage"
AdvSceneSwitcher.macroTab.currentRegisterDock="Enregistrer le widget du dock pour contrôler l'état de pause de la macro sélectionnée ou l'exécuter manuellement"
AdvSceneSwitcher.macroTab.currentDockAddRunButton="Ajouter un bouton pour exécuter la macro"
@@ -258,14 +257,14 @@ AdvSceneSwitcher.condition.video.colorDeviationThresholdDescription="À quel poi
AdvSceneSwitcher.condition.video.type.main="Sortie principale d'OBS"
AdvSceneSwitcher.condition.video.type.source="Source"
AdvSceneSwitcher.condition.video.type.scene="Scène"
AdvSceneSwitcher.condition.video.entry.modelPath="Données du modèle (classificateur de cascade de Haar) :{{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Nombre minimum de voisins :{{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Réduire la charge CPU en effectuant la vérification uniquement toutes les{{throttleCount}}millisecondes"
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Effectuer la vérification uniquement dans la zone"
AdvSceneSwitcher.condition.video.entry.orcColorPick="Vérifier la couleur du texte :{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.entry.orcTextType="Vérifier le type de texte :{{textType}}"
AdvSceneSwitcher.condition.video.entry.orcLanguage="Vérifier la langue :{{languageCode}}"
AdvSceneSwitcher.condition.video.entry.color="Vérifier la couleur :{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout.modelPath="Données du modèle (classificateur de cascade de Haar) :{{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="Nombre minimum de voisins :{{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.throttle="{{throttleEnable}}Réduire la charge CPU en effectuant la vérification uniquement toutes les{{throttleCount}}millisecondes"
AdvSceneSwitcher.condition.video.layout.checkAreaEnable="Effectuer la vérification uniquement dans la zone"
AdvSceneSwitcher.condition.video.layout.ocrColorPick="Vérifier la couleur du texte :{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout.ocrTextType="Vérifier le type de texte :{{textType}}"
AdvSceneSwitcher.condition.video.layout.ocrLanguage="Vérifier la langue :{{languageCode}}"
AdvSceneSwitcher.condition.video.layout.color="Vérifier la couleur :{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.minSize="Taille minimale :"
AdvSceneSwitcher.condition.video.maxSize="Taille maximale :"
AdvSceneSwitcher.condition.video.selectArea="Sélectionner la zone"
@@ -454,7 +453,7 @@ AdvSceneSwitcher.condition.slideshow="Diaporama"
AdvSceneSwitcher.condition.slideshow.condition.slideChanged="Diapositive changée"
AdvSceneSwitcher.condition.slideshow.condition.slideIndex="Le numéro de la diapositive actuelle est"
AdvSceneSwitcher.condition.slideshow.condition.slidePath="Le chemin de la diapositive actuelle est"
AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip="Les informations sur l'état du diaporama ne seront mises à jour qu'en fonction du temps configuré entre les diapositives"
AdvSceneSwitcher.condition.slideshow.updateInterval.tooltip="Les informations sur l'état du diaporama ne seront mises à jour qu'en fonction du temps configuré entre les diapositives"
; Macro Actions
AdvSceneSwitcher.action.scene="Changer de scène"
@@ -597,7 +596,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Déplacer vers le bas"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Déplacer en haut"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Déplacer en bas"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Déplacer à la position"
AdvSceneSwitcher.action.sceneOrder.entry="Sur{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="Sur{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformation de l'élément de la scène"
AdvSceneSwitcher.action.sceneTransform.type.manual="Transform"
AdvSceneSwitcher.action.sceneTransform.type.reset="Réinitialiser la transformation"
@@ -647,9 +646,8 @@ AdvSceneSwitcher.action.screenshot.save.default="Par défaut"
AdvSceneSwitcher.action.screenshot.save.custom="Personnalisé"
AdvSceneSwitcher.action.screenshot.type.source="Source"
AdvSceneSwitcher.action.screenshot.type.scene="Scène"
AdvSceneSwitcher.action.screenshot.mainOutput="Sortie principale d'OBS"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Les sources ou les scènes qui ne sont pas toujours rendues peuvent entraîner des parties de captures d'écran vides."
AdvSceneSwitcher.action.screenshot.entry="Capturer{{targetType}}{{sources}}{{scenes}}et enregistrer à l'emplacement{{saveType}}"
AdvSceneSwitcher.action.screenshot.entry="Capturer{{targetType}}{{sources}}{{scenes}}et enregistrer à l'emplacement{{saveType}}{{variables}}"
AdvSceneSwitcher.action.profile="Profil"
AdvSceneSwitcher.action.profile.entry="Changer le profil actif vers{{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Collection de scènes"
@@ -705,10 +703,10 @@ AdvSceneSwitcher.action.variable.invalidSelection="Sélection invalide !"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="La récupération de valeurs de variables à partir d'actions %1 n'est pas prise en charge !"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="La récupération de valeurs de variables à partir de conditions %1 n'est pas prise en charge !"
AdvSceneSwitcher.action.variable.currentSegmentValue="Valeur actuelle :"
AdvSceneSwitcher.action.variable.entry.substringIndex="Début de la sous-chaîne :{{subStringStart}}Taille de la sous-chaîne :{{subStringSize}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="Attribuer la valeur du match{{regexMatchIdx}}en utilisant une expression régulière :"
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Utiliser un message personnalisé{{inputPrompt}}"
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Remplir avec un indicateur de position{{inputPlaceholder}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Début de la sous-chaîne :{{subStringStart}}Taille de la sous-chaîne :{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Attribuer la valeur du match{{regexMatchIdx}}en utilisant une expression régulière :"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Utiliser un message personnalisé{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Remplir avec un indicateur de position{{inputPlaceholder}}"
AdvSceneSwitcher.action.projector="Projecteur"
AdvSceneSwitcher.action.projector.type.source="Source"
AdvSceneSwitcher.action.projector.type.scene="Scène"
@@ -718,8 +716,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivue"
AdvSceneSwitcher.action.projector.display="Affichage"
AdvSceneSwitcher.action.projector.windowed="Fenêtré"
AdvSceneSwitcher.action.projector.fullscreen="Plein écran"
AdvSceneSwitcher.action.projector.entry="Ouvrir le projecteur{{windowTypes}}de{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.monitor="sur{{monitors}}"
AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="Envoyer un message à{{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="Définir la sélection de messages MIDI sur les messages entrants de{{listenDevices}}:{{listenButton}}"
@@ -897,31 +893,6 @@ AdvSceneSwitcher.videoTab.ignoreInactiveSource="sauf si la source est inactive"
AdvSceneSwitcher.videoTab.entry="Lorsque{{videoSources}}{{condition}}{{filePath}}{{browseButton}}pendant{{duration}}, passez à{{scenes}}en utilisant{{transitions}}{{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Cet onglet vous permettra de basculer entre les scènes en fonction de la sortie vidéo actuelle des sources sélectionnées.<br/>Assurez-vous de consulter <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> pour une implémentation encore meilleure de cette fonctionnalité.<br/><br/> Cliquez sur le symbole plus en surbrillance pour continuer.</p></body></html>"
; Network Tab
AdvSceneSwitcher.networkTab.title="Réseau"
AdvSceneSwitcher.networkTab.description="Cet onglet vous permettra de contrôler à distance la scène active d'une autre instance OBS.\nVeuillez noter que les noms de scènes doivent correspondre exactement sur toutes les instances OBS."
AdvSceneSwitcher.networkTab.warning="Exécuter le serveur en dehors d'un réseau local permettra à des tiers de lire la scène active."
AdvSceneSwitcher.networkTab.server="Démarrer le serveur (Envoie des messages de changement de scène à tous les clients connectés)"
AdvSceneSwitcher.networkTab.server.port="Port"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Verrouiller le serveur pour n'utiliser que IPv4"
AdvSceneSwitcher.networkTab.server.sendSceneChange="Envoyer des messages pour les changements de scène"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Envoyer uniquement des messages pour les changements de scène automatisés"
AdvSceneSwitcher.networkTab.server.sendPreview="Envoyer des messages pour le changement de prévisualisation de scène lors de l'exécution en mode Studio"
AdvSceneSwitcher.networkTab.startFailed.message="Le serveur WebSocket n'a pas pu démarrer, peut-être parce que :\n - Le port TCP %1 est peut-être déjà utilisé ailleurs sur ce système, éventuellement par une autre application. Essayez de définir un port TCP différent dans les paramètres du serveur WebSocket, ou arrêtez toute application qui pourrait utiliser ce port.\n - Message d'erreur : %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="État actuel"
AdvSceneSwitcher.networkTab.server.status.notRunning="Non démarré"
AdvSceneSwitcher.networkTab.server.status.starting="Démarrage"
AdvSceneSwitcher.networkTab.server.status.running="En cours d'exécution"
AdvSceneSwitcher.networkTab.server.restart="Redémarrer le serveur"
AdvSceneSwitcher.networkTab.client="Démarrer le client (Reçoit des messages de changement de scène)"
AdvSceneSwitcher.networkTab.client.address="Nom d'hôte ou adresse IP"
AdvSceneSwitcher.networkTab.client.port="Port"
AdvSceneSwitcher.networkTab.client.status.currentStatus="État actuel"
AdvSceneSwitcher.networkTab.client.status.disconnected="Déconnecté"
AdvSceneSwitcher.networkTab.client.status.connecting="Connexion"
AdvSceneSwitcher.networkTab.client.status.connected="Connecté"
AdvSceneSwitcher.networkTab.client.reconnect="Forcer la reconnexion"
; Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="Groupe de Scènes"
AdvSceneSwitcher.sceneGroupTab.list="Groupes de Scènes"
@@ -942,30 +913,6 @@ AdvSceneSwitcher.sceneGroupTab.exists="Le nom du Groupe de Scènes ou de la Scè
AdvSceneSwitcher.sceneGroupTab.help="Les Groupes de Scènes peuvent être sélectionnés comme une cible, tout comme une scène régulière.\n\nComme son nom l'indique, un Groupe de Scènes est une collection de plusieurs scènes.\nLe Groupe de Scènes avancera dans la liste de ses scènes attribuées en fonction des paramètres configurés, que vous pouvez trouver à droite.\n\nVous pouvez configurer le Groupe de Scènes pour passer à la scène suivante dans la liste :\nAprès un certain nombre de fois où le Groupe de Scènes est sélectionné comme cible.\nAprès qu'un certain laps de temps se soit écoulé.\nOu au hasard.\n\nPar exemple, un Groupe de Scènes contenant les scènes...\nScène 1\nScène 2\nScène 3\n... activera "Scène 1" la première fois qu'il est sélectionné comme cible.\nLa deuxième fois, il activera "Scène 2".\nLes fois suivantes, "Scène 3" sera activée.\n\nCliquez sur le symbole plus en surbrillance ci-dessous pour ajouter un nouveau Groupe de Scènes."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Sélectionnez le groupe de scènes que vous souhaitez modifier à gauche.\n\nSélectionnez une scène à ajouter à ce groupe de scènes en sélectionnant la scène ci-dessus et en cliquant sur le symbole plus ci-dessous.\n\nUne scène peut être ajoutée plusieurs fois au même groupe de scènes."
; Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="Déclencheurs de Scène"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--sélectionnez un déclencheur--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="est active"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="n'est pas active"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="passé à autre chose"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--sélectionnez une action--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="démarrer l'enregistrement"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="mettre en pause l'enregistrement"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="reprendre l'enregistrement"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="arrêter l'enregistrement"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="arrêter la diffusion"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="démarrer la diffusion"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="démarrer le tampon de répétition"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="arrêter le tampon de répétition"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="mettre la source en sourdine"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="annuler la mise en sourdine de la source"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="démarrer le commutateur de scènes avancé"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="arrêter le commutateur de scènes avancé"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera="démarrer la caméra virtuelle"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="arrêter la caméra virtuelle"
AdvSceneSwitcher.sceneTriggerTab.entry="Lorsque{{scenes}}{{triggers}}{{actions}}{{audioSources}}après{{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="Cet onglet vous permet de déclencher des actions lors des changements de scènes, comme l'arrêt de l'enregistrement ou de la diffusion."
; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Démarrer le commutateur de scènes avancé"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Arrêter le commutateur de scènes avancé"
@@ -973,9 +920,9 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Basculer le démarrage/ar
AdvSceneSwitcher.hotkey.macro.pause="Mettre en pause la macro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Reprendre la macro %1"
AdvSceneSwitcher.hotkey.macro.togglePause="Basculer la pause de la macro %1"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Déplacer la sélection du segment de macro vers le haut"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Déplacer la sélection du segment de macro vers le bas"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Supprimer le segment de macro sélectionné"
AdvSceneSwitcher.hotkey.macro.segment.up="Déplacer la sélection du segment de macro vers le haut"
AdvSceneSwitcher.hotkey.macro.segment.down="Déplacer la sélection du segment de macro vers le bas"
AdvSceneSwitcher.hotkey.macro.segment.remove="Supprimer le segment de macro sélectionné"
AdvSceneSwitcher.askBackup="Une nouvelle version du commutateur de scènes avancé a été détectée.\nSouhaitez-vous créer une sauvegarde des anciens paramètres ?"
AdvSceneSwitcher.askForMacro="Sélectionnez la macro{{macroSelection}}"
@@ -1161,8 +1108,8 @@ AdvSceneSwitcher.sceneItemSelection.type.sourceName="Nom de la source"
AdvSceneSwitcher.sceneItemSelection.type.sourceVariable="Nom de la variable"
AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern="Le nom de la source correspond au motif"
AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry="{{nameConflictIndex}}correspondant à{{pattern}}{{regex}}"
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup="Sources de type"
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.entry="{{nameConflictIndex}}Type de source{{sourceGroups}}"
AdvSceneSwitcher.sceneItemSelection.type.sourceType="Sources de type"
AdvSceneSwitcher.sceneItemSelection.type.sourceType.entry="{{nameConflictIndex}}Type de source{{sourceTypes}}"
AdvSceneSwitcher.sceneItemSelection.type.index="Source avec index"
AdvSceneSwitcher.sceneItemSelection.type.index.entry="{{index}}source"
AdvSceneSwitcher.sceneItemSelection.type.indexRange="Sources dans la plage d'index"
@@ -1182,7 +1129,7 @@ AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Il semble que ce soit l
AdvSceneSwitcher.deprecatedTabWarning="Le développement de cet onglet est terminé !\nVeuillez envisager de passer à l'utilisation des Macros à la place.\nCette astuce peut être désactivée dans l'onglet Général."
AdvSceneSwitcher.unit.milliseconds="millisecondes"
AdvSceneSwitcher.unit.secends="secondes"
AdvSceneSwitcher.unit.seconds="secondes"
AdvSceneSwitcher.unit.minutes="minutes"
AdvSceneSwitcher.unit.hours="heures"
AdvSceneSwitcher.duration.condition.none="Aucun modificateur de durée"

View File

@@ -1,7 +1,7 @@
AdvSceneSwitcher.pluginName="高機能シーンスイッチャー"
AdvSceneSwitcher.windowTitle="高機能シーンスイッチャー"
; General Tab
# General Tab
AdvSceneSwitcher.generalTab.title="全般"
AdvSceneSwitcher.generalTab.status="ステータス"
AdvSceneSwitcher.generalTab.status.hotkeytips="OBS設定でホットキーを定義可能"
@@ -19,24 +19,24 @@ AdvSceneSwitcher.generalTab.status.autoStart.streaming="配信を開始した時
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="録画開始時または配信開始時"
AdvSceneSwitcher.generalTab.status.checkInterval="イベント条件を確認する頻度"
AdvSceneSwitcher.generalTab.generalBehavior="一般"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="何も操作を行わなかった場合"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="設定されたチェック間隔と同程度の精度のみが保証されます。"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="切り替えないでください"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="ランダムタブで任意のシーンに切り替え"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="切り替え先:"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch="何も操作を行わなかった場合"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatchDelay.tooltip="設定されたチェック間隔と同程度の精度のみが保証されます。"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="切り替えないでください"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="ランダムタブで任意のシーンに切り替え"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="切り替え先:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="アクションを実行した後、次のアクションをスキップする時間"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="この間、潜在的な一致は無視されます!"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel="ログレベル:"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="デフォルト"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.printActions="実行されたアクションのログ"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction="実行されたアクションのログ"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="詳細ログVerbose"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="詳細ログを有効にする(生成されるログファイルのサイズが増加します)"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="ウィンドウのサイズと位置を記憶する"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="システムトレイ通知を表示"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="UIヒントを無効にする"
AdvSceneSwitcher.generalTab.generalBehavior.comboBoxFilterDisable="ドロップダウンメニューでの文字入力によるフィルタリングを無効にする"
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="プラグインが読み込めない場合に警告を表示"
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>次のプラグイン ライブラリの読み込みに失敗したため、一部の高機能シーンスイッチャーが利用できなくなる可能性があります:%1詳細については、OBS ログを確認してください。<br>このメッセージは、[全般] タブで無効にすることができます。 </body></html>"
AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="プラグインが正しくインストールできておらずクラッシュする可能性があります!\nプラグインが正しくインストールされているか確認してください"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="旧来型マクロ設定のタブを非表示にする(廃止予定タブ)"
AdvSceneSwitcher.generalTab.matchBehavior="マッチした場合の動作(廃止予定の設定項目)"
AdvSceneSwitcher.generalTab.priority="優先順位"
@@ -46,11 +46,11 @@ AdvSceneSwitcher.generalTab.priority.threadPriorityNotice="(優先度を \"Norma
AdvSceneSwitcher.generalTab.saveOrLoadsettings="設定の保存・読み込み"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="エクスポート"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="インポート"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="高度なシーンスイッチャーの設定をファイルにエクスポート..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="高度なシーンスイッチャーの設定をファイルからインポート..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="テキストファイル(*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="高機能シーンスイッチャーの設定をファイルにエクスポート..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="高機能シーンスイッチャーの設定をファイルからインポート..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="テキストファイル(*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="高機能シーンスイッチャーの設定インポートに失敗しました"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="シーンスイッチャーの高度な設定が正常にインポートされました"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="高機能シーンスイッチャーの設定が正常にインポートされました"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportSensitiveDataWarning="警告:\nエクスポートされたデータにはセンシティブ情報が含まれている可能性があります"
AdvSceneSwitcher.generalTab.priority.fileContent="ファイル内容"
AdvSceneSwitcher.generalTab.priority.sceneSequence="シーンシーケンス"
@@ -69,7 +69,7 @@ AdvSceneSwitcher.generalTab.transitionOverride="トランジションオーバ
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="アクティブトランジションタイプを調整する"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="少なくとも1つのオプションが有効であること\n\n - トランジションオーバーライドの使用\n\n - アクティブなトランジションタイプを変更する"
; Variables Tab
# Variables Tab
AdvSceneSwitcher.variableTab.title="変数"
AdvSceneSwitcher.variableTab.help="変数はプラグイン内の様々な場所で使用できます。\n\n強調表示されたプラス記号をクリックすると、新しい変数を追加できます。"
AdvSceneSwitcher.variableTab.variableAddButton.tooltip="新しい変数を追加する"
@@ -81,14 +81,12 @@ AdvSceneSwitcher.variableTab.value.header="値"
AdvSceneSwitcher.variableTab.saveLoadBehavior.header="保存・読み込みの動作"
AdvSceneSwitcher.variableTab.saveLoadBehavior.text.default="デフォルト\"%1\""
AdvSceneSwitcher.variableTab.lastUsed.header="最後に使用された"
AdvSceneSwitcher.variableTab.lastUsed.text="%1 秒前"
; AdvSceneSwitcher.variableTab.lastUsed.text.never="決して"
; AdvSceneSwitcher.variableTab.lastUsed.text.never="Never"
AdvSceneSwitcher.variableTab.lastChanged.header="最終変更"
AdvSceneSwitcher.variableTab.lastChanged.text="%1 秒前"
AdvSceneSwitcher.variableTab.lastChanged.text.none="起動以来変更なし"
AdvSceneSwitcher.variableTab.lastChanged.tooltip="変更された時間: %1\n\n以前の値: %2"
; Action Queue Tab
# Action Queue Tab
AdvSceneSwitcher.actionQueueTab.title="アクションキュー"
AdvSceneSwitcher.actionQueueTab.help="アクションキューは、マクロシステムのリセットと並行して順次実行されます。"
AdvSceneSwitcher.actionQueueTab.queueAddButton.tooltip="新しいアクションキューを追加する"
@@ -102,7 +100,7 @@ AdvSceneSwitcher.actionQueueTab.no="いいえ"
AdvSceneSwitcher.actionQueueTab.removeSingleQueuePopup.text="本当に \"%1\"を削除しますか?"
AdvSceneSwitcher.actionQueueTab.removeMultipleQueuesPopup.text="アクション・キュー %1 を削除しますか?"
; Websocket Connections Tab
# Websocket Connections Tab
AdvSceneSwitcher.websocketConnectionTab.title="Websocket接続"
AdvSceneSwitcher.websocketConnectionTab.help="Websocket接続は、他のOBSインスタンスまたはプログラムと通信するために使用できます。"
AdvSceneSwitcher.websocketConnectionTab.websocketConnectionAddButton.tooltip="新しいWebsocket接続を追加する"
@@ -116,10 +114,10 @@ AdvSceneSwitcher.websocketConnectionTab.protocol.no="いいえ"
AdvSceneSwitcher.websocketConnectionTab.removeSingleConnectionPopup.text="本当に\"%1\"を削除しますか?"
AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text="本当に%1接続を削除しますか"
; Twitch Connections Tab
# Twitch Connections Tab
AdvSceneSwitcher.twitchConnectionTab.title="Twitch接続"
AdvSceneSwitcher.twitchConnectionTab.help="Twitch接続を使用すると、Twitchイベントをトリガーとしてアクションを実行したり、リンク先のTwitchアカウントでアクションを実行したりできます。"
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip.tooltip="新しいTwitch接続を追加する"
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip="新しいTwitch接続を追加する"
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionRemoveButton.tooltip="選択したTwitch接続を削除する"
AdvSceneSwitcher.twitchConnectionTab.name.header="名前"
AdvSceneSwitcher.twitchConnectionTab.isValid.header="有効ですか?"
@@ -129,7 +127,7 @@ AdvSceneSwitcher.twitchConnectionTab.no="いいえ"
AdvSceneSwitcher.twitchConnectionTab.removeSingleConnectionPopup.text="本当に\"%1\"を削除しますか?"
AdvSceneSwitcher.twitchConnectionTab.removeMultipleConnectionsPopup.text="本当に%1 Twitch接続を削除しますか?"
; Macro Tab
# Macro Tab
AdvSceneSwitcher.macroTab.title="マクロ"
AdvSceneSwitcher.macroTab.macros="マクロ"
AdvSceneSwitcher.macroTab.priorityWarning="注意: マクロを最優先機能に設定することをお勧めします。\nこの設定は [全般] タブで変更できます。"
@@ -175,10 +173,13 @@ AdvSceneSwitcher.macroTab.collapseAll="すべて折りたたむ"
AdvSceneSwitcher.macroTab.maximize="最大化"
AdvSceneSwitcher.macroTab.minimize="最小化"
AdvSceneSwitcher.macroTab.segment.useCustomLabel="カスタムラベルの使用"
AdvSceneSwitcher.macroTab.segment.customLabelRename="カスタムラベルの名前を変更する"
AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="マイ・ラベル"
AdvSceneSwitcher.macroTab.segment.setCustomLabel="ラベルを設定 ..."
AdvSceneSwitcher.macroTab.segment.copy="コピー"
AdvSceneSwitcher.macroTab.segment.paste="貼り付け"
AdvSceneSwitcher.macroTab.segment.pasteAction="アクションとして貼り付ける"
AdvSceneSwitcher.macroTab.segment.pasteElseAction="elseアクションとして貼り付ける"
AdvSceneSwitcher.macroTab.highlightSettings="ビジュアル設定"
AdvSceneSwitcher.macroTab.hotkeySettings="ホットキー設定"
AdvSceneSwitcher.macroTab.generalSettings="一般設定"
@@ -191,7 +192,7 @@ AdvSceneSwitcher.macroTab.highlightExecutedMacros="最近実行したマクロ
AdvSceneSwitcher.macroTab.highlightTrueConditions="現在選択されているマクロのうち、最近 true と評価された条件を強調表示します"
AdvSceneSwitcher.macroTab.highlightPerformedActions="現在選択されているマクロの最近実行されたアクションを強調表示します"
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="新規マクロの一時停止状態を制御するホットキーを登録"
AdvSceneSwitcher.macroTab.currentDisableHotkeys="選択したマクロの一時停止状態を制御するホットキーを登録"
AdvSceneSwitcher.macroTab.currentRegisterHotkeys="選択したマクロの一時停止状態を制御するホットキーを登録"
AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="起動時に現在のマクロのアクションの実行をスキップします"
AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone="新しい実行がトリガーされたときに、現在選択されているマクロのアクションがまだ実行中であれば、アクションを停止して再実行する。"
AdvSceneSwitcher.macroTab.currentRegisterDock="ドックウィジェットを登録して、選択したマクロの一時停止状態を制御したり、手動で実行したりできます"
@@ -238,11 +239,11 @@ AdvSceneSwitcher.macroDock.run="実行"
AdvSceneSwitcher.macroDock.statusLabel.true="結果はturuでした。"
AdvSceneSwitcher.macroDock.statusLabel.false="結果はfalseでした。"
; Macro List
# Macro List
AdvSceneSwitcher.macroList.deleted="削除"
AdvSceneSwitcher.macroList.duplicate="\"%1\" はすでに選択されています!"
; Macro Logic
# Macro Logic
AdvSceneSwitcher.logic.none="入力無視"
; AdvSceneSwitcher.logic.and="And"
; AdvSceneSwitcher.logic.or="Or"
@@ -251,7 +252,8 @@ AdvSceneSwitcher.logic.none="入力無視"
; AdvSceneSwitcher.logic.rootNone="If"
; AdvSceneSwitcher.logic.not="If not"
; Macro Conditions
# Macro Conditions
AdvSceneSwitcher.condition.unknown="不明な状態"
AdvSceneSwitcher.condition.audio="音声"
AdvSceneSwitcher.condition.audio.state.below="以下"
AdvSceneSwitcher.condition.audio.state.exact="同じ"
@@ -265,9 +267,9 @@ AdvSceneSwitcher.condition.audio.type.monitor="オーディオモニタリング
AdvSceneSwitcher.condition.audio.type.balance="オーディオバランス"
AdvSceneSwitcher.condition.audio.entry="{{audioSources}}の{{checkType}}は{{condition}}{{volume}}{{volumeDB}}{{percentDBToggle}}{{syncOffset}}{{monitorTypes}}です"
AdvSceneSwitcher.condition.cursor="カーソル"
AdvSceneSwitcher.condition.cursor.type.region="位置にあります"
AdvSceneSwitcher.condition.cursor.type.moving="動いている"
AdvSceneSwitcher.condition.cursor.type.click="クリックした"
AdvSceneSwitcher.condition.cursor.type.region="が次の範囲内にある場合"
AdvSceneSwitcher.condition.cursor.type.moving="動いた場合"
AdvSceneSwitcher.condition.cursor.type.click="クリックした場合"
AdvSceneSwitcher.condition.cursor.button.left="マウスの左ボタン"
AdvSceneSwitcher.condition.cursor.button.middle="マウスの中ボタン"
AdvSceneSwitcher.condition.cursor.button.right="マウスの右ボタン"
@@ -307,10 +309,13 @@ AdvSceneSwitcher.condition.file.local="ローカルファイル"
; AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
; AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="Media"
AdvSceneSwitcher.condition.media.checkType.state="状態が一致する"
AdvSceneSwitcher.condition.media.checkType.time="時間制限にマッチ"
AdvSceneSwitcher.condition.media.source="ソース"
AdvSceneSwitcher.condition.media.anyOnScene="あらゆるメディアソース"
AdvSceneSwitcher.condition.media.allOnScene="すべてのメディアソースはこちら"
AdvSceneSwitcher.condition.media.inconsistencyInfo="残念ながら、すべてのメディア ソース タイプが同じように動作するわけではありません (例: メディア ソースと VLC ビデオ ソースの \"Stopped\" 状態)。\nそのため、ご自身のセットアップに適したものを試してみてください。"
; AdvSceneSwitcher.condition.media.layout="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}{{states}}{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}の状態は{{states}}かつ{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="動画"
AdvSceneSwitcher.condition.video.condition.match="完全一致"
@@ -374,16 +379,14 @@ AdvSceneSwitcher.condition.video.colorDeviationThresholdDescription="一致と
AdvSceneSwitcher.condition.video.type.main="OBS's main output"
AdvSceneSwitcher.condition.video.type.source="ソース"
AdvSceneSwitcher.condition.video.type.scene="シーン"
; AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="モデルデータ (そのカスケード分類子):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="最小近傍数:{{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}:{{throttleCount}}ミリ秒ごとにのみチェックを実行することでCPU負荷を軽減します"
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="エリア内のみチェックを実施"
; AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.entry.orcColorPick="テキストの色の確認:{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.entry.orcTextType="テキストタイプの確認:{{textType}}"
AdvSceneSwitcher.condition.video.entry.orcLanguage="言語の確認:{{languageCode}}"
AdvSceneSwitcher.condition.video.entry.color="カラーを確認してください:{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout.modelPath="モデルデータ (そのカスケード分類子):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="最小近傍数:{{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.throttle="{{throttleEnable}}:{{throttleCount}}ミリ秒ごとにのみチェックを実行することでCPU負荷を軽減します"
AdvSceneSwitcher.condition.video.layout.checkAreaEnable="エリア内のみチェックを実施"
AdvSceneSwitcher.condition.video.layout.ocrColorPick="テキストの色の確認:{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout.ocrTextType="テキストタイプの確認:{{textType}}"
AdvSceneSwitcher.condition.video.layout.ocrLanguage="言語の確認:{{languageCode}}"
AdvSceneSwitcher.condition.video.layout.color="カラーを確認してください:{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.minSize="最小サイズ:"
AdvSceneSwitcher.condition.video.maxSize="最大サイズ:"
AdvSceneSwitcher.condition.video.selectArea="範囲指定"
@@ -463,7 +466,7 @@ AdvSceneSwitcher.condition.source.sizeCompare.less="より小さい"
AdvSceneSwitcher.condition.source.sizeCompare.equal="等しい"
AdvSceneSwitcher.condition.source.sizeCompare.more="より大きい"
AdvSceneSwitcher.condition.source.refresh="更新"
AdvSceneSwitcher.condition.source.refreshTooltip="名前が変数値と一致するソースの設定でソース設定の選択を再入力します。"
AdvSceneSwitcher.condition.source.refresh.tooltip="名前が変数値と一致するソースの設定でソース設定の選択を再入力します。"
AdvSceneSwitcher.condition.source.sceneVisibilityHint="シーン固有の可視性は \"Scene item visibility\" 条件で確認可能"
AdvSceneSwitcher.condition.source.getSettings="現在の設定を取得する"
; AdvSceneSwitcher.condition.source.entry.line1="{{sources}}{{conditions}}{{settingSelection}}{{refresh}}{{sizeCompareMethods}}{{size}}"
@@ -481,7 +484,7 @@ AdvSceneSwitcher.condition.filter.type.settingsChanged="設定変更"
AdvSceneSwitcher.condition.filter.type.individualSettingMatches="設定値が一致"
AdvSceneSwitcher.condition.filter.type.individualSettingChanged="設定値が変更されました"
AdvSceneSwitcher.condition.filter.refresh="更新"
AdvSceneSwitcher.condition.filter.refreshTooltip="変数値と一致する名前のフィルターの設定でフィルター設定の選択を再入力します。"
AdvSceneSwitcher.condition.filter.refresh.tooltip="変数値と一致する名前のフィルターの設定でフィルター設定の選択を再入力します。"
AdvSceneSwitcher.condition.filter.getSettings="現在の設定を取得する"
; AdvSceneSwitcher.condition.filter.entry.line1="On{{sources}}{{filters}}{{conditions}}{{settingSelection}}{{refresh}}"
; AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
@@ -531,8 +534,17 @@ AdvSceneSwitcher.condition.date.entry.pattern="現在の日付「{{currentDate}}
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}繰り返しの場合、選択した日付を繰り返しの日付に更新します"
; AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
; AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="現在の値を取得"
AdvSceneSwitcher.condition.sceneTransform.settingsType.all="全て"
AdvSceneSwitcher.condition.sceneTransform.settingsType.single="単一"
AdvSceneSwitcher.condition.sceneTransform.condition.match="マッチトランスフォーム"
AdvSceneSwitcher.condition.sceneTransform.condition.changed="変わりました"
AdvSceneSwitcher.condition.sceneTransform.compare.less="より小さい"
AdvSceneSwitcher.condition.sceneTransform.compare.equal="等しい"
AdvSceneSwitcher.condition.sceneTransform.compare.more="より大きい"
; AdvSceneSwitcher.condition.sceneTransform.entry="On{{scenes}}{{settingsType}}transform setting of{{sources}}{{conditions}}"
; AdvSceneSwitcher.condition.sceneTransform.entry.singleSettingCompare="{{setting}}{{compare}}{{singleSettingValue}}"
; AdvSceneSwitcher.condition.sceneTransform.entry.options="{{regex}}{{getSettings}}{{getCurrentValue}}"
; AdvSceneSwitcher.condition.transition="Transition"
; AdvSceneSwitcher.condition.transition.type.current="Current transition type is"
AdvSceneSwitcher.condition.transition.type.duration="Current transition duration is"
@@ -616,7 +628,7 @@ AdvSceneSwitcher.condition.slideshow="スライドショー"
AdvSceneSwitcher.condition.slideshow.condition.slideChanged="スライド変更しました"
AdvSceneSwitcher.condition.slideshow.condition.slideIndex="現在のスライド番号は"
AdvSceneSwitcher.condition.slideshow.condition.slidePath="現在のスライドパスは"
AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip="スライド ショーのステータスに関する情報は、設定されたスライド間の時間に基づいてのみ更新されます"
AdvSceneSwitcher.condition.slideshow.updateInterval.tooltip="スライド ショーのステータスに関する情報は、設定されたスライド間の時間に基づいてのみ更新されます"
; AdvSceneSwitcher.condition.slideshow.entry="{{sources}}{{conditions}}{{index}}{{path}}"
; AdvSceneSwitcher.condition.twitch="Twitch"
AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online="配信がオンラインになりました"
@@ -669,6 +681,16 @@ AdvSceneSwitcher.condition.twitch.type.polling.channel.live="配信中です"
AdvSceneSwitcher.condition.twitch.type.polling.channel.title="現在のタイトルマッチ"
AdvSceneSwitcher.condition.twitch.type.polling.channel.category="現在のカテゴリーは"
AdvSceneSwitcher.condition.twitch.type.chat.message="チャットメッセージ受信済み"
AdvSceneSwitcher.condition.twitch.type.chat.properties="チャットメッセージプロパティ:"
AdvSceneSwitcher.condition.twitch.type.chat.properties.select="チャットメッセージプロパティを選択します:"
AdvSceneSwitcher.condition.twitch.type.chat.properties.firstMessage="最初のメッセージ"
AdvSceneSwitcher.condition.twitch.type.chat.properties.emoteOnly="エモートのみ使用"
AdvSceneSwitcher.condition.twitch.type.chat.properties.mod="モデレータ"
AdvSceneSwitcher.condition.twitch.type.chat.properties.subscriber="サブスクライバー"
AdvSceneSwitcher.condition.twitch.type.chat.properties.turbo="ターボ・ユーザー"
AdvSceneSwitcher.condition.twitch.type.chat.properties.displayName="表示名が一致"
AdvSceneSwitcher.condition.twitch.type.chat.properties.loginName="ユーザー名が一致"
AdvSceneSwitcher.condition.twitch.type.chat.properties.badge="ユーザーがバッジを持っている"
AdvSceneSwitcher.condition.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません"
AdvSceneSwitcher.condition.twitch.entry="チャンネル{{channel}}{{conditions}}{{pointsReward}}{{streamTitle}}{{regex}}{{category}}"
AdvSceneSwitcher.condition.twitch.entry.account="アカウント{{account}}で確認してください"
@@ -682,7 +704,7 @@ AdvSceneSwitcher.condition.queue.entry.startStop="キュー{{queues}}は{{condit
AdvSceneSwitcher.condition.queue.entry.size="キュー{{queues}}{{conditions}}は{{size}}未満です"
AdvSceneSwitcher.condition.clipboard="クリップボード"
AdvSceneSwitcher.condition.clipboard.placeholder="クリップボードのテキスト"
AdvSceneSwitcher.condition.clipboard.urlTooltip="OS によっては、ファイルが URL として表されることもあります"
AdvSceneSwitcher.condition.clipboard.url.tooltip="OS によっては、ファイルが URL として表されることもあります"
AdvSceneSwitcher.condition.clipboard.condition.changed="クリップボードの内容が変更されました"
AdvSceneSwitcher.condition.clipboard.condition.isText="クリップボードにテキストが含まれています"
AdvSceneSwitcher.condition.clipboard.condition.isImage="クリップボードに画像が含まれています"
@@ -697,11 +719,22 @@ AdvSceneSwitcher.condition.folder.condition.fileChange="ファイルが変更さ
AdvSceneSwitcher.condition.folder.condition.fileRemove="ファイルが削除されました"
AdvSceneSwitcher.condition.folder.condition.folderAdd="ディレクトリが追加されました"
AdvSceneSwitcher.condition.folder.condition.folderRemove="ディレクトリが削除されました"
; ; AdvSceneSwitcher.condition.folder.entry="{{conditions}}in{{folder}}{{tooltip}}"
; AdvSceneSwitcher.condition.folder.entry="{{conditions}}in{{folder}}{{tooltip}}"
AdvSceneSwitcher.condition.folder.enableFilter="変更パスがパターンに一致する場合にのみtrueと評価されます"
; ; AdvSceneSwitcher.condition.folder.entry.filter="{{filter}}{{regex}}"
; AdvSceneSwitcher.condition.folder.entry.filter="{{filter}}{{regex}}"
; AdvSceneSwitcher.condition.usb="USB"
AdvSceneSwitcher.condition.usb.description="以下のプロパティに一致するUSBデバイスが接続されています:"
; AdvSceneSwitcher.condition.usb.vendorID="Vendor ID:"
; AdvSceneSwitcher.condition.usb.productID="Product ID:"
; AdvSceneSwitcher.condition.usb.busNumber="Bus Number:"
; AdvSceneSwitcher.condition.usb.deviceAddress="Device Address:"
; AdvSceneSwitcher.condition.usb.vendorName="Vendor Name:"
; AdvSceneSwitcher.condition.usb.productName="Product Name:"
; AdvSceneSwitcher.condition.usb.serialNumber="Serial Number:"
AdvSceneSwitcher.condition.noDevicesFoundWarning="USBデバイスが検出されません\nプラグインがUSBデバイスをチェックするのに必要な権限を持っていない可能性があります。"
; Macro Actions
# Macro Actions
AdvSceneSwitcher.action.unknown="不明なアクション"
AdvSceneSwitcher.action.scene="シーン切り替え"
AdvSceneSwitcher.action.scene.type.program="プログラム"
AdvSceneSwitcher.action.scene.type.preview="プレビュー"
@@ -745,9 +778,11 @@ AdvSceneSwitcher.action.recording.split.hint="まずOBS設定で自動ファイ
; AdvSceneSwitcher.action.recording.entry="{{actions}}{{recordFolder}}{{recordFileFormat}}{{pauseHint}}{{splitHint}}"
AdvSceneSwitcher.action.replay="リプレイバッファー"
AdvSceneSwitcher.action.replay.saveWarn="警告: 頻繁に保存しすぎると、リプレイ バッファーが実際には保存されなくなる可能性があります。"
AdvSceneSwitcher.action.replay.durationWarn="警告: 最大再生時間を変更すると、次に再生バッファが起動したときにのみ適用されます!"
AdvSceneSwitcher.action.replay.type.stop="リプレイバッファ停止"
AdvSceneSwitcher.action.replay.type.start="リプレイバッファ開始"
AdvSceneSwitcher.action.replay.type.save="リプレイバッファの保存"
; AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="配信"
AdvSceneSwitcher.action.streaming.type.stop="配信停止"
@@ -775,7 +810,7 @@ AdvSceneSwitcher.action.filter.type.toggle="切り替え"
AdvSceneSwitcher.action.filter.type.settings="設定を行う"
AdvSceneSwitcher.action.filter.type.pressSettingsButton="設定ボタンを押します"
AdvSceneSwitcher.action.filter.refresh="更新"
AdvSceneSwitcher.action.filter.refreshTooltip="変数値と一致する名前のフィルターの設定でフィルター設定の選択を再入力します。"
AdvSceneSwitcher.action.filter.refresh.tooltip="変数値と一致する名前のフィルターの設定でフィルター設定の選択を再入力します。"
; AdvSceneSwitcher.action.filter.entry="On{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
; AdvSceneSwitcher.action.filter.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}"
AdvSceneSwitcher.action.filter.getSettings="現在の設定を取得する"
@@ -813,7 +848,7 @@ AdvSceneSwitcher.action.source.inputMethod.individualManual="固定値に設定"
AdvSceneSwitcher.action.source.inputMethod.individualTempvar="マクロプロパティに設定"
AdvSceneSwitcher.action.source.inputMethod.json="設定用JSON文字列を設定します"
AdvSceneSwitcher.action.source.refresh="更新"
AdvSceneSwitcher.action.source.refreshTooltip="名前が変数値と一致するソースの設定でソース設定の選択を再入力します。"
AdvSceneSwitcher.action.source.refresh.tooltip="名前が変数値と一致するソースの設定でソース設定の選択を再入力します。"
; AdvSceneSwitcher.action.media="Media"
AdvSceneSwitcher.action.media.type.play="再生"
AdvSceneSwitcher.action.media.type.pause="一時停止"
@@ -850,7 +885,7 @@ AdvSceneSwitcher.action.macro.type.toggleAction="トグルアクション"
; AdvSceneSwitcher.action.macro.entry.run.condition="{{conditionBehaviors}}of{{conditionMacros}}"
; AdvSceneSwitcher.action.macro.entry.other="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="プラグインの状態"
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcherプラグインの停止"
AdvSceneSwitcher.action.pluginState.type.stop="高機能シーンスイッチャープラグインの停止"
AdvSceneSwitcher.action.pluginState.type.noMatch="不一致の動作を変更します:"
AdvSceneSwitcher.action.pluginState.type.import="設定のインポート元"
AdvSceneSwitcher.action.pluginState.importWarning="注意:設定ウィンドウを開いている間はアクションは無視されます。"
@@ -887,9 +922,10 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="下へ移動"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="トップへ移動"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="一番下へ移動"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="ポジションへの移動"
AdvSceneSwitcher.action.sceneOrder.entry="オン{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="オン{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="シーンアイテムの変形"
AdvSceneSwitcher.action.sceneTransform.type.manual="変形"
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="トランスフォーム設定を行う"
AdvSceneSwitcher.action.sceneTransform.type.reset="変形をリセット"
AdvSceneSwitcher.action.sceneTransform.type.rotate="回転させます"
AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal="左右反転"
@@ -900,6 +936,8 @@ AdvSceneSwitcher.action.sceneTransform.type.centerToScreen="画面中央から"
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="縦方向中央に配置"
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="水平方向に中央揃え"
AdvSceneSwitcher.action.sceneTransform.getTransform="変形を取得"
AdvSceneSwitcher.action.sceneTransform.getCurrentValue="現在の値を取得"
; AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
AdvSceneSwitcher.action.file="ファイル"
; AdvSceneSwitcher.action.file.type.write="Write"
; AdvSceneSwitcher.action.file.type.append="Append"
@@ -938,9 +976,8 @@ AdvSceneSwitcher.action.screenshot.save.default="デフォルト"
AdvSceneSwitcher.action.screenshot.save.custom="カスタム"
; AdvSceneSwitcher.action.screenshot.type.source="Source"
AdvSceneSwitcher.action.screenshot.type.scene="シーン"
AdvSceneSwitcher.action.screenshot.mainOutput="OBSの主な出力"
AdvSceneSwitcher.action.screenshot.blackscreenNote="常にレンダリングされるわけではないソースやシーンにより、スクリーンショットの一部が空白のままになる場合があります。"
AdvSceneSwitcher.action.screenshot.entry="スクリーンショット{{targetType}}{{sources}}{{scenes}}を作成し、{{saveType}}の場所に保存します"
AdvSceneSwitcher.action.screenshot.entry="スクリーンショット{{targetType}}{{sources}}{{scenes}}を作成し、{{saveType}}{{variables}}の場所に保存します"
AdvSceneSwitcher.action.profile="プロファイル"
AdvSceneSwitcher.action.profile.entry="アクティブなプロファイルを{{profiles}}に切り替えます"
; AdvSceneSwitcher.action.sceneCollection="Scene collection"
@@ -990,7 +1027,7 @@ AdvSceneSwitcher.action.variable.type.askForValue="入力ダイアログを表
AdvSceneSwitcher.action.variable.type.environmentVariable="特定の環境変数の値に設定します"
AdvSceneSwitcher.action.variable.type.sceneItemCount="特定のシーンのシーン項目の数の値にします"
AdvSceneSwitcher.action.variable.type.stringLength="変数値を指定された入力文字列の長さに設定"
AdvSceneSwitcher.action.variable.type.extractJson="変数から json フィールドを抽出する"
AdvSceneSwitcher.action.variable.type.extractJsonField="変数から json フィールドを抽出する"
AdvSceneSwitcher.action.variable.type.setToTempvar="マクロプロパティに設定"
AdvSceneSwitcher.action.variable.type.setToTempvar.help="このアクション タイプを使用すると、現在のマクロのセグメントから値を抽出し、それらの値を選択した変数に割り当てることができます。\nたとえば、シーン条件から現在のシーン名を取得し、この名前を変数に割り当てることができます。"
AdvSceneSwitcher.action.variable.type.sceneItemName="インデックスのシーン項目名に設定"
@@ -1010,14 +1047,14 @@ AdvSceneSwitcher.action.variable.invalidSelection="選択が無効です!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="%1 アクションからの変数値の取得はサポートされていません!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="%1 条件からの変数値の取得はサポートされていません!"
AdvSceneSwitcher.action.variable.currentSegmentValue="現在値:"
; AdvSceneSwitcher.action.variable.entry.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{paddingCharSelection}}を{{direction}}に追加します。"
AdvSceneSwitcher.action.variable.entry.truncate="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{direction}}から文字を削除します。"
AdvSceneSwitcher.action.variable.entry.substringIndex="部分文字列の開始:{{subStringStart}}部分文字列のサイズ:{{subStringSize}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="正規表現を使用して{{regexMatchIdx}}一致の値を割り当てます:"
; AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}カスタム プロンプトを使用する{{inputPrompt}}"
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}プレースホルダーを入力{{inputPlaceholder}}"
; AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{paddingCharSelection}}を{{direction}}に追加します。"
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}の{{variables}}の長さを{{stringLength}}にするには、{{direction}}から文字を削除します。"
AdvSceneSwitcher.action.variable.layout.substringIndex="部分文字列の開始:{{subStringStart}}部分文字列のサイズ:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="正規表現を使用して{{regexMatchIdx}}一致の値を割り当てます:"
; AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}カスタム プロンプトを使用する{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}プレースホルダーを入力{{inputPlaceholder}}"
AdvSceneSwitcher.action.projector="プロジェクター"
AdvSceneSwitcher.action.projector.type.source="ソース"
AdvSceneSwitcher.action.projector.type.scene="シーン"
@@ -1027,8 +1064,6 @@ AdvSceneSwitcher.action.projector.type.multiview="マルチビュー"
AdvSceneSwitcher.action.projector.display="ディスプレイ"
AdvSceneSwitcher.action.projector.windowed="ウィンドウ"
AdvSceneSwitcher.action.projector.fullscreen="フルスクリーン"
AdvSceneSwitcher.action.projector.entry="{{types}}{{scenes}}{{sources}}の{{windowTypes}}プロジェクターを開きます"
AdvSceneSwitcher.action.projector.entry.monitor="{{monitors}}上で"
; AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="メッセージを{{device}}に送信します:"
AdvSceneSwitcher.action.midi.entry.listen="MIDI メッセージの選択を {{listenDevices}} で受信するメッセージに設定します:{{listenButton}}"
@@ -1048,10 +1083,10 @@ AdvSceneSwitcher.action.twitch.type.clip.create="ストリームクリップの
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="チャット通知の送信"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="チャットのエモート専用モードを有効にする"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="チャットのエモート専用モードを無効にする"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="チャットメッセージを送る"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="チャットメッセージを送る"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません"
; AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}} でアカウント{{account}}{{actions}}を使用しています"
AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
AdvSceneSwitcher.action.twitch.title.title="タイトルを入力してください"
AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明"
AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します"
@@ -1086,18 +1121,18 @@ AdvSceneSwitcher.action.log="ログ"
AdvSceneSwitcher.action.log.placeholder="私のログメッセージ!"
AdvSceneSwitcher.action.log.entry="OBS ログに書き込む:{{logMessage}}"
; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="アドバンストシーンスイッチャーの起動"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="アドバンストシーンスイッチャーの停止"
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="アドバンストシーンスイッチャーの開始/停止切り替え"
# Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="高機能シーンスイッチャーの起動"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="高機能シーンスイッチャーの停止"
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="高機能シーンスイッチャーの開始/停止切り替え"
AdvSceneSwitcher.hotkey.macro.pause="マクロ %1 を一時停止します"
AdvSceneSwitcher.hotkey.macro.unpause="マクロ %1 の一時停止を解除します"
AdvSceneSwitcher.hotkey.macro.togglePause="マクロ %1 の一時停止を切り替えます"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="マクロセグメント選択を上に移動"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="マクロセグメント選択を下に移動"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="選択したマクロセグメントを削除"
AdvSceneSwitcher.hotkey.macro.segment.up="マクロセグメント選択を上に移動"
AdvSceneSwitcher.hotkey.macro.segment.down="マクロセグメント選択を下に移動"
AdvSceneSwitcher.hotkey.macro.segment.remove="選択したマクロセグメントを削除"
AdvSceneSwitcher.askBackup="Advanced Scene Switcherの新しいバージョンが検出されました。\n古い設定のバックアップを作成する必要がありますか?"
AdvSceneSwitcher.askBackup="高機能シーンスイッチャーの新しいバージョンが検出されました。\n古い設定のバックアップを作成する必要がありますか"
AdvSceneSwitcher.askForMacro="マクロ選択{{macroSelection}}"
AdvSceneSwitcher.close="閉じる"
@@ -1113,6 +1148,11 @@ AdvSceneSwitcher.item.emptyName="空の名前は使用できません!"
AdvSceneSwitcher.item.nameNotAvailable="名前がありません!"
AdvSceneSwitcher.item.nameReserved="名前は使用済みです!"
AdvSceneSwitcher.listControls.add="追加"
AdvSceneSwitcher.listControls.remove="削除"
AdvSceneSwitcher.listControls.up="上へ"
AdvSceneSwitcher.listControls.down="下へ"
AdvSceneSwitcher.macroSegmentSelection.invalid="選択が無効です!"
AdvSceneSwitcher.validation.text.maxLength="このフィールドには %1 文字のみを使用できます!"
@@ -1436,7 +1476,7 @@ AdvSceneSwitcher.tempVar.twitch.status.prediction.description="チャネルポ
AdvSceneSwitcher.tempVar.twitch.id.goal="目標ID"
AdvSceneSwitcher.tempVar.twitch.type.goal="ゴールタイプ"
AdvSceneSwitcher.tempVar.twitch.type.goal.description="ゴールの種類。可能な値は次のとおりです:\n follow — 目標はフォロワーを増やすことです。\n subscription — 目標はサブスクリプションを増やすことです。このタイプは、サブスクリプションに関連付けられたティアポイントの純増または減少を示します。\n subscription_count — 目標はサブスクリプションを増やすことです。このタイプは、サブスクリプション数の純増または減少を示します。\n new_subscription — 目標はサブスクリプションを増やすことです。このタイプは、サブスクリプションに関連付けられたティアポイントの純増のみを示します (目標開始後にサブスクリプションを解除したユーザーは考慮されません)。\n new_subscription_count — 目標はサブスクリプションを増やすことです。このタイプは、サブスクリプション数の純増のみを示します (目標開始後にサブスクリプションを解除したユーザーは考慮されません)。"
AdvSceneSwitcher.tempVar.twitch.type.goal.description="ゴールの種類。可能な値は次のとおりです:\n
AdvSceneSwitcher.tempVar.twitch.description.goal="目標説明"
AdvSceneSwitcher.tempVar.twitch.description.goal.description="指定されている場合、目標の説明。説明文は40文字以内です。"
AdvSceneSwitcher.tempVar.twitch.is_achieved.goal="目標達成"
@@ -1632,9 +1672,13 @@ AdvSceneSwitcher.tempVar.twitch.user_login.removeMod.description="削除され
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod="ユーザー名"
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod.description="削除されたモデレーターの表示名"
AdvSceneSwitcher.tempVar.twitch.chatter="ユーザーログイン"
AdvSceneSwitcher.tempVar.twitch.chatter.description="チャットメッセージを送信した人のユーザーログイン情報"
AdvSceneSwitcher.tempVar.twitch.chat_message="チャットメッセージ"
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive="ユーザーログイン"
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive.description="チャットメッセージを送信した人のユーザーログイン情報"
AdvSceneSwitcher.tempVar.twitch.user_name.chatReceive="ユーザー名"
AdvSceneSwitcher.tempVar.twitch.user_name.chatReceive.description="チャットメッセージを送信した人の表示名です。"
AdvSceneSwitcher.tempVar.twitch.chat_message.chatReceive="チャットメッセージ"
AdvSceneSwitcher.tempVar.twitch.badges.chatReceive="チャットバッジ"
AdvSceneSwitcher.tempVar.twitch.badges.chatReceive.description="このユーザーが使用できるバッジとその状態を表す文字列。\n例: vip/1,subscriber/42,hype-train/1"
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid="RaidクリエイターユーザーID"
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid.description="レイドを作成した配信者ID"
@@ -1760,6 +1804,24 @@ AdvSceneSwitcher.tempVar.source.settings="設定文字列"
AdvSceneSwitcher.tempVar.filter.setting="設定値"
AdvSceneSwitcher.tempVar.filter.settings="設定文字列"
AdvSceneSwitcher.tempVar.slideShow.index="スライドインデックス"
AdvSceneSwitcher.tempVar.slideShow.index.description="現在表示されているスライドのインデックス。\nインデックスを決定できない場合、-1が返されます。"
AdvSceneSwitcher.tempVar.slideShow.path="スライドパス"
AdvSceneSwitcher.tempVar.slideShow.path.description="現在表示されているスライドのファイルシステムパス。"
AdvSceneSwitcher.tempVar.slideShow.fileName="スライドファイル名"
AdvSceneSwitcher.tempVar.slideShow.fileName.description="現在表示されているスライドのファイル名です。"
; AdvSceneSwitcher.tempVar.usb.vendorID="ベンダーID"
; AdvSceneSwitcher.tempVar.usb.productID="プロダクトID"
; AdvSceneSwitcher.tempVar.usb.busNumber="バス番号"
; AdvSceneSwitcher.tempVar.usb.deviceAddress="デバイスアドレス"
; AdvSceneSwitcher.tempVar.usb.vendorName="ベンダー名"
; AdvSceneSwitcher.tempVar.usb.productName="製品名"
; AdvSceneSwitcher.tempVar.usb.serialNumber="シリアル番号"
AdvSceneSwitcher.tempVar.transform.setting="設定値"
AdvSceneSwitcher.tempVar.transform.settings="設定文字列"
AdvSceneSwitcher.selectScene="--シーンを選択--"
AdvSceneSwitcher.selectPreviousScene="前のシーン"
AdvSceneSwitcher.selectCurrentScene="現在のシーン"
@@ -1804,6 +1866,20 @@ AdvSceneSwitcher.settings.suffix.type.frameRate="(フレーム レート)"
AdvSceneSwitcher.settings.suffix.type.group="(グループ)"
AdvSceneSwitcher.settings.suffix.type.colorAlpha="(カラーアルファ)"
; AdvSceneSwitcher.setting.select="--select setting--"
AdvSceneSwitcher.setting.transform.alignment="位置合わせ"
AdvSceneSwitcher.setting.transform.cropBottom="[クロップ]下"
AdvSceneSwitcher.setting.transform.cropLeft="[クロップ]左"
AdvSceneSwitcher.setting.transform.cropRight="[クロップ]右"
AdvSceneSwitcher.setting.transform.cropTop="[クロップ]上"
AdvSceneSwitcher.setting.transform.boundingBoxType="バウンディングボックスの種類"
AdvSceneSwitcher.setting.transform.boundingBoxAlign="バウンディングボックス内の配置"
AdvSceneSwitcher.setting.transform.boundingBoxSize="バウンディングボックスのサイズ"
AdvSceneSwitcher.setting.transform.rotation="回転"
AdvSceneSwitcher.setting.transform.scale="拡大率"
AdvSceneSwitcher.setting.transform.size="サイズ"
AdvSceneSwitcher.setting.transform.position="位置"
AdvSceneSwitcher.setting.transform.height="高さ"
AdvSceneSwitcher.setting.transform.width="幅"
AdvSceneSwitcher.sceneItemSelection.configure="シーン項目選択タイプの設定"
@@ -1813,8 +1889,8 @@ AdvSceneSwitcher.sceneItemSelection.type.sourceVariable="変数名"
; AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.entry="{{nameConflictIndex}}{{variable}}"
AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern="ソース名に一致するパターン"
; AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry="{{nameConflictIndex}}matching{{pattern}}{{regex}}"
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup="ソースの種類"
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.entry="ソースタイプ{{nameConflictIndex}}{{sourceGroups}}"
AdvSceneSwitcher.sceneItemSelection.type.sourceType="ソースの種類"
AdvSceneSwitcher.sceneItemSelection.type.sourceType.entry="ソースタイプ{{nameConflictIndex}}{{sourceTypes}}"
AdvSceneSwitcher.sceneItemSelection.type.index="ソースインデックス(上から順)"
AdvSceneSwitcher.sceneItemSelection.type.index.entry="ソース{{index}}"
AdvSceneSwitcher.sceneItemSelection.type.indexRange="複数のソースインデックス(上から順)"
@@ -1831,12 +1907,12 @@ AdvSceneSwitcher.status.inactive="停止中"
AdvSceneSwitcher.running="プラグイン実行中"
AdvSceneSwitcher.stopped="プラグイン停止しました"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Advanced Scene Switcherが初めて起動したようです。<br>ガイドと例のリストについては <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> をご覧ください。<br>質問は <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">OBSフォーラムのプラグインのスレッド</span></a> で遠慮なくどうぞ!</p></body></html>"
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>質問は <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">OBSフォーラムのプラグインのスレッド</span></a> で遠慮なくどうぞ!</p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="このタブの開発は停止しました!\n代わりにマクロの使用に移行することを検討してください。\nこのヒントは [全般] タブで無効にすることができます。"
AdvSceneSwitcher.unit.milliseconds="ミリ秒"
AdvSceneSwitcher.unit.secends="秒"
AdvSceneSwitcher.unit.seconds="秒"
AdvSceneSwitcher.unit.minutes="分"
AdvSceneSwitcher.unit.hours="時間"
AdvSceneSwitcher.duration.condition.none="期間修飾子なし"
@@ -1851,8 +1927,46 @@ AdvSceneSwitcher.audio.monitor.both="モニターと出力"
AdvSceneSwitcher.noSettingsButtons="ボタンが見つかりません!"
; Legacy tabs below - please don't waste your time adding translations for these :)以下の従来のタブ - これらの翻訳の追加に時間を無駄にしないでください。
; Transition Tab
AdvSceneSwitcher.clearBufferOnMatch="マッチするメッセージが見つかったらメッセージバッファをクリアする"
AdvSceneSwitcher.script.settings="設定"
AdvSceneSwitcher.script.timeout="スクリプトタイムアウト:{{timeout}}"
# This secion is copied from the OBS locale files
# commonly shared locale
Browse="ブラウズ"
Show="表示"
Hide="隠す"
# properties window
; Basic.PropertiesWindow="Properties for '%1'"
Basic.PropertiesWindow.AutoSelectFormat="%1 (自動選択: %2)"
Basic.PropertiesWindow.SelectColor="色を選択"
Basic.PropertiesWindow.SelectFont="フォントを選択"
Basic.PropertiesWindow.SelectFont.WindowTitle="フォントを選ぶ"
Basic.PropertiesWindow.ConfirmTitle="変更された設定"
Basic.PropertiesWindow.Confirm="未保存の変更があります。保存しますか?"
Basic.PropertiesWindow.NoProperties="プロパティがありません"
Basic.PropertiesWindow.AddFiles="ファイルを追加"
Basic.PropertiesWindow.AddDir="ディレクトリを追加"
Basic.PropertiesWindow.AddURL="パス/URL を追加"
Basic.PropertiesWindow.AddEditableListDir="ディレクトリを以下に追加 '%1'"
Basic.PropertiesWindow.AddEditableListFiles="ファイルを以下に '%1'"
Basic.PropertiesWindow.AddEditableListEntry="エントリを以下に '%1'"
Basic.PropertiesWindow.EditEditableListEntry="エントリを以下に '%1'"
# properties view
; Basic.PropertiesView.FPS.Simple="Simple FPS Values"
; Basic.PropertiesView.FPS.Rational="Rational FPS Values"
; Basic.PropertiesView.FPS.ValidFPSRanges="Valid FPS Ranges:"
Basic.PropertiesView.UrlButton.Text="このリンクをデフォルトウェブブラウザで開きますか?"
Basic.PropertiesView.UrlButton.Text.Url="URL: %1"
Basic.PropertiesView.UrlButton.OpenUrl="URLを開く
# Legacy tabs below - please don't waste your time adding translations for these :)以下は従来のタブ - これらの翻訳の追加に時間を無駄にしないでください。
# Transition Tab
AdvSceneSwitcher.transitionTab.title="トランジションの変更"
AdvSceneSwitcher.transitionTab.transitionForAToB="シーンAからシーンBへの自動シーン切り替えにはトランジションを使用します"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>これらの設定は、シーン・スイッチャーによって引き起こされるトランジションに<span style=\"font-style:italic;\">のみ</span>影響します - マニュアルのシーン変更のために設定したい場合は、<a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a>をチェックしてください。<br/>ここで定義された設定は、シーン・スイッチャーの他の場所で設定されたトランジション設定よりも優先されます。<br/><br/>新しいエントリーを追加するには、下のプラスマークをクリックしてください。</p></body></html>"
@@ -1863,7 +1977,7 @@ AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="プラス記号をクリ
AdvSceneSwitcher.transitionTab.defaultTransition.delay="シーン切り替え後のトランジション{{defTransitionDelay}}を切り替えます。"
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="遅延は、トランジションの進行中にトランジション タイプが変更された場合に発生する可能性がある、キャンセルされたシーンの切り替えを回避するために使用されます。"
; Pause Scenes Tab
# Pause Scenes Tab
AdvSceneSwitcher.pauseTab.title="ポーズ"
AdvSceneSwitcher.pauseTab.pauseOnScene="シーンスイッチャーをシーン上で一時停止する"
AdvSceneSwitcher.pauseTab.pauseInFocus1="シーンスイッチャーを一時停止する場合"
@@ -1874,11 +1988,11 @@ AdvSceneSwitcher.pauseTab.pauseTargetAll="すべて"
AdvSceneSwitcher.pauseTab.pauseEntry="一時停止{{pauseTargets}}がチェックするタイミング{{pauseTypes}}{{scenes}}{{windows}}"
AdvSceneSwitcher.pauseTab.help="このタブでは、シーンがアクティブであるかウィンドウにフォーカスがある場合に、個々の切り替え方法を一時停止するように設定できます。\n\n続行するには、強調表示されたプラス記号をクリックしてください。"
; Window Title Tab
# Window Title Tab
AdvSceneSwitcher.windowTitleTab.title="ウィンドウタイトル"
AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>ウィンドウタイトルを直接入力するか、有効な正規表現を入力します。<a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span></a>を使って正規表現の構文とマッチをチェックすることができます。</p></body></html>"
AdvSceneSwitcher.windowTitleTab.stayInFocus1="このウィンドウ名は無視してください"
AdvSceneSwitcher.windowTitleTab.stayInFocus2=""
AdvSceneSwitcher.windowTitleTab.stayInFocus2=" "
AdvSceneSwitcher.windowTitleTab.fullscreen="フルスクリーン時にシーン転換"
AdvSceneSwitcher.windowTitleTab.maximized="ウィンドウを最大化するとシーン転換"
AdvSceneSwitcher.windowTitleTab.focused="ウィンドウにフォーカスするとシーン転換"
@@ -1886,14 +2000,14 @@ AdvSceneSwitcher.windowTitleTab.focused="ウィンドウにフォーカスする
AdvSceneSwitcher.windowTitleTab.windowsHelp="実行中のアプリケーションのウィンドウタイトルに基づいてシーンを切り替えます。\n次の追加条件を選択できます。\nウィンドウがフルスクリーンである\nウィンドウが最大化されている\nウィンドウがフォーカスされている\n\n強調表示されたプラス記号をクリックして続行します。"
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="ウィンドウ タイトルが無視された場合、シーン スイッチャーは、以前に選択したウィンドウにまだフォーカスがあるかのように動作します。\n これにより、別のウィンドウに頻繁に切り替える場合に、シーンの切り替えを回避できます。これにより、シーンがトリガーされなくなります。変更します。\n\nウィンドウを選択するか、上にウィンドウのタイトルを入力し、下のプラス記号をクリックしてリストに追加します。"
; Executable Tab
# Executable Tab
AdvSceneSwitcher.executableTab.title="実行中のプログラム"
AdvSceneSwitcher.executableTab.implemented="dasOvenによる実装"
AdvSceneSwitcher.executableTab.requiresFocus="集中している場合のみ"
AdvSceneSwitcher.executableTab.entry="{{processes}}が実行中の場合は、{{transitions}}{{requiresFocus}}を使用した{{scenes}}に切り替えます"
AdvSceneSwitcher.executableTab.help="このタブを使用すると、プロセスが実行中の場合にシーンを自動的に切り替えることができます。\nこれは、ウィンドウ名が変更される可能性がある場合やウィンドウ名が不明な場合に役立ちます。\n\n続行するには、強調表示されたプラス記号をクリックしてください。 ."
; Screen Region Tab
# Screen Region Tab
AdvSceneSwitcher.screenRegionTab.title="マウスカーソルの位置"
AdvSceneSwitcher.screenRegionTab.currentPosition="カーソルの現在位置:"
AdvSceneSwitcher.screenRegionTab.showGuideFrames="ガイド枠を表示する"
@@ -1902,7 +2016,7 @@ AdvSceneSwitcher.screenRegionTab.excludeScenes.None="選択なし"
AdvSceneSwitcher.screenRegionTab.entry="カーソルが{{minX}}{{minY}}x{{maxX}}{{maxY}}にある場合、{{excludeScenes}}でない限り、{{transitions}}を使用して{{scenes}}に切り替えます"
AdvSceneSwitcher.screenRegionTab.help="このタブでは、マウス カーソルの現在位置に基づいてシーンを自動的に切り替えることができます。\n\n次に進むには、強調表示されたプラス記号をクリックしてください。"
; Media Tab
# Media Tab
AdvSceneSwitcher.mediaTab.title="メディア"
; AdvSceneSwitcher.mediaTab.implemented="Implemented by Exeldro"
AdvSceneSwitcher.mediaTab.states.none="なし"
@@ -1923,7 +2037,7 @@ AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="残り時間が長くな
AdvSceneSwitcher.mediaTab.entry="{{mediaSources}}の状態が{{states}}で、{{timeRestrictions}}{{time}}が{{transitions}}を使用して{{scenes}}に切り替わるとき"
AdvSceneSwitcher.mediaTab.help="このタブでは、メディア ソースの状態に基づいてシーンを切り替えることができます。\nたとえば、選択したメディア ソースの再生が終了すると、自動的に前のシーンに切り替えることができます。\n\n続行するにはプラス記号を強調表示します。"
; File Tab
# File Tab
AdvSceneSwitcher.fileTab.title="ファイルの内容"
AdvSceneSwitcher.fileTab.readWriteSceneFile="シーンをファイルから読み書きする/ファイルに書き込む"
AdvSceneSwitcher.fileTab.currentSceneOutputFile="現在のシーンの名前をこのファイルに書き込みます:"
@@ -1948,13 +2062,13 @@ AdvSceneSwitcher.fileTab.entry="{{fileType}}{{filePath}}{{browseButton}}の内
; AdvSceneSwitcher.fileTab.entry3="{{useRegex}}{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.fileTab.help="このタブでは、リモートまたはローカル ファイルの内容に基づいてシーンを自動的に切り替えることができます。\n\n続行するには、強調表示されたプラス記号をクリックしてください。"
; Random Tab
# Random Tab
AdvSceneSwitcher.randomTab.title="ランダム"
AdvSceneSwitcher.randomTab.randomDisabledWarning="機能無効 - 有効にするには、全般タブの「切り替え条件が満たされない場合はランダムタブの任意のシーンに切り替える」を選択してください"
AdvSceneSwitcher.randomTab.entry="切り替え条件が満たされない場合は、{{delay}}の間、{{transitions}}を使用して{{scenes}}に切り替えます"
AdvSceneSwitcher.randomTab.help="シーン スイッチャーは、設定された時間の間、このタブ上のエントリをランダムに選択して切り替えます。\n同じエントリが連続して 2 回選択されることはないことに注意してください。\n\n強調表示されたプラス記号をクリックすると、続けます。"
; Time Tab
# Time Tab
AdvSceneSwitcher.timeTab.title="時間指定"
AdvSceneSwitcher.timeTab.anyDay="いつでも"
AdvSceneSwitcher.timeTab.mondays="月曜日"
@@ -1969,22 +2083,22 @@ AdvSceneSwitcher.timeTab.afterstart.tip="配信/録画開始からの相対時
AdvSceneSwitcher.timeTab.entry="{{triggers}}時{{time}}で{{transitions}}を使用して{{scenes}}に切り替えます"
AdvSceneSwitcher.timeTab.help="このタブを使用すると、現在の現地時間に基づいて別のシーンに自動的に切り替えることができます。\n\nシーン スイッチャーは、指定した正確な時刻にのみシーンを切り替えることに注意してください。\n設定が完了していることを確認してください。他の切り替え方法が優先されて選択した時点が失われることがないように、[全般] タブの優先順位を好みに合わせて設定します。\n\n強調表示されたプラス記号をクリックして続行します。"
; Idle Tab
# Idle Tab
AdvSceneSwitcher.idleTab.title="アイドル"
AdvSceneSwitcher.idleTab.enable="アイドル検知を有効にする"
AdvSceneSwitcher.idleTab.idleswitch="キーボードやマウスの入力がない状態が{{duration}}続いた後、{{transitions}}を使用してシーン{{scenes}}に切り替わります"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="次の場合は切り替えないでください"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="ピントが合っています"
; Scene Sequence Tab
# Scene Sequence Tab
AdvSceneSwitcher.sceneSequenceTab.title="シーケンス"
AdvSceneSwitcher.sceneSequenceTab.description="一連の自動シーン切り替えは、シーンスイッチャーの一時停止/停止、または手動で別のシーンに切り替えることでキャンセルできます"
AdvSceneSwitcher.sceneSequenceTab.save="シーンシーケンスをファイルに保存"
AdvSceneSwitcher.sceneSequenceTab.load="ファイルからシーンシーケンスをロード"
AdvSceneSwitcher.sceneSequenceTab.saveTitle="シーンシーケンスをファイルに保存…"
AdvSceneSwitcher.sceneSequenceTab.loadTitle="シーンシーケンスを読み込むファイルを選択してください…"
AdvSceneSwitcher.sceneSequenceTab.loadFail="Advanced Scene Switcherで設定インポートに失敗しました!"
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Scene Switcherの高度な設定がインポートされました!"
AdvSceneSwitcher.sceneSequenceTab.loadFail="高機能シーンスイッチャーの設定インポートに失敗しました!"
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="高機能シーンスイッチャーの設定がインポートされました!"
AdvSceneSwitcher.sceneSequenceTab.fileType="テキストファイル(*.txt)"
AdvSceneSwitcher.sceneSequenceTab.interruptible="中断可能"
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="このシーンシーケンスを中断する他の切り替え方法が許可されます"
@@ -1993,7 +2107,7 @@ AdvSceneSwitcher.sceneSequenceTab.extendEdit="拡張シーケンス"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="{{delay}}後、{{transitions}}を使用して{{scenes}}に切り替えてください"
AdvSceneSwitcher.sceneSequenceTab.help="このタブを使用すると、シーンが設定された期間アクティブだった場合に、別のシーンに自動的に切り替えることができます。\nたとえば、2 つのシーンを自動的に行き来できます。\n\nクリック強調表示されたプラス記号をクリックして続行します。"
; Audio Tab
# Audio Tab
AdvSceneSwitcher.audioTab.title="音声"
AdvSceneSwitcher.audioTab.condition.above="以上"
AdvSceneSwitcher.audioTab.condition.below="以下"
@@ -2003,7 +2117,7 @@ AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="複数のエントリが
AdvSceneSwitcher.audioTab.multiMatchfallback="...{{duration}}秒間、{{transitions}}を使用して{{scenes}}に切り替えます"
AdvSceneSwitcher.audioTab.help="このタブでは、ソースの音量に基づいてシーンを切り替えることができます。\nたとえば、マイクの音量が特定のしきい値に達した場合に、自動的に別のシーンに切り替えることができます。\n\n続行するにはプラス記号を強調表示します。"
; Video Tab
# Video Tab
AdvSceneSwitcher.videoTab.title="Video"
AdvSceneSwitcher.videoTab.getScreenshot="選択したエントリのスクリーンショットを取得"
AdvSceneSwitcher.videoTab.getScreenshotHelp="現在選択されているエントリーのビデオソースのスクリーンショットを取得し、自動的に対象画像として設定します"
@@ -2016,31 +2130,6 @@ AdvSceneSwitcher.videoTab.ignoreInactiveSource="ソースが非アクティブ
AdvSceneSwitcher.videoTab.entry="{{videoSources}}{{condition}}{{filePath}}{{browseButton}}が{{duration}}の間、{{transitions}}{{ignoreInactiveSource}}を使用して{{scenes}}に切り替わるとき"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>このタブでは、選択したソースの現在のビデオ出力に基づいてシーンを切り替えることができます。<br/>この機能のさらに優れた実装については、<a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a>を確認してください。<br/><br/> このタブでは、選択したソースの現在のビデオ出力に基づいてシーンを切り替えることができます。</p></body></html>"
; Network Tab
AdvSceneSwitcher.networkTab.title="Network"
AdvSceneSwitcher.networkTab.description="このタブを使用すると、別の OBS インスタンスのアクティブなシーンをリモートで制御できます。\nシーン名はすべての OBS インスタンスで正確に一致する必要があることに注意してください。"
AdvSceneSwitcher.networkTab.warning="ローカルネットワーク外でサーバーを実行すると、第三者がアクティブシーンを読み取ることができます。"
AdvSceneSwitcher.networkTab.server="サーバー起動(接続中の全クライアントにシーン切り替えメッセージを送信)"
AdvSceneSwitcher.networkTab.server.port="港"
AdvSceneSwitcher.networkTab.server.lockToIPv4="サーバーをIPv4のみにロックする"
AdvSceneSwitcher.networkTab.server.sendSceneChange="場面転換時のメッセージ送信"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="自動シーン切り替えのメッセージのみ送信"
AdvSceneSwitcher.networkTab.server.sendPreview="スタジオモード実行時のプレビューシーン変更メッセージ送信"
AdvSceneSwitcher.networkTab.startFailed.message="WebSocket サーバーの起動に失敗しました。おそらく次の理由が考えられます:\n - TCP ポート %1 は現在、このシステム上の別の場所 (おそらく別のアプリケーション) で使用されている可能性があります。 WebSocket サーバー設定で別の TCP ポートを設定してみるか、このポートを使用している可能性のあるアプリケーションを停止してください。\n - エラー メッセージ: %2"
; AdvSceneSwitcher.networkTab.server.status.currentStatus="Current status"
AdvSceneSwitcher.networkTab.server.status.notRunning="動いていない"
; AdvSceneSwitcher.networkTab.server.status.starting="Starting"
; AdvSceneSwitcher.networkTab.server.status.running="Running"
AdvSceneSwitcher.networkTab.server.restart="サーバーの再起動"
AdvSceneSwitcher.networkTab.client="クライアント起動(シーン切り替えメッセージ受信)"
AdvSceneSwitcher.networkTab.client.address="ホスト名またはIPアドレス"
; AdvSceneSwitcher.networkTab.client.port="Port"
; AdvSceneSwitcher.networkTab.client.status.currentStatus="Current status"
AdvSceneSwitcher.networkTab.client.status.disconnected="切断されました"
AdvSceneSwitcher.networkTab.client.status.connecting="接続中"
AdvSceneSwitcher.networkTab.client.status.connected="接続した"
AdvSceneSwitcher.networkTab.client.reconnect="強制再接続"
; Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="シーンのグループ化"
AdvSceneSwitcher.sceneGroupTab.list="シーングループ集"
@@ -2060,27 +2149,3 @@ AdvSceneSwitcher.sceneGroupTab.defaultname="シーングループ %1"
AdvSceneSwitcher.sceneGroupTab.exists="シーングループまたはシーン名が既に存在します"
AdvSceneSwitcher.sceneGroupTab.help="シーン グループは、通常のシーンと同様に対象として選択できます。\n\nシーン グループは、名前が示すように、複数のシーンの集合です。\nシーン グループは、割り当てられたシーンのリストを順に進みます。構成された設定に応じて、右側に表示されます。\n\nリスト内の次のシーンに進むようにシーン グループを構成できます:\nシーン グループが何度も選択された後\n一定時間経過後。\nまたはランダム。\n\nたとえば、シーンを含むシーングループ...\nシーン1\nシーン2 \nシーン 3 \n... 初めてターゲットとして選択されると、「シーン 1」がアクティブになります。\n2 回目には、「シーン 2」がアクティブになります。\n残りの回「シーン 3」が有効になります。\n\n下の強調表示されたプラス記号をクリックして、新しいシーン グループを追加します。"
AdvSceneSwitcher.sceneGroupTab.scenes.help="左側で変更したいシーングループを選択します。\n\n上のシーンを選択し、下のプラス記号をクリックして、このシーングループに追加するシーンを選択します。\n\nシーンは次のとおりです。同じシーングループに複数回追加しました。"
; Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="シーントリガー"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--トリガーを選択--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="アクティブです"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="活動していません"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="から切り替えた"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--アクションを選択--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="録画開始"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="録画一時停止"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="録画の一時停止を解除"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="録画停止"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="配信停止"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="配信開始"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="リプレイバッファ開始"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="リプレイバッファ停止"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="ソースをミュート"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="ソースのミュートを解除"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="シーンスイッチャーを起動します"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="シーンスイッチャーを停止する"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera="仮想カメラ起動"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="仮想カメラ停止"
; AdvSceneSwitcher.sceneTriggerTab.entry="When{{scenes}}{{triggers}}{{actions}}{{audioSources}}after{{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="このタブでは、録画や配信の停止など、シーンの変更時にアクションをトリガーできます。"

View File

@@ -19,18 +19,17 @@ AdvSceneSwitcher.generalTab.status.autoStart.streaming="Transmitindo"
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Gravando ou Transmitindo"
AdvSceneSwitcher.generalTab.status.checkInterval="Verificar condições a cada"
AdvSceneSwitcher.generalTab.generalBehavior="Comportamento geral"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="Se nenhuma ação for executada por"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Será sempre tão preciso quanto o intervalo de verificação configurado."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Não trocar"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Trocar para qualquer cena na guia Aleatória"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Trocar para:"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch="Se nenhuma ação for executada por"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatchDelay.tooltip="Será sempre tão preciso quanto o intervalo de verificação configurado."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="Não trocar"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Trocar para qualquer cena na guia Aleatória"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Trocar para:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Após realizar ações, pular a execução de ações por"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="Durante este tempo, correspondências potenciais serão ignoradas!"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel="Nível de log:"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="Padrão"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.printActions="Registrar ações executadas"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction="Registrar ações executadas"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="Registro detalhado"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ativar registro detalhado"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Salvar posição e tamanho da janela"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Mostrar notificações na área de notificação"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Desativar dicas de interface"
@@ -49,7 +48,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Exportar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Importar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Exportar configurações do Advanced Scene Switcher para arquivo ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Importar configurações do Advanced Scene Switcher de arquivo ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Arquivos de texto (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Arquivos de texto (*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="O Advanced Scene Switcher falhou ao importar configurações"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Configurações do Advanced Scene Switcher importadas com sucesso"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportSensitiveDataWarning="Aviso:\nOs dados exportados podem conter informações sensíveis!"
@@ -82,10 +81,8 @@ AdvSceneSwitcher.variableTab.value.header="Valor"
AdvSceneSwitcher.variableTab.saveLoadBehavior.header="Comportamento de Salvar/Carregar"
AdvSceneSwitcher.variableTab.saveLoadBehavior.text.default="Padrão para \"%1\""
AdvSceneSwitcher.variableTab.lastUsed.header="Último uso"
AdvSceneSwitcher.variableTab.lastUsed.text="%1 segundos atrás"
AdvSceneSwitcher.variableTab.lastUsed.text.never="Nunca"
AdvSceneSwitcher.variableTab.lastChanged.header="Última alteração"
AdvSceneSwitcher.variableTab.lastChanged.text="%1 segundos atrás"
AdvSceneSwitcher.variableTab.lastChanged.text.none="Sem alterações desde o lançamento"
AdvSceneSwitcher.variableTab.lastChanged.tooltip="Vezes alteradas: %1\n\nValor anterior: %2"
@@ -120,7 +117,7 @@ AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text="Tem
; Twitch Connections Tab
AdvSceneSwitcher.twitchConnectionTab.title="Conexões Twitch"
AdvSceneSwitcher.twitchConnectionTab.help="As conexões Twitch podem ser usadas para utilizar eventos do Twitch como gatilhos para executar ações ou realizar ações na conta do Twitch vinculada.\n\nClique no símbolo de mais destacado para adicionar uma nova conexão."
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip.tooltip="Adicionar nova conexão Twitch"
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip="Adicionar nova conexão Twitch"
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionRemoveButton.tooltip="Remover conexões Twitch selecionadas"
AdvSceneSwitcher.twitchConnectionTab.name.header="Nome"
AdvSceneSwitcher.twitchConnectionTab.isValid.header="É válida?"
@@ -192,7 +189,7 @@ AdvSceneSwitcher.macroTab.highlightExecutedMacros="Destacar macros executadas re
AdvSceneSwitcher.macroTab.highlightTrueConditions="Destacar condições da macro selecionada que avaliaram como verdadeiras recentemente"
AdvSceneSwitcher.macroTab.highlightPerformedActions="Destacar ações recentemente realizadas da macro selecionada"
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Registrar teclas de atalho para controlar o estado de pausa das novas macros"
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Registrar teclas de atalho para controlar o estado de pausa da macro selecionada"
AdvSceneSwitcher.macroTab.currentRegisterHotkeys="Registrar teclas de atalho para controlar o estado de pausa da macro selecionada"
AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="Pular a execução das ações da macro atual na inicialização"
AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone="Parar e reexecutar ações da macro atualmente selecionada, se as ações ainda estiverem em execução, quando uma nova execução for acionada"
AdvSceneSwitcher.macroTab.currentRegisterDock="Registrar widget de dock para controlar o estado de pausa da macro selecionada ou executá-la manualmente"
@@ -286,8 +283,6 @@ AdvSceneSwitcher.condition.scene.type.previousPattern="A cena anterior correspon
AdvSceneSwitcher.condition.scene.type.previewPattern="A cena de pré-visualização corresponde"
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Durante a transição, verificar a cena de destino da transição"
AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="Durante a transição, verificar a cena de origem da transição"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Janela"
AdvSceneSwitcher.condition.window.entry.window="{{checkTitle}}O título da janela corresponde{{windowRegex}}{{windows}}"
AdvSceneSwitcher.condition.window.entry.fullscreen="{{fullscreen}}A janela está em tela cheia"
@@ -377,16 +372,16 @@ AdvSceneSwitcher.condition.video.colorDeviationThresholdDescription="Quão semel
AdvSceneSwitcher.condition.video.type.main="Saída principal do OBS"
AdvSceneSwitcher.condition.video.type.source="Fonte"
AdvSceneSwitcher.condition.video.type.scene="Cena"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Dados do modelo (classificador em cascata haar):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Vizinhos mínimos:{{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduzir carga de CPU realizando a verificação apenas a cada {{throttleCount}} milissegundos"
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Realizar verificação apenas na área"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.entry.orcColorPick="Verificar cor do texto:{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.entry.orcTextType="Verificar tipo de texto:{{textType}}"
AdvSceneSwitcher.condition.video.entry.orcLanguage="Verificar idioma:{{languageCode}}"
AdvSceneSwitcher.condition.video.entry.color="Verificar cor:{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.layout.modelPath="Dados do modelo (classificador em cascata haar):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="Vizinhos mínimos:{{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.throttle="{{throttleEnable}}Reduzir carga de CPU realizando a verificação apenas a cada {{throttleCount}} milissegundos"
AdvSceneSwitcher.condition.video.layout.checkAreaEnable="Realizar verificação apenas na área"
AdvSceneSwitcher.condition.video.layout.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.layout.ocrColorPick="Verificar cor do texto:{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout.ocrTextType="Verificar tipo de texto:{{textType}}"
AdvSceneSwitcher.condition.video.layout.ocrLanguage="Verificar idioma:{{languageCode}}"
AdvSceneSwitcher.condition.video.layout.color="Verificar cor:{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.minSize="Tamanho mínimo:"
AdvSceneSwitcher.condition.video.maxSize="Tamanho máximo:"
AdvSceneSwitcher.condition.video.selectArea="Selecionar área"
@@ -399,7 +394,6 @@ AdvSceneSwitcher.condition.stream.state.stop="Streaming parado"
AdvSceneSwitcher.condition.stream.state.starting="Iniciando o streaming"
AdvSceneSwitcher.condition.stream.state.stopping="Parando o streaming"
AdvSceneSwitcher.condition.stream.state.keyFrameInterval="Intervalo de keyframe igual a"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Gravação"
AdvSceneSwitcher.condition.record.state.start="Gravação em execução"
AdvSceneSwitcher.condition.record.state.pause="Gravação pausada"
@@ -466,7 +460,7 @@ AdvSceneSwitcher.condition.source.sizeCompare.less="Menor que"
AdvSceneSwitcher.condition.source.sizeCompare.equal="Igual a"
AdvSceneSwitcher.condition.source.sizeCompare.more="Maior que"
AdvSceneSwitcher.condition.source.refresh="Atualizar"
AdvSceneSwitcher.condition.source.refreshTooltip="Repopule a seleção de configurações da fonte com as configurações da fonte cujo nome corresponde ao valor da variável."
AdvSceneSwitcher.condition.source.refresh.tooltip="Repopule a seleção de configurações da fonte com as configurações da fonte cujo nome corresponde ao valor da variável."
AdvSceneSwitcher.condition.source.sceneVisibilityHint="A visibilidade específica da cena pode ser verificada usando a condição \"Visibilidade do item da cena\""
AdvSceneSwitcher.condition.source.getSettings="Obter configurações atuais"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}}{{conditions}}{{settingSelection}}{{refresh}}{{sizeCompareMethods}}{{size}}"
@@ -484,7 +478,7 @@ AdvSceneSwitcher.condition.filter.type.settingsChanged="Configurações alterada
AdvSceneSwitcher.condition.filter.type.individualSettingMatches="Valor das configurações coincide"
AdvSceneSwitcher.condition.filter.type.individualSettingChanged="Valor das configurações alterado"
AdvSceneSwitcher.condition.filter.refresh="Atualizar"
AdvSceneSwitcher.condition.filter.refreshTooltip="Repopule a seleção de configurações do filtro com as configurações do filtro cujo nome corresponda ao valor da variável."
AdvSceneSwitcher.condition.filter.refresh.tooltip="Repopule a seleção de configurações do filtro com as configurações do filtro cujo nome corresponda ao valor da variável."
AdvSceneSwitcher.condition.filter.getSettings="Obter configurações atuais"
AdvSceneSwitcher.condition.filter.entry.line1="Em{{sources}}{{filters}}{{conditions}}{{settingSelection}}{{refresh}}"
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
@@ -628,7 +622,7 @@ AdvSceneSwitcher.condition.slideshow="Apresentação de slides"
AdvSceneSwitcher.condition.slideshow.condition.slideChanged="Slide alterado"
AdvSceneSwitcher.condition.slideshow.condition.slideIndex="Número do slide atual é"
AdvSceneSwitcher.condition.slideshow.condition.slidePath="Caminho do slide atual é"
AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip="As informações sobre o status da apresentação de slides serão atualizadas apenas com base no tempo configurado entre os slides"
AdvSceneSwitcher.condition.slideshow.updateInterval.tooltip="As informações sobre o status da apresentação de slides serão atualizadas apenas com base no tempo configurado entre os slides"
AdvSceneSwitcher.condition.slideshow.entry="{{sources}}{{conditions}}{{index}}{{path}}{{regex}}"
AdvSceneSwitcher.condition.twitch="Twitch"
AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online="Transmissão foi online"
@@ -694,7 +688,7 @@ AdvSceneSwitcher.condition.queue.entry.startStop="Fila{{queues}}está{{condition
AdvSceneSwitcher.condition.queue.entry.size="Fila{{queues}}{{conditions}}é menor que{{size}}"
AdvSceneSwitcher.condition.clipboard="Área de transferência"
AdvSceneSwitcher.condition.clipboard.placeholder="Texto da área de transferência"
AdvSceneSwitcher.condition.clipboard.urlTooltip="Dependendo do SO, arquivos podem ser representados também como URLs!"
AdvSceneSwitcher.condition.clipboard.url.tooltip="Dependendo do SO, arquivos podem ser representados também como URLs!"
AdvSceneSwitcher.condition.clipboard.condition.changed="Conteúdo da área de transferência alterado"
AdvSceneSwitcher.condition.clipboard.condition.isText="Área de transferência contém texto"
AdvSceneSwitcher.condition.clipboard.condition.isImage="Área de transferência contém uma imagem"
@@ -798,7 +792,7 @@ AdvSceneSwitcher.action.filter.type.toggle="Alternar"
AdvSceneSwitcher.action.filter.type.settings="Definir configurações"
AdvSceneSwitcher.action.filter.type.pressSettingsButton="Pressionar botão de configurações"
AdvSceneSwitcher.action.filter.refresh="Atualizar"
AdvSceneSwitcher.action.filter.refreshTooltip="Repovoar a seleção de configurações do filtro com as configurações do filtro cujo nome corresponda ao valor da variável."
AdvSceneSwitcher.action.filter.refresh.tooltip="Repovoar a seleção de configurações do filtro com as configurações do filtro cujo nome corresponda ao valor da variável."
AdvSceneSwitcher.action.filter.entry="Em{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
AdvSceneSwitcher.action.filter.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}"
AdvSceneSwitcher.action.filter.getSettings="Obter configurações atuais"
@@ -836,7 +830,7 @@ AdvSceneSwitcher.action.source.inputMethod.individualManual="Definir valor fixo"
AdvSceneSwitcher.action.source.inputMethod.individualTempvar="Definir propriedade de macro"
AdvSceneSwitcher.action.source.inputMethod.json="Definir string JSON de configuração"
AdvSceneSwitcher.action.source.refresh="Atualizar"
AdvSceneSwitcher.action.source.refreshTooltip="Repovoar a seleção de configurações da fonte com as configurações da fonte cujo nome corresponda ao valor da variável."
AdvSceneSwitcher.action.source.refresh.tooltip="Repovoar a seleção de configurações da fonte com as configurações da fonte cujo nome corresponda ao valor da variável."
AdvSceneSwitcher.action.media="Mídia"
; Macro Actions
AdvSceneSwitcher.action.media.type.play="Reproduzir"
@@ -911,7 +905,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover para baixo"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover para o topo"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover para o fundo"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover para posição"
AdvSceneSwitcher.action.sceneOrder.entry="Em{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="Em{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformação de item de cena"
AdvSceneSwitcher.action.sceneTransform.type.manual="Definir string de transformação"
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="Definir configuração de transformação"
@@ -965,9 +959,8 @@ AdvSceneSwitcher.action.screenshot.save.default="Padrão"
AdvSceneSwitcher.action.screenshot.save.custom="Personalizado"
AdvSceneSwitcher.action.screenshot.type.source="Fonte"
AdvSceneSwitcher.action.screenshot.type.scene="Cena"
AdvSceneSwitcher.action.screenshot.mainOutput="Saída principal do OBS"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Fontes ou cenas, que nem sempre são renderizadas, podem resultar em algumas partes das capturas de tela permanecendo em branco."
AdvSceneSwitcher.action.screenshot.entry="Captura de tela{{targetType}}{{sources}}{{scenes}}e salvar em{{saveType}}localização"
AdvSceneSwitcher.action.screenshot.entry="Captura de tela{{targetType}}{{sources}}{{scenes}}e salvar em{{saveType}}{{variables}}localização"
AdvSceneSwitcher.action.profile="Perfil"
AdvSceneSwitcher.action.profile.entry="Mudar perfil ativo para{{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Coleção de cenas"
@@ -1018,7 +1011,7 @@ AdvSceneSwitcher.action.variable.type.askForValue="Obter entrada do usuário"
AdvSceneSwitcher.action.variable.type.environmentVariable="Definir valor da variável de ambiente"
AdvSceneSwitcher.action.variable.type.sceneItemCount="Definir para contagem de itens de cena"
AdvSceneSwitcher.action.variable.type.stringLength="Definir comprimento da string"
AdvSceneSwitcher.action.variable.type.extractJson="Extrair campo json com nome"
AdvSceneSwitcher.action.variable.type.extractJsonField="Extrair campo json com nome"
AdvSceneSwitcher.action.variable.type.setToTempvar="Definir para propriedade da macro"
AdvSceneSwitcher.action.variable.type.setToTempvar.help="Este tipo de ação permite extrair valores de segmentos da macro atual e atribuir esses valores à variável selecionada.\nPor exemplo, você pode obter o nome da cena atual de uma condição de cena e atribuir esse nome a uma variável."
AdvSceneSwitcher.action.variable.type.sceneItemName="Definir para nome do item de cena no índice"
@@ -1038,14 +1031,13 @@ AdvSceneSwitcher.action.variable.invalidSelection="Seleção inválida!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Obter valores de variáveis das ações %1 não é suportado!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Obter valores de variáveis das condições %1 não é suportado!"
AdvSceneSwitcher.action.variable.currentSegmentValue="Valor atual:"
AdvSceneSwitcher.action.variable.entry.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}de{{variables}}para comprimento{{stringLength}}adicionando{{paddingCharSelection}}ao{{direction}}"
AdvSceneSwitcher.action.variable.entry.truncate="{{actions}}de{{variables}}para comprimento{{stringLength}}removendo caracteres da{{direction}}"
AdvSceneSwitcher.action.variable.entry.substringIndex="Início da substring:{{subStringStart}}Tamanho da substring:{{subStringSize}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="Atribuir valor do{{regexMatchIdx}}correspondência usando expressão regular:"
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Usar prompt personalizado{{inputPrompt}}"
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Preencher com placeholder{{inputPlaceholder}}"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}de{{variables}}para comprimento{{stringLength}}adicionando{{paddingCharSelection}}ao{{direction}}"
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}de{{variables}}para comprimento{{stringLength}}removendo caracteres da{{direction}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Início da substring:{{subStringStart}}Tamanho da substring:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Atribuir valor do{{regexMatchIdx}}correspondência usando expressão regular:"
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Usar prompt personalizado{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Preencher com placeholder{{inputPlaceholder}}"
AdvSceneSwitcher.action.projector="Projetor"
AdvSceneSwitcher.action.projector.type.source="Fonte"
AdvSceneSwitcher.action.projector.type.scene="Cena"
@@ -1055,8 +1047,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivisão"
AdvSceneSwitcher.action.projector.display="Exibição"
AdvSceneSwitcher.action.projector.windowed="Janela"
AdvSceneSwitcher.action.projector.fullscreen="Tela cheia"
AdvSceneSwitcher.action.projector.entry="Abrir{{windowTypes}}projetor de{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.monitor="em{{monitors}}"
AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="Enviar mensagem para{{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="Definir seleção de mensagem MIDI para mensagens recebidas em{{listenDevices}}:{{listenButton}}"
@@ -1076,10 +1066,10 @@ AdvSceneSwitcher.action.twitch.type.clip.create="Criar clipe da transmissão"
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Enviar anúncio de chat"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}"
AdvSceneSwitcher.action.twitch.title.title="Digite o título"
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe"
@@ -1121,9 +1111,9 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Alternar Iniciar/Parar pa
AdvSceneSwitcher.hotkey.macro.pause="Pausar macro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Retomar macro %1"
AdvSceneSwitcher.hotkey.macro.togglePause="Alternar pausa da macro %1"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Mover seleção de segmento de macro para cima"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Mover seleção de segmento de macro para baixo"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Remover segmento de macro selecionado"
AdvSceneSwitcher.hotkey.macro.segment.up="Mover seleção de segmento de macro para cima"
AdvSceneSwitcher.hotkey.macro.segment.down="Mover seleção de segmento de macro para baixo"
AdvSceneSwitcher.hotkey.macro.segment.remove="Remover segmento de macro selecionado"
AdvSceneSwitcher.askBackup="Detectada uma nova versão do Advanced Scene Switcher.\nDeve ser criado um backup das configurações antigas?"
AdvSceneSwitcher.askForMacro="Selecionar macro{{macroSelection}}"
@@ -1657,9 +1647,9 @@ AdvSceneSwitcher.tempVar.twitch.user_login.removeMod.description="O login do usu
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod="Nome de usuário"
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod.description="O nome de exibição do usuário que foi removido como moderador."
AdvSceneSwitcher.tempVar.twitch.chatter="Nome de login do usuário"
AdvSceneSwitcher.tempVar.twitch.chatter.description="O nome de login da pessoa que enviou a mensagem de chat."
AdvSceneSwitcher.tempVar.twitch.chat_message="Mensagem de chat"
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive="Nome de login do usuário"
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive.description="O nome de login da pessoa que enviou a mensagem de chat."
AdvSceneSwitcher.tempVar.twitch.chat_message.chatReceive="Mensagem de chat"
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid="ID do usuário criador do raid"
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid.description="O ID do broadcaster que criou o raid."
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_login.raid="Nome de login do criador do raid"
@@ -1866,8 +1856,8 @@ AdvSceneSwitcher.sceneItemSelection.type.sourceVariable="Nome da variável"
AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.entry="{{nameConflictIndex}}{{variable}}"
AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern="Nome da fonte corresponde ao padrão"
AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry="{{nameConflictIndex}}correspondendo{{pattern}}{{regex}}"
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup="Fontes do tipo"
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.entry="Tipo de fonte{{nameConflictIndex}}{{sourceGroups}}"
AdvSceneSwitcher.sceneItemSelection.type.sourceType="Fontes do tipo"
AdvSceneSwitcher.sceneItemSelection.type.sourceType.entry="Tipo de fonte{{nameConflictIndex}}{{sourceTypes}}"
AdvSceneSwitcher.sceneItemSelection.type.index="Fonte com índice"
AdvSceneSwitcher.sceneItemSelection.type.index.entry="{{index}}fonte"
AdvSceneSwitcher.sceneItemSelection.type.indexRange="Fontes no intervalo de índices"
@@ -1889,7 +1879,7 @@ AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Parece que esta é a pr
AdvSceneSwitcher.deprecatedTabWarning="Desenvolvimento para esta aba interrompido!\nPor favor, considere a transição para o uso de Macros em vez disso.\nEsta dica pode ser desativada na aba Geral."
AdvSceneSwitcher.unit.milliseconds="milissegundos"
AdvSceneSwitcher.unit.secends="segundos"
AdvSceneSwitcher.unit.seconds="segundos"
AdvSceneSwitcher.unit.minutes="minutos"
AdvSceneSwitcher.unit.hours="horas"
AdvSceneSwitcher.duration.condition.none="Nenhum modificador de duração"
@@ -2069,30 +2059,6 @@ AdvSceneSwitcher.videoTab.condition.hasChanged="mudou"
AdvSceneSwitcher.videoTab.ignoreInactiveSource="a menos que a fonte esteja inativa"
AdvSceneSwitcher.videoTab.entry="Quando{{videoSources}}{{condition}}{{filePath}}{{browseButton}}por{{duration}}trocar para{{scenes}}usando{{transitions}}{{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Esta aba permitirá que você troque cenas com base na saída de vídeo atual das fontes selecionadas.<br/>Certifique-se de conferir <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> para uma implementação ainda melhor dessa funcionalidade.<br/><br/> Clique no símbolo de mais destacado para continuar.</p></body></html>"
; Network Tab
AdvSceneSwitcher.networkTab.title="Rede"
AdvSceneSwitcher.networkTab.description="Esta aba permitirá que você controle remotamente a cena ativa de outra instância do OBS.\nObserve que os nomes das cenas devem corresponder exatamente em todas as instâncias do OBS."
AdvSceneSwitcher.networkTab.warning="Executar o servidor fora de uma rede local permitirá que terceiros leiam a cena ativa."
AdvSceneSwitcher.networkTab.server="Iniciar servidor (Envia mensagens de troca de cena para todos os clientes conectados)"
AdvSceneSwitcher.networkTab.server.port="Porta"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Bloquear servidor para usar apenas IPv4"
AdvSceneSwitcher.networkTab.server.sendSceneChange="Enviar mensagens para mudanças de cena"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Enviar mensagens apenas para trocas de cena automatizadas"
AdvSceneSwitcher.networkTab.server.sendPreview="Enviar mensagens para mudança de cena de pré-visualização ao executar no modo Studio"
AdvSceneSwitcher.networkTab.startFailed.message="O servidor WebSockets falhou ao iniciar, talvez porque:\n - A porta TCP %1 pode estar atualmente em uso em outro lugar neste sistema, possivelmente por outro aplicativo. Tente definir uma porta TCP diferente nas configurações do servidor WebSocket, ou pare qualquer aplicativo que possa estar usando esta porta.\n - Mensagem de erro: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Status atual"
AdvSceneSwitcher.networkTab.server.status.notRunning="Não está em execução"
AdvSceneSwitcher.networkTab.server.status.starting="Iniciando"
AdvSceneSwitcher.networkTab.server.status.running="Em execução"
AdvSceneSwitcher.networkTab.server.restart="Reiniciar servidor"
AdvSceneSwitcher.networkTab.client="Iniciar cliente (Recebe mensagens de trocas de cena)"
AdvSceneSwitcher.networkTab.client.address="Nome do host ou endereço IP"
AdvSceneSwitcher.networkTab.client.port="Porta"
AdvSceneSwitcher.networkTab.client.status.currentStatus="Status atual"
AdvSceneSwitcher.networkTab.client.status.disconnected="Desconectado"
AdvSceneSwitcher.networkTab.client.status.connecting="Conectando"
AdvSceneSwitcher.networkTab.client.status.connected="Conectado"
AdvSceneSwitcher.networkTab.client.reconnect="Forçar reconexão"
; Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="Grupo de Cenas"
@@ -2114,26 +2080,3 @@ AdvSceneSwitcher.sceneGroupTab.exists="Grupo de Cenas ou Nome de Cena já existe
AdvSceneSwitcher.sceneGroupTab.help="Os Grupos de Cenas podem ser selecionados como um alvo, assim como uma cena normal.\n\nComo o nome sugere, um grupo de cenas é uma coleção de várias cenas.\nO grupo de cenas avançará pela lista de suas cenas atribuídas dependendo das configurações configuradas, que podem ser encontradas no lado direito.\n\nVocê pode configurar o grupo de cenas para avançar para a próxima cena na lista:\nApós um número de vezes em que o grupo de cenas é selecionado como alvo.\nApós um certo período de tempo ter passado.\nOu aleatoriamente.\n\nPor exemplo, um grupo de cenas contendo as cenas ...\nCena 1\nCena 2\nCena 3\n... ativará \"Cena 1\" na primeira vez que for selecionado como alvo.\nNa segunda vez, ativará \"Cena 2\".\nAs vezes restantes, \"Cena 3\" será ativada.\n\nClique no símbolo de mais destacado abaixo para adicionar um novo grupo de cenas."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Selecione o grupo de cenas que você deseja modificar à esquerda.\n\nSelecione uma cena para adicionar a este grupo de cenas selecionando a cena acima e clicando no símbolo de mais abaixo.\n\nUma cena pode ser adicionada várias vezes ao mesmo grupo de cenas."
; Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="Gatilhos de Cena"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--selecione gatilho--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="está ativa"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="não está ativa"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="foi trocada"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--selecione ação--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="iniciar gravação"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="pausar gravação"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="retomar gravação"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="parar gravação"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="parar transmissão"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="iniciar transmissão"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="iniciar buffer de replay"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="parar buffer de replay"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="silenciar fonte"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="desilenciar fonte"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="iniciar o switcher de cena"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="parar o switcher de cena"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera="iniciar câmera virtual"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="parar câmera virtual"
AdvSceneSwitcher.sceneTriggerTab.entry="Quando{{scenes}}{{triggers}}{{actions}}{{audioSources}}após{{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="Esta aba permite que você acione ações nas mudanças de cena, como parar a gravação ou a transmissão."

View File

@@ -19,13 +19,12 @@ AdvSceneSwitcher.generalTab.status.autoStart.streaming="Вещание"
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Запись или потоковое вещание"
AdvSceneSwitcher.generalTab.status.checkInterval="Проверять условия переключения каждый раз"
AdvSceneSwitcher.generalTab.generalBehavior="Общее поведение"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="Если не выполняется условие переключения для"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Будет только настолько точным, насколько настроен интервал проверки."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Не переключаться"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Переключиться на любую сцену на вкладке Random"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Переключиться на:"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch="Если не выполняется условие переключения для"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatchDelay.tooltip="Будет только настолько точным, насколько настроен интервал проверки."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="Не переключаться"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Переключиться на любую сцену на вкладке Random"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Переключиться на:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="В течение этого времени потенциальные совпадения будут игнорироваться!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Включить ведение подробного журнала"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Сохранять положение и размер окна"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Отключить подсказки пользовательского интерфейса"
AdvSceneSwitcher.generalTab.priority="Приоритет"
@@ -37,7 +36,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Экспорт"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Импорт"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Экспортировать настройки расширенного переключателя сцен в файл ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Импортировать настройки Advanced Scene Switcher из файла ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Текстовые файлы (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Текстовые файлы (*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="Advanced Scene Switcher не удалось импортировать настройки"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Настройки Advanced Scene Switcher импортированы успешно"
AdvSceneSwitcher.generalTab.priority.fileContent="Содержание файла"
@@ -307,28 +306,6 @@ AdvSceneSwitcher.videoTab.ignoreInactiveSource="если источник неа
AdvSceneSwitcher.videoTab.entry="Когда {{videoSources}} {{condition}} {{filePath}} {{browseButton}} для {{duration}} переключиться на {{scenes}} используя {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Эта вкладка позволит вам переключать сцены на основе текущего видеовыхода выбранных источников.<br/>Обязательно проверьте <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> для еще лучшей реализации этой функциональности.<br/><br/> Нажмите на выделенный символ плюса, чтобы продолжить.</p></body></html>"
; Network Tab
AdvSceneSwitcher.networkTab.title="Сеть"
AdvSceneSwitcher.networkTab.warning="Запуск сервера вне локальной сети позволит третьим лицам читать активную сцену."
AdvSceneSwitcher.networkTab.server="Запустить сервер (отправляет сообщения о переключении сцены всем подключенным клиентам)"
AdvSceneSwitcher.networkTab.server.port="Порт"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Заблокировать сервер на использование только IPv4"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Отправлять сообщения только для автоматических переключателей сцен"
AdvSceneSwitcher.networkTab.startFailed.message="Сервер WebSockets не удалось запустить, возможно потому, что:\n - TCP порт %1 может в настоящее время использоваться в другом месте в этой системе, возможно, другим приложением. Попробуйте установить другой TCP порт в настройках сервера WebSocket, или остановите любое приложение, которое может использовать этот порт.\n - Сообщение об ошибке: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Текущий статус"
AdvSceneSwitcher.networkTab.server.status.notRunning="Не запущен"
AdvSceneSwitcher.networkTab.server.status.starting="Запуск"
AdvSceneSwitcher.networkTab.server.status.running="Работает"
AdvSceneSwitcher.networkTab.server.restart="Перезапустить сервер"
AdvSceneSwitcher.networkTab.client="Запустить клиент (Получает сообщения о переключении сцен)"
AdvSceneSwitcher.networkTab.client.address="Имя хоста или IP-адрес"
AdvSceneSwitcher.networkTab.client.port="Порт"
AdvSceneSwitcher.networkTab.client.status.currentStatus="Текущий статус"
AdvSceneSwitcher.networkTab.client.status.disconnected="Отключено"
AdvSceneSwitcher.networkTab.client.status.connecting="Подключение"
AdvSceneSwitcher.networkTab.client.status.connected="Подключено"
AdvSceneSwitcher.networkTab.client.reconnect="Принудительное переподключение"
; Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="Группа сцен"
AdvSceneSwitcher.sceneGroupTab.list="Группы сцен"
@@ -349,28 +326,6 @@ AdvSceneSwitcher.sceneGroupTab.exists="Группа сцен или назван
AdvSceneSwitcher.sceneGroupTab.help="Группы сцен могут быть выбраны в качестве цели так же, как и обычные сцены.\n\nКак следует из названия, группа сцен представляет собой набор из нескольких сцен.\nГруппа сцен будет продвигаться по списку назначенных ей сцен в зависимости от настроенных параметров, которые можно найти справа.\n\nВы можете настроить группу сцен на переход к следующей сцене в списке:\nПосле определенного количества раз, когда группа сцен выбрана в качестве цели.\nПо истечении определенного времени.\nИли случайным образом.\n\nНапример, группа сцен, содержащая сцены ...\nScene 1\nScene 2\nScene 3 \n... активирует \"Scene 1\" в первый раз, когда он выбран в качестве цели.\nВо второй раз он активируется \"Scene 2\".\nОставшееся время \"Scene 3\" будет активирован.\n\nНажмите на выделенный символ плюса ниже, чтобы добавить новую группу сцен."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Выберите группу сцен, которую вы хотите изменить слева.\n\nВыберите сцену для добавления в эту группу сцен, выбрав сцену выше и нажав на символ плюса ниже.\n\nСцена может быть добавлена несколько раз в одну и ту же группу сцен."
; Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="Триггеры сцены"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--выбрать триггер--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="активен"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="не активен"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="переключился с"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--выбрать действие--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="начать запись"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="приостановить запись"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="отменить паузу записи"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="остановить запись"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="остановить потоковое вещание"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="начать потоковое вещание"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="запустить буфер воспроизведения"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="остановить буфер воспроизведения"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="отключить источник"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="включить источник"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="запустить переключатель сцены"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="остановить переключатель сцены"
AdvSceneSwitcher.sceneTriggerTab.entry="Когда {{scenes}} {{triggers}} {{actions}} {{audioSources}} после {{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="Эта вкладка позволяет запускать действия при изменении сцены, например, остановку записи или потоковой передачи."
; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Запустить Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="становить Advanced Scene Switcher"
@@ -399,6 +354,6 @@ AdvSceneSwitcher.status.active="Активный"
AdvSceneSwitcher.status.inactive="Неактивен"
AdvSceneSwitcher.unit.milliseconds="миллисекунды"
AdvSceneSwitcher.unit.secends="секунды"
AdvSceneSwitcher.unit.seconds="секунды"
AdvSceneSwitcher.unit.minutes="минуты"
AdvSceneSwitcher.unit.hours="часы"

View File

@@ -19,14 +19,13 @@ 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.onNoMatch="...için herhangi bir işlem yapılmazsa"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatchDelay.tooltip="Yalnızca yapılandırılmış kontrol aralığı kadar doğru olacaktır."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="Geçiş Yapma"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Rastgele sekmesinde herhangi bir sahneye geçin"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.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"
@@ -39,7 +38,7 @@ 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.textType="Text dosyası (*.txt *.json)"
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"
@@ -107,8 +106,6 @@ 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}}{{pattern}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Pencere"
AdvSceneSwitcher.condition.file="Dosya"
AdvSceneSwitcher.condition.file.entry.line1="İçerik{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
@@ -142,16 +139,15 @@ AdvSceneSwitcher.condition.video.screenshotFail="Kaynağın ekran görüntüsü
AdvSceneSwitcher.condition.video.patternMatchFail="Desen bulunamadı!"
AdvSceneSwitcher.condition.video.objectMatchFail="Nesne bulunamadı!"
AdvSceneSwitcher.condition.video.modelLoadFail="Model verileri yüklenemedi!"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{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.video.layout="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.layout.modelPath="Model verileri (haar kademeli sınıflandırıcı):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="Minimum komşular: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.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}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Kayıt"
AdvSceneSwitcher.condition.record.state.start="Kayıt Çalışıyor"
AdvSceneSwitcher.condition.record.state.pause="Kayıt durakladı"
@@ -338,7 +334,7 @@ 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.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -359,7 +355,6 @@ 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.screenshot="Ekran görüntüsü"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
AdvSceneSwitcher.action.profile="Profil"
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"
@@ -529,31 +524,6 @@ 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ı"
@@ -574,30 +544,6 @@ 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"
@@ -645,7 +591,7 @@ 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.seconds="saniye"
AdvSceneSwitcher.unit.minutes="dakika"
AdvSceneSwitcher.unit.hours="saat"
AdvSceneSwitcher.duration.condition.none="Zaman kısıtlaması yok"

File diff suppressed because it is too large Load Diff

1
deps/date vendored Submodule

Submodule deps/date added at 5bdb7e6f31

13156
deps/exprtk/exprtk.hpp vendored

File diff suppressed because it is too large Load Diff

1
deps/jsoncons vendored Submodule

Submodule deps/jsoncons added at 64b9da1e9f

2
deps/libremidi vendored

View File

@@ -29,7 +29,8 @@ extern "C" {
#endif
typedef void *obs_websocket_vendor;
typedef void (*obs_websocket_request_callback_function)(obs_data_t *, obs_data_t *, void *);
typedef void (*obs_websocket_request_callback_function)(obs_data_t *,
obs_data_t *, void *);
struct obs_websocket_request_response {
unsigned int status_code;
@@ -44,7 +45,7 @@ struct obs_websocket_request_callback {
void *priv_data;
};
inline proc_handler_t *_ph;
static proc_handler_t *_ph;
/* ==================== INTERNAL API FUNCTIONS ==================== */
@@ -53,9 +54,10 @@ static inline proc_handler_t *obs_websocket_get_ph(void)
proc_handler_t *global_ph = obs_get_proc_handler();
assert(global_ph != NULL);
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
if (!proc_handler_call(global_ph, "obs_websocket_api_get_ph", &cd))
blog(LOG_DEBUG, "Unable to fetch obs-websocket proc handler object. obs-websocket not installed?");
blog(LOG_DEBUG,
"Unable to fetch obs-websocket proc handler object. obs-websocket not installed?");
proc_handler_t *ret = (proc_handler_t *)calldata_ptr(&cd, "ph");
calldata_free(&cd);
@@ -69,7 +71,9 @@ static inline bool obs_websocket_ensure_ph(void)
return _ph != NULL;
}
static inline bool obs_websocket_vendor_run_simple_proc(obs_websocket_vendor vendor, const char *proc_name, calldata_t *cd)
static inline bool
obs_websocket_vendor_run_simple_proc(obs_websocket_vendor vendor,
const char *proc_name, calldata_t *cd)
{
if (!obs_websocket_ensure_ph())
return false;
@@ -91,12 +95,12 @@ static inline unsigned int obs_websocket_get_api_version(void)
if (!obs_websocket_ensure_ph())
return 0;
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
if (!proc_handler_call(_ph, "get_api_version", &cd))
return 1; // API v1 does not include get_api_version
unsigned int ret = calldata_int(&cd, "version");
unsigned int ret = (unsigned int)calldata_int(&cd, "version");
calldata_free(&cd);
@@ -104,7 +108,12 @@ static inline unsigned int obs_websocket_get_api_version(void)
}
// Calls an obs-websocket request. Free response with `obs_websocket_request_response_free()`
static inline obs_websocket_request_response *obs_websocket_call_request(const char *request_type, obs_data_t *request_data = NULL)
static inline struct obs_websocket_request_response *
obs_websocket_call_request(const char *request_type, obs_data_t *request_data
#ifdef __cplusplus
= NULL
#endif
)
{
if (!obs_websocket_ensure_ph())
return NULL;
@@ -113,14 +122,16 @@ static inline obs_websocket_request_response *obs_websocket_call_request(const c
if (request_data)
request_data_string = obs_data_get_json(request_data);
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "request_type", request_type);
calldata_set_string(&cd, "request_data", request_data_string);
proc_handler_call(_ph, "call_request", &cd);
auto ret = (struct obs_websocket_request_response *)calldata_ptr(&cd, "response");
struct obs_websocket_request_response *ret =
(struct obs_websocket_request_response *)calldata_ptr(
&cd, "response");
calldata_free(&cd);
@@ -128,7 +139,8 @@ static inline obs_websocket_request_response *obs_websocket_call_request(const c
}
// Free a request response object returned by `obs_websocket_call_request()`
static inline void obs_websocket_request_response_free(struct obs_websocket_request_response *response)
static inline void obs_websocket_request_response_free(
struct obs_websocket_request_response *response)
{
if (!response)
return;
@@ -144,12 +156,13 @@ static inline void obs_websocket_request_response_free(struct obs_websocket_requ
// ALWAYS CALL ONLY VIA `obs_module_post_load()` CALLBACK!
// Registers a new "vendor" (Example: obs-ndi)
static inline obs_websocket_vendor obs_websocket_register_vendor(const char *vendor_name)
static inline obs_websocket_vendor
obs_websocket_register_vendor(const char *vendor_name)
{
if (!obs_websocket_ensure_ph())
return NULL;
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "name", vendor_name);
@@ -161,32 +174,37 @@ static inline obs_websocket_vendor obs_websocket_register_vendor(const char *ven
}
// Registers a new request for a vendor
static inline bool obs_websocket_vendor_register_request(obs_websocket_vendor vendor, const char *request_type,
obs_websocket_request_callback_function request_callback, void *priv_data)
static inline bool obs_websocket_vendor_register_request(
obs_websocket_vendor vendor, const char *request_type,
obs_websocket_request_callback_function request_callback,
void *priv_data)
{
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
struct obs_websocket_request_callback cb = {};
cb.callback = request_callback;
cb.priv_data = priv_data;
struct obs_websocket_request_callback cb = {request_callback,
priv_data};
calldata_set_string(&cd, "type", request_type);
calldata_set_ptr(&cd, "callback", &cb);
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_request_register", &cd);
bool success = obs_websocket_vendor_run_simple_proc(
vendor, "vendor_request_register", &cd);
calldata_free(&cd);
return success;
}
// Unregisters an existing vendor request
static inline bool obs_websocket_vendor_unregister_request(obs_websocket_vendor vendor, const char *request_type)
static inline bool
obs_websocket_vendor_unregister_request(obs_websocket_vendor vendor,
const char *request_type)
{
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "type", request_type);
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_request_unregister", &cd);
bool success = obs_websocket_vendor_run_simple_proc(
vendor, "vendor_request_unregister", &cd);
calldata_free(&cd);
return success;
@@ -194,14 +212,17 @@ static inline bool obs_websocket_vendor_unregister_request(obs_websocket_vendor
// Does not affect event_data refcount.
// Emits an event under the vendor's name
static inline bool obs_websocket_vendor_emit_event(obs_websocket_vendor vendor, const char *event_name, obs_data_t *event_data)
static inline bool obs_websocket_vendor_emit_event(obs_websocket_vendor vendor,
const char *event_name,
obs_data_t *event_data)
{
calldata_t cd = {0};
calldata_t cd = {0, 0, 0, 0};
calldata_set_string(&cd, "type", event_name);
calldata_set_ptr(&cd, "data", (void *)event_data);
bool success = obs_websocket_vendor_run_simple_proc(vendor, "vendor_event_emit", &cd);
bool success = obs_websocket_vendor_run_simple_proc(
vendor, "vendor_event_emit", &cd);
calldata_free(&cd);
return success;

1
deps/paho.mqtt.cpp vendored Submodule

Submodule deps/paho.mqtt.cpp added at 165476b1dc

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
#include "advanced-scene-switcher.hpp"
#include "backup.hpp"
#include "curl-helper.hpp"
#include "log-helper.hpp"
#include "macro-helpers.hpp"
#include "obs-module-helper.hpp"
@@ -14,15 +13,20 @@
#include "tab-helpers.hpp"
#include "utility.hpp"
#include "version.h"
#include "websocket-api.hpp"
#include <filesystem>
#include <obs-frontend-api.h>
#include <QAction>
#include <QDirIterator>
#include <QLibrary>
#include <QMainWindow>
#include <QTextStream>
#include <regex>
#ifdef _WIN32
#include <Windows.h>
#endif
namespace advss {
AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr;
@@ -85,8 +89,8 @@ static void DisplayMissingDataDirWarning()
"Please check installation instructions!\n\n"
"Data most likely expected at:\n\n";
#ifdef _WIN32
msg += QString::fromStdString(
(std::filesystem::current_path().string()));
msg += QDir::currentPath();
msg += "/";
#endif
msg += obs_get_module_data_path(obs_current_module());
@@ -126,9 +130,7 @@ void AdvSceneSwitcher::LoadUI()
SetupTimeTab();
SetupAudioTab();
SetupVideoTab();
SetupNetworkTab();
SetupSceneGroupTab();
SetupTriggerTab();
SetupMacroTab();
SetupOtherTabs(ui->tabWidget);
@@ -309,7 +311,7 @@ void SwitcherData::Thread()
}
}
ResetForNextInterval();
RunIntervalResetSteps();
if (match) {
if (macroMatch) {
@@ -359,14 +361,6 @@ void SwitcherData::SetPreconditions()
InvalidateMacroTempVarValues();
}
void SwitcherData::ResetForNextInterval()
{
// Plugin reset functions
for (const auto &func : resetIntervalSteps) {
func();
}
}
bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
OBSWeakSource &transition, int &linger,
bool &setPrevSceneAfterLinger,
@@ -447,7 +441,7 @@ void AutoStartActionQueues();
void SwitcherData::Start()
{
if (!(th && th->isRunning())) {
ResetForNextInterval();
RunIntervalResetSteps();
ResetMacros();
AutoStartActionQueues();
@@ -455,17 +449,7 @@ void SwitcherData::Start()
th = new SwitcherThread();
th->start((QThread::Priority)threadPriority);
// Will be overwritten quickly but might be useful
writeToStatusFile("Advanced Scene Switcher running");
}
if (networkConfig.ServerEnabled) {
server.start(networkConfig.ServerPort,
networkConfig.LockToIPv4);
}
if (networkConfig.ClientEnabled) {
client.connect(networkConfig.GetClientUri());
RunStartSteps();
}
if (showSystemTrayNotifications) {
@@ -492,12 +476,9 @@ void SwitcherData::Stop()
th->wait();
delete th;
th = nullptr;
writeToStatusFile("Advanced Scene Switcher stopped");
RunStopSteps();
}
server.stop();
client.disconnect();
if (showSystemTrayNotifications) {
DisplayTrayMessage(
obs_module_text("AdvSceneSwitcher.pluginName"),
@@ -565,12 +546,7 @@ static void handleSceneChange()
GetWeakSourceName(switcher->previousScene).c_str());
}
switcher->checkTriggers();
switcher->checkDefaultSceneTransitions();
if (switcher->networkConfig.ShouldSendFrontendSceneChange()) {
switcher->server.sendMessage({ws.Get(), nullptr, 0});
}
}
static void setLiveTime()
@@ -599,17 +575,6 @@ static void checkAutoStartStreaming()
switcher->Start();
}
static void handlePeviewSceneChange()
{
if (switcher->networkConfig.ShouldSendPrviewSceneChange()) {
OBSSourceAutoRelease source =
obs_frontend_get_current_preview_scene();
OBSWeakSourceAutoRelease weak =
obs_source_get_weak_source(source);
switcher->server.sendMessage({weak.Get(), nullptr, 0}, true);
}
}
static void handleTransitionEnd()
{
GetMacroTransitionCV().notify_all();
@@ -687,9 +652,6 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
handleSceneChange();
break;
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
handlePeviewSceneChange();
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
setLiveTime();
checkAutoStartRecording();
@@ -762,12 +724,7 @@ void OpenSettingsWindow()
}
}
QWidget *GetSettingsWindow()
{
return SettingsWindowIsOpened() ? AdvSceneSwitcher::window : nullptr;
}
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
void AdvSceneSwitcher::HighlightMacroSettingsButton(bool enable)
{
static QObject *highlight = nullptr;
if ((highlight && enable) || (!highlight && !enable)) {
@@ -787,13 +744,13 @@ void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
highlight = HighlightWidget(ui->macroSettings, Qt::green);
}
void HighligthMacroSettingsButton(bool enable)
void HighlightMacroSettingsButton(bool enable)
{
auto window = GetSettingsWindow();
if (!window) {
return;
}
static_cast<AdvSceneSwitcher *>(window)->HighligthMacroSettingsButton(
static_cast<AdvSceneSwitcher *>(window)->HighlightMacroSettingsButton(
enable);
}

View File

@@ -11,6 +11,7 @@ namespace advss {
class MacroActionEdit;
class MacroConditionEdit;
class MacroSegment;
class Duration;
class SequenceWidget;
struct SceneGroup;
@@ -66,6 +67,7 @@ public slots:
void on_showTrayNotifications_stateChanged(int state);
void on_uiHintsDisable_stateChanged(int state);
void on_disableComboBoxFilter_stateChanged(int state);
void on_disableMacroWidgetCache_stateChanged(int state);
void on_warnPluginLoadFailure_stateChanged(int state);
void on_hideLegacyTabs_stateChanged(int state);
void on_priorityUp_clicked();
@@ -82,31 +84,33 @@ public:
void RemoveMacro(std::shared_ptr<Macro> &);
void RemoveSelectedMacros();
void RenameMacro(std::shared_ptr<Macro> &, const QString &name);
std::shared_ptr<Macro> GetSelectedMacro();
std::vector<std::shared_ptr<Macro>> GetSelectedMacros();
std::shared_ptr<Macro> GetSelectedMacro() const;
std::vector<std::shared_ptr<Macro>> GetSelectedMacros() const;
void SetEditMacro(Macro &m);
void SetMacroEditAreaDisabled(bool);
void HighlightAction(int idx, QColor color = QColor(Qt::green));
void HighlightElseAction(int idx, QColor color = QColor(Qt::green));
void HighlightCondition(int idx, QColor color = QColor(Qt::green));
void SetMacroEditAreaDisabled(bool) const;
void HighlightAction(int idx, QColor color = QColor(Qt::green)) const;
void HighlightElseAction(int idx,
QColor color = QColor(Qt::green)) const;
void HighlightCondition(int idx,
QColor color = QColor(Qt::green)) const;
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroElseActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
void SetActionData(Macro &m);
void SetElseActionData(Macro &m);
void SetConditionData(Macro &m);
void SetActionData(Macro &m) const;
void SetElseActionData(Macro &m) const;
void SetConditionData(Macro &m) const;
void SwapActions(Macro *m, int pos1, int pos2);
void SwapConditions(Macro *m, int pos1, int pos2);
void HighligthMacroSettingsButton(bool enable = true);
void HighlightMacroSettingsButton(bool enable = true);
public slots:
void on_macroAdd_clicked();
void on_macroRemove_clicked();
void on_macroUp_clicked();
void on_macroDown_clicked();
void on_macroUp_clicked() const;
void on_macroDown_clicked() const;
void on_macroName_editingFinished();
void on_runMacroInParallel_stateChanged(int value);
void on_runMacroOnChange_stateChanged(int value);
void on_runMacroInParallel_stateChanged(int value) const;
void on_runMacroOnChange_stateChanged(int value) const;
void on_conditionAdd_clicked();
void on_conditionRemove_clicked();
void on_conditionTop_clicked();
@@ -119,14 +123,14 @@ public slots:
void on_actionUp_clicked();
void on_actionDown_clicked();
void on_actionBottom_clicked();
void on_toggleElseActions_clicked();
void on_toggleElseActions_clicked() const;
void on_elseActionAdd_clicked();
void on_elseActionRemove_clicked();
void on_elseActionTop_clicked();
void on_elseActionUp_clicked();
void on_elseActionDown_clicked();
void on_elseActionBottom_clicked();
void MacroSelectionAboutToChange();
void MacroSelectionAboutToChange() const;
void MacroSelectionChanged();
void UpMacroSegementHotkey();
void DownMacroSegementHotkey();
@@ -137,22 +141,22 @@ public slots:
void ShowMacroConditionsContextMenu(const QPoint &);
void CopyMacro();
void RenameSelectedMacro();
void ExportMacros();
void ExportMacros() const;
void ImportMacros();
void ExpandAllActions();
void ExpandAllElseActions();
void ExpandAllConditions();
void CollapseAllActions();
void CollapseAllElseActions();
void CollapseAllConditions();
void MinimizeActions();
void MaximizeActions();
void MinimizeElseActions();
void MaximizeElseActions();
void MinimizeConditions();
void MaximizeConditions();
void SetElseActionsStateToHidden();
void SetElseActionsStateToVisible();
void ExpandAllActions() const;
void ExpandAllElseActions() const;
void ExpandAllConditions() const;
void CollapseAllActions() const;
void CollapseAllElseActions() const;
void CollapseAllConditions() const;
void MinimizeActions() const;
void MaximizeActions() const;
void MinimizeElseActions() const;
void MaximizeElseActions() const;
void MinimizeConditions() const;
void MaximizeConditions() const;
void SetElseActionsStateToHidden() const;
void SetElseActionsStateToVisible() const;
void MacroActionSelectionChanged(int idx);
void MacroActionReorder(int to, int target);
void AddMacroAction(Macro *macro, int idx, const std::string &id,
@@ -178,8 +182,8 @@ public slots:
void RemoveMacroCondition(int idx);
void MoveMacroConditionUp(int idx);
void MoveMacroConditionDown(int idx);
void HighlightControls();
void HighlightOnChange();
void HighlightControls() const;
void HighlightOnChange() const;
void on_macroSettings_clicked();
void CopyMacroSegment();
void PasteMacroSegment();
@@ -189,7 +193,7 @@ signals:
void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString &newName);
void MacroSegmentOrderChanged();
void SegmentTempVarsChanged();
void SegmentTempVarsChanged(MacroSegment *);
void HighlightMacrosChanged(bool value);
void ConnectionAdded(const QString &);
@@ -359,24 +363,6 @@ public slots:
void on_videoDown_clicked();
void on_getScreenshot_clicked();
// Scene group tab
public:
void SetupNetworkTab();
public slots:
void on_serverSettings_toggled(bool on);
void on_serverPort_valueChanged(int value);
void on_lockToIPv4_stateChanged(int state);
void on_serverRestart_clicked();
void UpdateServerStatus();
void on_clientSettings_toggled(bool on);
void on_clientHostname_textChanged(const QString &text);
void on_clientPort_valueChanged(int value);
void on_sendSceneChange_stateChanged(int state);
void on_restrictSend_stateChanged(int state);
void on_sendPreview_stateChanged(int state);
void on_clientReconnect_clicked();
void UpdateClientStatus();
// Scene group tab
public:
void SetupSceneGroupTab();
@@ -398,20 +384,12 @@ signals:
void SceneGroupRemoved(const QString &name);
void SceneGroupRenamed(const QString &oldName, const QString newName);
// Trigger tab
public:
void SetupTriggerTab();
public slots:
void on_triggerAdd_clicked();
void on_triggerRemove_clicked();
void on_triggerUp_clicked();
void on_triggerDown_clicked();
/* --- End of legacy tab section --- */
private:
void SetCheckIntervalTooLowVisibility() const;
};
void OpenSettingsWindow();
QWidget *GetSettingsWindow();
void HighligthMacroSettingsButton(bool enable);
void HighlightMacroSettingsButton(bool enable);
} // namespace advss

View File

@@ -113,12 +113,12 @@ void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
static_cast<SwitcherData::StartupBehavior>(index);
}
void AdvSceneSwitcher::on_logLevel_currentIndexChanged(int value)
void AdvSceneSwitcher::on_logLevel_currentIndexChanged(int idx)
{
if (loading) {
return;
}
switcher->logLevel = static_cast<SwitcherData::LogLevel>(value);
SetLogLevel(static_cast<LogLevel>(ui->logLevel->itemData(idx).toInt()));
}
void AdvSceneSwitcher::on_autoStartEvent_currentIndexChanged(int index)
@@ -150,6 +150,8 @@ void AdvSceneSwitcher::on_checkInterval_valueChanged(int value)
std::lock_guard<std::mutex> lock(switcher->m);
switcher->interval = value;
SetCheckIntervalTooLowVisibility();
}
void AdvSceneSwitcher::closeEvent(QCloseEvent *)
@@ -203,6 +205,16 @@ void AdvSceneSwitcher::on_disableComboBoxFilter_stateChanged(int state)
FilterComboBox::SetFilterBehaviourEnabled(!state);
}
void AdvSceneSwitcher::on_disableMacroWidgetCache_stateChanged(int state)
{
if (loading) {
return;
}
switcher->disableMacroWidgetCache = state;
MacroSegmentList::SetCachingEnabled(!state);
}
void AdvSceneSwitcher::on_warnPluginLoadFailure_stateChanged(int state)
{
if (loading) {
@@ -216,7 +228,6 @@ static bool isLegacyTab(const QString &name)
{
return name == obs_module_text(
"AdvSceneSwitcher.sceneGroupTab.title") ||
name == obs_module_text("AdvSceneSwitcher.networkTab.title") ||
name == obs_module_text(
"AdvSceneSwitcher.transitionTab.title") ||
name == obs_module_text(
@@ -234,9 +245,7 @@ static bool isLegacyTab(const QString &name)
"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");
name == obs_module_text("AdvSceneSwitcher.pauseTab.title");
}
void AdvSceneSwitcher::on_hideLegacyTabs_stateChanged(int state)
@@ -248,6 +257,10 @@ void AdvSceneSwitcher::on_hideLegacyTabs_stateChanged(int state)
ui->tabWidget->setTabVisible(idx, !state);
}
}
// Changing priority of legacy tabs will very likely not be necessary if
// the legacy tabs are hidden
ui->priorityBox->setVisible(!switcher->hideLegacyTabs);
}
void AdvSceneSwitcher::SetDeprecationWarnings()
@@ -272,12 +285,15 @@ static bool containsSensitiveData(obs_data_t *data)
obs_data_get_array(data, "twitchConnections");
OBSDataArrayAutoRelease websocketConnections =
obs_data_get_array(data, "websocketConnections");
OBSDataArrayAutoRelease mqttConnections =
obs_data_get_array(data, "mqttConnections");
auto isNotEmpty = [](obs_data_array *array) {
return obs_data_array_count(array) > 0;
};
return isNotEmpty(twitchTokens) || isNotEmpty(websocketConnections);
return isNotEmpty(twitchTokens) || isNotEmpty(websocketConnections) ||
isNotEmpty(mqttConnections);
}
void AdvSceneSwitcher::on_exportSettings_clicked()
@@ -341,6 +357,10 @@ void AdvSceneSwitcher::on_importSettings_clicked()
return;
}
// We have to make sure to that no macro is currently being edited while
// the new settings are loaded
ui->macros->clearSelection();
std::lock_guard<std::mutex> lock(switcher->m);
switcher->LoadSettings(obj);
switcher->lastImportPath = path.toStdString();
@@ -376,6 +396,7 @@ void AdvSceneSwitcher::CheckFirstTimeSetup()
switcher->firstBoot = false;
DisplayMessage(
obs_module_text("AdvSceneSwitcher.firstBootMessage"));
switcher->Start();
}
}
@@ -456,8 +477,6 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
loadTimeSwitches(obj);
loadAudioSwitches(obj);
loadVideoSwitches(obj);
loadNetworkSettings(obj);
loadSceneTriggers(obj);
LoadGeneralSettings(obj);
LoadHotkeys(obj);
LoadUISettings(obj);
@@ -492,8 +511,6 @@ void SwitcherData::SaveSettings(obs_data_t *obj)
saveTimeSwitches(obj);
saveAudioSwitches(obj);
saveVideoSwitches(obj);
saveNetworkSwitches(obj);
saveSceneTriggers(obj);
SaveGeneralSettings(obj);
SaveHotkeys(obj);
SaveUISettings(obj);
@@ -526,12 +543,15 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
obs_data_set_int(obj, "autoStartEvent",
static_cast<int>(autoStartEvent));
obs_data_set_int(obj, "logLevel", static_cast<int>(logLevel));
SaveLogLevel(obj);
obs_data_set_bool(obj, "showSystemTrayNotifications",
showSystemTrayNotifications);
obs_data_set_bool(obj, "disableHints", disableHints);
obs_data_set_bool(obj, "disableFilterComboboxFilter",
disableFilterComboboxFilter);
obs_data_set_bool(obj, "disableMacroWidgetCache",
disableMacroWidgetCache);
obs_data_set_bool(obj, "warnPluginLoadFailure", warnPluginLoadFailure);
obs_data_set_bool(obj, "hideLegacyTabs", hideLegacyTabs);
@@ -582,12 +602,15 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
autoStartEvent =
static_cast<AutoStart>(obs_data_get_int(obj, "autoStartEvent"));
logLevel = static_cast<LogLevel>(obs_data_get_int(obj, "logLevel"));
LoadLogLevel(obj);
showSystemTrayNotifications =
obs_data_get_bool(obj, "showSystemTrayNotifications");
disableHints = obs_data_get_bool(obj, "disableHints");
disableFilterComboboxFilter =
obs_data_get_bool(obj, "disableFilterComboboxFilter");
disableMacroWidgetCache =
obs_data_get_bool(obj, "disableMacroWidgetCache");
obs_data_set_default_bool(obj, "warnPluginLoadFailure", true);
warnPluginLoadFailure = obs_data_get_bool(obj, "warnPluginLoadFailure");
obs_data_set_default_bool(obj, "hideLegacyTabs", true);
@@ -810,6 +833,28 @@ static void setupGeneralTabInactiveWarning(QTabWidget *tabs)
inactiveTimer->start();
}
void advss::AdvSceneSwitcher::SetCheckIntervalTooLowVisibility() const
{
auto macro = GetMacroWithInvalidConditionInterval();
if (!macro) {
ui->checkIntervalTooLowWarning->hide();
return;
}
const QString labelTextFormat(obs_module_text(
"AdvSceneSwitcher.generalTab.status.checkIntervalTooLow"));
const QString labelTooltipFormat(obs_module_text(
"AdvSceneSwitcher.generalTab.status.checkIntervalTooLow.tooltip"));
const QString name = QString::fromStdString(macro->Name());
const QString duration = QString::fromStdString(
macro->GetCustomConditionCheckInterval().ToString());
ui->checkIntervalTooLowWarning->setText(labelTextFormat.arg(name));
ui->checkIntervalTooLowWarning->setToolTip(
labelTooltipFormat.arg(name).arg(duration).arg(name));
ui->checkIntervalTooLowWarning->show();
}
void AdvSceneSwitcher::SetupGeneralTab()
{
PopulateSceneSelection(ui->noMatchSwitchScene, false);
@@ -831,13 +876,14 @@ void AdvSceneSwitcher::SetupGeneralTab()
DurationSelection *noMatchDelay = new DurationSelection();
noMatchDelay->SetDuration(switcher->noMatchDelay);
noMatchDelay->setToolTip(obs_module_text(
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip"));
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMatchDelay.tooltip"));
ui->noMatchLayout->addWidget(noMatchDelay);
QWidget::connect(noMatchDelay,
SIGNAL(DurationChanged(const Duration &)), this,
SLOT(NoMatchDelayDurationChanged(const Duration &)));
ui->checkInterval->setValue(switcher->interval);
SetCheckIntervalTooLowVisibility();
ui->enableCooldown->setChecked(switcher->enableCooldown);
ui->cooldownTime->setEnabled(switcher->enableCooldown);
@@ -848,7 +894,9 @@ void AdvSceneSwitcher::SetupGeneralTab()
SIGNAL(DurationChanged(const Duration &)), this,
SLOT(CooldownDurationChanged(const Duration &)));
ui->logLevel->setCurrentIndex(static_cast<int>(switcher->logLevel));
PopulateLogLevelSelection(ui->logLevel);
ui->logLevel->setCurrentIndex(
ui->logLevel->findData(static_cast<int>(GetLogLevel())));
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
ui->showTrayNotifications->setChecked(
@@ -858,6 +906,9 @@ void AdvSceneSwitcher::SetupGeneralTab()
switcher->disableFilterComboboxFilter);
FilterComboBox::SetFilterBehaviourEnabled(
!switcher->disableFilterComboboxFilter);
ui->disableMacroWidgetCache->setChecked(
switcher->disableMacroWidgetCache);
MacroSegmentList::SetCachingEnabled(!switcher->disableMacroWidgetCache);
ui->warnPluginLoadFailure->setChecked(switcher->warnPluginLoadFailure);
ui->hideLegacyTabs->setChecked(switcher->hideLegacyTabs);

View File

@@ -170,8 +170,10 @@ void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
item->setData(Qt::UserRole, text);
ui->sceneGroups->setCurrentItem(item);
addPulse->deleteLater();
addPulse = nullptr;
if (addPulse) {
addPulse->deleteLater();
addPulse = nullptr;
}
ui->sceneGroupHelp->setVisible(false);
emit SceneGroupAdded(QString::fromStdString(name));

View File

@@ -1,581 +0,0 @@
#include "advanced-scene-switcher.hpp"
#include "layout-helpers.hpp"
#include "selection-helpers.hpp"
#include "source-helpers.hpp"
#include "switcher-data.hpp"
#include "ui-helpers.hpp"
#include "utility.hpp"
#include <obs-frontend-api.h>
#include <thread>
namespace advss {
bool SceneTrigger::pause = false;
static QObject *addPulse = nullptr;
void AdvSceneSwitcher::on_triggerAdd_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
switcher->sceneTriggers.emplace_back();
listAddClicked(ui->sceneTriggers,
new SceneTriggerWidget(this,
&switcher->sceneTriggers.back()),
addPulse);
ui->triggerHelp->setVisible(false);
}
void AdvSceneSwitcher::on_triggerRemove_clicked()
{
QListWidgetItem *item = ui->sceneTriggers->currentItem();
if (!item) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->sceneTriggers->currentRow();
auto &switches = switcher->sceneTriggers;
switches.erase(switches.begin() + idx);
}
delete item;
}
void AdvSceneSwitcher::on_triggerUp_clicked()
{
int index = ui->sceneTriggers->currentRow();
if (!listMoveUp(ui->sceneTriggers)) {
return;
}
SceneTriggerWidget *s1 =
(SceneTriggerWidget *)ui->sceneTriggers->itemWidget(
ui->sceneTriggers->item(index));
SceneTriggerWidget *s2 =
(SceneTriggerWidget *)ui->sceneTriggers->itemWidget(
ui->sceneTriggers->item(index - 1));
SceneTriggerWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->sceneTriggers[index],
switcher->sceneTriggers[index - 1]);
}
void AdvSceneSwitcher::on_triggerDown_clicked()
{
int index = ui->sceneTriggers->currentRow();
if (!listMoveDown(ui->sceneTriggers)) {
return;
}
SceneTriggerWidget *s1 =
(SceneTriggerWidget *)ui->sceneTriggers->itemWidget(
ui->sceneTriggers->item(index));
SceneTriggerWidget *s2 =
(SceneTriggerWidget *)ui->sceneTriggers->itemWidget(
ui->sceneTriggers->item(index + 1));
SceneTriggerWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->sceneTriggers[index],
switcher->sceneTriggers[index + 1]);
}
void SceneTrigger::logMatch()
{
std::string sceneName = "";
std::string statusName = "";
std::string actionName = "";
switch (triggerType) {
case sceneTriggerType::NONE:
statusName = "NONE";
break;
case sceneTriggerType::SCENE_ACTIVE:
statusName = "SCENE ACTIVE";
break;
case sceneTriggerType::SCENE_INACTIVE:
statusName = "SCENE INACTIVE";
break;
case sceneTriggerType::SCENE_LEAVE:
statusName = "SCENE LEAVE";
break;
default:
break;
}
switch (triggerAction) {
case sceneTriggerAction::NONE:
actionName = "NONE";
break;
case sceneTriggerAction::START_RECORDING:
actionName = "START RECORDING";
break;
case sceneTriggerAction::PAUSE_RECORDING:
actionName = "PAUSE RECORDING";
break;
case sceneTriggerAction::UNPAUSE_RECORDING:
actionName = "UNPAUSE RECORDING";
break;
case sceneTriggerAction::STOP_RECORDING:
actionName = "STOP RECORDING";
break;
case sceneTriggerAction::START_STREAMING:
actionName = "START STREAMING";
break;
case sceneTriggerAction::STOP_STREAMING:
actionName = "STOP STREAMING";
break;
case sceneTriggerAction::START_REPLAY_BUFFER:
actionName = "START REPLAY BUFFER";
break;
case sceneTriggerAction::STOP_REPLAY_BUFFER:
actionName = "STOP REPLAY BUFFER";
break;
case sceneTriggerAction::MUTE_SOURCE:
actionName = "MUTE (" + GetWeakSourceName(audioSource) + ")";
break;
case sceneTriggerAction::UNMUTE_SOURCE:
actionName = "UNMUTE (" + GetWeakSourceName(audioSource) + ")";
break;
case sceneTriggerAction::START_SWITCHER:
actionName = "START SCENE SWITCHER";
break;
case sceneTriggerAction::STOP_SWITCHER:
actionName = "STOP SCENE SWITCHER";
break;
case sceneTriggerAction::START_VCAM:
actionName = "START VIRTUAL CAMERA";
break;
case sceneTriggerAction::STOP_VCAM:
actionName = "STOP VIRTUAL CAMERA";
break;
default:
actionName = "UNKNOWN";
break;
}
blog(LOG_INFO,
"scene '%s' in status '%s' triggering action '%s' after %f seconds",
GetWeakSourceName(scene).c_str(), statusName.c_str(),
actionName.c_str(), duration.Seconds());
}
void frontEndActionThread(sceneTriggerAction action, double delay)
{
long long mil = delay * 1000;
std::this_thread::sleep_for(std::chrono::milliseconds(mil));
switch (action) {
case sceneTriggerAction::NONE:
break;
case sceneTriggerAction::START_RECORDING:
obs_frontend_recording_start();
break;
case sceneTriggerAction::PAUSE_RECORDING:
obs_frontend_recording_pause(true);
break;
case sceneTriggerAction::UNPAUSE_RECORDING:
obs_frontend_recording_pause(false);
break;
case sceneTriggerAction::STOP_RECORDING:
obs_frontend_recording_stop();
break;
case sceneTriggerAction::START_STREAMING:
obs_frontend_streaming_start();
break;
case sceneTriggerAction::STOP_STREAMING:
obs_frontend_streaming_stop();
break;
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(26, 0, 0)
case sceneTriggerAction::START_REPLAY_BUFFER:
obs_frontend_replay_buffer_start();
break;
case sceneTriggerAction::STOP_REPLAY_BUFFER:
obs_frontend_replay_buffer_stop();
break;
#endif
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 0, 0)
case sceneTriggerAction::START_VCAM:
obs_frontend_start_virtualcam();
break;
case sceneTriggerAction::STOP_VCAM:
obs_frontend_stop_virtualcam();
break;
#endif
default:
blog(LOG_WARNING, "ignoring unexpected frontend action '%d'",
static_cast<int>(action));
break;
}
}
void muteThread(OBSWeakSource source, double delay, bool mute)
{
long long mil = delay * 1000;
std::this_thread::sleep_for(std::chrono::milliseconds(mil));
auto s = obs_weak_source_get_source(source);
obs_source_set_muted(s, mute);
obs_source_release(s);
}
void statusThread(double delay, bool stop)
{
long long mil = delay * 1000;
std::this_thread::sleep_for(std::chrono::milliseconds(mil));
if (stop) {
switcher->Stop();
} else {
switcher->Start();
}
}
bool isFrontendAction(sceneTriggerAction triggerAction)
{
return triggerAction == sceneTriggerAction::START_RECORDING ||
triggerAction == sceneTriggerAction::PAUSE_RECORDING ||
triggerAction == sceneTriggerAction::UNPAUSE_RECORDING ||
triggerAction == sceneTriggerAction::STOP_RECORDING ||
triggerAction == sceneTriggerAction::START_STREAMING ||
triggerAction == sceneTriggerAction::STOP_STREAMING ||
triggerAction == sceneTriggerAction::START_REPLAY_BUFFER ||
triggerAction == sceneTriggerAction::STOP_REPLAY_BUFFER ||
triggerAction == sceneTriggerAction::START_VCAM ||
triggerAction == sceneTriggerAction::STOP_VCAM;
}
bool isAudioAction(sceneTriggerAction t)
{
return t == sceneTriggerAction::MUTE_SOURCE ||
t == sceneTriggerAction::UNMUTE_SOURCE;
}
bool isSwitcherStatusAction(sceneTriggerAction t)
{
return t == sceneTriggerAction::START_SWITCHER ||
t == sceneTriggerAction::STOP_SWITCHER;
}
void SceneTrigger::performAction()
{
if (triggerAction == sceneTriggerAction::NONE) {
return;
}
std::thread t;
if (isFrontendAction(triggerAction)) {
t = std::thread(frontEndActionThread, triggerAction,
duration.Seconds());
} else if (isAudioAction(triggerAction)) {
bool mute = triggerAction == sceneTriggerAction::MUTE_SOURCE;
t = std::thread(muteThread, audioSource, duration.Seconds(),
mute);
} else if (isSwitcherStatusAction(triggerAction)) {
bool stop = triggerAction == sceneTriggerAction::STOP_SWITCHER;
t = std::thread(statusThread, duration.Seconds(), stop);
} else {
blog(LOG_WARNING, "ignoring unknown action '%d'",
static_cast<int>(triggerAction));
}
t.detach();
}
bool SceneTrigger::checkMatch(OBSWeakSource currentScene,
OBSWeakSource previousScene)
{
switch (triggerType) {
case sceneTriggerType::NONE:
return false;
case sceneTriggerType::SCENE_ACTIVE:
return currentScene == scene;
case sceneTriggerType::SCENE_INACTIVE:
return currentScene != scene;
case sceneTriggerType::SCENE_LEAVE:
return previousScene == scene;
}
return false;
}
void SwitcherData::checkTriggers()
{
if (SceneTrigger::pause) {
return;
}
for (auto &t : sceneTriggers) {
if (stop && !isSwitcherStatusAction(t.triggerAction)) {
continue;
}
if (t.checkMatch(currentScene, previousScene)) {
t.logMatch();
t.performAction();
}
}
}
void SwitcherData::saveSceneTriggers(obs_data_t *obj)
{
obs_data_array_t *triggerArray = obs_data_array_create();
for (auto &s : sceneTriggers) {
obs_data_t *array_obj = obs_data_create();
s.save(array_obj);
obs_data_array_push_back(triggerArray, array_obj);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "triggers", triggerArray);
obs_data_array_release(triggerArray);
}
void SwitcherData::loadSceneTriggers(obs_data_t *obj)
{
sceneTriggers.clear();
obs_data_array_t *triggerArray = obs_data_get_array(obj, "triggers");
size_t count = obs_data_array_count(triggerArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(triggerArray, i);
sceneTriggers.emplace_back();
sceneTriggers.back().load(array_obj);
obs_data_release(array_obj);
}
obs_data_array_release(triggerArray);
}
void AdvSceneSwitcher::SetupTriggerTab()
{
for (auto &s : switcher->sceneTriggers) {
QListWidgetItem *item;
item = new QListWidgetItem(ui->sceneTriggers);
ui->sceneTriggers->addItem(item);
SceneTriggerWidget *sw = new SceneTriggerWidget(this, &s);
item->setSizeHint(sw->minimumSizeHint());
ui->sceneTriggers->setItemWidget(item, sw);
}
if (switcher->sceneTriggers.size() == 0) {
if (!switcher->disableHints) {
addPulse = HighlightWidget(ui->triggerAdd,
QColor(Qt::green));
}
ui->triggerHelp->setVisible(true);
} else {
ui->triggerHelp->setVisible(false);
}
}
void SceneTrigger::save(obs_data_t *obj)
{
obs_data_set_string(obj, "scene", GetWeakSourceName(scene).c_str());
obs_data_set_int(obj, "triggerType", static_cast<int>(triggerType));
obs_data_set_int(obj, "triggerAction", static_cast<int>(triggerAction));
duration.Save(obj, "duration");
obs_data_set_string(obj, "audioSource",
GetWeakSourceName(audioSource).c_str());
}
void SceneTrigger::load(obs_data_t *obj)
{
const char *sceneName = obs_data_get_string(obj, "scene");
scene = GetWeakSourceByName(sceneName);
triggerType = static_cast<sceneTriggerType>(
obs_data_get_int(obj, "triggerType"));
triggerAction = static_cast<sceneTriggerAction>(
obs_data_get_int(obj, "triggerAction"));
duration.Load(obj, "duration");
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
audioSource = GetWeakSourceByName(audioSourceName);
}
static inline void populateTriggers(QComboBox *list)
{
AddSelectionEntry(
list,
obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave"));
}
inline void populateActions(QComboBox *list)
{
AddSelectionEntry(
list,
obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera"));
}
SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
: SwitchWidget(parent, s, false, false)
{
triggers = new QComboBox();
actions = new QComboBox();
duration = new DurationSelection();
audioSources = new QComboBox();
QWidget::connect(triggers, SIGNAL(currentIndexChanged(int)), this,
SLOT(TriggerTypeChanged(int)));
QWidget::connect(actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(TriggerActionChanged(int)));
QWidget::connect(duration, SIGNAL(DurationChanged(const Duration &)),
this, SLOT(DurationChanged(const Duration &)));
QWidget::connect(audioSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(AudioSourceChanged(const QString &)));
populateTriggers(triggers);
populateActions(actions);
PopulateAudioSelection(audioSources);
if (s) {
triggers->setCurrentIndex(static_cast<int>(s->triggerType));
actions->setCurrentIndex(static_cast<int>(s->triggerAction));
duration->SetDuration(s->duration);
audioSources->setCurrentText(
GetWeakSourceName(s->audioSource).c_str());
if (isAudioAction(s->triggerAction)) {
audioSources->show();
} else {
audioSources->hide();
}
}
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{triggers}}", triggers},
{"{{actions}}", actions},
{"{{audioSources}}", audioSources},
{"{{duration}}", duration},
{"{{scenes}}", scenes}};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.sceneTriggerTab.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
switchData = s;
loading = false;
}
SceneTrigger *SceneTriggerWidget::getSwitchData()
{
return switchData;
}
void SceneTriggerWidget::setSwitchData(SceneTrigger *s)
{
switchData = s;
}
void SceneTriggerWidget::swapSwitchData(SceneTriggerWidget *s1,
SceneTriggerWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
SceneTrigger *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void SceneTriggerWidget::TriggerTypeChanged(int index)
{
if (loading || !switchData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switchData->triggerType = static_cast<sceneTriggerType>(index);
}
void SceneTriggerWidget::TriggerActionChanged(int index)
{
if (loading || !switchData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
switchData->triggerAction =
static_cast<sceneTriggerAction>(index);
}
if (isAudioAction(switchData->triggerAction)) {
audioSources->show();
} else {
audioSources->hide();
}
}
void SceneTriggerWidget::DurationChanged(const Duration &duration)
{
if (loading || !switchData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switchData->duration = duration;
}
void SceneTriggerWidget::AudioSourceChanged(const QString &text)
{
if (loading || !switchData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switchData->audioSource = GetWeakSourceByQString(text);
}
} // namespace advss

View File

@@ -1,88 +0,0 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include "switch-generic.hpp"
#include "duration-control.hpp"
namespace advss {
enum class sceneTriggerType {
NONE = 0,
SCENE_ACTIVE = 1,
SCENE_INACTIVE = 2,
SCENE_LEAVE = 3,
};
enum class sceneTriggerAction {
NONE = 0,
START_RECORDING,
PAUSE_RECORDING,
UNPAUSE_RECORDING,
STOP_RECORDING,
START_STREAMING,
STOP_STREAMING,
START_REPLAY_BUFFER,
STOP_REPLAY_BUFFER,
MUTE_SOURCE,
UNMUTE_SOURCE,
START_SWITCHER,
STOP_SWITCHER,
START_VCAM,
STOP_VCAM,
};
struct SceneTrigger : SceneSwitcherEntry {
static bool pause;
sceneTriggerType triggerType = sceneTriggerType::NONE;
sceneTriggerAction triggerAction = sceneTriggerAction::NONE;
Duration duration;
OBSWeakSource audioSource = nullptr;
const char *getType() { return "trigger"; }
void save(obs_data_t *obj);
void load(obs_data_t *obj);
bool checkMatch(OBSWeakSource currentScene,
OBSWeakSource previousScene);
void performAction();
void logMatch();
};
class SceneTriggerWidget : public SwitchWidget {
Q_OBJECT
public:
SceneTriggerWidget(QWidget *parent, SceneTrigger *s);
SceneTrigger *getSwitchData();
void setSwitchData(SceneTrigger *s);
static void swapSwitchData(SceneTriggerWidget *s1,
SceneTriggerWidget *s2);
private slots:
void TriggerTypeChanged(int index);
void TriggerActionChanged(int index);
void DurationChanged(const Duration &);
void AudioSourceChanged(const QString &text);
private:
QComboBox *triggers;
QComboBox *actions;
DurationSelection *duration;
QComboBox *audioSources;
SceneTrigger *switchData;
};
} // namespace advss

View File

@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_audioAdd_clicked()
AudioSwitchWidget *sw =
new AudioSwitchWidget(this, &switcher->audioSwitches.back());
listAddClicked(ui->audioSwitches, sw, addPulse);
listAddClicked(ui->audioSwitches, sw, &addPulse);
ui->audioHelp->setVisible(false);
}
@@ -259,7 +259,8 @@ void AudioSwitch::setVolumeLevel(void *data, const float *,
}
}
obs_volmeter_t *AddVolmeterToSource(AudioSwitch *entry, obs_weak_source *source)
static obs_volmeter_t *addVolmeterToSource(AudioSwitch *entry,
obs_weak_source *source)
{
obs_volmeter_t *volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, AudioSwitch::setVolumeLevel, entry);
@@ -279,7 +280,7 @@ void AudioSwitch::resetVolmeter()
obs_volmeter_remove_callback(volmeter, setVolumeLevel, this);
obs_volmeter_destroy(volmeter);
volmeter = AddVolmeterToSource(this, audioSource);
volmeter = addVolmeterToSource(this, audioSource);
}
bool AudioSwitch::initialized()
@@ -318,7 +319,7 @@ void AudioSwitch::load(obs_data_t *obj)
duration.Load(obj, "duration");
ignoreInactiveSource = obs_data_get_bool(obj, "ignoreInactiveSource");
volmeter = AddVolmeterToSource(this, audioSource);
volmeter = addVolmeterToSource(this, audioSource);
}
void AudioSwitchFallback::save(obs_data_t *obj)
@@ -349,7 +350,7 @@ AudioSwitch::AudioSwitch(const AudioSwitch &other)
condition(other.condition),
duration(other.duration)
{
volmeter = AddVolmeterToSource(this, other.audioSource);
volmeter = addVolmeterToSource(this, other.audioSource);
}
AudioSwitch::AudioSwitch(AudioSwitch &&other) noexcept

View File

@@ -19,7 +19,7 @@ void AdvSceneSwitcher::on_executableAdd_clicked()
listAddClicked(ui->executables,
new ExecutableSwitchWidget(
this, &switcher->executableSwitches.back()),
addPulse);
&addPulse);
ui->exeHelp->setVisible(false);
}

View File

@@ -21,6 +21,29 @@ bool FileSwitch::pause = false;
static QObject *addPulse = nullptr;
static std::hash<std::string> strHash;
static void writeToStatusFile(const QString &msg)
{
if (!GetSwitcher() || !GetSwitcher()->fileIO.writeEnabled ||
GetSwitcher()->fileIO.writePath.empty()) {
return;
}
QFile file(QString::fromStdString(GetSwitcher()->fileIO.writePath));
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
stream << msg << Qt::endl;
}
file.close();
}
static bool _ = []() {
AddStartStep(
[]() { writeToStatusFile("Advanced Scene Switcher running"); });
AddStopStep(
[]() { writeToStatusFile("Advanced Scene Switcher stopped"); });
return true;
}();
void AdvSceneSwitcher::on_browseButton_clicked()
{
QString path = QFileDialog::getOpenFileName(
@@ -113,24 +136,6 @@ void SwitcherData::writeSceneInfoToFile()
}
}
void SwitcherData::writeToStatusFile(const QString &msg)
{
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
return;
}
QFile file(QString::fromStdString(fileIO.writePath));
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
stream << msg << "\n";
#else
stream << msg << Qt::endl;
#endif
}
file.close();
}
bool SwitcherData::checkSwitchInfoFromFile(OBSWeakSource &scene,
OBSWeakSource &transition)
{
@@ -291,7 +296,7 @@ void AdvSceneSwitcher::on_fileAdd_clicked()
listAddClicked(ui->fileSwitches,
new FileSwitchWidget(this,
&switcher->fileSwitches.back()),
addPulse);
&addPulse);
ui->fileHelp->setVisible(false);
}

View File

@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_mediaAdd_clicked()
listAddClicked(ui->mediaSwitches,
new MediaSwitchWidget(this,
&switcher->mediaSwitches.back()),
addPulse);
&addPulse);
ui->mediaHelp->setVisible(false);
}

View File

@@ -1,717 +0,0 @@
/*
Most of this code is based on https://github.com/Palakis/obs-websocket
*/
#include "advanced-scene-switcher.hpp"
#include "obs-module-helper.hpp"
#include "scene-switch-helpers.hpp"
#include "source-helpers.hpp"
#include "switcher-data.hpp"
#include <obs-frontend-api.h>
#include <QMessageBox>
#include <QTime>
#include <QMainWindow>
namespace advss {
#define PARAM_SERVER_ENABLE "ServerEnabled"
#define PARAM_SERVER_PORT "ServerPort"
#define PARAM_LOCKTOIPV4 "LockToIPv4"
#define PARAM_CLIENT_ENABLE "ClientEnabled"
#define PARAM_CLIENT_PORT "ClientPort"
#define PARAM_ADDRESS "Address"
#define PARAM_CLIENT_SEND_SCENE_CHANGE "SendSceneChange"
#define PARAM_CLIENT_SEND_SCENE_CHANGE_ALL "SendSceneChangeAll"
#define PARAM_CLIENT_SENDPREVIEW "SendPreview"
#define RECONNECT_DELAY 10
#define SCENE_ENTRY "scene"
#define TRANSITION_ENTRY "transition"
#define TRANSITION_DURATION "duration"
#define SET_PREVIEW "preview"
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
NetworkConfig::NetworkConfig()
: ServerEnabled(false),
ServerPort(55555),
LockToIPv4(false),
ClientEnabled(false),
Address(""),
ClientPort(55555),
SendSceneChange(true),
SendSceneChangeAll(true),
SendPreview(true)
{
}
void NetworkConfig::Load(obs_data_t *obj)
{
SetDefaults(obj);
ServerEnabled = obs_data_get_bool(obj, PARAM_SERVER_ENABLE);
ServerPort = obs_data_get_int(obj, PARAM_SERVER_PORT);
LockToIPv4 = obs_data_get_bool(obj, PARAM_LOCKTOIPV4);
ClientEnabled = obs_data_get_bool(obj, PARAM_CLIENT_ENABLE);
Address = obs_data_get_string(obj, PARAM_ADDRESS);
ClientPort = obs_data_get_int(obj, PARAM_CLIENT_PORT);
SendSceneChange =
obs_data_get_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE);
SendSceneChangeAll =
obs_data_get_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL);
SendPreview = obs_data_get_bool(obj, PARAM_CLIENT_SENDPREVIEW);
}
void NetworkConfig::Save(obs_data_t *obj)
{
obs_data_set_bool(obj, PARAM_SERVER_ENABLE, ServerEnabled);
obs_data_set_int(obj, PARAM_SERVER_PORT, ServerPort);
obs_data_set_bool(obj, PARAM_LOCKTOIPV4, LockToIPv4);
obs_data_set_bool(obj, PARAM_CLIENT_ENABLE, ClientEnabled);
obs_data_set_string(obj, PARAM_ADDRESS, Address.c_str());
obs_data_set_int(obj, PARAM_CLIENT_PORT, ClientPort);
obs_data_set_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE, SendSceneChange);
obs_data_set_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL,
SendSceneChangeAll);
obs_data_set_bool(obj, PARAM_CLIENT_SENDPREVIEW, SendPreview);
}
void NetworkConfig::SetDefaults(obs_data_t *obj)
{
obs_data_set_default_bool(obj, PARAM_SERVER_ENABLE, false);
obs_data_set_default_int(obj, PARAM_SERVER_PORT, ServerPort);
obs_data_set_default_bool(obj, PARAM_LOCKTOIPV4, LockToIPv4);
obs_data_set_default_bool(obj, PARAM_CLIENT_ENABLE, false);
obs_data_set_default_string(obj, PARAM_ADDRESS, Address.c_str());
obs_data_set_default_int(obj, PARAM_CLIENT_PORT, ClientPort);
obs_data_set_default_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE,
SendSceneChange);
obs_data_set_default_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL,
SendSceneChangeAll);
obs_data_set_default_bool(obj, PARAM_CLIENT_SENDPREVIEW, SendPreview);
}
std::string NetworkConfig::GetClientUri()
{
return "ws://" + Address + ":" + std::to_string(ClientPort);
}
bool NetworkConfig::ShouldSendSceneChange()
{
return ServerEnabled && SendSceneChange;
}
bool NetworkConfig::ShouldSendFrontendSceneChange()
{
return ShouldSendSceneChange() && SendSceneChangeAll;
}
bool NetworkConfig::ShouldSendPrviewSceneChange()
{
return ServerEnabled && SendPreview;
}
WSServer::WSServer() : QObject(nullptr), _connections(), _clMutex()
{
_server.get_alog().clear_channels(
websocketpp::log::alevel::frame_header |
websocketpp::log::alevel::frame_payload |
websocketpp::log::alevel::control);
_server.init_asio();
#ifndef _WIN32
_server.set_reuse_addr(true);
#endif
_server.set_open_handler(bind(&WSServer::onOpen, this, _1));
_server.set_close_handler(bind(&WSServer::onClose, this, _1));
_server.set_message_handler(bind(&WSServer::onMessage, this, _1, _2));
}
WSServer::~WSServer()
{
stop();
}
void WSServer::start(quint16 port, bool lockToIPv4)
{
if (_server.is_listening() &&
(port == _serverPort && _lockToIPv4 == lockToIPv4)) {
blog(LOG_INFO,
"WSServer::start: server already on this port and protocol mode. no restart needed");
return;
}
if (_server.is_listening()) {
stop();
}
_server.reset();
_serverPort = port;
_lockToIPv4 = lockToIPv4;
websocketpp::lib::error_code errorCode;
if (lockToIPv4) {
blog(LOG_INFO, "WSServer::start: Locked to IPv4 bindings");
_server.listen(websocketpp::lib::asio::ip::tcp::v4(),
_serverPort, errorCode);
} else {
blog(LOG_INFO, "WSServer::start: Not locked to IPv4 bindings");
_server.listen(_serverPort, errorCode);
}
if (errorCode) {
std::string errorCodeMessage = errorCode.message();
blog(LOG_INFO, "server: listen failed: %s",
errorCodeMessage.c_str());
QString errorTitle =
obs_module_text("AdvSceneSwitcher.windowTitle");
QString errorMessage =
QString(obs_module_text(
"AdvSceneSwitcher.networkTab.startFailed.message"))
.arg(_serverPort)
.arg(errorCodeMessage.c_str());
QMainWindow *mainWindow = reinterpret_cast<QMainWindow *>(
obs_frontend_get_main_window());
QMessageBox::warning(mainWindow, errorTitle, errorMessage);
return;
}
switcher->serverStatus = ServerStatus::STARTING;
_server.start_accept();
_threadPool.start(Compatability::CreateFunctionRunnable([=]() {
blog(LOG_INFO, "WSServer::start: io thread started");
_server.run();
blog(LOG_INFO, "WSServer::start: io thread exited");
}));
switcher->serverStatus = ServerStatus::RUNNING;
blog(LOG_INFO,
"WSServer::start: server started successfully on port %d",
_serverPort);
}
void WSServer::stop()
{
if (!_server.is_listening()) {
return;
}
_server.stop_listening();
for (connection_hdl hdl : _connections) {
websocketpp::lib::error_code ec;
_server.close(hdl, websocketpp::close::status::going_away,
"Server stopping", ec);
}
_threadPool.waitForDone();
while (_connections.size() > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
switcher->serverStatus = ServerStatus::NOT_RUNNING;
blog(LOG_INFO, "server stopped successfully");
}
void WSServer::sendMessage(SceneSwitchInfo sceneSwitch, bool preview)
{
if (!sceneSwitch.scene) {
return;
}
OBSData data = obs_data_create();
obs_data_set_string(data, SCENE_ENTRY,
GetWeakSourceName(sceneSwitch.scene).c_str());
obs_data_set_string(data, TRANSITION_ENTRY,
GetWeakSourceName(sceneSwitch.transition).c_str());
obs_data_set_int(data, TRANSITION_DURATION, sceneSwitch.duration);
obs_data_set_bool(data, SET_PREVIEW, preview);
std::string message = obs_data_get_json(data);
obs_data_release(data);
for (connection_hdl hdl : _connections) {
websocketpp::lib::error_code ec;
_server.send(hdl, message, websocketpp::frame::opcode::text,
ec);
if (ec) {
std::string errorCodeMessage = ec.message();
blog(LOG_INFO, "server: send failed: %s",
errorCodeMessage.c_str());
}
}
if (VerboseLoggingEnabled()) {
blog(LOG_INFO, "server sent message:\n%s", message.c_str());
}
}
void WSServer::onOpen(connection_hdl hdl)
{
{
std::lock_guard<std::recursive_mutex> lock(_clMutex);
_connections.insert(hdl);
}
QString clientIp = getRemoteEndpoint(hdl);
blog(LOG_INFO, "new client connection from %s",
clientIp.toUtf8().constData());
}
std::string processMessage(std::string payload)
{
auto config = switcher->networkConfig;
std::string msgContainer(payload);
const char *msg = msgContainer.c_str();
OBSData data = obs_data_create_from_json(msg);
if (!data) {
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
return "invalid JSON payload";
}
if (!obs_data_has_user_value(data, SCENE_ENTRY) ||
!obs_data_has_user_value(data, TRANSITION_ENTRY) ||
!obs_data_has_user_value(data, TRANSITION_DURATION) ||
!obs_data_has_user_value(data, SET_PREVIEW)) {
return "missing request parameters";
}
std::string sceneName = obs_data_get_string(data, SCENE_ENTRY);
std::string transitionName =
obs_data_get_string(data, TRANSITION_ENTRY);
int duration = obs_data_get_int(data, TRANSITION_DURATION);
bool preview = obs_data_get_bool(data, SET_PREVIEW);
obs_data_release(data);
auto scene = GetWeakSourceByName(sceneName.c_str());
if (!scene) {
return "ignoring request - unknown scene '" + sceneName + "'";
}
std::string ret = "message ok";
auto transition = GetWeakTransitionByName(transitionName.c_str());
if (VerboseLoggingEnabled() && !transition) {
ret += " - ignoring invalid transition: '" + transitionName +
"'";
}
if (preview) {
SwitchPreviewScene(scene);
} else {
SwitchScene({scene, transition, duration});
}
return ret;
}
void WSServer::onMessage(connection_hdl, server::message_ptr message)
{
auto opcode = message->get_opcode();
if (opcode != websocketpp::frame::opcode::text) {
return;
}
_threadPool.start(Compatability::CreateFunctionRunnable([=]() {
if (message->get_payload() != "message ok") {
blog(LOG_WARNING, "received response: %s",
message->get_payload().c_str());
}
}));
}
void WSServer::onClose(connection_hdl hdl)
{
{
std::lock_guard<std::recursive_mutex> lock(_clMutex);
_connections.erase(hdl);
}
auto conn = _server.get_con_from_hdl(hdl);
auto localCloseCode = conn->get_local_close_code();
if (localCloseCode != websocketpp::close::status::going_away) {
QString clientIp = getRemoteEndpoint(hdl);
blog(LOG_INFO, "client %s disconnected",
clientIp.toUtf8().constData());
}
}
QString WSServer::getRemoteEndpoint(connection_hdl hdl)
{
auto conn = _server.get_con_from_hdl(hdl);
return QString::fromStdString(conn->get_remote_endpoint());
}
WSClient::WSClient() : QObject(nullptr)
{
_client.get_alog().clear_channels(
websocketpp::log::alevel::frame_header |
websocketpp::log::alevel::frame_payload |
websocketpp::log::alevel::control);
_client.init_asio();
#ifndef _WIN32
_client.set_reuse_addr(true);
#endif
_client.set_open_handler(bind(&WSClient::onOpen, this, _1));
_client.set_fail_handler(bind(&WSClient::onFail, this, _1));
_client.set_message_handler(bind(&WSClient::onMessage, this, _1, _2));
_client.set_close_handler(bind(&WSClient::onClose, this, _1));
}
WSClient::~WSClient()
{
disconnect();
}
void WSClient::connectThread()
{
while (_retry) {
_client.reset();
switcher->clientStatus = ClientStatus::CONNECTING;
// Create a connection to the given URI and queue it for connection once
// the event loop starts
websocketpp::lib::error_code ec;
client::connection_ptr con = _client.get_connection(_uri, ec);
if (ec) {
_failMsg = ec.message();
blog(LOG_INFO, "client: connect failed: %s",
_failMsg.c_str());
switcher->clientStatus = ClientStatus::FAIL;
} else {
_client.connect(con);
_connection = connection_hdl(con);
// Start the ASIO io_service run loop
blog(LOG_INFO, "WSClient::connect: io thread started");
_connected = true;
_client.run();
_connected = false;
blog(LOG_INFO, "WSClient::connect: io thread exited");
}
if (_retry) {
std::unique_lock<std::mutex> lck(_waitMtx);
blog(LOG_INFO,
"trying to reconnect to %s in %d seconds.",
_uri.c_str(), RECONNECT_DELAY);
_cv.wait_for(lck,
std::chrono::seconds(RECONNECT_DELAY));
}
}
}
void WSClient::connect(std::string uri)
{
disconnect();
_uri = uri;
_retry = true;
_thread = std::thread(&WSClient::connectThread, this);
switcher->clientStatus = ClientStatus::DISCONNECTED;
blog(LOG_INFO, "WSClient::connect: exited");
}
void WSClient::disconnect()
{
_retry = false;
websocketpp::lib::error_code ec;
_client.close(_connection, websocketpp::close::status::normal,
"Client stopping", ec);
{
std::unique_lock<std::mutex> waitLck(_waitMtx);
blog(LOG_INFO, "trying to reconnect to %s in %d seconds.",
_uri.c_str(), RECONNECT_DELAY);
_cv.notify_all();
}
while (_connected) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
_client.close(_connection, websocketpp::close::status::normal,
"Client stopping", ec);
}
if (_thread.joinable()) {
_thread.join();
}
}
void WSClient::onOpen(connection_hdl)
{
blog(LOG_INFO, "connection to %s opened", _uri.c_str());
switcher->clientStatus = ClientStatus::CONNECTED;
}
void WSClient::onFail(connection_hdl)
{
blog(LOG_INFO, "connection to %s failed", _uri.c_str());
}
void WSClient::onMessage(connection_hdl hdl, client::message_ptr message)
{
auto opcode = message->get_opcode();
if (opcode != websocketpp::frame::opcode::text) {
return;
}
std::string payload = message->get_payload();
std::string response = processMessage(payload);
websocketpp::lib::error_code errorCode;
_client.send(hdl, response, websocketpp::frame::opcode::text,
errorCode);
if (errorCode) {
std::string errorCodeMessage = errorCode.message();
blog(LOG_INFO, "client(response): send failed: %s",
errorCodeMessage.c_str());
}
if (VerboseLoggingEnabled()) {
blog(LOG_INFO, "client sent message:\n%s", response.c_str());
}
}
void WSClient::onClose(connection_hdl)
{
blog(LOG_INFO, "client-connection to %s closed.", _uri.c_str());
switcher->clientStatus = ClientStatus::DISCONNECTED;
}
void SwitcherData::loadNetworkSettings(obs_data_t *obj)
{
networkConfig.Load(obj);
}
void SwitcherData::saveNetworkSwitches(obs_data_t *obj)
{
networkConfig.Save(obj);
if (!networkConfig.ServerEnabled) {
server.stop();
}
}
void AdvSceneSwitcher::SetupNetworkTab()
{
ui->serverSettings->setChecked(switcher->networkConfig.ServerEnabled);
ui->serverPort->setValue(switcher->networkConfig.ServerPort);
ui->lockToIPv4->setChecked(switcher->networkConfig.LockToIPv4);
ui->clientSettings->setChecked(switcher->networkConfig.ClientEnabled);
ui->clientHostname->setText(switcher->networkConfig.Address.c_str());
ui->clientPort->setValue(switcher->networkConfig.ClientPort);
ui->sendSceneChange->setChecked(
switcher->networkConfig.SendSceneChange);
ui->restrictSend->setChecked(
!switcher->networkConfig.SendSceneChangeAll);
ui->sendPreview->setChecked(switcher->networkConfig.SendPreview);
ui->restrictSend->setDisabled(!switcher->networkConfig.SendSceneChange);
QTimer *statusTimer = new QTimer(this);
connect(statusTimer, SIGNAL(timeout()), this,
SLOT(UpdateClientStatus()));
connect(statusTimer, SIGNAL(timeout()), this,
SLOT(UpdateServerStatus()));
statusTimer->start(500);
}
void AdvSceneSwitcher::on_serverSettings_toggled(bool on)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.ServerEnabled = on;
if (on) {
switcher->server.start(switcher->networkConfig.ServerPort,
switcher->networkConfig.LockToIPv4);
} else {
switcher->server.stop();
}
}
void AdvSceneSwitcher::on_serverPort_valueChanged(int value)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.ServerPort = value;
}
void AdvSceneSwitcher::on_lockToIPv4_stateChanged(int state)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.LockToIPv4 = state;
}
void AdvSceneSwitcher::on_serverRestart_clicked()
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->server.start(switcher->networkConfig.ServerPort,
switcher->networkConfig.LockToIPv4);
}
void AdvSceneSwitcher::UpdateServerStatus()
{
switch (switcher->serverStatus) {
case ServerStatus::NOT_RUNNING:
ui->serverStatus->setText(obs_module_text(
"AdvSceneSwitcher.networkTab.server.status.notRunning"));
break;
case ServerStatus::STARTING:
ui->serverStatus->setText(obs_module_text(
"AdvSceneSwitcher.networkTab.server.status.starting"));
break;
case ServerStatus::RUNNING:
ui->serverStatus->setText(obs_module_text(
"AdvSceneSwitcher.networkTab.server.status.running"));
break;
default:
break;
}
}
void AdvSceneSwitcher::on_clientSettings_toggled(bool on)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.ClientEnabled = on;
if (on) {
switcher->client.connect(
switcher->networkConfig.GetClientUri());
} else {
switcher->client.disconnect();
}
}
void AdvSceneSwitcher::on_clientHostname_textChanged(const QString &text)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.Address = text.toUtf8().constData();
}
void AdvSceneSwitcher::on_clientPort_valueChanged(int value)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.ClientPort = value;
}
void AdvSceneSwitcher::on_sendSceneChange_stateChanged(int state)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.SendSceneChange = state;
ui->restrictSend->setDisabled(!state);
}
void AdvSceneSwitcher::on_restrictSend_stateChanged(int state)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.SendSceneChangeAll = !state;
}
void AdvSceneSwitcher::on_sendPreview_stateChanged(int state)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.SendPreview = state;
}
void AdvSceneSwitcher::on_clientReconnect_clicked()
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->client.connect(switcher->networkConfig.GetClientUri());
}
void AdvSceneSwitcher::UpdateClientStatus()
{
switch (switcher->clientStatus) {
case ClientStatus::DISCONNECTED:
ui->clientStatus->setText(obs_module_text(
"AdvSceneSwitcher.networkTab.client.status.disconnected"));
break;
case ClientStatus::CONNECTING:
ui->clientStatus->setText(obs_module_text(
"AdvSceneSwitcher.networkTab.client.status.connecting"));
break;
case ClientStatus::CONNECTED:
ui->clientStatus->setText(obs_module_text(
"AdvSceneSwitcher.networkTab.client.status.connected"));
break;
case ClientStatus::FAIL:
ui->clientStatus->setText(QString("Error: ") +
switcher->client.getFail().c_str());
break;
default:
break;
}
}
void Compatability::StdFunctionRunnable::run()
{
cb();
}
QRunnable *Compatability::CreateFunctionRunnable(std::function<void()> func)
{
return new Compatability::StdFunctionRunnable(std::move(func));
}
Compatability::StdFunctionRunnable::StdFunctionRunnable(
std::function<void()> func)
: cb(std::move(func))
{
}
} // namespace advss

View File

@@ -1,137 +0,0 @@
/*
Most of this code is based on https://github.com/Palakis/obs-websocket
*/
#pragma once
#include <set>
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#include <QtCore/QVariantHash>
#include <QtCore/QThreadPool>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <QRunnable>
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <websocketpp/client.hpp>
namespace advss {
using websocketpp::connection_hdl;
typedef websocketpp::server<websocketpp::config::asio> server;
typedef websocketpp::client<websocketpp::config::asio_client> client;
struct SceneSwitchInfo;
class NetworkConfig {
public:
NetworkConfig();
void Load(obs_data_t *obj);
void Save(obs_data_t *obj);
void SetDefaults(obs_data_t *obj);
std::string GetClientUri();
bool ShouldSendSceneChange();
bool ShouldSendFrontendSceneChange();
bool ShouldSendPrviewSceneChange();
// Server
bool ServerEnabled;
uint64_t ServerPort;
bool LockToIPv4;
// Client
bool ClientEnabled;
std::string Address;
uint64_t ClientPort;
bool SendSceneChange;
bool SendSceneChangeAll;
bool SendPreview;
};
class WSServer : public QObject {
Q_OBJECT
public:
explicit WSServer();
virtual ~WSServer();
void start(quint16 port, bool lockToIPv4);
void stop();
void sendMessage(SceneSwitchInfo sceneSwitch, bool preview = false);
QThreadPool *threadPool() { return &_threadPool; }
private:
void onOpen(connection_hdl hdl);
void onMessage(connection_hdl hdl, server::message_ptr message);
void onClose(connection_hdl hdl);
QString getRemoteEndpoint(connection_hdl hdl);
server _server;
quint16 _serverPort = 55555;
bool _lockToIPv4 = false;
std::set<connection_hdl, std::owner_less<connection_hdl>> _connections;
std::recursive_mutex _clMutex;
QThreadPool _threadPool;
};
enum class ServerStatus {
NOT_RUNNING,
STARTING,
RUNNING,
};
class WSClient : public QObject {
Q_OBJECT
public:
explicit WSClient();
virtual ~WSClient();
void connect(std::string uri);
void disconnect();
std::string getFail() { return _failMsg; }
private:
void onOpen(connection_hdl hdl);
void onFail(connection_hdl hdl);
void onMessage(connection_hdl hdl, client::message_ptr message);
void onClose(connection_hdl hdl);
void connectThread();
client _client;
std::string _uri;
connection_hdl _connection;
std::thread _thread;
bool _retry = false;
std::atomic_bool _connected = {false};
std::mutex _waitMtx;
std::condition_variable _cv;
std::string _failMsg;
};
enum class ClientStatus {
DISCONNECTED,
CONNECTING,
CONNECTED,
FAIL,
};
namespace Compatability {
// Reimplement QRunnable for std::function. Retrocompatability for Qt < 5.15
class StdFunctionRunnable : public QRunnable {
std::function<void()> cb;
public:
StdFunctionRunnable(std::function<void()> func);
void run() override;
};
QRunnable *CreateFunctionRunnable(std::function<void()> func);
} // namespace Compatability
} // namespace advss

View File

@@ -20,7 +20,7 @@ void AdvSceneSwitcher::on_pauseAdd_clicked()
listAddClicked(ui->pauseEntries,
new PauseEntryWidget(this,
&switcher->pauseEntries.back()),
addPulse);
&addPulse);
ui->pauseHelp->setVisible(false);
}

View File

@@ -19,7 +19,7 @@ void AdvSceneSwitcher::on_randomAdd_clicked()
listAddClicked(ui->randomSwitches,
new RandomSwitchWidget(this,
&switcher->randomSwitches.back()),
addPulse);
&addPulse);
ui->randomHelp->setVisible(false);
}
@@ -143,7 +143,7 @@ void AdvSceneSwitcher::SetupRandomTab()
border-style: outset; \
border-width: 2px; \
border-radius: 7px; \
border-color: rgb(0,0,0,0) \
border-color: rgb(0,0,0) \
}");
if (switcher->switchIfNotMatching != NoMatchBehavior::RANDOM_SWITCH) {
if (!switcher->disableHints) {

View File

@@ -79,7 +79,7 @@ void AdvSceneSwitcher::on_screenRegionAdd_clicked()
listAddClicked(ui->screenRegionSwitches,
new ScreenRegionWidget(
this, &switcher->screenRegionSwitches.back()),
addPulse);
&addPulse);
ui->regionHelp->setVisible(false);
}

View File

@@ -26,7 +26,7 @@ void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
listAddClicked(ui->sceneSequenceSwitches,
new SequenceWidget(
this, &switcher->sceneSequenceSwitches.back()),
addPulse);
&addPulse);
ui->sequenceHelp->setVisible(false);
}
@@ -585,6 +585,8 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
QVariant(QStringLiteral("addIconSmall")));
reduce->setProperty("themeID",
QVariant(QStringLiteral("removeIconSmall")));
extend->setProperty("class", QVariant(QStringLiteral("icon-plus")));
reduce->setProperty("class", QVariant(QStringLiteral("icon-trash")));
extend->setMaximumSize(22, 22);
reduce->setMaximumSize(22, 22);

View File

@@ -17,7 +17,7 @@ void AdvSceneSwitcher::on_timeAdd_clicked()
listAddClicked(ui->timeSwitches,
new TimeSwitchWidget(this,
&switcher->timeSwitches.back()),
addPulse);
&addPulse);
ui->timeHelp->setVisible(false);
}

View File

@@ -24,7 +24,7 @@ void AdvSceneSwitcher::on_videoAdd_clicked()
VideoSwitchWidget *sw =
new VideoSwitchWidget(this, &switcher->videoSwitches.back());
listAddClicked(ui->videoSwitches, sw, addPulse);
listAddClicked(ui->videoSwitches, sw, &addPulse);
ui->videoHelp->setVisible(false);
}
@@ -105,7 +105,7 @@ void AdvSceneSwitcher::on_getScreenshot_clicked()
}
auto source = obs_weak_source_get_source(s->videoSource);
auto screenshotData = std::make_unique<ScreenshotHelper>(source);
auto screenshotData = std::make_unique<Screenshot>(source);
obs_source_release(source);
QString filePath = QFileDialog::getSaveFileName(this);
@@ -121,16 +121,16 @@ void AdvSceneSwitcher::on_getScreenshot_clicked()
// During selection of the save path enough time should usually have
// passed already
// Add this just in case ...
if (!screenshotData->done) {
if (!screenshotData->IsDone()) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (!screenshotData->done) {
if (!screenshotData->IsDone()) {
DisplayMessage("Failed to get screenshot of source!");
return;
}
screenshotData->image.save(file.fileName());
screenshotData->GetImage().save(file.fileName());
sw->SetFilePath(file.fileName());
}
@@ -262,7 +262,7 @@ void VideoSwitch::load(obs_data_t *obj)
void VideoSwitch::getScreenshot()
{
auto source = obs_weak_source_get_source(videoSource);
screenshotData = std::make_unique<ScreenshotHelper>(source);
screenshotData = std::make_unique<Screenshot>(source);
obs_source_release(source);
}
@@ -293,56 +293,55 @@ bool VideoSwitch::checkMatch()
bool match = false;
if (screenshotData) {
if (screenshotData->done) {
bool conditionMatch = false;
switch (condition) {
case videoSwitchType::MATCH:
conditionMatch = screenshotData->image ==
matchImage;
break;
case videoSwitchType::DIFFER:
conditionMatch = screenshotData->image !=
matchImage;
break;
case videoSwitchType::HAS_NOT_CHANGED:
conditionMatch = screenshotData->image ==
matchImage;
break;
case videoSwitchType::HAS_CHANGED:
conditionMatch = screenshotData->image !=
matchImage;
break;
default:
break;
}
if (conditionMatch) {
currentMatchDuration +=
std::chrono::duration_cast<
std::chrono::milliseconds>(
screenshotData->time -
previousTime);
} else {
currentMatchDuration = {};
}
bool durationMatch = currentMatchDuration.count() >=
duration * 1000;
if (conditionMatch && durationMatch) {
match = true;
}
if (!requiresFileInput(condition)) {
matchImage = std::move(screenshotData->image);
}
previousTime = std::move(screenshotData->time);
screenshotData.reset(nullptr);
}
if (!screenshotData) {
getScreenshot();
return match;
}
if (!screenshotData->IsDone()) {
getScreenshot();
return match;
}
bool conditionMatch = false;
switch (condition) {
case videoSwitchType::MATCH:
conditionMatch = screenshotData->GetImage() == matchImage;
break;
case videoSwitchType::DIFFER:
conditionMatch = screenshotData->GetImage() != matchImage;
break;
case videoSwitchType::HAS_NOT_CHANGED:
conditionMatch = screenshotData->GetImage() == matchImage;
break;
case videoSwitchType::HAS_CHANGED:
conditionMatch = screenshotData->GetImage() != matchImage;
break;
default:
break;
}
if (conditionMatch) {
currentMatchDuration +=
std::chrono::duration_cast<std::chrono::milliseconds>(
screenshotData->GetScreenshotTime() -
previousTime);
} else {
currentMatchDuration = {};
}
bool durationMatch = currentMatchDuration.count() >= duration * 1000;
if (conditionMatch && durationMatch) {
match = true;
}
if (!requiresFileInput(condition)) {
matchImage = screenshotData->GetImage();
}
previousTime = screenshotData->GetScreenshotTime();
screenshotData.reset(nullptr);
getScreenshot();
return match;

View File

@@ -32,7 +32,7 @@ struct VideoSwitch : virtual SceneSwitcherEntry {
double duration = 0;
bool ignoreInactiveSource = false;
std::unique_ptr<ScreenshotHelper> screenshotData = nullptr;
std::unique_ptr<Screenshot> screenshotData = nullptr;
std::chrono::high_resolution_clock::time_point previousTime{};
QImage matchImage;

View File

@@ -21,7 +21,7 @@ void AdvSceneSwitcher::on_windowAdd_clicked()
listAddClicked(ui->windowSwitches,
new WindowSwitchWidget(this,
&switcher->windowSwitches.back()),
addPulse);
&addPulse);
ui->windowHelp->setVisible(false);
}

View File

@@ -384,7 +384,11 @@ static void getProcessListProcps2(QStringList &processes)
return;
}
while ((stack = procps_pids_get_(info, PIDS_FETCH_TASKS_ONLY))) {
#ifdef PROCPS2_USE_INFO
auto cmd = PIDS_VAL(0, str, stack, info);
#else
auto cmd = PIDS_VAL(0, str, stack);
#endif
QString procName(cmd);
if (!procName.isEmpty() && !processes.contains(procName)) {
processes << procName;
@@ -546,6 +550,11 @@ static void initProc2()
#endif
}
int ignoreXerror(Display *d, XErrorEvent *e)
{
return 0;
}
void PlatformInit()
{
auto display = disp();
@@ -556,6 +565,7 @@ void PlatformInit()
initXss();
initProcps();
initProc2();
XSetErrorHandler(ignoreXerror);
}
static void cleanupHelper(QLibrary *lib)
@@ -572,6 +582,7 @@ void PlatformCleanup()
cleanupHelper(libprocps);
cleanupHelper(libproc2);
cleanupDisplay();
XSetErrorHandler(NULL);
}
} // namespace advss

View File

@@ -7,8 +7,6 @@
#include "section.hpp"
#include "switch-button.hpp"
#include <QGraphicsOpacityEffect>
namespace advss {
static inline void populateActionSelection(QComboBox *list)
@@ -17,6 +15,9 @@ static inline void populateActionSelection(QComboBox *list)
QString entry(obs_module_text(action._name.c_str()));
if (list->findText(entry) == -1) {
list->addItem(entry);
qobject_cast<QListView *>(list->view())
->setRowHidden(list->count() - 1,
action._hidden);
} else {
blog(LOG_WARNING,
"did not insert duplicate action entry with name \"%s\"",
@@ -34,12 +35,14 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
_enable(new SwitchButton()),
_entryData(entryData)
{
auto actionStateTimer = new QTimer(this);
QWidget::connect(_actionSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ActionSelectionChanged(const QString &)));
QWidget::connect(_enable, SIGNAL(checked(bool)), this,
SLOT(ActionEnableChanged(bool)));
QWidget::connect(&_actionStateTimer, SIGNAL(timeout()), this,
QWidget::connect(actionStateTimer, SIGNAL(timeout()), this,
SLOT(UpdateActionState()));
populateActionSelection(_actionSelection);
@@ -60,12 +63,40 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData(id);
SetupWidgets(true);
_actionStateTimer.start(300);
actionStateTimer->start(300);
_loading = false;
}
void MacroActionEdit::SetupWidgets(bool basicSetup)
{
if (_allWidgetsAreSetup) {
return;
}
const auto id = (*_entryData)->GetId();
_actionSelection->setCurrentText(
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
const bool enabled = (*_entryData)->Enabled();
_enable->setChecked(enabled);
SetDisableEffect(!enabled);
HeaderInfoChanged(
QString::fromStdString((*_entryData)->GetShortDesc()));
if (basicSetup) {
return;
}
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
_section->SetContent(widget, (*_entryData)->GetCollapsed());
SetFocusPolicyOfWidgets();
_allWidgetsAreSetup = true;
}
void MacroActionEdit::ActionSelectionChanged(const QString &text)
{
if (_loading || !_entryData) {
@@ -95,38 +126,11 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
SetFocusPolicyOfWidgets();
}
void MacroActionEdit::UpdateEntryData(const std::string &id)
{
_actionSelection->setCurrentText(
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
const bool enabled = (*_entryData)->Enabled();
_enable->setChecked(enabled);
SetDisableEffect(!enabled);
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
HeaderInfoChanged(
QString::fromStdString((*_entryData)->GetShortDesc()));
_section->SetContent(widget, (*_entryData)->GetCollapsed());
SetFocusPolicyOfWidgets();
}
void MacroActionEdit::SetEntryData(std::shared_ptr<MacroAction> *data)
{
_entryData = data;
}
void MacroActionEdit::SetDisableEffect(bool value)
{
if (value) {
auto effect = new QGraphicsOpacityEffect(this);
effect->setOpacity(0.5);
_section->setGraphicsEffect(effect);
} else {
_section->setGraphicsEffect(nullptr);
}
}
void MacroActionEdit::ActionEnableChanged(bool value)
{
if (_loading || !_entryData) {
@@ -144,13 +148,9 @@ void MacroActionEdit::UpdateActionState()
return;
}
SetEnableAppearance((*_entryData)->Enabled());
}
void MacroActionEdit::SetEnableAppearance(bool value)
{
_enable->setChecked(value);
SetDisableEffect(!value);
const bool enabled = (*_entryData)->Enabled();
SetEnableAppearance(enabled);
_enable->setChecked(enabled);
}
std::shared_ptr<MacroSegment> MacroActionEdit::Data() const

View File

@@ -17,7 +17,7 @@ public:
QWidget *parent = nullptr,
std::shared_ptr<MacroAction> * = nullptr,
const std::string &id = MacroAction::GetDefaultID().data());
void UpdateEntryData(const std::string &id);
void SetupWidgets(bool basicSetup = false);
void SetEntryData(std::shared_ptr<MacroAction> *);
private slots:
@@ -27,14 +27,11 @@ private slots:
private:
std::shared_ptr<MacroSegment> Data() const;
void SetDisableEffect(bool);
void SetEnableAppearance(bool);
FilterComboBox *_actionSelection;
SwitchButton *_enable;
std::shared_ptr<MacroAction> *_entryData;
QTimer _actionStateTimer;
bool _loading = true;
};

View File

@@ -21,7 +21,7 @@ public:
} // namespace
static std::mutex mutex;
static std::recursive_mutex mutex;
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
{
@@ -31,7 +31,7 @@ std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info;
return true;
@@ -41,7 +41,7 @@ bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
bool MacroActionFactory::Deregister(const std::string &id)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
if (GetMap().count(id) == 0) {
return false;
}
@@ -57,7 +57,7 @@ static std::shared_ptr<MacroAction> createUnknownAction(Macro *m)
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
Macro *m)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._create(m);
}
@@ -74,7 +74,7 @@ QWidget *MacroActionFactory::CreateWidget(const std::string &id,
QWidget *parent,
std::shared_ptr<MacroAction> action)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._createWidget(parent, action);
}
@@ -84,7 +84,7 @@ QWidget *MacroActionFactory::CreateWidget(const std::string &id,
std::string MacroActionFactory::GetActionName(const std::string &id)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name;
}
@@ -93,7 +93,7 @@ std::string MacroActionFactory::GetActionName(const std::string &id)
std::string MacroActionFactory::GetIdByName(const QString &name)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;

View File

@@ -12,6 +12,7 @@ struct MacroActionInfo {
std::function<std::shared_ptr<MacroAction>(Macro *m)> _create = nullptr;
CreateActionWidget _createWidget = nullptr;
std::string _name;
bool _hidden = false;
};
class MacroActionFactory {
@@ -19,7 +20,7 @@ public:
MacroActionFactory() = delete;
EXPORT static bool Register(const std::string &id, MacroActionInfo);
static bool Deregister(const std::string &id);
EXPORT static bool Deregister(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,

View File

@@ -12,25 +12,6 @@ bool MacroActionMacro::_registered = MacroActionFactory::Register(
{MacroActionMacro::Create, MacroActionMacroEdit::Create,
"AdvSceneSwitcher.action.macro"});
const static std::map<MacroActionMacro::Action, std::string> actionTypes = {
{MacroActionMacro::Action::PAUSE,
"AdvSceneSwitcher.action.macro.type.pause"},
{MacroActionMacro::Action::UNPAUSE,
"AdvSceneSwitcher.action.macro.type.unpause"},
{MacroActionMacro::Action::RESET_COUNTER,
"AdvSceneSwitcher.action.macro.type.resetCounter"},
{MacroActionMacro::Action::RUN,
"AdvSceneSwitcher.action.macro.type.run"},
{MacroActionMacro::Action::STOP,
"AdvSceneSwitcher.action.macro.type.stop"},
{MacroActionMacro::Action::DISABLE_ACTION,
"AdvSceneSwitcher.action.macro.type.disableAction"},
{MacroActionMacro::Action::ENABLE_ACTION,
"AdvSceneSwitcher.action.macro.type.enableAction"},
{MacroActionMacro::Action::TOGGLE_ACTION,
"AdvSceneSwitcher.action.macro.type.toggleAction"},
};
bool MacroActionMacro::PerformAction()
{
auto macro = _macro.GetMacro();
@@ -45,6 +26,9 @@ bool MacroActionMacro::PerformAction()
case Action::UNPAUSE:
macro->SetPaused(false);
break;
case Action::TOGGLE_PAUSE:
macro->SetPaused(!macro->Paused());
break;
case Action::RESET_COUNTER:
macro->ResetRunCount();
break;
@@ -196,13 +180,13 @@ void MacroActionMacro::RunActions(Macro *actionMacro) const
}
if (_runOptions.reevaluateConditionState) {
conditionMacro->CeckMatch(true);
conditionMacro->CheckConditions(true);
}
if ((_runOptions.logic == RunOptions::Logic::CONDITIONS &&
conditionMacro->Matched()) ||
conditionMacro->ConditionsMatched()) ||
(_runOptions.logic == RunOptions::Logic::INVERT_CONDITIONS &&
!conditionMacro->Matched())) {
!conditionMacro->ConditionsMatched())) {
runActionsHelper(actionMacro, _runOptions.runElseActions,
_runOptions.setInputs, _runOptions.inputs);
}
@@ -210,8 +194,31 @@ void MacroActionMacro::RunActions(Macro *actionMacro) const
static void populateActionSelection(QComboBox *list)
{
for (const auto &[_, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()));
static const std::vector<std::pair<MacroActionMacro::Action, std::string>>
actions = {
{MacroActionMacro::Action::PAUSE,
"AdvSceneSwitcher.action.macro.type.pause"},
{MacroActionMacro::Action::UNPAUSE,
"AdvSceneSwitcher.action.macro.type.unpause"},
{MacroActionMacro::Action::TOGGLE_PAUSE,
"AdvSceneSwitcher.action.macro.type.togglePause"},
{MacroActionMacro::Action::RESET_COUNTER,
"AdvSceneSwitcher.action.macro.type.resetCounter"},
{MacroActionMacro::Action::RUN,
"AdvSceneSwitcher.action.macro.type.run"},
{MacroActionMacro::Action::STOP,
"AdvSceneSwitcher.action.macro.type.stop"},
{MacroActionMacro::Action::DISABLE_ACTION,
"AdvSceneSwitcher.action.macro.type.disableAction"},
{MacroActionMacro::Action::ENABLE_ACTION,
"AdvSceneSwitcher.action.macro.type.enableAction"},
{MacroActionMacro::Action::TOGGLE_ACTION,
"AdvSceneSwitcher.action.macro.type.toggleAction"},
};
for (const auto &[value, name] : actions) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(value));
}
}
@@ -309,11 +316,11 @@ MacroActionMacroEdit::MacroActionMacroEdit(
_loading = false;
}
void HighligthMacroSettingsButton(bool enable);
void HighlightMacroSettingsButton(bool enable);
MacroActionMacroEdit::~MacroActionMacroEdit()
{
HighligthMacroSettingsButton(false);
HighlightMacroSettingsButton(false);
}
void MacroActionMacroEdit::UpdateEntryData()
@@ -321,7 +328,8 @@ void MacroActionMacroEdit::UpdateEntryData()
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_actions->setCurrentIndex(
_actions->findData(static_cast<int>(_entryData->_action)));
_actionIndex->SetValue(_entryData->_actionIndex);
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
_macros->SetCurrentMacro(_entryData->_macro);
@@ -350,10 +358,11 @@ void MacroActionMacroEdit::MacroChanged(const QString &text)
SetWidgetVisibility();
}
void MacroActionMacroEdit::ActionChanged(int value)
void MacroActionMacroEdit::ActionChanged(int idx)
{
GUARD_LOADING_AND_LOCK();
_entryData->_action = static_cast<MacroActionMacro::Action>(value);
_entryData->_action = static_cast<MacroActionMacro::Action>(
_actions->itemData(idx).toInt());
SetWidgetVisibility();
}
@@ -480,7 +489,7 @@ void MacroActionMacroEdit::SetWidgetVisibility()
_inputs->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN &&
_entryData->_runOptions.setInputs);
HighligthMacroSettingsButton(_entryData->_action ==
HighlightMacroSettingsButton(_entryData->_action ==
MacroActionMacro::Action::RUN &&
_entryData->_runOptions.setInputs &&
!_inputs->HasInputsToSet());

View File

@@ -49,6 +49,7 @@ public:
DISABLE_ACTION,
ENABLE_ACTION,
TOGGLE_ACTION,
TOGGLE_PAUSE,
};
Action _action = Action::RUN;
IntVariable _actionIndex = 1;

File diff suppressed because it is too large Load Diff

View File

@@ -28,8 +28,8 @@ public:
int GetSegmentIndexValue() const;
void ResolveVariablesToFixedValues();
enum class Type {
SET_FIXED_VALUE,
enum class Action {
SET_VALUE,
APPEND,
APPEND_VAR,
INCREMENT,
@@ -50,18 +50,23 @@ public:
PAD,
TRUNCATE,
SWAP_VALUES,
TRIM,
CHANGE_CASE,
RANDOM_NUMBER,
QUERY_JSON,
ARRAY_JSON,
};
Type _type = Type::SET_FIXED_VALUE;
Action _action = Action::SET_VALUE;
std::weak_ptr<Variable> _variable;
std::weak_ptr<Variable> _variable2;
StringVariable _strValue = "";
double _numValue = 0;
int _subStringStart = 0;
int _subStringSize = 0;
DoubleVariable _numValue = 0;
IntVariable _subStringStart = 0;
IntVariable _subStringSize = 0;
RegexConfig _subStringRegex = RegexConfig::PartialMatchRegexConfig();
std::string _regexPattern = ".*";
int _regexMatchIdx = 0;
IntVariable _regexMatchIdx = 0;
RegexConfig _findRegex;
StringVariable _findStr = obs_module_text(
"AdvSceneSwitcher.action.variable.findAndReplace.find");
@@ -89,13 +94,30 @@ public:
IntVariable _stringLength = 1;
char _paddingChar = '0';
enum class CaseType {
LOWER_CASE,
UPPER_CASE,
CAPITALIZED,
START_CASE,
};
CaseType _caseType = CaseType::LOWER_CASE;
DoubleVariable _randomNumberStart = 0;
DoubleVariable _randomNumberEnd = 100;
bool _generateInteger = true;
StringVariable _jsonQuery = "$.some.nested.value";
IntVariable _jsonIndex = 0;
private:
void DecrementCurrentSegmentVariableRef();
void HandleIndexSubString(Variable *);
void HandleRegexSubString(Variable *);
void HandleFindAndReplace(Variable *);
void HandleMathExpression(Variable *);
void HandleCaseChange(Variable *);
void SetToSceneItemName(Variable *);
void GenerateRandomNumber(Variable *);
std::weak_ptr<MacroSegment> _macroSegment;
int _segmentIdxLoadValue = -1;
@@ -124,15 +146,15 @@ private slots:
void Variable2Changed(const QString &);
void ActionChanged(int);
void StrValueChanged();
void NumValueChanged(double);
void NumValueChanged(const NumberVariable<double> &value);
void SegmentIndexChanged(const IntVariable &);
void UpdateSegmentVariableValue();
void MacroSegmentOrderChanged();
void SubStringStartChanged(int val);
void SubStringSizeChanged(int val);
void SubStringStartChanged(const NumberVariable<int> &start);
void SubStringSizeChanged(const NumberVariable<int> &size);
void SubStringRegexChanged(const RegexConfig &conf);
void RegexPatternChanged();
void RegexMatchIdxChanged(int val);
void RegexMatchIdxChanged(const NumberVariable<int> &index);
void FindStrValueChanged();
void FindRegexChanged(const RegexConfig &conf);
void ReplaceStrValueChanged();
@@ -148,6 +170,12 @@ private slots:
void DirectionChanged(int);
void StringLengthChanged(const NumberVariable<int> &);
void CharSelectionChanged(const QString &);
void CaseTypeChanged(int index);
void RandomNumberStartChanged(const NumberVariable<double> &);
void RandomNumberEndChanged(const NumberVariable<double> &);
void GenerateIntegerChanged(int);
void JsonQueryChanged();
void JsonIndexChanged(const NumberVariable<int> &);
signals:
void HeaderInfoChanged(const QString &);
@@ -160,18 +188,18 @@ private:
VariableSelection *_variables2;
FilterComboBox *_actions;
VariableTextEdit *_strValue;
QDoubleSpinBox *_numValue;
VariableDoubleSpinBox *_numValue;
MacroSegmentSelection *_segmentIdx;
QLabel *_segmentValueStatus;
ResizingPlainTextEdit *_segmentValue;
QVBoxLayout *_substringLayout;
QHBoxLayout *_subStringIndexEntryLayout;
QHBoxLayout *_subStringRegexEntryLayout;
QSpinBox *_subStringStart;
QSpinBox *_subStringSize;
VariableSpinBox *_subStringStart;
VariableSpinBox *_subStringSize;
RegexConfigWidget *_substringRegex;
ResizingPlainTextEdit *_regexPattern;
QSpinBox *_regexMatchIdx;
VariableSpinBox *_regexMatchIdx;
QHBoxLayout *_findReplaceLayout;
RegexConfigWidget *_findRegex;
VariableTextEdit *_findStr;
@@ -192,6 +220,14 @@ private:
QComboBox *_direction;
VariableSpinBox *_stringLength;
SingleCharSelection *_paddingCharSelection;
FilterComboBox *_caseType;
VariableDoubleSpinBox *_randomNumberStart;
VariableDoubleSpinBox *_randomNumberEnd;
QCheckBox *_generateInteger;
QVBoxLayout *_randomLayout;
VariableLineEdit *_jsonQuery;
QLabel *_jsonQueryHelp;
VariableSpinBox *_jsonIndex;
QHBoxLayout *_entryLayout;
std::shared_ptr<MacroActionVariable> _entryData;

View File

@@ -11,15 +11,12 @@ bool MacroAction::Save(obs_data_t *obj) const
{
MacroSegment::Save(obj);
obs_data_set_string(obj, "id", GetId().c_str());
obs_data_set_bool(obj, "enabled", _enabled);
return true;
}
bool MacroAction::Load(obs_data_t *obj)
{
MacroSegment::Load(obj);
obs_data_set_default_bool(obj, "enabled", true);
_enabled = obs_data_get_bool(obj, "enabled");
return true;
}
@@ -28,16 +25,6 @@ void MacroAction::LogAction() const
ablog(LOG_INFO, "performed action %s", GetId().c_str());
}
void MacroAction::SetEnabled(bool value)
{
_enabled = value;
}
bool MacroAction::Enabled() const
{
return _enabled;
}
void MacroAction::ResolveVariablesToFixedValues() {}
std::string_view MacroAction::GetDefaultID()

View File

@@ -19,13 +19,9 @@ public:
// Used to resolve variables before actions are added to action queues
virtual void ResolveVariablesToFixedValues();
void SetEnabled(bool);
bool Enabled() const;
static std::string_view GetDefaultID();
private:
bool _enabled = true;
};
class EXPORT MacroRefAction : virtual public MacroAction {

View File

@@ -138,7 +138,7 @@ MacroConditionEdit::MacroConditionEdit(
mainLayout->addWidget(_frame);
setLayout(mainLayout);
UpdateEntryData(id);
SetupWidgets(true);
_loading = false;
}
@@ -152,9 +152,11 @@ void MacroConditionEdit::LogicSelectionChanged(int idx)
const auto logic = static_cast<Logic::Type>(
_logicSelection->itemData(idx).toInt());
(*_entryData)->SetLogicType(logic);
SetEnableAppearance(logic != Logic::Type::NONE);
}
bool MacroConditionEdit::IsRootNode()
bool MacroConditionEdit::IsRootNode() const
{
return _isRoot;
}
@@ -164,6 +166,7 @@ void MacroConditionEdit::SetLogicSelection()
const auto logic = (*_entryData)->GetLogicType();
_logicSelection->setCurrentIndex(
_logicSelection->findData(static_cast<int>(logic)));
SetEnableAppearance(logic != Logic::Type::NONE);
}
void MacroConditionEdit::SetRootNode(bool root)
@@ -175,23 +178,36 @@ void MacroConditionEdit::SetRootNode(bool root)
SetLogicSelection();
}
void MacroConditionEdit::UpdateEntryData(const std::string &id)
void MacroConditionEdit::SetupWidgets(bool basicSetup)
{
if (_allWidgetsAreSetup) {
return;
}
const auto id = (*_entryData)->GetId();
_conditionSelection->setCurrentText(obs_module_text(
MacroConditionFactory::GetConditionName(id).c_str()));
auto widget =
MacroConditionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
HeaderInfoChanged(
QString::fromStdString((*_entryData)->GetShortDesc()));
SetLogicSelection();
_section->SetContent(widget, (*_entryData)->GetCollapsed());
_dur->setVisible(MacroConditionFactory::UsesDurationModifier(id));
auto modifier = (*_entryData)->GetDurationModifier();
_dur->SetValue(modifier);
if (basicSetup) {
return;
}
auto widget =
MacroConditionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
_section->SetContent(widget, (*_entryData)->GetCollapsed());
SetFocusPolicyOfWidgets();
_allWidgetsAreSetup = true;
}
void MacroConditionEdit::SetEntryData(std::shared_ptr<MacroCondition> *data)

View File

@@ -39,9 +39,9 @@ public:
std::shared_ptr<MacroCondition> * = nullptr,
const std::string &id = MacroCondition::GetDefaultID().data(),
bool root = true);
bool IsRootNode();
bool IsRootNode() const;
void SetRootNode(bool);
void UpdateEntryData(const std::string &id);
void SetupWidgets(bool basicSetup = false);
void SetEntryData(std::shared_ptr<MacroCondition> *);
private slots:

View File

@@ -17,7 +17,7 @@ public:
} // namespace
static std::mutex mutex;
static std::recursive_mutex mutex;
std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
{
@@ -28,7 +28,7 @@ std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
bool MacroConditionFactory::Register(const std::string &id,
MacroConditionInfo info)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info;
return true;
@@ -38,7 +38,7 @@ bool MacroConditionFactory::Register(const std::string &id,
bool MacroConditionFactory::Deregister(const std::string &id)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
if (GetMap().count(id) == 0) {
return false;
}
@@ -54,7 +54,7 @@ static std::shared_ptr<MacroCondition> createUnknownCondition(Macro *m)
std::shared_ptr<MacroCondition>
MacroConditionFactory::Create(const std::string &id, Macro *m)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._create(m);
}
@@ -71,7 +71,7 @@ QWidget *
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._createWidget(parent, cond);
}
@@ -80,7 +80,7 @@ MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
std::string MacroConditionFactory::GetConditionName(const std::string &id)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name;
}
@@ -89,7 +89,7 @@ std::string MacroConditionFactory::GetConditionName(const std::string &id)
std::string MacroConditionFactory::GetIdByName(const QString &name)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
@@ -100,7 +100,7 @@ std::string MacroConditionFactory::GetIdByName(const QString &name)
bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
{
std::lock_guard<std::mutex> lock(mutex);
std::lock_guard<std::recursive_mutex> lock(mutex);
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._useDurationModifier;
}

View File

@@ -20,7 +20,7 @@ class MacroConditionFactory {
public:
MacroConditionFactory() = delete;
EXPORT static bool Register(const std::string &, MacroConditionInfo);
static bool Deregister(const std::string &);
EXPORT static bool Deregister(const std::string &);
static std::shared_ptr<MacroCondition> Create(const std::string &,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,

View File

@@ -58,7 +58,7 @@ bool MacroConditionMacro::CheckStateCondition()
return false;
}
return macro->Matched();
return macro->ConditionsMatched();
}
bool MacroConditionMacro::CheckMultiStateCondition()
@@ -72,7 +72,7 @@ bool MacroConditionMacro::CheckMultiStateCondition()
if (!macro) {
continue;
}
if (macro->Matched()) {
if (macro->ConditionsMatched()) {
matchedCount++;
}
}

View File

@@ -9,7 +9,6 @@
#include <QPushButton>
#include <QHBoxLayout>
#include <QTimer>
#include <QListWidget>
namespace advss {

View File

@@ -27,6 +27,9 @@ bool MacroConditionQueue::CheckCondition()
return false;
}
SetTempVarValue("size", std::to_string(queue->Size()));
SetTempVarValue("running", queue->IsRunning());
switch (_condition) {
case Condition::STARTED:
return queue->IsRunning();
@@ -63,6 +66,18 @@ std::string MacroConditionQueue::GetShortDesc() const
return GetActionQueueName(_queue);
}
void MacroConditionQueue::SetupTempVars()
{
MacroCondition::SetupTempVars();
AddTempvar("size",
obs_module_text("AdvSceneSwitcher.tempVar.queue.size"));
AddTempvar(
"running",
obs_module_text("AdvSceneSwitcher.tempVar.queue.running"),
obs_module_text(
"AdvSceneSwitcher.tempVar.queue.running.description"));
}
static inline void populateQueueTypeSelection(QComboBox *list)
{
for (const auto &[_, name] : conditionTypes) {
@@ -141,6 +156,7 @@ void MacroConditionQueueEdit::UpdateEntryData()
}
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_queues->SetActionQueue(_entryData->_queue);
_size->SetValue(_entryData->_size);
SetWidgetVisibility();
}

View File

@@ -32,6 +32,8 @@ public:
IntVariable _size = 1;
private:
void SetupTempVars();
static bool _registered;
static const std::string id;
};

View File

@@ -138,8 +138,9 @@ void MacroDock::UpdateText()
_pauseToggle->setText(macro->Paused() ? _unpauseButtonText.c_str()
: _pauseButtonText.c_str());
_statusText->setText(macro->Matched() ? _conditionsTrueText.c_str()
: _conditionsFalseText.c_str());
_statusText->setText(macro->ConditionsMatched()
? _conditionsTrueText.c_str()
: _conditionsFalseText.c_str());
}
void MacroDock::Highlight()

View File

@@ -67,9 +67,7 @@ void SetMacroSwitchedScene(bool value)
{
static bool setupDone = false;
if (!setupDone) {
// Will always be called with switcher lock already held
AddIntervalResetStep([]() { macroSceneSwitched = false; },
false);
AddIntervalResetStep([]() { macroSceneSwitched = false; });
setupDone = true;
}
macroSceneSwitched = value;
@@ -80,33 +78,43 @@ bool MacroSwitchedScene()
return macroSceneSwitched;
}
std::string GetMacroName(Macro *macro)
std::string GetMacroName(const Macro *macro)
{
return macro ? macro->Name() : "";
}
int64_t MillisecondsSinceMacroConditionCheck(Macro *macro)
std::chrono::high_resolution_clock::time_point
LastMacroConditionCheckTime(const Macro *macro)
{
return macro ? macro->MsSinceLastCheck() : 0;
return macro ? macro->LastConditionCheckTime()
: std::chrono::high_resolution_clock::time_point{};
}
bool MacroIsStopped(Macro *macro)
bool MacroIsStopped(const Macro *macro)
{
return macro ? macro->GetStop() : true;
}
bool MacroIsPaused(Macro *macro)
bool MacroIsPaused(const Macro *macro)
{
return macro ? macro->Paused() : true;
}
bool MacroWasPausedSince(
Macro *macro,
const Macro *macro,
const std::chrono::high_resolution_clock::time_point &time)
{
return macro ? macro->WasPausedSince(time) : false;
}
bool MacroWasCheckedSinceLastStart(const Macro *macro)
{
if (!macro) {
return false;
}
return macro->LastConditionCheckTime().time_since_epoch().count() != 0;
}
void AddMacroHelperThread(Macro *macro, std::thread &&newThread)
{
if (!macro) {
@@ -136,7 +144,7 @@ void ResetMacroRunCount(Macro *macro)
macro->ResetRunCount();
}
bool IsValidMacroSegmentIndex(Macro *m, const int idx, bool isCondition)
bool IsValidMacroSegmentIndex(const Macro *m, const int idx, bool isCondition)
{
if (!m || idx < 0) {
return false;

View File

@@ -41,15 +41,17 @@ EXPORT std::atomic_int &GetShutdownConditionCount();
EXPORT void SetMacroSwitchedScene(bool value);
EXPORT bool MacroSwitchedScene();
EXPORT std::string GetMacroName(Macro *);
EXPORT std::string GetMacroName(const Macro *);
EXPORT int64_t MillisecondsSinceMacroConditionCheck(Macro *);
EXPORT std::chrono::high_resolution_clock::time_point
LastMacroConditionCheckTime(const Macro *);
EXPORT bool MacroIsStopped(Macro *);
EXPORT bool MacroIsPaused(Macro *);
EXPORT bool MacroIsStopped(const Macro *);
EXPORT bool MacroIsPaused(const Macro *);
EXPORT bool
MacroWasPausedSince(Macro *,
MacroWasPausedSince(const Macro *,
const std::chrono::high_resolution_clock::time_point &);
EXPORT bool MacroWasCheckedSinceLastStart(const Macro *);
EXPORT void AddMacroHelperThread(Macro *, std::thread &&);
@@ -66,6 +68,6 @@ EXPORT void InvalidateMacroTempVarValues();
EXPORT void ResetMacroConditionTimers(Macro *);
EXPORT void ResetMacroRunCount(Macro *);
bool IsValidMacroSegmentIndex(Macro *m, const int idx, bool isCondition);
bool IsValidMacroSegmentIndex(const Macro *m, const int idx, bool isCondition);
} // namespace advss

View File

@@ -181,15 +181,7 @@ void MacroInputEdit::SetInputVariablesAndValues(
_variables = inputs;
_values = values;
if ((size_t)_values.size() < _variables._inputVariables.size()) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
for (int i = 0;
_variables._inputVariables.size() - (size_t)_values.size();
i++) {
_values.push_back({});
}
#else
_values.resize(_variables._inputVariables.size());
#endif
}
SetupWidgets();
}
@@ -199,7 +191,7 @@ bool MacroInputEdit::HasInputsToSet() const
return !_variables._inputVariables.empty();
}
void HighligthMacroSettingsButton(bool enable);
void HighlightMacroSettingsButton(bool enable);
void MacroInputEdit::SetupWidgets()
{

View File

@@ -1,7 +1,6 @@
#include "macro-segment-copy-paste.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro.hpp"
#include "ui-helpers.hpp"
#include <QShortcut>
@@ -76,22 +75,10 @@ void AdvSceneSwitcher::PasteMacroSegment()
break;
}
case MacroSegmentCopyInfo::Type::ACTION:
if (IsCursorInWidgetArea(ui->macroElseActions)) {
AddMacroElseAction(macro.get(),
macro->ElseActions().size(),
copyInfo.segment->GetId(),
data.Get());
break;
}
AddMacroAction(macro.get(), macro->Actions().size(),
copyInfo.segment->GetId(), data.Get());
break;
case MacroSegmentCopyInfo::Type::ELSE:
if (IsCursorInWidgetArea(ui->macroActions)) {
AddMacroAction(macro.get(), macro->Actions().size(),
copyInfo.segment->GetId(), data.Get());
break;
}
AddMacroElseAction(macro.get(), macro->ElseActions().size(),
copyInfo.segment->GetId(), data.Get());
break;
@@ -111,6 +98,21 @@ bool MacroActionIsInClipboard()
copyInfo.type == MacroSegmentCopyInfo::Type::ELSE;
}
void SetCopySegmentTargetActionType(bool setToElseAction)
{
if (copyInfo.type == MacroSegmentCopyInfo::Type::ACTION &&
setToElseAction) {
copyInfo.type = MacroSegmentCopyInfo::Type::ELSE;
return;
}
if (copyInfo.type == MacroSegmentCopyInfo::Type::ELSE &&
!setToElseAction) {
copyInfo.type = MacroSegmentCopyInfo::Type::ACTION;
return;
}
}
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window)
{
auto copyShortcut = new QShortcut(QKeySequence("Ctrl+C"), window);

View File

@@ -4,6 +4,7 @@ class AdvSceneSwitcher;
bool MacroSegmentIsInClipboard();
bool MacroActionIsInClipboard();
void SetCopySegmentTargetActionType(bool setToElseAction);
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window);
} // namespace advss

View File

@@ -13,6 +13,8 @@
namespace advss {
bool MacroSegmentList::_useCache = true;
MacroSegmentList::MacroSegmentList(QWidget *parent)
: QScrollArea(parent),
_layout(new QVBoxLayout),
@@ -35,14 +37,26 @@ MacroSegmentList::MacroSegmentList(QWidget *parent)
setWidget(wrapper);
setWidgetResizable(true);
setAcceptDrops(true);
connect(verticalScrollBar(), &QScrollBar::valueChanged, this,
[this]() { SetupVisibleMacroSegmentWidgets(); });
}
static void clearWidgetVector(const std::vector<QWidget *> &widgets)
{
for (auto widget : widgets) {
widget->deleteLater();
}
};
MacroSegmentList::~MacroSegmentList()
{
if (_autoScrollThread.joinable()) {
_autoScroll = false;
_autoScrollThread.join();
}
ClearWidgetCache();
}
static bool posIsInScrollbar(const QScrollBar *scrollbar, const QPoint &pos)
@@ -53,13 +67,13 @@ static bool posIsInScrollbar(const QScrollBar *scrollbar, const QPoint &pos)
if (!scrollbar->isVisible()) {
return false;
}
const auto geo = scrollbar->geometry();
const auto &geo = scrollbar->geometry();
const auto globalGeo = QRect(scrollbar->mapToGlobal(geo.topLeft()),
scrollbar->mapToGlobal(geo.bottomRight()));
return globalGeo.contains(pos);
}
int MacroSegmentList::GetDragIndex(const QPoint &pos)
int MacroSegmentList::GetDragIndex(const QPoint &pos) const
{
// Don't drag widget when interacting with the scrollbars
if (posIsInScrollbar(horizontalScrollBar(), mapTo(this, pos)) ||
@@ -88,38 +102,95 @@ int MacroSegmentList::GetDragIndex(const QPoint &pos)
return -1;
}
void MacroSegmentList::SetHelpMsg(const QString &msg)
void MacroSegmentList::SetHelpMsg(const QString &msg) const
{
_helpMsg->setText(msg);
}
void MacroSegmentList::SetHelpMsgVisible(bool visible)
void MacroSegmentList::SetHelpMsgVisible(bool visible) const
{
_helpMsg->setVisible(visible);
}
void MacroSegmentList::Insert(int idx, MacroSegmentEdit *widget)
void MacroSegmentList::Insert(int idx, QWidget *widget)
{
widget->installEventFilter(this);
_contentLayout->insertWidget(idx, widget);
SetupVisibleMacroSegmentWidgets();
}
void MacroSegmentList::Add(QWidget *widget)
{
widget->installEventFilter(this);
_contentLayout->addWidget(widget);
SetupVisibleMacroSegmentWidgets();
}
void MacroSegmentList::Remove(int idx)
void MacroSegmentList::Remove(int idx) const
{
DeleteLayoutItemWidget(_contentLayout->takeAt(idx));
}
void MacroSegmentList::Clear(int idx)
void MacroSegmentList::Clear(int idx) const
{
ClearLayout(_contentLayout, idx);
}
void MacroSegmentList::SetCachingEnabled(bool enable)
{
_useCache = enable;
}
void MacroSegmentList::CacheCurrentWidgetsFor(const Macro *macro)
{
if (!_useCache) {
return;
}
std::vector<QWidget *> result;
int idx = 0;
QLayoutItem *item;
while ((item = _contentLayout->takeAt(idx))) {
if (!item || !item->widget()) {
continue;
}
auto widget = item->widget();
widget->hide();
result.emplace_back(widget);
}
_widgetCache[macro] = result;
}
bool MacroSegmentList::PopulateWidgetsFromCache(const Macro *macro)
{
if (!_useCache) {
return false;
}
auto it = _widgetCache.find(macro);
if (it == _widgetCache.end()) {
return false;
}
for (auto widget : it->second) {
_contentLayout->addWidget(widget);
widget->show();
}
return true;
}
void MacroSegmentList::ClearWidgetsFromCacheFor(const Macro *macro)
{
auto it = _widgetCache.find(macro);
if (it == _widgetCache.end()) {
return;
}
clearWidgetVector(it->second);
_widgetCache.erase(it);
}
void MacroSegmentList::Highlight(int idx, QColor color)
{
auto item = _contentLayout->itemAt(idx);
@@ -133,7 +204,7 @@ void MacroSegmentList::Highlight(int idx, QColor color)
HighlightWidget(widget, color, QColor(0, 0, 0, 0), true);
}
void MacroSegmentList::SetCollapsed(bool collapse)
void MacroSegmentList::SetCollapsed(bool collapse) const
{
QLayoutItem *item = nullptr;
for (int i = 0; i < _contentLayout->count(); i++) {
@@ -145,7 +216,7 @@ void MacroSegmentList::SetCollapsed(bool collapse)
}
}
void MacroSegmentList::SetSelection(int idx)
void MacroSegmentList::SetSelection(int idx) const
{
for (int i = 0; i < _contentLayout->count(); ++i) {
auto widget = static_cast<MacroSegmentEdit *>(
@@ -178,11 +249,7 @@ void MacroSegmentList::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton ||
event->button() == Qt::RightButton) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
_dragPosition = GetDragIndex(event->globalPos());
#else
_dragPosition = GetDragIndex(event->globalPosition().toPoint());
#endif
emit SelectionChanged(_dragPosition);
} else {
_dragPosition = -1;
@@ -235,7 +302,7 @@ void MacroSegmentList::dragEnterEvent(QDragEnterEvent *event)
}
}
MacroSegmentEdit *MacroSegmentList::WidgetAt(int idx)
MacroSegmentEdit *MacroSegmentList::WidgetAt(int idx) const
{
if (idx < 0 || idx >= _contentLayout->count()) {
return nullptr;
@@ -247,11 +314,16 @@ MacroSegmentEdit *MacroSegmentList::WidgetAt(int idx)
return static_cast<MacroSegmentEdit *>(item->widget());
}
MacroSegmentEdit *MacroSegmentList::WidgetAt(const QPoint &pos)
MacroSegmentEdit *MacroSegmentList::WidgetAt(const QPoint &pos) const
{
return WidgetAt(GetSegmentIndexFromPos(mapToGlobal(pos)));
}
int MacroSegmentList::IndexAt(const QPoint &pos) const
{
return GetSegmentIndexFromPos(mapToGlobal(pos));
}
void MacroSegmentList::HideLastDropLine()
{
if (_dropLineIdx >= 0 && _dropLineIdx < _contentLayout->count()) {
@@ -264,6 +336,13 @@ void MacroSegmentList::HideLastDropLine()
_dropLineIdx = -1;
}
void MacroSegmentList::ClearWidgetCache()
{
for (const auto &[_, widgets] : _widgetCache) {
clearWidgetVector(widgets);
}
}
static bool isInUpperHalfOf(const QPoint &pos, const QRect &rect)
{
return QRect(rect.topLeft(),
@@ -295,15 +374,11 @@ void MacroSegmentList::dragMoveEvent(QDragMoveEvent *event)
return;
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
_dragCursorPos = (mapToGlobal(event->pos()));
#else
_dragCursorPos = (mapToGlobal(event->position().toPoint()));
#endif
CheckDropLine(_dragCursorPos);
}
QRect MacroSegmentList::GetContentItemRectWithPadding(int idx)
QRect MacroSegmentList::GetContentItemRectWithPadding(int idx) const
{
auto item = _contentLayout->itemAt(idx);
if (!item) {
@@ -325,7 +400,37 @@ QRect MacroSegmentList::GetContentItemRectWithPadding(int idx)
return rect;
}
int MacroSegmentList::GetSegmentIndexFromPos(const QPoint &pos)
void MacroSegmentList::SetVisibilityCheckEnable(bool enable)
{
_checkVisibility = enable;
if (_checkVisibility) {
QTimer::singleShot(0, this, [this]() {
SetupVisibleMacroSegmentWidgets();
});
}
}
void MacroSegmentList::SetupVisibleMacroSegmentWidgets()
{
if (!_checkVisibility) {
return;
}
const auto viewportRect = viewport()->rect();
for (auto segment : widget()->findChildren<MacroSegmentEdit *>()) {
const auto pos = segment->mapTo(viewport(), QPoint(0, 0));
const QRect rect(pos, segment->size());
if (!viewportRect.intersects(rect)) {
continue;
}
segment->SetupWidgets();
}
}
int MacroSegmentList::GetSegmentIndexFromPos(const QPoint &pos) const
{
int idx = -1;
for (int i = 0; i < _contentLayout->count(); ++i) {
@@ -337,7 +442,7 @@ int MacroSegmentList::GetSegmentIndexFromPos(const QPoint &pos)
return idx;
}
void MacroSegmentList::CheckScroll()
void MacroSegmentList::CheckScroll() const
{
while (_autoScroll) {
const int scrollTrigger = 15;
@@ -407,14 +512,14 @@ void MacroSegmentList::CheckDropLine(const QPoint &pos)
}
}
bool MacroSegmentList::IsInListArea(const QPoint &pos)
bool MacroSegmentList::IsInListArea(const QPoint &pos) const
{
const QRect layoutRect(mapToGlobal(_layout->contentsRect().topLeft()),
_layout->contentsRect().size());
return layoutRect.contains(pos);
}
int MacroSegmentList::GetDropIndex(const QPoint &pos)
int MacroSegmentList::GetDropIndex(const QPoint &pos) const
{
int idx = GetSegmentIndexFromPos(pos);
if (idx == _dragPosition) {
@@ -445,16 +550,6 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
{
HideLastDropLine();
auto widget = qobject_cast<QWidget *>(event->source());
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (widget && !widget->geometry().contains(event->pos()) &&
widgetIsInLayout(widget, _contentLayout)) {
int dropPosition = GetDropIndex(mapToGlobal(event->pos()));
if (dropPosition == -1) {
return;
}
emit Reorder(dropPosition, _dragPosition);
}
#else
if (widget &&
!widget->geometry().contains(event->position().toPoint()) &&
widgetIsInLayout(widget, _contentLayout)) {
@@ -465,8 +560,13 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
}
emit Reorder(dropPosition, _dragPosition);
}
#endif
_dragPosition = -1;
}
void MacroSegmentList::resizeEvent(QResizeEvent *event)
{
QScrollArea::resizeEvent(event);
SetupVisibleMacroSegmentWidgets();
}
} // namespace advss

View File

@@ -9,24 +9,32 @@
namespace advss {
class Macro;
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);
MacroSegmentEdit *WidgetAt(const QPoint &);
void Insert(int idx, MacroSegmentEdit *widget);
~MacroSegmentList();
void SetHelpMsg(const QString &msg) const;
void SetHelpMsgVisible(bool visible) const;
MacroSegmentEdit *WidgetAt(int idx) const;
MacroSegmentEdit *WidgetAt(const QPoint &) const;
int IndexAt(const QPoint &) const;
void Insert(int idx, QWidget *widget);
void Add(QWidget *widget);
void Remove(int idx);
void Clear(int idx = 0); // Clear all elements >= idx
void Remove(int idx) const;
void Clear(int idx = 0) const; // Clear all elements >= idx
static void SetCachingEnabled(bool enable);
void CacheCurrentWidgetsFor(const Macro *);
bool PopulateWidgetsFromCache(const Macro *);
void ClearWidgetsFromCacheFor(const Macro *);
void Highlight(int idx, QColor color = QColor(Qt::green));
void SetCollapsed(bool);
void SetSelection(int idx);
QVBoxLayout *ContentLayout() { return _contentLayout; }
void SetCollapsed(bool) const;
void SetSelection(int idx) const;
QVBoxLayout *ContentLayout() const { return _contentLayout; }
void SetVisibilityCheckEnable(bool enable);
signals:
void SelectionChanged(int idx);
@@ -41,16 +49,19 @@ protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
void resizeEvent(QResizeEvent *event);
private:
int GetDragIndex(const QPoint &);
int GetDropIndex(const QPoint &);
int GetSegmentIndexFromPos(const QPoint &);
void CheckScroll();
void SetupVisibleMacroSegmentWidgets();
int GetSegmentIndexFromPos(const QPoint &) const;
int GetDragIndex(const QPoint &) const;
int GetDropIndex(const QPoint &) const;
void CheckScroll() const;
void CheckDropLine(const QPoint &);
bool IsInListArea(const QPoint &);
QRect GetContentItemRectWithPadding(int idx);
bool IsInListArea(const QPoint &) const;
QRect GetContentItemRectWithPadding(int idx) const;
void HideLastDropLine();
void ClearWidgetCache();
int _dragPosition = -1;
int _dropLineIdx = -1;
@@ -61,6 +72,11 @@ private:
QVBoxLayout *_layout;
QVBoxLayout *_contentLayout;
QLabel *_helpMsg;
bool _checkVisibility = true;
static bool _useCache;
std::unordered_map<const Macro *, std::vector<QWidget *>> _widgetCache;
};
} // namespace advss

View File

@@ -21,7 +21,7 @@ MacroSegmentSelection::MacroSegmentSelection(QWidget *parent, Type type,
{
_index->setMinimum(0);
_index->setMaximum(99);
_index->specialValueText("-");
_index->setSpecialValueText("-");
if (!allowVariables) {
_index->DisableVariableSelection();
}
@@ -70,7 +70,8 @@ void MacroSegmentSelection::MacroSegmentOrderChanged()
SetupDescription();
}
QString GetMacroSegmentDescription(Macro *macro, int idx, bool isCondition)
static QString GetMacroSegmentDescription(Macro *macro, int idx,
bool isCondition)
{
if (!macro) {
return "";

View File

@@ -6,6 +6,7 @@
#include <QApplication>
#include <QEvent>
#include <QGraphicsOpacityEffect>
#include <QLabel>
#include <QMouseEvent>
#include <QScrollBar>
@@ -24,24 +25,24 @@ bool MacroSegment::Save(obs_data_t *obj) const
obs_data_set_bool(data, "collapsed", _collapsed);
obs_data_set_bool(data, "useCustomLabel", _useCustomLabel);
obs_data_set_string(data, "customLabel", _customLabel.c_str());
obs_data_set_bool(data, "enabled", _enabled);
obs_data_set_int(data, "version", 1);
obs_data_set_obj(obj, "segmentSettings", data);
return true;
}
bool MacroSegment::Load(obs_data_t *obj)
{
OBSDataAutoRelease data = obs_data_get_obj(obj, "segmentSettings");
_collapsed = obs_data_get_bool(data, "collapsed");
_useCustomLabel = obs_data_get_bool(data, "useCustomLabel");
_customLabel = obs_data_get_string(data, "customLabel");
obs_data_set_default_bool(data, "enabled", true);
_enabled = obs_data_get_bool(data, "enabled");
// TODO: remove this fallback at some point
if (obs_data_has_user_value(obj, "segmentSettings")) {
OBSDataAutoRelease data =
obs_data_get_obj(obj, "segmentSettings");
_collapsed = obs_data_get_bool(data, "collapsed");
_useCustomLabel = obs_data_get_bool(data, "useCustomLabel");
_customLabel = obs_data_get_string(data, "customLabel");
} else {
_collapsed = obs_data_get_bool(obj, "collapsed");
_useCustomLabel = false;
_customLabel = obs_module_text(
"AdvSceneSwitcher.macroTab.segment.defaultCustomLabel");
if (!obs_data_has_user_value(data, "version")) {
_enabled = obs_data_get_bool(obj, "enabled");
}
ClearAvailableTempvars();
@@ -73,6 +74,16 @@ bool MacroSegment::GetHighlightAndReset()
return false;
}
void MacroSegment::SetEnabled(bool value)
{
_enabled = value;
}
bool MacroSegment::Enabled() const
{
return _enabled;
}
std::string MacroSegment::GetVariableValue() const
{
if (_supportsVariableValue) {
@@ -96,7 +107,7 @@ void MacroSegment::SetupTempVars()
void MacroSegment::ClearAvailableTempvars()
{
_tempVariables.clear();
NotifyUIAboutTempVarChange();
NotifyUIAboutTempVarChange(this);
}
static std::shared_ptr<MacroSegment>
@@ -138,7 +149,7 @@ void MacroSegment::AddTempvar(const std::string &id, const std::string &name,
TempVariable var(id, name, description, sharedSegment);
_tempVariables.emplace_back(std::move(var));
NotifyUIAboutTempVarChange();
NotifyUIAboutTempVarChange(this);
}
void MacroSegment::SetTempVarValue(const std::string &id,
@@ -294,13 +305,8 @@ bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
QMouseEvent *newEvent = new QMouseEvent(
mouseEvent->type(),
_headerInfo->mapTo(this, mouseEvent->pos()),
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
mouseEvent->globalPos(),
#else
mouseEvent->globalPosition(),
#endif
mouseEvent->button(), mouseEvent->buttons(),
mouseEvent->modifiers());
mouseEvent->globalPosition(), mouseEvent->button(),
mouseEvent->buttons(), mouseEvent->modifiers());
QApplication::sendEvent(parentWidget(), newEvent);
}
return QWidget::eventFilter(obj, ev);
@@ -318,13 +324,29 @@ void MacroSegmentEdit::HeaderInfoChanged(const QString &text)
_headerInfo->setText(text);
}
void MacroSegmentEdit::Collapsed(bool collapsed)
void MacroSegmentEdit::Collapsed(bool collapsed) const
{
if (Data()) {
Data()->SetCollapsed(collapsed);
}
}
void MacroSegmentEdit::SetDisableEffect(bool value)
{
if (value) {
auto effect = new QGraphicsOpacityEffect(this);
effect->setOpacity(0.5);
_section->setGraphicsEffect(effect);
} else {
_section->setGraphicsEffect(nullptr);
}
}
void MacroSegmentEdit::SetEnableAppearance(bool value)
{
SetDisableEffect(!value);
}
void MacroSegmentEdit::SetFocusPolicyOfWidgets()
{
QList<QWidget *> widgets = this->findChildren<QWidget *>();

View File

@@ -39,6 +39,8 @@ public:
virtual std::string GetId() const = 0;
void EnableHighlight();
bool GetHighlightAndReset();
void SetEnabled(bool);
bool Enabled() const;
virtual std::string GetVariableValue() const;
protected:
@@ -46,13 +48,22 @@ protected:
friend void IncrementVariableRef(MacroSegment *);
friend void DecrementVariableRef(MacroSegment *);
void SetVariableValue(const std::string &value);
bool IsReferencedInVars() { return _variableRefs != 0; }
bool IsReferencedInVars() const { return _variableRefs != 0; }
virtual void SetupTempVars();
void AddTempvar(const std::string &id, const std::string &name,
const std::string &description = "");
void SetTempVarValue(const std::string &id, const std::string &value);
template<typename T, typename = std::enable_if_t<
std::is_same<std::decay_t<T>, bool>::value>>
void SetTempVarValue(const std::string &id, T value)
{
SetTempVarValue(id, value ? std::string("true")
: std::string("false"));
}
private:
void ClearAvailableTempvars();
std::optional<const TempVariable>
@@ -66,6 +77,7 @@ private:
// UI helper
bool _highlight = false;
bool _collapsed = false;
bool _enabled = true;
// Custom header labels
bool _useCustomLabel = false;
@@ -94,12 +106,13 @@ public:
void SetCollapsed(bool collapsed);
void SetSelected(bool);
virtual std::shared_ptr<MacroSegment> Data() const = 0;
virtual void SetupWidgets(bool basicSetup = false) = 0;
public slots:
void HeaderInfoChanged(const QString &);
protected slots:
void Collapsed(bool);
void Collapsed(bool) const;
signals:
void MacroAdded(const QString &name);
void MacroRemoved(const QString &name);
@@ -109,12 +122,16 @@ signals:
void SceneGroupRenamed(const QString &oldName, const QString newName);
protected:
void SetDisableEffect(bool);
void SetEnableAppearance(bool);
bool eventFilter(QObject *obj, QEvent *ev) override;
Section *_section;
QLabel *_headerInfo;
QWidget *_frame;
QVBoxLayout *_contentLayout;
bool _allWidgetsAreSetup = false;
private:
enum class DropLineState {

View File

@@ -2,6 +2,7 @@
#include "advanced-scene-switcher.hpp"
#include "layout-helpers.hpp"
#include "macro.hpp"
#include "ui-helpers.hpp"
#include <QDialogButtonBox>
#include <QStandardItemModel>
@@ -68,19 +69,23 @@ void MacroSelection::HideSelectedMacro()
void MacroSelection::ShowAllMacros()
{
auto v = qobject_cast<QListView *>(view());
for (int i = count(); i > 0; i--) {
for (int i = count() - 1; i >= 0; i--) {
v->setRowHidden(i, false);
}
}
void MacroSelection::MacroRemove(const QString &name)
{
const bool currentSelectionWasRemoved = name == currentText();
int idx = findText(name);
if (idx == -1) {
return;
}
removeItem(idx);
setCurrentIndex(-1);
if (currentSelectionWasRemoved) {
setCurrentIndex(-1);
}
}
void MacroSelection::MacroRename(const QString &oldName, const QString &newName)

View File

@@ -1,7 +1,8 @@
#include "macro-settings.hpp"
#include "layout-helpers.hpp"
#include "obs-module-helper.hpp"
#include "macro.hpp"
#include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp"
#include <QDialogButtonBox>
#include <QScrollArea>
@@ -18,8 +19,14 @@ void GlobalMacroSettings::Save(obs_data_t *obj) const
obs_data_set_bool(data, "highlightExecuted", _highlightExecuted);
obs_data_set_bool(data, "highlightConditions", _highlightConditions);
obs_data_set_bool(data, "highlightActions", _highlightActions);
obs_data_set_bool(data, "newMacroCheckInParallel",
_newMacroCheckInParallel);
obs_data_set_bool(data, "newMacroRegisterHotkey",
_newMacroRegisterHotkeys);
obs_data_set_bool(data, "newMacroUseShortCircuitEvaluation",
_newMacroUseShortCircuitEvaluation);
obs_data_set_bool(data, "saveSettingsOnMacroChange",
_saveSettingsOnMacroChange);
obs_data_set_obj(obj, "macroSettings", data);
obs_data_release(data);
}
@@ -36,8 +43,16 @@ void GlobalMacroSettings::Load(obs_data_t *obj)
_highlightExecuted = obs_data_get_bool(data, "highlightExecuted");
_highlightConditions = obs_data_get_bool(data, "highlightConditions");
_highlightActions = obs_data_get_bool(data, "highlightActions");
_newMacroCheckInParallel =
obs_data_get_bool(data, "newMacroCheckInParallel");
_newMacroRegisterHotkeys =
obs_data_get_bool(data, "newMacroRegisterHotkey");
_newMacroUseShortCircuitEvaluation =
obs_data_get_bool(data, "newMacroUseShortCircuitEvaluation");
_saveSettingsOnMacroChange =
obs_data_has_user_value(data, "saveSettingsOnMacroChange")
? obs_data_get_bool(data, "saveSettingsOnMacroChange")
: true;
obs_data_release(data);
}
@@ -45,16 +60,33 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
const GlobalMacroSettings &settings,
Macro *macro)
: QDialog(parent),
_executed(new QCheckBox(obs_module_text(
_highlightExecutedMacros(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightExecutedMacros"))),
_conditions(new QCheckBox(obs_module_text(
_highlightConditions(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightTrueConditions"))),
_actions(new QCheckBox(obs_module_text(
_highlightActions(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightPerformedActions"))),
_newMacroCheckInParallel(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.newMacroCheckInParallel"))),
_newMacroRegisterHotkeys(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))),
_newMacroUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation"))),
_saveSettingsOnMacroChange(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange"))),
_currentCheckInParallel(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentCheckInParallel"))),
_currentMacroRegisterHotkeys(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentDisableHotkeys"))),
"AdvSceneSwitcher.macroTab.currentRegisterHotkeys"))),
_currentUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentUseShortCircuitEvaluation"))),
_currentUseCustomConditionCheckInterval(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentUseCustomConditionCheckInterval"))),
_currentCustomConditionCheckInterval(
new DurationSelection(this, true, 0.01)),
_currentCustomConditionCheckIntervalWarning(new QLabel(obs_module_text(
"AdvSceneSwitcher.macroTab.currentUseCustomConditionCheckIntervalWarning"))),
_currentPauseSaveBehavior(new QComboBox(this)),
_currentSkipOnStartup(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))),
_currentStopActionsIfNotDone(new QCheckBox(obs_module_text(
@@ -83,12 +115,37 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
setWindowModality(Qt::WindowModality::WindowModal);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
_newMacroCheckInParallel->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.checkInParallel.tooltip"));
_currentCheckInParallel->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.checkInParallel.tooltip"));
_newMacroUseShortCircuitEvaluation->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.shortCircuit.tooltip"));
_saveSettingsOnMacroChange->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange.tooltip"));
_currentUseShortCircuitEvaluation->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.shortCircuit.tooltip"));
_currentPauseSaveBehavior->addItem(
obs_module_text(
"AdvSceneSwitcher.macroTab.pauseStateSaveBehavior.persist"),
static_cast<int>(Macro::PauseStateSaveBehavior::PERSIST));
_currentPauseSaveBehavior->addItem(
obs_module_text(
"AdvSceneSwitcher.macroTab.pauseStateSaveBehavior.pause"),
static_cast<int>(Macro::PauseStateSaveBehavior::PAUSE));
_currentPauseSaveBehavior->addItem(
obs_module_text(
"AdvSceneSwitcher.macroTab.pauseStateSaveBehavior.unpause"),
static_cast<int>(Macro::PauseStateSaveBehavior::UNPAUSE));
auto highlightOptions = new QGroupBox(
obs_module_text("AdvSceneSwitcher.macroTab.highlightSettings"));
auto highlightLayout = new QVBoxLayout;
highlightLayout->addWidget(_executed);
highlightLayout->addWidget(_conditions);
highlightLayout->addWidget(_actions);
highlightLayout->addWidget(_highlightExecutedMacros);
highlightLayout->addWidget(_highlightConditions);
highlightLayout->addWidget(_highlightActions);
highlightOptions->setLayout(highlightLayout);
auto hotkeyOptions = new QGroupBox(
@@ -101,8 +158,31 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
auto generalOptions = new QGroupBox(
obs_module_text("AdvSceneSwitcher.macroTab.generalSettings"));
auto generalLayout = new QVBoxLayout;
generalLayout->addWidget(_saveSettingsOnMacroChange);
generalLayout->addWidget(_currentCheckInParallel);
generalLayout->addWidget(_newMacroCheckInParallel);
generalLayout->addWidget(_currentSkipOnStartup);
generalLayout->addWidget(_currentStopActionsIfNotDone);
generalLayout->addWidget(_currentUseShortCircuitEvaluation);
generalLayout->addWidget(_newMacroUseShortCircuitEvaluation);
auto durationLayout = new QHBoxLayout();
durationLayout->addWidget(_currentUseCustomConditionCheckInterval);
durationLayout->addWidget(_currentCustomConditionCheckInterval);
durationLayout->addStretch();
auto customConditionIntervalLayout = new QVBoxLayout();
customConditionIntervalLayout->addLayout(durationLayout);
customConditionIntervalLayout->addWidget(
_currentCustomConditionCheckIntervalWarning);
generalLayout->addLayout(customConditionIntervalLayout);
auto pauseStateSaveBehavorLayout = new QHBoxLayout();
pauseStateSaveBehavorLayout->addWidget(new QLabel(obs_module_text(
"AdvSceneSwitcher.macroTab.pauseStateSaveBehavior")));
pauseStateSaveBehavorLayout->addWidget(_currentPauseSaveBehavior);
pauseStateSaveBehavorLayout->addStretch();
generalLayout->addLayout(pauseStateSaveBehavorLayout);
generalOptions->setLayout(generalLayout);
auto inputOptions = new QGroupBox(
@@ -178,6 +258,13 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
&MacroSettingsDialog::PauseButtonEnableChanged);
connect(_currentMacroDockAddStatusLabel, &QCheckBox::stateChanged, this,
&MacroSettingsDialog::StatusLabelEnableChanged);
connect(_currentUseCustomConditionCheckInterval,
&QCheckBox::stateChanged, this, [this](int state) {
_currentCustomConditionCheckInterval->setEnabled(state);
});
connect(_currentCustomConditionCheckInterval,
&DurationSelection::DurationChanged, this,
[this]() { SetCustomConditionIntervalWarningVisibility(); });
auto scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
@@ -198,20 +285,47 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
dialogLayout->addWidget(buttonbox);
setLayout(dialogLayout);
_executed->setChecked(settings._highlightExecuted);
_conditions->setChecked(settings._highlightConditions);
_actions->setChecked(settings._highlightActions);
_highlightExecutedMacros->setChecked(settings._highlightExecuted);
_highlightConditions->setChecked(settings._highlightConditions);
_highlightActions->setChecked(settings._highlightActions);
_newMacroCheckInParallel->setChecked(settings._newMacroCheckInParallel);
_newMacroRegisterHotkeys->setChecked(settings._newMacroRegisterHotkeys);
_newMacroUseShortCircuitEvaluation->setChecked(
settings._newMacroUseShortCircuitEvaluation);
_saveSettingsOnMacroChange->setChecked(
settings._saveSettingsOnMacroChange);
if (!macro || macro->IsGroup()) {
hotkeyOptions->hide();
generalOptions->hide();
// General group
_currentSkipOnStartup->hide();
_currentStopActionsIfNotDone->hide();
_currentUseShortCircuitEvaluation->hide();
_currentCheckInParallel->hide();
SetLayoutVisible(customConditionIntervalLayout, false);
SetLayoutVisible(pauseStateSaveBehavorLayout, false);
// Hotkey group
_currentMacroRegisterHotkeys->hide();
inputOptions->hide();
_dockOptions->hide();
return;
}
_currentCheckInParallel->setChecked(macro->CheckInParallel());
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
_currentUseShortCircuitEvaluation->setChecked(
macro->ShortCircuitEvaluationEnabled());
_currentUseCustomConditionCheckInterval->setChecked(
macro->CustomConditionCheckIntervalEnabled());
_currentCustomConditionCheckInterval->SetDuration(
macro->GetCustomConditionCheckInterval());
_currentCustomConditionCheckInterval->setEnabled(
macro->CustomConditionCheckIntervalEnabled());
SetCustomConditionIntervalWarningVisibility();
_currentPauseSaveBehavior->setCurrentIndex(
_currentPauseSaveBehavior->findData(
static_cast<int>(macro->GetPauseStateSaveBehavior())));
_currentSkipOnStartup->setChecked(macro->SkipExecOnStart());
_currentStopActionsIfNotDone->setChecked(macro->StopActionsIfNotDone());
_currentInputs->SetInputs(macro->GetInputVariables());
@@ -313,6 +427,15 @@ void MacroSettingsDialog::Resize()
_dockOptions->updateGeometry();
}
void MacroSettingsDialog::SetCustomConditionIntervalWarningVisibility() const
{
_currentCustomConditionCheckIntervalWarning->setVisible(
_currentUseCustomConditionCheckInterval->isChecked() &&
GetIntervalValue() >
_currentCustomConditionCheckInterval->GetDuration()
.Milliseconds());
}
bool MacroSettingsDialog::AskForSettings(QWidget *parent,
GlobalMacroSettings &userInput,
Macro *macro)
@@ -322,17 +445,36 @@ bool MacroSettingsDialog::AskForSettings(QWidget *parent,
if (dialog.exec() != DialogCode::Accepted) {
return false;
}
userInput._highlightExecuted = dialog._executed->isChecked();
userInput._highlightConditions = dialog._conditions->isChecked();
userInput._highlightActions = dialog._actions->isChecked();
userInput._highlightExecuted =
dialog._highlightExecutedMacros->isChecked();
userInput._highlightConditions =
dialog._highlightConditions->isChecked();
userInput._highlightActions = dialog._highlightActions->isChecked();
userInput._newMacroCheckInParallel =
dialog._newMacroCheckInParallel->isChecked();
userInput._newMacroRegisterHotkeys =
dialog._newMacroRegisterHotkeys->isChecked();
userInput._newMacroUseShortCircuitEvaluation =
dialog._newMacroUseShortCircuitEvaluation->isChecked();
userInput._saveSettingsOnMacroChange =
dialog._saveSettingsOnMacroChange->isChecked();
if (!macro) {
return true;
}
macro->SetCheckInParallel(dialog._currentCheckInParallel->isChecked());
macro->EnablePauseHotkeys(
dialog._currentMacroRegisterHotkeys->isChecked());
macro->SetShortCircuitEvaluation(
dialog._currentUseShortCircuitEvaluation->isChecked());
macro->SetCustomConditionCheckIntervalEnabled(
dialog._currentUseCustomConditionCheckInterval->isChecked());
macro->SetCustomConditionCheckInterval(
dialog._currentCustomConditionCheckInterval->GetDuration());
macro->SetPauseStateSaveBehavior(
static_cast<Macro::PauseStateSaveBehavior>(
dialog._currentPauseSaveBehavior->currentData()
.toInt()));
macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked());
macro->SetStopActionsIfNotDone(
dialog._currentStopActionsIfNotDone->isChecked());

View File

@@ -1,5 +1,6 @@
#pragma once
#include "variable-line-edit.hpp"
#include "duration-control.hpp"
#include "macro-input.hpp"
#include <QWidget>
@@ -19,10 +20,13 @@ public:
void Save(obs_data_t *obj) const;
void Load(obs_data_t *obj);
bool _highlightExecuted = false;
bool _highlightConditions = false;
bool _highlightActions = false;
bool _highlightExecuted = true;
bool _highlightConditions = true;
bool _highlightActions = true;
bool _newMacroCheckInParallel = false;
bool _newMacroRegisterHotkeys = true;
bool _newMacroUseShortCircuitEvaluation = false;
bool _saveSettingsOnMacroChange = true;
};
// Dialog for configuring global and individual macro specific settings
@@ -43,13 +47,24 @@ private slots:
private:
void Resize();
void SetCustomConditionIntervalWarningVisibility() const;
QCheckBox *_executed;
QCheckBox *_conditions;
QCheckBox *_actions;
// Global macro settings
QCheckBox *_highlightExecutedMacros;
QCheckBox *_highlightConditions;
QCheckBox *_highlightActions;
QCheckBox *_newMacroCheckInParallel;
QCheckBox *_newMacroRegisterHotkeys;
QCheckBox *_newMacroUseShortCircuitEvaluation;
QCheckBox *_saveSettingsOnMacroChange;
// Current macro specific settings
QCheckBox *_currentCheckInParallel;
QCheckBox *_currentMacroRegisterHotkeys;
QCheckBox *_currentUseShortCircuitEvaluation;
QCheckBox *_currentUseCustomConditionCheckInterval;
DurationSelection *_currentCustomConditionCheckInterval;
QLabel *_currentCustomConditionCheckIntervalWarning;
QComboBox *_currentPauseSaveBehavior;
QCheckBox *_currentSkipOnStartup;
QCheckBox *_currentStopActionsIfNotDone;
MacroInputSelection *_currentInputs;

View File

@@ -27,6 +27,14 @@ namespace advss {
static QObject *addPulse = nullptr;
static QTimer onChangeHighlightTimer;
static void disableAddButtonHighlight()
{
if (addPulse) {
addPulse->deleteLater();
addPulse = nullptr;
}
}
static bool macroNameExists(const std::string &name)
{
return !!GetMacroByName(name.c_str());
@@ -86,25 +94,88 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
return false;
}
res = std::make_shared<Macro>(
name, GetGlobalMacroSettings()._newMacroRegisterHotkeys);
res = std::make_shared<Macro>(name, GetGlobalMacroSettings());
return true;
}
void AdvSceneSwitcher::on_macroAdd_clicked()
{
// Hotkey to add new macro will also use this function.
// Since we don't want the hotkey to have an effect if the macro tab is
// not focused we need to check this here.
if (!MacroTabIsInFocus()) {
return;
}
std::string name;
std::shared_ptr<Macro> newMacro;
if (!AddNewMacro(newMacro, name)) {
return;
}
ui->macros->Add(newMacro);
addPulse->deleteLater();
addPulse = nullptr;
disableAddButtonHighlight();
auto selectedMacro = GetSelectedMacro();
if (!selectedMacro) {
ui->macros->Add(newMacro);
emit MacroAdded(QString::fromStdString(name));
return;
}
if (selectedMacro->IsGroup()) {
ui->macros->Add(newMacro, selectedMacro);
emit MacroAdded(QString::fromStdString(name));
return;
}
auto selectedMacroGroup = selectedMacro->Parent();
if (!selectedMacroGroup) {
ui->macros->Add(newMacro, selectedMacro);
emit MacroAdded(QString::fromStdString(name));
return;
}
Macro::PrepareMoveToGroup(selectedMacroGroup, newMacro);
ui->macros->Add(newMacro, selectedMacro);
emit MacroAdded(QString::fromStdString(name));
}
static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> &macros,
const std::shared_ptr<Macro> &group)
{
std::vector<std::shared_ptr<Macro>> subitems;
subitems.reserve(group->GroupSize());
// Find all subitems
auto allMacros = GetMacros();
for (auto it = allMacros.begin(); it < allMacros.end(); it++) {
if ((*it)->Name() != group->Name()) {
continue;
}
for (uint32_t i = 1; i <= group->GroupSize(); i++) {
subitems.emplace_back(*std::next(it, i));
}
break;
}
// Remove subitems which were already selected to avoid duplicates
for (const auto &subitem : subitems) {
auto it = std::find(macros.begin(), macros.end(), subitem);
if (it == macros.end()) {
continue;
}
macros.erase(it);
}
// Add group subitems
auto it = std::find(macros.begin(), macros.end(), group);
if (it == macros.end()) {
return;
}
it = std::next(it);
macros.insert(it, subitems.begin(), subitems.end());
}
void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> &macro)
{
if (!macro) {
@@ -120,7 +191,27 @@ void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> &macro)
}
}
const auto clearWidgetCache = [this](Macro *macro) {
ui->conditionsList->ClearWidgetsFromCacheFor(macro);
ui->actionsList->ClearWidgetsFromCacheFor(macro);
ui->elseActionsList->ClearWidgetsFromCacheFor(macro);
};
if (macro->IsGroup()) {
std::vector<std::shared_ptr<Macro>> macros = {macro};
addGroupSubitems(macros, macro);
for (auto macro : macros) {
clearWidgetCache(macro.get());
}
} else {
clearWidgetCache(macro.get());
}
// Don't cache widgets for about to be deleted macros
MacroSegmentList::SetCachingEnabled(false);
ui->macros->Remove(macro);
MacroSegmentList::SetCachingEnabled(!switcher->disableMacroWidgetCache);
emit MacroRemoved(name);
}
@@ -135,7 +226,7 @@ void AdvSceneSwitcher::RemoveSelectedMacros()
if (macroCount == 1) {
QString deleteWarning = obs_module_text(
"AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text");
auto macro = macros.at(0);
auto &macro = macros.at(0);
deleteWarning = deleteWarning.arg(
QString::fromStdString(macro->Name()));
@@ -175,7 +266,7 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
RemoveSelectedMacros();
}
void AdvSceneSwitcher::on_macroUp_clicked()
void AdvSceneSwitcher::on_macroUp_clicked() const
{
auto macro = GetSelectedMacro();
if (!macro) {
@@ -184,7 +275,7 @@ void AdvSceneSwitcher::on_macroUp_clicked()
ui->macros->Up(macro);
}
void AdvSceneSwitcher::on_macroDown_clicked()
void AdvSceneSwitcher::on_macroDown_clicked() const
{
auto macro = GetSelectedMacro();
if (!macro) {
@@ -219,42 +310,7 @@ void AdvSceneSwitcher::RenameSelectedMacro()
ui->macroName->setText(QString::fromStdString(name));
}
static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> &macros,
const std::shared_ptr<Macro> &group)
{
std::vector<std::shared_ptr<Macro>> subitems;
subitems.reserve(group->GroupSize());
// Find all subitems
auto allMacros = GetMacros();
for (auto it = allMacros.begin(); it < allMacros.end(); it++) {
if ((*it)->Name() == group->Name()) {
for (uint32_t i = 1; i <= group->GroupSize(); i++) {
subitems.emplace_back(*std::next(it, i));
}
break;
}
}
// Remove subitems which were already selected to avoid duplicates
for (const auto &subitem : subitems) {
auto it = std::find(macros.begin(), macros.end(), subitem);
if (it == macros.end()) {
continue;
}
macros.erase(it);
}
// Add group subitems
auto it = std::find(macros.begin(), macros.end(), group);
if (it == macros.end()) {
return;
}
it = std::next(it);
macros.insert(it, subitems.begin(), subitems.end());
}
void AdvSceneSwitcher::ExportMacros()
void AdvSceneSwitcher::ExportMacros() const
{
auto selectedMacros = GetSelectedMacros();
auto macros = selectedMacros;
@@ -465,6 +521,7 @@ void AdvSceneSwitcher::ImportMacros()
ui->macros->Reset(GetMacros(),
GetGlobalMacroSettings()._highlightExecuted);
disableAddButtonHighlight();
}
void AdvSceneSwitcher::on_macroName_editingFinished()
@@ -485,7 +542,7 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
RenameMacro(macro, newName);
}
void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value)
void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value) const
{
auto macro = GetSelectedMacro();
if (!macro) {
@@ -495,7 +552,7 @@ void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value)
macro->SetRunInParallel(value);
}
void AdvSceneSwitcher::on_runMacroOnChange_stateChanged(int value)
void AdvSceneSwitcher::on_runMacroOnChange_stateChanged(int value) const
{
auto macro = GetSelectedMacro();
if (!macro) {
@@ -508,29 +565,69 @@ void AdvSceneSwitcher::on_runMacroOnChange_stateChanged(int value)
void AdvSceneSwitcher::PopulateMacroActions(Macro &m, uint32_t afterIdx)
{
auto &actions = m.Actions();
ui->actionsList->SetHelpMsgVisible(actions.size() == 0);
if (ui->actionsList->PopulateWidgetsFromCache(&m)) {
return;
}
// The layout system has not completed geometry propagation yet, so we
// can skip those checks for now
ui->actionsList->SetVisibilityCheckEnable(false);
for (; afterIdx < actions.size(); afterIdx++) {
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
actions[afterIdx]->GetId());
ui->actionsList->Add(newEntry);
}
ui->actionsList->SetHelpMsgVisible(actions.size() == 0);
// Give the layout system time before enabling the visibility checks and
// fully constructing the visible macro segments
QTimer::singleShot(0, this, [this]() {
ui->actionsList->SetVisibilityCheckEnable(true);
});
}
void AdvSceneSwitcher::PopulateMacroElseActions(Macro &m, uint32_t afterIdx)
{
auto &actions = m.ElseActions();
ui->elseActionsList->SetHelpMsgVisible(actions.size() == 0);
if (ui->elseActionsList->PopulateWidgetsFromCache(&m)) {
return;
}
// The layout system has not completed geometry propagation yet, so we
// can skip those checks for now
ui->elseActionsList->SetVisibilityCheckEnable(false);
for (; afterIdx < actions.size(); afterIdx++) {
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
actions[afterIdx]->GetId());
ui->elseActionsList->Add(newEntry);
}
ui->elseActionsList->SetHelpMsgVisible(actions.size() == 0);
// Give the layout system time before enabling the visibility checks and
// fully constructing the visible macro segments
QTimer::singleShot(0, this, [this]() {
ui->elseActionsList->SetVisibilityCheckEnable(true);
});
}
void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
{
bool root = afterIdx == 0;
auto &conditions = m.Conditions();
ui->conditionsList->SetHelpMsgVisible(conditions.size() == 0);
if (ui->conditionsList->PopulateWidgetsFromCache(&m)) {
return;
}
// The layout system has not completed geometry propagation yet, so we
// can skip those checks for now
ui->conditionsList->SetVisibilityCheckEnable(false);
for (; afterIdx < conditions.size(); afterIdx++) {
auto newEntry = new MacroConditionEdit(
this, &conditions[afterIdx],
@@ -538,10 +635,15 @@ void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
ui->conditionsList->Add(newEntry);
root = false;
}
ui->conditionsList->SetHelpMsgVisible(conditions.size() == 0);
// Give the layout system time before enabling the visibility checks and
// fully constructing the visible macro segments
QTimer::singleShot(0, this, [this]() {
ui->conditionsList->SetVisibilityCheckEnable(true);
});
}
void AdvSceneSwitcher::SetActionData(Macro &m)
void AdvSceneSwitcher::SetActionData(Macro &m) const
{
auto &actions = m.Actions();
for (int idx = 0; idx < ui->actionsList->ContentLayout()->count();
@@ -558,7 +660,7 @@ void AdvSceneSwitcher::SetActionData(Macro &m)
}
}
void AdvSceneSwitcher::SetElseActionData(Macro &m)
void AdvSceneSwitcher::SetElseActionData(Macro &m) const
{
auto &actions = m.ElseActions();
for (int idx = 0; idx < ui->elseActionsList->ContentLayout()->count();
@@ -575,7 +677,7 @@ void AdvSceneSwitcher::SetElseActionData(Macro &m)
}
}
void AdvSceneSwitcher::SetConditionData(Macro &m)
void AdvSceneSwitcher::SetConditionData(Macro &m) const
{
auto &conditions = m.Conditions();
for (int idx = 0; idx < ui->conditionsList->ContentLayout()->count();
@@ -653,7 +755,7 @@ void AdvSceneSwitcher::SetEditMacro(Macro &m)
m.GetElseActionSplitterPosition());
}
void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable)
void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) const
{
ui->macroName->setDisabled(disable);
ui->runMacro->setDisabled(disable);
@@ -664,32 +766,32 @@ void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable)
ui->macroActionConditionSplitter->setDisabled(disable);
}
void AdvSceneSwitcher::HighlightAction(int idx, QColor color)
void AdvSceneSwitcher::HighlightAction(int idx, QColor color) const
{
ui->actionsList->Highlight(idx, color);
}
void AdvSceneSwitcher::HighlightElseAction(int idx, QColor color)
void AdvSceneSwitcher::HighlightElseAction(int idx, QColor color) const
{
ui->elseActionsList->Highlight(idx, color);
}
void AdvSceneSwitcher::HighlightCondition(int idx, QColor color)
void AdvSceneSwitcher::HighlightCondition(int idx, QColor color) const
{
ui->conditionsList->Highlight(idx, color);
}
std::shared_ptr<Macro> AdvSceneSwitcher::GetSelectedMacro()
std::shared_ptr<Macro> AdvSceneSwitcher::GetSelectedMacro() const
{
return ui->macros->GetCurrentMacro();
}
std::vector<std::shared_ptr<Macro>> AdvSceneSwitcher::GetSelectedMacros()
std::vector<std::shared_ptr<Macro>> AdvSceneSwitcher::GetSelectedMacros() const
{
return ui->macros->GetCurrentMacros();
}
void AdvSceneSwitcher::MacroSelectionAboutToChange()
void AdvSceneSwitcher::MacroSelectionAboutToChange() const
{
if (loading) {
return;
@@ -717,6 +819,10 @@ void AdvSceneSwitcher::MacroSelectionAboutToChange()
}
macro->SetElseActionSplitterPosition(
ui->macroElseActionSplitter->sizes());
ui->conditionsList->CacheCurrentWidgetsFor(macro);
ui->actionsList->CacheCurrentWidgetsFor(macro);
ui->elseActionsList->CacheCurrentWidgetsFor(macro);
}
void AdvSceneSwitcher::MacroSelectionChanged()
@@ -739,10 +845,13 @@ void AdvSceneSwitcher::MacroSelectionChanged()
return;
}
SetEditMacro(*macro);
obs_frontend_save();
if (GetGlobalMacroSettings()._saveSettingsOnMacroChange) {
obs_frontend_save();
}
}
void AdvSceneSwitcher::HighlightOnChange()
void AdvSceneSwitcher::HighlightOnChange() const
{
if (!GetGlobalMacroSettings()._highlightActions &&
!GetGlobalMacroSettings()._highlightExecuted) {
@@ -760,6 +869,16 @@ void AdvSceneSwitcher::HighlightOnChange()
}
}
static void resetSegmentHighlights(MacroSegmentList *list)
{
MacroSegmentEdit *widget = nullptr;
for (int i = 0; (widget = list->WidgetAt(i)); i++) {
if (widget && widget->Data()) {
(void)widget->Data()->GetHighlightAndReset();
}
}
}
void AdvSceneSwitcher::on_macroSettings_clicked()
{
GlobalMacroSettings prop = GetGlobalMacroSettings();
@@ -768,8 +887,22 @@ void AdvSceneSwitcher::on_macroSettings_clicked()
if (!accepted) {
return;
}
GetGlobalMacroSettings() = prop;
emit HighlightMacrosChanged(prop._highlightExecuted);
// Reset highlights to avoid all macro segments being highlighted, which
// would have been highlighted at least once since the moment the
// highlighting was disabled
if (prop._highlightConditions) {
resetSegmentHighlights(ui->conditionsList);
}
if (prop._highlightActions) {
resetSegmentHighlights(ui->actionsList);
resetSegmentHighlights(ui->elseActionsList);
}
SetCheckIntervalTooLowVisibility();
}
static void moveControlsToSplitter(QSplitter *splitter, int idx,
@@ -1128,22 +1261,46 @@ static void setupCopyPasteContextMenuEnry(AdvSceneSwitcher *ss,
[ss]() { ss->CopyMacroSegment(); });
copy->setEnabled(!!segmentEdit);
bool pasteAsElseAction = true;
const char *pasteText = "AdvSceneSwitcher.macroTab.segment.paste";
if (MacroActionIsInClipboard()) {
if (IsCursorInWidgetArea(ss->ui->macroActions)) {
pasteAsElseAction = false;
pasteText =
"AdvSceneSwitcher.macroTab.segment.pasteAction";
} else if (IsCursorInWidgetArea(ss->ui->macroElseActions)) {
pasteAsElseAction = true;
pasteText =
"AdvSceneSwitcher.macroTab.segment.pasteElseAction";
}
}
auto paste = menu.addAction(obs_module_text(pasteText), ss,
[ss]() { ss->PasteMacroSegment(); });
auto paste = menu.addAction(
obs_module_text(pasteText), ss, [ss, pasteAsElseAction]() {
SetCopySegmentTargetActionType(pasteAsElseAction);
ss->PasteMacroSegment();
});
paste->setEnabled(MacroSegmentIsInClipboard());
}
static void setupRemoveContextMenuEnry(
AdvSceneSwitcher *ss,
const std::function<void(AdvSceneSwitcher *, int)> &remove,
const QPoint &pos, MacroSegmentList *list, QMenu &menu)
{
const auto segmentEditIndex = list->IndexAt(pos);
if (segmentEditIndex == -1) {
return;
}
menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.segment.remove"), ss,
[ss, remove, segmentEditIndex]() {
remove(ss, segmentEditIndex);
});
}
static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
std::function<void(AdvSceneSwitcher *, int)> remove,
std::function<void(AdvSceneSwitcher *)> expand,
std::function<void(AdvSceneSwitcher *)> collapse,
std::function<void(AdvSceneSwitcher *)> maximize,
@@ -1155,6 +1312,8 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
setupCopyPasteContextMenuEnry(ss, segmentEdit, menu);
menu.addSeparator();
setupRemoveContextMenuEnry(ss, remove, pos, list, menu);
menu.addSeparator();
setupSegmentLabelContextMenuEntries(segmentEdit, menu);
menu.addSeparator();
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
@@ -1171,7 +1330,8 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
void AdvSceneSwitcher::ShowMacroActionsContextMenu(const QPoint &pos)
{
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllActions,
setupConextMenu(this, pos, &AdvSceneSwitcher::RemoveMacroAction,
&AdvSceneSwitcher::ExpandAllActions,
&AdvSceneSwitcher::CollapseAllActions,
&AdvSceneSwitcher::MaximizeActions,
&AdvSceneSwitcher::MinimizeActions, ui->actionsList);
@@ -1179,7 +1339,8 @@ void AdvSceneSwitcher::ShowMacroActionsContextMenu(const QPoint &pos)
void AdvSceneSwitcher::ShowMacroElseActionsContextMenu(const QPoint &pos)
{
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllElseActions,
setupConextMenu(this, pos, &AdvSceneSwitcher::RemoveMacroElseAction,
&AdvSceneSwitcher::ExpandAllElseActions,
&AdvSceneSwitcher::CollapseAllElseActions,
&AdvSceneSwitcher::MaximizeElseActions,
&AdvSceneSwitcher::MinimizeElseActions,
@@ -1188,7 +1349,8 @@ void AdvSceneSwitcher::ShowMacroElseActionsContextMenu(const QPoint &pos)
void AdvSceneSwitcher::ShowMacroConditionsContextMenu(const QPoint &pos)
{
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllConditions,
setupConextMenu(this, pos, &AdvSceneSwitcher::RemoveMacroCondition,
&AdvSceneSwitcher::ExpandAllConditions,
&AdvSceneSwitcher::CollapseAllConditions,
&AdvSceneSwitcher::MaximizeConditions,
&AdvSceneSwitcher::MinimizeConditions,
@@ -1218,13 +1380,12 @@ void AdvSceneSwitcher::CopyMacro()
Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
ui->macros->Add(newMacro, macro);
addPulse->deleteLater();
addPulse = nullptr;
disableAddButtonHighlight();
emit MacroAdded(QString::fromStdString(name));
}
void setCollapsedHelper(const std::shared_ptr<Macro> &m, MacroSegmentList *list,
bool collapsed)
static void setCollapsedHelper(const std::shared_ptr<Macro> &m,
MacroSegmentList *list, bool collapsed)
{
if (!m) {
return;
@@ -1232,32 +1393,32 @@ void setCollapsedHelper(const std::shared_ptr<Macro> &m, MacroSegmentList *list,
list->SetCollapsed(collapsed);
}
void AdvSceneSwitcher::ExpandAllActions()
void AdvSceneSwitcher::ExpandAllActions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->actionsList, false);
}
void AdvSceneSwitcher::ExpandAllElseActions()
void AdvSceneSwitcher::ExpandAllElseActions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->elseActionsList, false);
}
void AdvSceneSwitcher::ExpandAllConditions()
void AdvSceneSwitcher::ExpandAllConditions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->conditionsList, false);
}
void AdvSceneSwitcher::CollapseAllActions()
void AdvSceneSwitcher::CollapseAllActions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->actionsList, true);
}
void AdvSceneSwitcher::CollapseAllElseActions()
void AdvSceneSwitcher::CollapseAllElseActions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->elseActionsList, true);
}
void AdvSceneSwitcher::CollapseAllConditions()
void AdvSceneSwitcher::CollapseAllConditions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->conditionsList, true);
}
@@ -1272,7 +1433,7 @@ static void reduceSizeOfSplitterIdx(QSplitter *splitter, int idx)
splitter->setSizes(sizes);
}
void AdvSceneSwitcher::MinimizeActions()
void AdvSceneSwitcher::MinimizeActions() const
{
auto macro = GetSelectedMacro();
if (!macro) {
@@ -1286,13 +1447,13 @@ void AdvSceneSwitcher::MinimizeActions()
}
}
void AdvSceneSwitcher::MaximizeActions()
void AdvSceneSwitcher::MaximizeActions() const
{
MinimizeElseActions();
MinimizeConditions();
}
void AdvSceneSwitcher::MinimizeElseActions()
void AdvSceneSwitcher::MinimizeElseActions() const
{
auto macro = GetSelectedMacro();
if (!macro) {
@@ -1305,24 +1466,24 @@ void AdvSceneSwitcher::MinimizeElseActions()
}
}
void AdvSceneSwitcher::MaximizeElseActions()
void AdvSceneSwitcher::MaximizeElseActions() const
{
MinimizeConditions();
reduceSizeOfSplitterIdx(ui->macroElseActionSplitter, 0);
}
void AdvSceneSwitcher::MinimizeConditions()
void AdvSceneSwitcher::MinimizeConditions() const
{
reduceSizeOfSplitterIdx(ui->macroActionConditionSplitter, 0);
}
void AdvSceneSwitcher::MaximizeConditions()
void AdvSceneSwitcher::MaximizeConditions() const
{
MinimizeElseActions();
MinimizeActions();
}
void AdvSceneSwitcher::on_toggleElseActions_clicked()
void AdvSceneSwitcher::on_toggleElseActions_clicked() const
{
auto elsePosition = ui->macroElseActionSplitter->sizes();
if (elsePosition[1] == 0) {
@@ -1333,14 +1494,14 @@ void AdvSceneSwitcher::on_toggleElseActions_clicked()
maximizeFirstSplitterEntry(ui->macroElseActionSplitter);
}
void AdvSceneSwitcher::SetElseActionsStateToHidden()
void AdvSceneSwitcher::SetElseActionsStateToHidden() const
{
ui->toggleElseActions->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.toggleElseActions.show.tooltip"));
ui->toggleElseActions->setChecked(false);
}
void AdvSceneSwitcher::SetElseActionsStateToVisible()
void AdvSceneSwitcher::SetElseActionsStateToVisible() const
{
ui->toggleElseActions->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.toggleElseActions.hide.tooltip"));
@@ -1518,7 +1679,7 @@ static void fadeWidgets(const std::vector<QWidget *> &widgets, bool fadeOut)
}
}
void AdvSceneSwitcher::HighlightControls()
void AdvSceneSwitcher::HighlightControls() const
{
const std::vector<QWidget *> conditionControls{
ui->conditionAdd, ui->conditionRemove, ui->conditionTop,

View File

@@ -160,7 +160,7 @@ void MacroTreeItem::Update(bool force)
_spacer = nullptr;
}
if (_type == Type::Group) {
if (_type == Type::Group && _expand) {
_boxLayout->removeWidget(_expand);
_expand->deleteLater();
_expand = nullptr;
@@ -178,6 +178,7 @@ void MacroTreeItem::Update(bool force)
QSizePolicy::Maximum);
_expand->setMaximumSize(10, 16);
_expand->setMinimumSize(10, 0);
_expand->setProperty("class", "checkbox-icon indicator-expand");
#ifdef __APPLE__
_expand->setAttribute(Qt::WA_LayoutUsesWidgetRect);
#endif
@@ -888,14 +889,7 @@ void MacroTree::dropEvent(QDropEvent *event)
QModelIndexList indices = selectedIndexes();
DropIndicatorPosition indicator = dropIndicatorPosition();
int row = indexAt(
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
event->position().toPoint()
#else
event->pos()
#endif
)
.row();
int row = indexAt(event->position().toPoint()).row();
bool emptyDrop = row == -1;
if (emptyDrop) {
@@ -1307,10 +1301,10 @@ QSize MacroTreeDelegate::sizeHint(const QStyleOptionViewItem &option,
QWidget *item = tree->indexWidget(index);
if (!item) {
return (QSize(0, 0));
return QStyledItemDelegate::sizeHint(option, index);
}
return (QSize(option.widget->minimumWidth(), item->height()));
return QSize(item->sizeHint());
}
} // namespace advss

View File

@@ -3,6 +3,7 @@
#include "macro-condition-factory.hpp"
#include "macro-dock.hpp"
#include "macro-helpers.hpp"
#include "macro-settings.hpp"
#include "plugin-state-helpers.hpp"
#include "splitter-helpers.hpp"
#include "sync-helpers.hpp"
@@ -76,13 +77,21 @@ namespace advss {
static std::deque<std::shared_ptr<Macro>> macros;
Macro::Macro(const std::string &name, const bool addHotkey)
Macro::Macro(const std::string &name)
{
SetName(name);
if (addHotkey) {
}
Macro::Macro(const std::string &name, const GlobalMacroSettings &settings)
: Macro(name)
{
if (settings._newMacroRegisterHotkeys) {
SetupHotkeys();
}
_registerHotkeys = addHotkey;
_registerHotkeys = settings._newMacroRegisterHotkeys;
_checkInParallel = settings._newMacroCheckInParallel;
_useShortCircuitEvaluation =
settings._newMacroUseShortCircuitEvaluation;
}
Macro::~Macro()
@@ -102,7 +111,8 @@ std::shared_ptr<Macro>
Macro::CreateGroup(const std::string &name,
std::vector<std::shared_ptr<Macro>> &children)
{
auto group = std::make_shared<Macro>(name, false);
auto group = std::make_shared<Macro>(name);
group->_registerHotkeys = false;
for (auto &c : children) {
c->SetParent(group);
}
@@ -181,41 +191,113 @@ static bool checkCondition(const std::shared_ptr<MacroCondition> &condition)
return conditionMatched;
}
bool Macro::CeckMatch(bool ignorePause)
bool Macro::CheckConditionHelper(
const std::shared_ptr<MacroCondition> &condition) const
{
bool conditionMatched = false;
bool wasEvaluated = false;
const auto evaluateCondition = [&condition, &conditionMatched,
&wasEvaluated]() -> bool {
conditionMatched = checkCondition(condition);
conditionMatched =
condition->CheckDurationModifier(conditionMatched);
wasEvaluated = true;
return conditionMatched;
};
const auto logicType = condition->GetLogicType();
if (logicType == Logic::Type::NONE) {
vblog(LOG_INFO, "ignoring condition '%s' for '%s'",
condition->GetId().c_str(), _name.c_str());
if (!_useShortCircuitEvaluation) {
(void)evaluateCondition();
}
return _matched;
}
bool result = _useShortCircuitEvaluation
// Evaluate the condition result if needed
? Logic::ApplyConditionLogic(logicType, _matched,
evaluateCondition,
_name.c_str())
// Evaluate the condition result right away
: Logic::ApplyConditionLogic(logicType, _matched,
evaluateCondition(),
_name.c_str());
const bool isNegativeLogicType = Logic::IsNegationType(logicType);
if (wasEvaluated && ((conditionMatched && !isNegativeLogicType) ||
(!conditionMatched && isNegativeLogicType))) {
condition->EnableHighlight();
}
if (VerboseLoggingEnabled()) {
if (wasEvaluated) {
blog(LOG_INFO, "condition %s returned %d",
condition->GetId().c_str(), conditionMatched);
} else {
blog(LOG_INFO,
"condition %s evaluation skipped (short circuit)",
condition->GetId().c_str());
}
}
return result;
}
bool Macro::CheckConditions(bool ignorePause)
{
if (_isGroup) {
return false;
}
_matched = false;
for (auto &condition : _conditions) {
if (_paused && !ignorePause) {
vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
const auto checkConditionsTask =
[this,
ignorePause](const std::deque<std::shared_ptr<MacroCondition>>
&conditions) {
for (auto &condition : conditions) {
if (!condition) {
continue;
}
if (_paused && !ignorePause) {
vblog(LOG_INFO, "Macro %s is paused",
_name.c_str());
return false;
}
_matched = CheckConditionHelper(condition);
}
return _matched;
};
if (CheckInParallel()) {
if (!_conditionCheckFuture.valid()) {
_stop = false;
_matched = false;
_conditionCheckFuture = std::async(
std::launch::async,
[this, checkConditionsTask]() {
// Copy to avoid settings modifications
// causing issues
const auto conditionsCopy = _conditions;
checkConditionsTask(conditionsCopy);
});
return false;
}
bool conditionMatched = checkCondition(condition);
conditionMatched =
condition->CheckDurationModifier(conditionMatched);
const auto logicType = condition->GetLogicType();
if (logicType == Logic::Type::NONE) {
vblog(LOG_INFO, "ignoring condition '%s' for '%s'",
condition->GetId().c_str(), _name.c_str());
continue;
if (_conditionCheckFuture.wait_for(std::chrono::seconds(0)) !=
std::future_status::ready) {
vblog(LOG_INFO,
"Macro %s still waiting for condition check result",
_name.c_str());
return false;
}
vblog(LOG_INFO, "condition %s returned %d",
condition->GetId().c_str(), conditionMatched);
const bool isNegativeLogicType =
Logic::IsNegationType(logicType);
if ((conditionMatched && !isNegativeLogicType) ||
(!conditionMatched && isNegativeLogicType)) {
condition->EnableHighlight();
}
_matched = Logic::ApplyConditionLogic(
logicType, _matched, conditionMatched, _name.c_str());
_conditionCheckFuture.get();
} else {
_stop = false;
_matched = false;
_matched = checkConditionsTask(_conditions);
}
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
@@ -232,7 +314,9 @@ bool Macro::CeckMatch(bool ignorePause)
bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
{
if (!_done) {
if (_actionRunFuture.valid() &&
_actionRunFuture.wait_for(std::chrono::seconds(0)) !=
std::future_status::ready) {
vblog(LOG_INFO, "Macro %s already running", _name.c_str());
if (!_stopActionsIfNotDone) {
@@ -250,14 +334,15 @@ bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
: std::bind(&Macro::RunElseActions, this,
std::placeholders::_1);
_stop = false;
_done = false;
bool ret = true;
if (_runInParallel || forceParallel) {
if (_backgroundThread.joinable()) {
_backgroundThread.join();
if (_actionRunFuture.valid()) {
_actionRunFuture.get();
}
_backgroundThread = std::thread(
[this, runFunc, ignorePause] { runFunc(ignorePause); });
_actionRunFuture = std::async(std::launch::async,
[this, runFunc, ignorePause] {
runFunc(ignorePause);
});
} else {
ret = runFunc(ignorePause);
}
@@ -273,14 +358,35 @@ bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
return ret;
}
bool Macro::WasExecutedSince(
const std::chrono::high_resolution_clock::time_point &time) const
bool Macro::WasExecutedSince(const TimePoint &time) const
{
return _lastExecutionTime > time;
}
bool Macro::ConditionsShouldBeChecked() const
{
if (!_useCustomConditionCheckInterval) {
return true;
}
const auto timePassed = std::chrono::high_resolution_clock::now() -
LastConditionCheckTime();
const auto timePassedMs =
std::chrono::duration_cast<std::chrono::milliseconds>(
timePassed);
return timePassedMs.count() >=
_customConditionCheckInterval.Milliseconds();
}
bool Macro::ShouldRunActions() const
{
if (CheckInParallel() && _conditionCheckFuture.valid()) {
vblog(LOG_INFO,
"%s not ready to perform actions as condition check is still running",
_name.c_str());
return false;
}
const bool hasActionsToExecute =
!_paused && (_matched || _elseActions.size() > 0) &&
(!_performActionsOnChange || _conditionSateChanged);
@@ -301,18 +407,6 @@ bool Macro::ShouldRunActions() const
return hasActionsToExecute;
}
int64_t Macro::MsSinceLastCheck() const
{
if (_lastCheckTime.time_since_epoch().count() == 0) {
return 0;
}
const auto timePassed =
std::chrono::high_resolution_clock::now() - _lastCheckTime;
return std::chrono::duration_cast<std::chrono::milliseconds>(timePassed)
.count() +
1;
}
void Macro::SetName(const std::string &name)
{
const bool nameChanged = _name == name;
@@ -345,6 +439,9 @@ bool Macro::RunActionsHelper(
bool actionsExecutedSuccessfully = true;
for (auto &action : actions) {
if (!action) {
continue;
}
if (action->Enabled()) {
action->LogAction();
actionsExecutedSuccessfully =
@@ -362,24 +459,22 @@ bool Macro::RunActionsHelper(
action->EnableHighlight();
}
}
_done = true;
return actionsExecutedSuccessfully;
}
bool Macro::RunActions(bool ignorePause)
{
vblog(LOG_INFO, "running actions of %s", _name.c_str());
mblog(LOG_INFO, "running actions of %s", _name.c_str());
return RunActionsHelper(_actions, ignorePause);
}
bool Macro::RunElseActions(bool ignorePause)
{
vblog(LOG_INFO, "running else actions of %s", _name.c_str());
mblog(LOG_INFO, "running else actions of %s", _name.c_str());
return RunActionsHelper(_elseActions, ignorePause);
}
bool Macro::WasPausedSince(
const std::chrono::high_resolution_clock::time_point &time) const
bool Macro::WasPausedSince(const TimePoint &time) const
{
return _lastUnpauseTime > time;
}
@@ -394,6 +489,36 @@ void Macro::SetStopActionsIfNotDone(bool stopActionsIfNotDone)
_stopActionsIfNotDone = stopActionsIfNotDone;
}
void Macro::SetShortCircuitEvaluation(bool useShortCircuitEvaluation)
{
_useShortCircuitEvaluation = useShortCircuitEvaluation;
}
bool Macro::ShortCircuitEvaluationEnabled() const
{
return _useShortCircuitEvaluation;
}
void Macro::SetCustomConditionCheckIntervalEnabled(bool enable)
{
_useCustomConditionCheckInterval = enable;
}
bool Macro::CustomConditionCheckIntervalEnabled() const
{
return _useCustomConditionCheckInterval;
}
void Macro::SetCustomConditionCheckInterval(const Duration &duration)
{
_customConditionCheckInterval = duration;
}
Duration Macro::GetCustomConditionCheckInterval() const
{
return _customConditionCheckInterval;
}
void Macro::SetPaused(bool pause)
{
if (_paused && !pause) {
@@ -414,6 +539,16 @@ void Macro::AddHelperThread(std::thread &&newThread)
_helperThreads.push_back(std::move(newThread));
}
void Macro::SetPauseStateSaveBehavior(PauseStateSaveBehavior behavior)
{
_pauseSaveBehavior = behavior;
}
Macro::PauseStateSaveBehavior Macro::GetPauseStateSaveBehavior() const
{
return _pauseSaveBehavior;
}
void Macro::Stop()
{
_stop = true;
@@ -423,9 +558,35 @@ void Macro::Stop()
t.join();
}
}
if (_backgroundThread.joinable()) {
_backgroundThread.join();
if (_actionRunFuture.valid()) {
_actionRunFuture.get();
}
if (_conditionCheckFuture.valid()) {
_conditionCheckFuture.get();
}
}
void Macro::SetCheckInParallel(bool parallel)
{
_checkInParallel = parallel;
_conditionCheckFuture = {};
}
bool Macro::ParallelTasksCompleted() const
{
// A parallel action run might be triggered by RunInParallel() or the
// "Run Macro" button, so checking just for RunInParallel() will not
// suffice
if (!CheckInParallel() && !_actionRunFuture.valid()) {
return true;
}
if (_actionRunFuture.valid()) {
return false;
}
if (CheckInParallel() && _conditionCheckFuture.valid()) {
return false;
}
return true;
}
MacroInputVariables Macro::GetInputVariables() const
@@ -563,16 +724,31 @@ std::deque<std::shared_ptr<MacroCondition>> &Macro::Conditions()
return _conditions;
}
const std::deque<std::shared_ptr<MacroCondition>> &Macro::Conditions() const
{
return _conditions;
}
std::deque<std::shared_ptr<MacroAction>> &Macro::Actions()
{
return _actions;
}
const std::deque<std::shared_ptr<MacroAction>> &Macro::Actions() const
{
return _actions;
}
std::deque<std::shared_ptr<MacroAction>> &Macro::ElseActions()
{
return _elseActions;
}
const std::deque<std::shared_ptr<MacroAction>> &Macro::ElseActions() const
{
return _elseActions;
}
static void updateIndicesHelper(std::deque<std::shared_ptr<MacroSegment>> &list)
{
int idx = 0;
@@ -613,11 +789,6 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const
if (!saveForCopy) {
obs_data_set_string(obj, "name", _name.c_str());
}
obs_data_set_bool(obj, "pause", _paused);
obs_data_set_bool(obj, "parallel", _runInParallel);
obs_data_set_bool(obj, "onChange", _performActionsOnChange);
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
obs_data_set_bool(obj, "group", _isGroup);
if (_isGroup) {
@@ -628,6 +799,20 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const
return true;
}
obs_data_set_int(obj, "pauseSaveBehavior",
static_cast<int>(_pauseSaveBehavior));
obs_data_set_bool(obj, "pause", _paused);
obs_data_set_bool(obj, "parallel", _runInParallel);
obs_data_set_bool(obj, "checkConditionsInParallel", _checkInParallel);
obs_data_set_bool(obj, "onChange", _performActionsOnChange);
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
obs_data_set_bool(obj, "useShortCircuitEvaluation",
_useShortCircuitEvaluation);
obs_data_set_bool(obj, "useCustomConditionCheckInterval",
_useCustomConditionCheckInterval);
_customConditionCheckInterval.Save(obj, "customConditionCheckInterval");
SaveDockSettings(obj, saveForCopy);
SaveSplitterPos(_actionConditionSplitterPosition, obj,
@@ -676,11 +861,6 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const
bool Macro::Load(obs_data_t *obj)
{
_name = obs_data_get_string(obj, "name");
_paused = obs_data_get_bool(obj, "pause");
_runInParallel = obs_data_get_bool(obj, "parallel");
_performActionsOnChange = obs_data_get_bool(obj, "onChange");
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
_isGroup = obs_data_get_bool(obj, "group");
if (_isGroup) {
@@ -692,6 +872,33 @@ bool Macro::Load(obs_data_t *obj)
return true;
}
_pauseSaveBehavior = static_cast<PauseStateSaveBehavior>(
obs_data_get_int(obj, "pauseSaveBehavior"));
switch (_pauseSaveBehavior) {
case PauseStateSaveBehavior::PERSIST:
_paused = obs_data_get_bool(obj, "pause");
break;
case PauseStateSaveBehavior::PAUSE:
_paused = true;
break;
case PauseStateSaveBehavior::UNPAUSE:
_paused = false;
break;
default:
_paused = obs_data_get_bool(obj, "pause");
break;
}
_runInParallel = obs_data_get_bool(obj, "parallel");
_checkInParallel = obs_data_get_bool(obj, "checkConditionsInParallel");
_performActionsOnChange = obs_data_get_bool(obj, "onChange");
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
_useShortCircuitEvaluation =
obs_data_get_bool(obj, "useShortCircuitEvaluation");
_useCustomConditionCheckInterval =
obs_data_get_bool(obj, "useCustomConditionCheckInterval");
_customConditionCheckInterval.Load(obj, "customConditionCheckInterval");
LoadDockSettings(obj);
LoadSplitterPos(_actionConditionSplitterPosition, obj,
@@ -1253,7 +1460,15 @@ bool CheckMacros()
{
bool matchFound = false;
for (const auto &m : macros) {
if (m->CeckMatch() || m->ElseActions().size() > 0) {
if (!m->ConditionsShouldBeChecked()) {
vblog(LOG_INFO,
"skipping condition check for macro \"%s\" "
"(custom check interval)",
m->Name().c_str());
continue;
}
if (m->CheckConditions() || m->ElseActions().size() > 0) {
matchFound = true;
// This has to be performed here for now as actions are
// not performed immediately after checking conditions.
@@ -1299,7 +1514,7 @@ bool RunMacros()
continue;
}
vblog(LOG_INFO, "running macro: %s", m->Name().c_str());
if (!m->PerformActions(m->Matched())) {
if (!m->PerformActions(m->ConditionsMatched())) {
blog(LOG_WARNING, "abort macro: %s", m->Name().c_str());
}
}
@@ -1346,8 +1561,39 @@ std::weak_ptr<Macro> GetWeakMacroByName(const char *name)
void InvalidateMacroTempVarValues()
{
for (const auto &m : macros) {
// Do not invalidate the temp vars set during condition checks
// or action executions running in parallel to the "main" macro
// loop, as otherwise access to the information stored in those
// variables might get lost while those checks or actions are
// still ongoing.
if (!m->ParallelTasksCompleted()) {
continue;
}
m->InvalidateTempVarValues();
}
}
std::shared_ptr<Macro> GetMacroWithInvalidConditionInterval()
{
if (macros.empty()) {
return {};
}
for (const auto &macro : macros) {
if (!macro) {
continue;
}
if (!macro->CustomConditionCheckIntervalEnabled()) {
continue;
}
if (macro->GetCustomConditionCheckInterval().Milliseconds() <
GetIntervalValue()) {
return macro;
}
}
return {};
}
} // namespace advss

View File

@@ -7,6 +7,7 @@
#include "variable-string.hpp"
#include "temp-variable.hpp"
#include <future>
#include <QString>
#include <QByteArray>
#include <string>
@@ -20,26 +21,36 @@
namespace advss {
class MacroDock;
class GlobalMacroSettings;
class Macro {
using TimePoint = std::chrono::high_resolution_clock::time_point;
public:
Macro(const std::string &name = "", const bool addHotkey = false);
virtual ~Macro();
enum class PauseStateSaveBehavior { PERSIST, PAUSE, UNPAUSE };
Macro(const std::string &name = "");
Macro(const std::string &name, const GlobalMacroSettings &settings);
~Macro();
std::string Name() const { return _name; }
void SetName(const std::string &name);
bool CeckMatch(bool ignorePause = false);
bool Matched() const { return _matched; }
int64_t MsSinceLastCheck() const;
bool CheckConditions(bool ignorePause = false);
bool ConditionsMatched() const { return _matched; }
TimePoint LastConditionCheckTime() const { return _lastCheckTime; }
bool ConditionsShouldBeChecked() const;
bool ShouldRunActions() const;
bool PerformActions(bool match, bool forceParallel = false,
bool ignorePause = false);
void SetPaused(bool pause = true);
bool Paused() const { return _paused; }
bool WasPausedSince(
const std::chrono::high_resolution_clock::time_point &) const;
bool WasPausedSince(const TimePoint &) const;
void SetPauseStateSaveBehavior(PauseStateSaveBehavior);
PauseStateSaveBehavior GetPauseStateSaveBehavior() const;
void Stop();
bool GetStop() const { return _stop; }
@@ -54,12 +65,23 @@ public:
void SetStopActionsIfNotDone(bool stopActionsIfNotDone);
bool StopActionsIfNotDone() const { return _stopActionsIfNotDone; }
void SetShortCircuitEvaluation(bool useShortCircuitEvaluation);
bool ShortCircuitEvaluationEnabled() const;
void SetCustomConditionCheckIntervalEnabled(bool enable);
bool CustomConditionCheckIntervalEnabled() const;
void SetCustomConditionCheckInterval(const Duration &);
Duration GetCustomConditionCheckInterval() const;
int RunCount() const { return _runCount; };
void ResetRunCount() { _runCount = 0; };
void AddHelperThread(std::thread &&);
void SetRunInParallel(bool parallel) { _runInParallel = parallel; }
bool RunInParallel() const { return _runInParallel; }
bool CheckInParallel() const { return _checkInParallel; }
void SetCheckInParallel(bool parallel);
bool ParallelTasksCompleted() const;
// Input variables
MacroInputVariables GetInputVariables() const;
@@ -73,8 +95,11 @@ public:
// Macro segments
std::deque<std::shared_ptr<MacroCondition>> &Conditions();
const std::deque<std::shared_ptr<MacroCondition>> &Conditions() const;
std::deque<std::shared_ptr<MacroAction>> &Actions();
const std::deque<std::shared_ptr<MacroAction>> &Actions() const;
std::deque<std::shared_ptr<MacroAction>> &ElseActions();
const std::deque<std::shared_ptr<MacroAction>> &ElseActions() const;
void UpdateActionIndices();
void UpdateElseActionIndices();
void UpdateConditionIndices();
@@ -113,8 +138,7 @@ public:
void SetElseActionSplitterPosition(const QList<int>);
const QList<int> &GetElseActionSplitterPosition() const;
bool HasValidSplitterPositions() const;
bool WasExecutedSince(
const std::chrono::high_resolution_clock::time_point &) const;
bool WasExecutedSince(const TimePoint &) const;
bool OnChangePreventedActionsRecently();
void ResetUIHelpers();
@@ -149,6 +173,9 @@ private:
void ClearHotkeys() const;
void SetHotkeysDesc() const;
bool
CheckConditionHelper(const std::shared_ptr<MacroCondition> &) const;
bool RunActionsHelper(
const std::deque<std::shared_ptr<MacroAction>> &actions,
bool ignorePause);
@@ -163,11 +190,10 @@ private:
std::string _name = "";
bool _die = false;
bool _stop = false;
bool _done = true;
std::chrono::high_resolution_clock::time_point _lastCheckTime{};
std::chrono::high_resolution_clock::time_point _lastUnpauseTime{};
std::chrono::high_resolution_clock::time_point _lastExecutionTime{};
std::thread _backgroundThread;
std::future<void> _actionRunFuture;
TimePoint _lastCheckTime{};
TimePoint _lastUnpauseTime{};
TimePoint _lastExecutionTime{};
std::vector<std::thread> _helperThreads;
std::deque<std::shared_ptr<MacroCondition>> _conditions;
@@ -179,10 +205,16 @@ private:
bool _isGroup = false;
bool _isCollapsed = false;
bool _runInParallel = false;
bool _matched = false;
bool _lastMatched = false;
bool _useShortCircuitEvaluation = false;
bool _useCustomConditionCheckInterval = false;
Duration _customConditionCheckInterval = 0.3;
bool _conditionSateChanged = false;
bool _runInParallel = false;
bool _checkInParallel = false;
bool _matched = false;
std::future<void> _conditionCheckFuture;
bool _lastMatched = false;
bool _performActionsOnChange = true;
bool _skipExecOnStart = false;
bool _stopActionsIfNotDone = false;
@@ -193,6 +225,9 @@ private:
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID;
PauseStateSaveBehavior _pauseSaveBehavior =
PauseStateSaveBehavior::PERSIST;
MacroInputVariables _inputVariables;
// UI helpers
@@ -230,5 +265,6 @@ Macro *GetMacroByName(const char *name);
Macro *GetMacroByQString(const QString &name);
std::weak_ptr<Macro> GetWeakMacroByName(const char *name);
void InvalidateMacroTempVarValues();
std::shared_ptr<Macro> GetMacroWithInvalidConditionInterval();
} // namespace advss

View File

@@ -5,6 +5,7 @@
#include "plugin-state-helpers.hpp"
#include "sync-helpers.hpp"
#include "tab-helpers.hpp"
#include "time-helpers.hpp"
#include "ui-helpers.hpp"
#include <QTimer>
@@ -39,8 +40,8 @@ ActionQueueTable *ActionQueueTable::Create()
void ActionQueueTable::Add()
{
auto newQueue = std::make_shared<ActionQueue>();
auto accepted =
ActionQueueSettingsDialog::AskForSettings(this, *newQueue);
auto accepted = ActionQueueSettingsDialog::AskForSettings(
GetSettingsWindow(), *newQueue);
if (!accepted) {
return;
}
@@ -55,6 +56,13 @@ void ActionQueueTable::Add()
QString::fromStdString(newQueue->Name()));
}
static double getSecondsSinceLastEmpty(ActionQueue *queue)
{
const auto time = std::chrono::high_resolution_clock::now() -
queue->GetLastEmptyTime();
return std::chrono::duration_cast<std::chrono::seconds>(time).count();
}
static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
{
assert(queue);
@@ -64,6 +72,7 @@ static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
result << QString::fromStdString(queue->Name());
}
result << QString::number(queue->Size())
<< FormatRelativeTime(getSecondsSinceLastEmpty(queue))
<< QString::fromStdString(obs_module_text(
queue->IsRunning()
? "AdvSceneSwitcher.actionQueueTab.yes"
@@ -115,7 +124,7 @@ static void openSettingsDialog()
auto oldName = queue->Name();
bool accepted = ActionQueueSettingsDialog::AskForSettings(
tabWidget->Table(), *queue.get());
GetSettingsWindow(), *queue.get());
if (accepted && oldName != queue->Name()) {
ActionQueueSignalManager::Instance()->Rename(
QString::fromStdString(oldName),
@@ -199,6 +208,8 @@ ActionQueueTable::ActionQueueTable(QTabWidget *parent)
"AdvSceneSwitcher.actionQueueTab.name.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.size.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.lastEmpty.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.isRunning.header")
<< obs_module_text(

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