mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 12:45:28 -05:00
Compare commits
254 Commits
1.25.0-bet
...
1.28.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d9519c849 | ||
|
|
0fd5f47982 | ||
|
|
62ce83a7f6 | ||
|
|
8ecee0070d | ||
|
|
a4970b1b07 | ||
|
|
140dc9c6f4 | ||
|
|
9a4d2935f8 | ||
|
|
335c964419 | ||
|
|
72b2a7a07a | ||
|
|
f863909f34 | ||
|
|
621ca201ef | ||
|
|
563ea1a5e7 | ||
|
|
2af6ee883b | ||
|
|
0e9875e6b5 | ||
|
|
c2d2e883c6 | ||
|
|
a196e56078 | ||
|
|
0536e4a60b | ||
|
|
55ab096e49 | ||
|
|
c39ea0854a | ||
|
|
aef080984e | ||
|
|
1aa2425e81 | ||
|
|
f5eadfa39c | ||
|
|
faa65facfe | ||
|
|
b60235d964 | ||
|
|
718b18248e | ||
|
|
4316e53039 | ||
|
|
790ac50385 | ||
|
|
3a87a8d155 | ||
|
|
91912f2aa5 | ||
|
|
624b841ca1 | ||
|
|
9eb4e90291 | ||
|
|
94263cc7a0 | ||
|
|
2d035758f4 | ||
|
|
86697002f7 | ||
|
|
2247152aa8 | ||
|
|
2528449c84 | ||
|
|
f2df3d18be | ||
|
|
c7c48d03e9 | ||
|
|
0906c6bb3f | ||
|
|
9f2daf1a15 | ||
|
|
6fb8619671 | ||
|
|
36fb83246b | ||
|
|
e729d15e97 | ||
|
|
7836298076 | ||
|
|
354880ad52 | ||
|
|
c1ac9bb890 | ||
|
|
49bf1ad379 | ||
|
|
a6e30a1e20 | ||
|
|
2f54c2ee62 | ||
|
|
6ccbaad41c | ||
|
|
57d78a996a | ||
|
|
2d3dada30e | ||
|
|
5bf20d155f | ||
|
|
2b95598958 | ||
|
|
b561a20dde | ||
|
|
835722af34 | ||
|
|
93efc7cab3 | ||
|
|
d69364ec19 | ||
|
|
d75066df5f | ||
|
|
863c84026e | ||
|
|
a523081d77 | ||
|
|
66534519d6 | ||
|
|
b3b2114c45 | ||
|
|
510f83246e | ||
|
|
c969b21f93 | ||
|
|
6ec40ef8e9 | ||
|
|
27859e83b3 | ||
|
|
263565700a | ||
|
|
5bb10a4aef | ||
|
|
ee72bb192c | ||
|
|
1a1028cbaa | ||
|
|
2f4f8bcc74 | ||
|
|
4113615cd6 | ||
|
|
9522d7c0b4 | ||
|
|
cc3ea79836 | ||
|
|
f72e802d00 | ||
|
|
36201cbfb4 | ||
|
|
685e28d161 | ||
|
|
82d23dcf0e | ||
|
|
ad322d54f0 | ||
|
|
8bac6fe829 | ||
|
|
ac7531decb | ||
|
|
7af4969ec6 | ||
|
|
a088325968 | ||
|
|
57bc58be8e | ||
|
|
f9730b1bc2 | ||
|
|
d4025214e5 | ||
|
|
c431e6c31d | ||
|
|
9941cfe9b5 | ||
|
|
15998012e5 | ||
|
|
bcb043f997 | ||
|
|
74116382b1 | ||
|
|
24f33fb0d2 | ||
|
|
d80df57ef8 | ||
|
|
240c47975c | ||
|
|
bedb3b8dc6 | ||
|
|
8032dde045 | ||
|
|
bfc51b2df5 | ||
|
|
2160da59f3 | ||
|
|
d3164e3ed6 | ||
|
|
17c72b772d | ||
|
|
3ff9a1f270 | ||
|
|
7d120d8b6a | ||
|
|
53cfe4b030 | ||
|
|
4dcd748a1f | ||
|
|
223ce0d0a0 | ||
|
|
5bd3341681 | ||
|
|
bb527d6910 | ||
|
|
dfedbcf31a | ||
|
|
1bbfd16b3f | ||
|
|
7f5737d03f | ||
|
|
7702541d81 | ||
|
|
3bc15e585c | ||
|
|
d77101b6aa | ||
|
|
c1e23f9c7a | ||
|
|
70415cd8f4 | ||
|
|
f50cd76493 | ||
|
|
e4af698881 | ||
|
|
33120a359c | ||
|
|
091a364097 | ||
|
|
e3ff6f34fc | ||
|
|
ed75a221fe | ||
|
|
7b48cb2b8d | ||
|
|
9d4c8ab475 | ||
|
|
3d61ea7d25 | ||
|
|
99c8a46296 | ||
|
|
7e1d20031c | ||
|
|
0e4b000fb1 | ||
|
|
fc2451ae08 | ||
|
|
be44577967 | ||
|
|
3480ff238e | ||
|
|
ee68427036 | ||
|
|
37c32dd1ed | ||
|
|
c05a92d417 | ||
|
|
d72a0c0d38 | ||
|
|
235b31fccc | ||
|
|
286f781f4d | ||
|
|
b7516cac5b | ||
|
|
45967a090f | ||
|
|
687fb4623e | ||
|
|
4157ae4ed2 | ||
|
|
5e1469b0d8 | ||
|
|
b3bde6c59f | ||
|
|
032f5e2fe9 | ||
|
|
91c053dfd8 | ||
|
|
1f1ef4ca03 | ||
|
|
7cc44b4470 | ||
|
|
54356d9410 | ||
|
|
defbdf8b7a | ||
|
|
a01af6cfc3 | ||
|
|
4d9c7f1054 | ||
|
|
ced36e2b5b | ||
|
|
d2749f29b0 | ||
|
|
b78c8bc1de | ||
|
|
cc4a3560d8 | ||
|
|
3ab2ea9e66 | ||
|
|
69b82ee2a3 | ||
|
|
f14e6bf252 | ||
|
|
e51ae79b4c | ||
|
|
8f55856fd5 | ||
|
|
a360d53419 | ||
|
|
d28164d02f | ||
|
|
f723212394 | ||
|
|
a0cb08d18f | ||
|
|
d18044b764 | ||
|
|
406b5d61f5 | ||
|
|
9a4eef4a83 | ||
|
|
efaf9a2ef3 | ||
|
|
edb2952fd6 | ||
|
|
088828a351 | ||
|
|
e359b7fa00 | ||
|
|
44fc5177d6 | ||
|
|
604a27141f | ||
|
|
fd65a84d7d | ||
|
|
966b389807 | ||
|
|
a6bcce5a63 | ||
|
|
530fbdd282 | ||
|
|
54f1051456 | ||
|
|
d136bf5561 | ||
|
|
5cba948e02 | ||
|
|
ab922f6735 | ||
|
|
d71c87535f | ||
|
|
ce501bd972 | ||
|
|
ec5944f53c | ||
|
|
5e64e0bbaa | ||
|
|
e16f1d732d | ||
|
|
cdacfc946c | ||
|
|
26e854f0a3 | ||
|
|
2bc89364b2 | ||
|
|
e080d2de9b | ||
|
|
ca1262aeb7 | ||
|
|
cbc95e2095 | ||
|
|
9c5051cbf8 | ||
|
|
e6ca3390a2 | ||
|
|
e74b531905 | ||
|
|
01437183ac | ||
|
|
e5ab2ceca3 | ||
|
|
e19f4ddf7c | ||
|
|
4e3a062084 | ||
|
|
96db0ad507 | ||
|
|
6b4bcc074a | ||
|
|
14dd390680 | ||
|
|
3fe8ea8961 | ||
|
|
3ee913ea98 | ||
|
|
d18277653c | ||
|
|
8c478cc330 | ||
|
|
003ffc161b | ||
|
|
74dc0c871b | ||
|
|
c8545dea4d | ||
|
|
e1b8edd444 | ||
|
|
f3996855c3 | ||
|
|
ea17e8ea08 | ||
|
|
00fc313609 | ||
|
|
c72f9abc2b | ||
|
|
6df818503e | ||
|
|
b17a6cc109 | ||
|
|
82d1aed472 | ||
|
|
eb6fc6c140 | ||
|
|
2710406f5f | ||
|
|
a634499423 | ||
|
|
71b3fbaa19 | ||
|
|
c656bb4571 | ||
|
|
6c4e4b8cd8 | ||
|
|
7b1a256f8a | ||
|
|
66f9eaf8c2 | ||
|
|
7fa0ba3355 | ||
|
|
eec9244e4c | ||
|
|
e265e4828e | ||
|
|
ad6e720912 | ||
|
|
70dad8ebb4 | ||
|
|
497b3b3e04 | ||
|
|
891811aa47 | ||
|
|
be9ddaa4c9 | ||
|
|
03494c2915 | ||
|
|
bcf7c247bf | ||
|
|
132d89b350 | ||
|
|
daf16357c9 | ||
|
|
8080abc93c | ||
|
|
902715bd3e | ||
|
|
8271a6e8db | ||
|
|
6c121a093b | ||
|
|
ab0cce9eaa | ||
|
|
9acbead87b | ||
|
|
03e71e9183 | ||
|
|
f8dadd38b4 | ||
|
|
2928775f5b | ||
|
|
673ac8078b | ||
|
|
234ae27205 | ||
|
|
a740a29798 | ||
|
|
341a58b8e4 | ||
|
|
ba9a16e718 | ||
|
|
2b2bab8fbc | ||
|
|
f50f31ec4c | ||
|
|
3a1c353ea3 |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/build-aux/*.zsh eol=lf
|
||||
/build-aux/.functions/* eol=lf
|
||||
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -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.
|
||||
111
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
111
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
# 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 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
|
||||
7
.github/ISSUE_TEMPLATE/config.yml
vendored
7
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -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.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -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.
|
||||
37
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
37
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal 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 projectReport a bug or crash
|
||||
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
|
||||
3
.github/scripts/.Aptfile
vendored
3
.github/scripts/.Aptfile
vendored
@@ -9,4 +9,5 @@ package 'libxtst-dev'
|
||||
package 'libxss-dev'
|
||||
package 'libopencv-dev'
|
||||
package 'libtesseract-dev'
|
||||
package 'libprocps-dev'
|
||||
package 'libprocps-dev'
|
||||
package 'libusb-1.0-0-dev'
|
||||
3
.github/scripts/.Brewfile
vendored
3
.github/scripts/.Brewfile
vendored
@@ -1,6 +1,7 @@
|
||||
brew "ccache"
|
||||
brew "coreutils"
|
||||
brew "cmake"
|
||||
brew "git"
|
||||
brew "jq"
|
||||
brew "xcbeautify"
|
||||
brew "automake"
|
||||
brew "libtool"
|
||||
|
||||
3
.github/scripts/.Wingetfile
vendored
3
.github/scripts/.Wingetfile
vendored
@@ -1,3 +1,4 @@
|
||||
package 'cmake', path: 'Cmake\bin', bin: 'cmake'
|
||||
package 'innosetup', path: 'Inno Setup 6', bin: 'iscc'
|
||||
package 'OpenSSL', path: 'OpenSSL', bin: 'openssl'
|
||||
package 'OpenSSL', path: 'OpenSSL', bin: 'openssl'
|
||||
package 'Microsoft.VisualStudio.Locator', path: 'vswhere', bin: 'vswhere'
|
||||
128
.github/scripts/.build-deps.zsh
vendored
128
.github/scripts/.build-deps.zsh
vendored
@@ -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,34 +358,46 @@ 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}
|
||||
log_info "Prepare openssl ..."
|
||||
rm -rf openssl
|
||||
git clone git://git.openssl.org/openssl.git --branch openssl-3.1.2 --depth 1
|
||||
git clone https://github.com/openssl/openssl.git --branch openssl-3.1.2 --depth 1
|
||||
mv openssl openssl_x86
|
||||
cp -r openssl_x86 openssl_arm
|
||||
|
||||
@@ -402,12 +419,73 @@ 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 "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 "Configure libusb arm ..."
|
||||
make clean
|
||||
rm -r ${project_root}/deps/libusb/out_x86
|
||||
mkdir ${project_root}/deps/libusb/out_x86
|
||||
./configure --host=aarch64-apple-darwin --prefix=${project_root}/deps/libusb/out_x86
|
||||
make && make install
|
||||
|
||||
log_info "Building libusb arm ..."
|
||||
make clean
|
||||
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 arm64 -isysroot $SDKROOT"
|
||||
export CXXFLAGS="-arch arm64 -isysroot $SDKROOT"
|
||||
export LDFLAGS="-arch arm64 -isysroot $SDKROOT"
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.15
|
||||
mkdir ${project_root}/deps/libusb/out_arm
|
||||
./configure --host=aarch64-apple-darwin --prefix=${project_root}/deps/libusb/out_arm
|
||||
make && make install
|
||||
|
||||
log_info "Combine arm and x86 libusb binaries ..."
|
||||
lipo -create ${project_root}/deps/libusb/out_x86/lib/libusb-1.0.0.dylib \
|
||||
${project_root}/deps/libusb/out_arm/lib/libusb-1.0.0.dylib \
|
||||
-output ${advss_dep_path}/lib/temp-libusb-1.0.0.dylib
|
||||
mv ${advss_dep_path}/lib/temp-libusb-1.0.0.dylib ${advss_dep_path}/lib/libusb-1.0.0.dylib
|
||||
install_name_tool -id @rpath/libusb-1.0.0.dylib ${advss_dep_path}/lib/libusb-1.0.0.dylib
|
||||
log_info "Clean up libusb ..."
|
||||
|
||||
unset SDKROOT
|
||||
unset CC
|
||||
unset CXX
|
||||
unset CFLAGS
|
||||
unset CXXFLAGS
|
||||
unset LDFLAGS
|
||||
unset MACOSX_DEPLOYMENT_TARGET
|
||||
|
||||
popd
|
||||
;;
|
||||
linux)
|
||||
# Nothing to do for now
|
||||
# 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
|
||||
}
|
||||
;;
|
||||
}
|
||||
}
|
||||
|
||||
12
.github/scripts/.package.zsh
vendored
12
.github/scripts/.package.zsh
vendored
@@ -163,9 +163,13 @@ ${_usage_host:-}"
|
||||
|
||||
local product_name
|
||||
local product_version
|
||||
local git_tag="$(git describe --tags)"
|
||||
read -r product_name product_version <<< \
|
||||
"$(jq -r '. | {name, version} | join(" ")' ${buildspec_file})"
|
||||
|
||||
log_info "Using git tag as version identifier '${git_tag}'"
|
||||
product_version="${git_tag}"
|
||||
|
||||
if [[ ${host_os} == macos ]] {
|
||||
autoload -Uz check_packages read_codesign read_codesign_installer read_codesign_pass
|
||||
|
||||
@@ -247,15 +251,21 @@ ${_usage_host:-}"
|
||||
cmake --build build_${target##*-} --config ${config} -t package_source ${cmake_args}
|
||||
popd
|
||||
|
||||
local output_name="${product_name}-${product_version}-${target##*-}-linux-gnu"
|
||||
|
||||
if (( package )) {
|
||||
log_group "Packaging ${product_name}..."
|
||||
pushd ${project_root}
|
||||
cmake --build build_${target##*-} --config ${config} -t package ${cmake_args}
|
||||
|
||||
if [ ! -e ${project_root}/release/${output_name}.deb ]; then
|
||||
mv ${project_root}/release/*.deb ${project_root}/release/${output_name}.deb
|
||||
mv ${project_root}/release/*.ddeb ${project_root}/release/${output_name}.ddeb
|
||||
fi
|
||||
popd
|
||||
}
|
||||
|
||||
log_group "Archiving ${product_name}..."
|
||||
local output_name="${product_name}-${product_version}-${target##*-}-linux-gnu"
|
||||
local _tarflags='cJf'
|
||||
if (( _loglevel > 1 || ${+CI} )) _tarflags="v${_tarflags}"
|
||||
|
||||
|
||||
20
.github/scripts/Build-Deps-Windows.ps1
vendored
20
.github/scripts/Build-Deps-Windows.ps1
vendored
@@ -199,6 +199,26 @@ function Build {
|
||||
|
||||
Log-Information "Install tesseract..."
|
||||
Invoke-External cmake --install "${TesseractBuildPath}" --prefix "${ADVSSDepPath}" @TesseractCmakeArgs
|
||||
|
||||
Push-Location -Stack BuildLibusbTemp
|
||||
Ensure-Location $ProjectRoot
|
||||
|
||||
$LibusbPath = "${ProjectRoot}/deps/libusb"
|
||||
|
||||
Log-Information "Building libusb..."
|
||||
$msbuildExe = vswhere -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1
|
||||
|
||||
if ($msbuildExe) {
|
||||
Invoke-External $msbuildExe "${LibusbPath}/msvc/libusb.sln" /property:Configuration=Release /property:Platform=x64
|
||||
|
||||
$libusbBuildResultDirectory = "${LibusbPath}/build/v143/x64/Release"
|
||||
if (-not (Test-Path -Path $libusbBuildResultDirectory)) {
|
||||
$libusbBuildResultDirectory = "${LibusbPath}/x64/Release/dll"
|
||||
}
|
||||
Copy-Item -Path "${libusbBuildResultDirectory}/*" -Destination ${ADVSSDepPath} -Recurse -Force
|
||||
} else {
|
||||
Log-Information "Failed to locate msbuild.exe - skipping libusb build"
|
||||
}
|
||||
}
|
||||
|
||||
Build
|
||||
|
||||
4
.github/scripts/Package-Windows.ps1
vendored
4
.github/scripts/Package-Windows.ps1
vendored
@@ -48,6 +48,10 @@ function Package {
|
||||
$ProductName = $BuildSpec.name
|
||||
$ProductVersion = $BuildSpec.version
|
||||
|
||||
$GitOutput = git describe --tags
|
||||
Log-Information "Using git tag as version identifier '${GitOutput}'"
|
||||
$ProductVersion = $GitOutput
|
||||
|
||||
$OutputName = "${ProductName}-${ProductVersion}-windows-${Target}"
|
||||
|
||||
if ( ! $SkipDeps ) {
|
||||
|
||||
0
.github/test
vendored
Normal file
0
.github/test
vendored
Normal file
2
.github/workflows/build-debian.yml
vendored
2
.github/workflows/build-debian.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
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
|
||||
|
||||
6
.github/workflows/build-project.yaml
vendored
6
.github/workflows/build-project.yaml
vendored
@@ -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
|
||||
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -7,6 +7,7 @@
|
||||
!/cmake
|
||||
!/data
|
||||
!/deps
|
||||
!/scripting
|
||||
!/forms
|
||||
!/lib
|
||||
!/module
|
||||
@@ -14,7 +15,9 @@
|
||||
!/tests
|
||||
!.clang-format
|
||||
!.cmake-format.json
|
||||
!.gitattributes
|
||||
!.gitignore
|
||||
!.pre-commit-config.yaml
|
||||
!BUILDING.md
|
||||
!buildspec.json
|
||||
!CMakeLists.txt
|
||||
@@ -22,6 +25,9 @@
|
||||
!LICENSE
|
||||
!README.md
|
||||
|
||||
# Exclude .orig leftovers
|
||||
*.orig
|
||||
|
||||
# Exclude lock files
|
||||
*.lock.json
|
||||
|
||||
@@ -29,4 +35,7 @@
|
||||
.DS_Store
|
||||
|
||||
# Exclude CMake build number cache
|
||||
/cmake/.CMakeBuildNumber
|
||||
/cmake/.CMakeBuildNumber
|
||||
|
||||
# Exclude python caches
|
||||
/**/__pycache__
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -25,3 +25,6 @@
|
||||
[submodule "deps/json"]
|
||||
path = deps/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
[submodule "deps/libusb"]
|
||||
path = deps/libusb
|
||||
url = https://github.com/libusb/libusb.git
|
||||
|
||||
24
.pre-commit-config.yaml
Normal file
24
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v16.0.6
|
||||
hooks:
|
||||
- id: clang-format
|
||||
args: [-fallback-style=none]
|
||||
types_or: [c++, c]
|
||||
exclude: |
|
||||
(?x)^(
|
||||
deps/.*|
|
||||
tests/catch\.hpp|
|
||||
.*\.mm
|
||||
)$
|
||||
|
||||
- repo: https://github.com/cheshirekow/cmake-format-precommit
|
||||
rev: v0.6.13
|
||||
hooks:
|
||||
- id: cmake-format
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.3.2
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
types_or: [python]
|
||||
48
BUILDING.md
48
BUILDING.md
@@ -4,11 +4,13 @@ You have the option to ...
|
||||
- either add the plugin to the OBS source tree directly and build the plugin while building OBS itself. (**in tree**)
|
||||
- or you can move the sources of this plugin outside of the OBS source tree and build it separately from OBS. (**out of tree**)
|
||||
|
||||
As both methods require you to have a working [OBS Studio development environment](https://obsproject.com/wiki/Building-OBS-Studio) and [CMake](https://cmake.org/download/) it is recommended to build the plugin in tree as it is easier to set up and will enable straightforward debugging.
|
||||
Both methods require [CMake](https://cmake.org/download/).
|
||||
|
||||
The plugin can be compiled for OBS 25 and above, although using the latest version of OBS is recommended to support all features.
|
||||
The plugin can be compiled for OBS 27 and above, although using the latest version of OBS is recommended to support all features.
|
||||
|
||||
## Compiling in tree (recommended for development)
|
||||
This section assumes that you have a working [OBS Studio development environment](https://obsproject.com/wiki/Building-OBS-Studio).
|
||||
|
||||
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
|
||||
```
|
||||
cd obs-studio/UI/frontend-plugins/
|
||||
@@ -46,12 +48,12 @@ cmake -DOpenCV_DIR="C:/Users/BuildUser/Documents/OBS/opencv/build/" -DLeptonica_
|
||||
First you will need to clone the plugin sources by running the following command:
|
||||
```
|
||||
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
|
||||
cd SceneSwitcher
|
||||
```
|
||||
|
||||
Next you will need [CMake](https://cmake.org/download/) and run the command with suitable arguments for your particular platform.
|
||||
Next you will need [CMake](https://cmake.org/download/) and run it with suitable arguments for your particular platform.
|
||||
For example, on Windows you might want to run this command:
|
||||
```
|
||||
cd SceneSwitcher
|
||||
cmake --preset windows-x64
|
||||
```
|
||||
Next, you can build the plugin and install the files into a folder named release using the following commands:
|
||||
@@ -72,7 +74,7 @@ cd SceneSwitcher
|
||||
cmake -DOpenCV_DIR="C:/Users/BuildUser/Documents/OBS/opencv/build/" -DLeptonica_DIR="C:/Users/BuildUser/Documents/OBS/leptonica/build" -DTesseract_DIR="C:/Users/BuildUser/Documents/OBS/tesseract/build/lib/cmake/tesseract" --preset windows-x64
|
||||
```
|
||||
|
||||
You can rely on the CI scripts to build the dependencies for you, although it is not guaranteed that they will work in every environment:
|
||||
You can rely on the CI scripts to build the dependencies for you, although it is not guaranteed that they will function in every environment:
|
||||
|
||||
| Platform | Command |
|
||||
| ----------- | ----------- |
|
||||
@@ -83,10 +85,10 @@ You can rely on the CI scripts to build the dependencies for you, although it is
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions to the plugin are always welcome and if you need any assistance do not hesitate to reach out.
|
||||
Contributions to the plugin are always welcome and if you need any assistance do not hesitate to reach out!
|
||||
|
||||
If you would like to expand upon the macro system by adding a new condition or action type have a look at the examples in `plugins/base`.
|
||||
In general changes in the `lib/legacy` folder should be avoided.
|
||||
In general changes in the `lib/legacy` folder should be avoided, if possible.
|
||||
|
||||
## Macro condition
|
||||
Macro conditions should inherit from the `MacroCondition` class and must implement the following functions:
|
||||
@@ -97,12 +99,12 @@ public:
|
||||
MacroConditionExample(Macro *m) : MacroCondition(m) {}
|
||||
// This function should perform the condition check
|
||||
bool CheckCondition();
|
||||
// This function should store the required condition data to "obj"
|
||||
// This function should store the required condition data to "data"
|
||||
// For example called on OBS shutdown
|
||||
bool Save(obs_data_t *obj);
|
||||
// This function should load the condition data from "obj"
|
||||
bool Save(obs_data_t *data);
|
||||
// This function should load the condition data from "data"
|
||||
// For example called on OBS startup
|
||||
bool Load(obs_data_t *obj);
|
||||
bool Load(obs_data_t *data);
|
||||
// This function should return a unique id for this condition type
|
||||
// The _id is defined below
|
||||
std::string GetId() { return _id; };
|
||||
@@ -120,7 +122,7 @@ private:
|
||||
static const std::string _id;
|
||||
};
|
||||
```
|
||||
When defining the widget used to control the settings of the condition type, it is important to add a static `Create()` method.
|
||||
When defining the widget used to control the settings of the condition type, a static `Create()` method is required.
|
||||
It will be called whenever a new condition MacroConditionExample is created. (See `MacroConditionFactory::Register()`)
|
||||
```
|
||||
class MacroConditionExampleEdit : public QWidget {
|
||||
@@ -151,7 +153,7 @@ bool MacroConditionExample::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionExample::id, // Unique string identifying this condition type
|
||||
{
|
||||
MacroConditionExample::Create, // Function called to create the object performing the condition
|
||||
MacroConditionExampleEdit::Create, // Function called to create the widget configure the condition
|
||||
MacroConditionExampleEdit::Create, // Function called to create the widget to configure the settings
|
||||
"AdvSceneSwitcher.condition.example", // User facing name of the condition type (will be translated)
|
||||
true // Condition type supports duration modifiers (default true)
|
||||
}
|
||||
@@ -167,16 +169,18 @@ The differences are highlighted in the comments below.
|
||||
class MacroActionExample : public MacroAction {
|
||||
public:
|
||||
MacroActionExample(Macro *m) : MacroAction(m) {}
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m);
|
||||
// Used to create a copy of the action (used for action queues)
|
||||
std::shared_ptr<MacroAction> Copy() const;
|
||||
// This function should perform the action
|
||||
// If false is returned the macro will aborted
|
||||
bool PerformAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
bool Save(obs_data_t *data);
|
||||
bool Load(obs_data_t *data);
|
||||
std::string GetId() { return _id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionExample>(m);
|
||||
}
|
||||
// Optional:
|
||||
// Might be called when action is inserted to action queue
|
||||
void ResolveVariablesToFixedValues();
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -207,11 +211,11 @@ MacroActionFactory::Register(
|
||||
MacroActionExample::id, // Unique string identifying this action type
|
||||
{
|
||||
MacroActionExample::Create, // Function called to create the object performing the action
|
||||
MacroActionExampleEdit::Create, // Function called to create the widget configure the action
|
||||
MacroActionExampleEdit::Create, // Function called to create the widget to configure the settings
|
||||
"AdvSceneSwitcher.action.example" // User facing name of the action type
|
||||
}
|
||||
);
|
||||
```
|
||||
## External dependencies
|
||||
If your intention is to add macro functionality which depends on external libraries, which is likely not to exist on all user setups, have a look at the folders in the `plugins/` directory, which are not named base.
|
||||
For example `plugins/video` will only be loaded if the `OpenCV` dependencies are met.
|
||||
If your intention is to add macro functionality which depends on external libraries, which might not exist on all user environments, have a look at the folders in the `plugins/` directory, which are not named `base`.
|
||||
For example, `plugins/video` will only be loaded if the `OpenCV` dependencies are met.
|
||||
|
||||
@@ -52,8 +52,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 +64,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,6 +92,8 @@ 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
|
||||
@@ -108,6 +106,8 @@ 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
|
||||
@@ -120,22 +120,30 @@ target_sources(
|
||||
lib/macro/macro-export-import-dialog.hpp
|
||||
lib/macro/macro-helpers.cpp
|
||||
lib/macro/macro-helpers.hpp
|
||||
lib/macro/macro-input.cpp
|
||||
lib/macro/macro-input.hpp
|
||||
lib/macro/macro-list.cpp
|
||||
lib/macro/macro-list.hpp
|
||||
lib/macro/macro-properties.cpp
|
||||
lib/macro/macro-properties.hpp
|
||||
lib/macro/macro-ref.cpp
|
||||
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
|
||||
lib/macro/macro-segment.hpp
|
||||
lib/macro/macro-selection.cpp
|
||||
lib/macro/macro-selection.hpp
|
||||
lib/macro/macro-settings.cpp
|
||||
lib/macro/macro-settings.hpp
|
||||
lib/macro/macro-tab.cpp
|
||||
lib/macro/macro-tree.cpp
|
||||
lib/macro/macro-tree.hpp
|
||||
@@ -145,12 +153,24 @@ target_sources(
|
||||
# Utility function sources
|
||||
target_sources(
|
||||
${LIB_NAME}
|
||||
PRIVATE lib/utils/action-queue.cpp
|
||||
lib/utils/action-queue.hpp
|
||||
PRIVATE lib/queue/action-queue.cpp
|
||||
lib/queue/action-queue.hpp
|
||||
lib/queue/action-queue-tab.cpp
|
||||
lib/queue/action-queue-tab.hpp
|
||||
lib/utils/backup.cpp
|
||||
lib/utils/backup.hpp
|
||||
lib/utils/condition-logic.cpp
|
||||
lib/utils/condition-logic.hpp
|
||||
lib/utils/curl-helper.cpp
|
||||
lib/utils/curl-helper.hpp
|
||||
lib/utils/cursor-shape-changer.cpp
|
||||
lib/utils/cursor-shape-changer.hpp
|
||||
lib/utils/double-slider.cpp
|
||||
lib/utils/double-slider.hpp
|
||||
lib/utils/duration-control.cpp
|
||||
lib/utils/duration-control.hpp
|
||||
lib/utils/duration-modifier.cpp
|
||||
lib/utils/duration-modifier.hpp
|
||||
lib/utils/duration.cpp
|
||||
lib/utils/duration.hpp
|
||||
lib/utils/export-symbol-helper.hpp
|
||||
@@ -158,10 +178,16 @@ target_sources(
|
||||
lib/utils/file-selection.hpp
|
||||
lib/utils/filter-combo-box.cpp
|
||||
lib/utils/filter-combo-box.hpp
|
||||
lib/utils/help-icon.hpp
|
||||
lib/utils/help-icon.cpp
|
||||
lib/utils/item-selection-helpers.cpp
|
||||
lib/utils/item-selection-helpers.hpp
|
||||
lib/utils/layout-helpers.cpp
|
||||
lib/utils/layout-helpers.hpp
|
||||
lib/utils/list-controls.cpp
|
||||
lib/utils/list-controls.hpp
|
||||
lib/utils/list-editor.cpp
|
||||
lib/utils/list-editor.hpp
|
||||
lib/utils/log-helper.cpp
|
||||
lib/utils/log-helper.hpp
|
||||
lib/utils/math-helpers.cpp
|
||||
@@ -174,7 +200,6 @@ target_sources(
|
||||
lib/utils/name-dialog.hpp
|
||||
lib/utils/non-modal-dialog.cpp
|
||||
lib/utils/non-modal-dialog.hpp
|
||||
lib/utils/obs-dock.hpp
|
||||
lib/utils/obs-module-helper.cpp
|
||||
lib/utils/obs-module-helper.hpp
|
||||
lib/utils/path-helpers.cpp
|
||||
@@ -183,10 +208,17 @@ 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
|
||||
@@ -197,6 +229,8 @@ target_sources(
|
||||
lib/utils/section.hpp
|
||||
lib/utils/selection-helpers.cpp
|
||||
lib/utils/selection-helpers.hpp
|
||||
lib/utils/single-char-selection.cpp
|
||||
lib/utils/single-char-selection.hpp
|
||||
lib/utils/slider-spinbox.cpp
|
||||
lib/utils/slider-spinbox.hpp
|
||||
lib/utils/source-helpers.cpp
|
||||
@@ -207,18 +241,26 @@ target_sources(
|
||||
lib/utils/splitter-helpers.hpp
|
||||
lib/utils/status-control.cpp
|
||||
lib/utils/status-control.hpp
|
||||
lib/utils/string-list.cpp
|
||||
lib/utils/string-list.hpp
|
||||
lib/utils/switch-button.cpp
|
||||
lib/utils/switch-button.hpp
|
||||
lib/utils/sync-helpers.cpp
|
||||
lib/utils/sync-helpers.hpp
|
||||
lib/utils/tab-helpers.cpp
|
||||
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
|
||||
@@ -228,6 +270,7 @@ target_sources(
|
||||
lib/variables/variable-string.cpp
|
||||
lib/variables/variable-string.hpp
|
||||
lib/variables/variable-tab.cpp
|
||||
lib/variables/variable-tab.hpp
|
||||
lib/variables/variable-text-edit.cpp
|
||||
lib/variables/variable-text-edit.hpp
|
||||
lib/variables/variable.cpp
|
||||
@@ -235,6 +278,11 @@ target_sources(
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
# Subfolder for advanced scene switcher plugins
|
||||
set(ADVSS_PLUGIN_FOLDER "advanced-scene-switcher-plugins")
|
||||
target_compile_definitions(
|
||||
${LIB_NAME} PRIVATE ADVSS_PLUGIN_FOLDER=\"${ADVSS_PLUGIN_FOLDER}\")
|
||||
|
||||
include(cmake/common/advss_helpers.cmake)
|
||||
setup_obs_lib_dependency(${LIB_NAME})
|
||||
setup_obs_lib_dependency(${PROJECT_NAME})
|
||||
@@ -262,6 +310,7 @@ target_include_directories(
|
||||
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/lib"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib/legacy"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib/macro"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib/queue"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib/utils"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib/variables"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/forms")
|
||||
@@ -277,13 +326,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")
|
||||
@@ -319,6 +364,9 @@ if(OS_WINDOWS)
|
||||
if(MSVC)
|
||||
target_compile_options(${LIB_NAME} PUBLIC /MP /d2FH4- /wd4267 /wd4267
|
||||
/bigobj)
|
||||
|
||||
# Workaround for MSVC incompatibility in CI environment
|
||||
add_compile_definitions(${target_name} _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
|
||||
endif()
|
||||
target_sources(${LIB_NAME} PRIVATE lib/win/advanced-scene-switcher-win.cpp)
|
||||
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
|
||||
@@ -376,8 +424,7 @@ 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 ---
|
||||
|
||||
@@ -22,4 +22,6 @@ sudo snap install obs-studio
|
||||
## Contributing
|
||||
|
||||
- If you wish to contribute code to the project, have a look at this [section](BUILDING.md) describing how to compile the plugin.
|
||||
- You can add custom conditions and actions at runtime using the API described [here](https://github.com/WarmUpTill/SceneSwitcher/wiki/Scripting).
|
||||
- You can optionally use [pre-commit](https://pre-commit.com) to automatically handle formatting.
|
||||
- If you wish to contribute translations, feel free to submit pull requests for the corresponding files under `data/locale`.
|
||||
|
||||
@@ -49,7 +49,16 @@ invoke_formatter() {
|
||||
exit 2
|
||||
}
|
||||
|
||||
local -a source_files=(src/**/*.(c|cpp|h|hpp|m|mm)(.N))
|
||||
local -a source_files=()
|
||||
for folder ("lib" "module" "plugins" "tests") {
|
||||
source_files+=(${folder}/**/*.(c|cpp|h|hpp)(.N))
|
||||
}
|
||||
|
||||
for file (${source_files}) {
|
||||
if [[ $file == *"catch.hpp" ]]; then
|
||||
source_files=("${source_files[@]/$file}")
|
||||
fi
|
||||
}
|
||||
|
||||
local -a format_args=(-style=file -fallback-style=none)
|
||||
if (( _loglevel > 2 )) format_args+=(--verbose)
|
||||
|
||||
@@ -1,19 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
defaultLocaleFile = "en-US.ini"
|
||||
commentChars = ";#"
|
||||
|
||||
|
||||
class localeEntry:
|
||||
locale = ""
|
||||
placeholders = []
|
||||
widgetPlaceholders = []
|
||||
qStringArgs = []
|
||||
lineNum = -1
|
||||
|
||||
def __init__(self, locale, widgets) -> None:
|
||||
def __init__(self, locale, widgetPlaceholders, qStringArgs, lineNum) -> None:
|
||||
self.locale = locale
|
||||
self.placeholders = widgets
|
||||
self.widgetPlaceholders = widgetPlaceholders
|
||||
self.qStringArgs = qStringArgs
|
||||
self.lineNum = lineNum
|
||||
|
||||
|
||||
def isCommentLine(line):
|
||||
for char in commentChars:
|
||||
if line.startswith(char):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def getNonDefaultLocales(dir):
|
||||
@@ -22,21 +35,35 @@ def getNonDefaultLocales(dir):
|
||||
f = os.path.join(dir, filename)
|
||||
if os.path.isfile(f) and not f.endswith(defaultLocaleFile):
|
||||
files.append(f)
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def getAllLocaleEntriesWithWidgetPlaceholders(file):
|
||||
def getAllLocaleEntries(file):
|
||||
localeEntries = []
|
||||
with open(file, 'r', encoding='UTF-8') as f:
|
||||
for line in f.readlines():
|
||||
with open(file, "r", encoding="UTF-8") as f:
|
||||
for lineNum, line in enumerate(f):
|
||||
widgetPlaceholders = []
|
||||
qStringArgs = []
|
||||
|
||||
if isCommentLine(line):
|
||||
continue
|
||||
|
||||
for word in line.split("{{"):
|
||||
if not "}}" in word:
|
||||
continue
|
||||
word = "{{" + word[:word.rfind("}}")] + "}}"
|
||||
widgetPlaceholders.append(word)
|
||||
localeEntries.append(localeEntry(
|
||||
line.split("=")[0], widgetPlaceholders))
|
||||
placeholder = "{{" + word[: word.rfind("}}")] + "}}"
|
||||
widgetPlaceholders.append(placeholder)
|
||||
|
||||
for match in re.finditer(r"%\d+", line):
|
||||
qStringArgs.append(match.group(0))
|
||||
|
||||
localeEntries.append(
|
||||
localeEntry(
|
||||
line.split("=")[0], widgetPlaceholders, qStringArgs, lineNum + 1
|
||||
)
|
||||
)
|
||||
|
||||
return localeEntries
|
||||
|
||||
|
||||
@@ -44,47 +71,95 @@ def getLocaleEntryFrom(entry, list):
|
||||
for element in list:
|
||||
if element.locale == entry.locale:
|
||||
return element
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def checkWidgetPlacehodlers(file, expectedPlaceholders):
|
||||
localeEntries = getAllLocaleEntriesWithWidgetPlaceholders(file)
|
||||
def checkLocaleEntries(file, expectedLocaleEntries):
|
||||
localeEntries = getAllLocaleEntries(file)
|
||||
result = True
|
||||
|
||||
for localeEntry in localeEntries:
|
||||
expectedEntry = getLocaleEntryFrom(localeEntry, expectedPlaceholders)
|
||||
if expectedEntry is None:
|
||||
expectedLocaleEntry = getLocaleEntryFrom(localeEntry, expectedLocaleEntries)
|
||||
|
||||
if expectedLocaleEntry is None:
|
||||
result = False
|
||||
print(
|
||||
"WARNING: Locale entry \"{}\" from \"{}\" not found in \"{}\"".format(localeEntry.locale, file, defaultLocaleFile))
|
||||
'ERROR: Locale entry "{}" from "{}:{}" not found in "{}"'.format(
|
||||
localeEntry.locale, file, localeEntry.lineNum, defaultLocaleFile
|
||||
)
|
||||
)
|
||||
continue
|
||||
for p in localeEntry.placeholders:
|
||||
if p not in expectedEntry.placeholders:
|
||||
print("WARNING: Locale entry \"{}\" from \"{}\" does contain \"{}\" while \"{}\" does not".format(
|
||||
localeEntry.locale, file, p, defaultLocaleFile))
|
||||
for p in expectedEntry.placeholders:
|
||||
if p not in localeEntry.placeholders:
|
||||
|
||||
for placeholder in localeEntry.widgetPlaceholders:
|
||||
if placeholder not in expectedLocaleEntry.widgetPlaceholders:
|
||||
result = False
|
||||
print("ERROR: Locale entry \"{}\" from \"{}\" does not contain \"{}\"".format(
|
||||
localeEntry.locale, file, p))
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}:{}" does contain "{}" widget placeholder while "{}:{}" does not'.format(
|
||||
localeEntry.locale,
|
||||
file,
|
||||
localeEntry.lineNum,
|
||||
placeholder,
|
||||
defaultLocaleFile,
|
||||
expectedLocaleEntry.lineNum,
|
||||
)
|
||||
)
|
||||
|
||||
for placeholder in expectedLocaleEntry.widgetPlaceholders:
|
||||
if placeholder not in localeEntry.widgetPlaceholders:
|
||||
result = False
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}:{}" does not contain "{}" widget placeholder'.format(
|
||||
localeEntry.locale, file, localeEntry.lineNum, placeholder
|
||||
)
|
||||
)
|
||||
|
||||
for arg in localeEntry.qStringArgs:
|
||||
if arg not in expectedLocaleEntry.qStringArgs:
|
||||
result = False
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}:{}" does contain "{}" QString arg while "{}:{}" does not'.format(
|
||||
localeEntry.locale,
|
||||
file,
|
||||
localeEntry.lineNum,
|
||||
arg,
|
||||
defaultLocaleFile,
|
||||
expectedLocaleEntry.lineNum,
|
||||
)
|
||||
)
|
||||
|
||||
for arg in expectedLocaleEntry.qStringArgs:
|
||||
if arg not in localeEntry.qStringArgs:
|
||||
result = False
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}:{}" does not contain "{}" QString arg'.format(
|
||||
localeEntry.locale, file, localeEntry.lineNum, arg
|
||||
)
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Checks for inconsistencies regarding widget placeholders in the different locale files.')
|
||||
parser.add_argument(
|
||||
'-p', '--path', help='Path to locale folder', required=True)
|
||||
description="Checks for inconsistencies regarding widget placeholders in the different locale files."
|
||||
)
|
||||
parser.add_argument("-p", "--path", help="Path to locale folder", required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
placeholders = getAllLocaleEntriesWithWidgetPlaceholders(
|
||||
os.path.join(args.path, defaultLocaleFile))
|
||||
defaultLocaleEntries = getAllLocaleEntries(
|
||||
os.path.join(args.path, defaultLocaleFile)
|
||||
)
|
||||
nonDefaultLocales = getNonDefaultLocales(args.path)
|
||||
|
||||
result = True
|
||||
for f in nonDefaultLocales:
|
||||
if checkWidgetPlacehodlers(f, placeholders) == False:
|
||||
for file in nonDefaultLocales:
|
||||
if checkLocaleEntries(file, defaultLocaleEntries) == False:
|
||||
result = False
|
||||
|
||||
if result == False:
|
||||
sys.exit(1)
|
||||
|
||||
print("SUCCESS: No issues found!")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
BIN
build-aux/CI/linux/ubuntu22/libproc2-0_4.0.2-3_amd64.deb
Normal file
BIN
build-aux/CI/linux/ubuntu22/libproc2-0_4.0.2-3_amd64.deb
Normal file
Binary file not shown.
BIN
build-aux/CI/linux/ubuntu22/libproc2-dev_4.0.2-3_amd64.deb
Normal file
BIN
build-aux/CI/linux/ubuntu22/libproc2-dev_4.0.2-3_amd64.deb
Normal file
Binary file not shown.
@@ -1,33 +1,33 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"obs-studio": {
|
||||
"version": "29.1.2",
|
||||
"version": "30.1.2",
|
||||
"baseUrl": "https://github.com/obsproject/obs-studio/archive/refs/tags",
|
||||
"label": "OBS sources",
|
||||
"hashes": {
|
||||
"macos": "215f1fa5772c5dd9f3d6e35b0cb573912b00320149666a77864f9d305525504b",
|
||||
"windows-x64": "46d451f3f42b9d2c59339ec268165849c7b7904cdf1cc2a8d44c015815a9e37d"
|
||||
"macos": "490bae1c392b3b344b0270afd8cb887da4bc50bd92c0c426e96713c1ccb9701a",
|
||||
"windows-x64": "c2dd03fa7fd01fad5beafce8f7156da11f9ed9a588373fd40b44a06f4c03b867"
|
||||
}
|
||||
},
|
||||
"prebuilt": {
|
||||
"version": "2023-04-12",
|
||||
"version": "2024-03-19",
|
||||
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
|
||||
"label": "Pre-Built obs-deps",
|
||||
"hashes": {
|
||||
"macos": "9535c6e1ad96f7d49960251e85a245774088d48da1d602bb82f734b10219125a",
|
||||
"windows-x64": "c13a14a1acc4224b21304d97b63da4121de1ed6981297e50496fbc474abc0503"
|
||||
"macos": "2e9bfb55a5e0e4c1086fa1fda4cf268debfead473089df2aaea80e1c7a3ca7ff",
|
||||
"windows-x64": "6e86068371526a967e805f6f9903f9407adb683c21820db5f07da8f30d11e998"
|
||||
}
|
||||
},
|
||||
"qt6": {
|
||||
"version": "2023-04-12",
|
||||
"version": "2024-03-19",
|
||||
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
|
||||
"label": "Pre-Built Qt6",
|
||||
"hashes": {
|
||||
"macos": "eb7614544ab4f3d2c6052c797635602280ca5b028a6b987523d8484222ce45d1",
|
||||
"windows-x64": "4d39364b8a8dee5aa24fcebd8440d5c22bb4551c6b440ffeacce7d61f2ed1add"
|
||||
"macos": "694f1e639c017e3b1f456f735330dc5afae287cbea85757101af1368de3142c8",
|
||||
"windows-x64": "72d1df34a0ef7413a681d5fcc88cae81da60adc03dcd23ef17862ab170bcc0dd"
|
||||
},
|
||||
"debugSymbols": {
|
||||
"windows-x64": "f34ee5067be19ed370268b15c53684b7b8aaa867dc800b68931df905d679e31f"
|
||||
"windows-x64": "fbddd1f659c360f2291911ac5709b67b6f8182e6bca519d24712e4f6fd3cc865"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -8,14 +8,11 @@ if(BUILD_OUT_OF_TREE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Subfolder for advanced scene switcher plugins
|
||||
set(_PLUGIN_FOLDER "adv-ss-plugins")
|
||||
|
||||
# --- MACOS section ---
|
||||
if(OS_MACOS)
|
||||
set(ADVSS_BUNDLE_DIR "advanced-scene-switcher.plugin")
|
||||
set(ADVSS_BUNDLE_MODULE_DIR "${ADVSS_BUNDLE_DIR}/Contents/MacOS")
|
||||
set(ADVSS_BUNDLE_PLUGIN_DIR ${ADVSS_BUNDLE_MODULE_DIR}/${_PLUGIN_FOLDER})
|
||||
set(ADVSS_BUNDLE_PLUGIN_DIR ${ADVSS_BUNDLE_MODULE_DIR}/${ADVSS_PLUGIN_FOLDER})
|
||||
|
||||
function(install_advss_lib_helper target where)
|
||||
install(
|
||||
@@ -60,10 +57,36 @@ if(OS_MACOS)
|
||||
${dep}_Development)
|
||||
endfunction()
|
||||
|
||||
function(install_advss_plugin_dependency_file ${target} dep)
|
||||
target_sources(advanced-scene-switcher PRIVATE ${dep})
|
||||
set_source_files_properties(${dep} PROPERTIES MACOSX_PACKAGE_LOCATION
|
||||
${ADVSS_BUNDLE_PLUGIN_DIR})
|
||||
function(install_advss_plugin_dependency_file target dep)
|
||||
get_filename_component(_FILENAME ${dep} NAME)
|
||||
string(REGEX REPLACE "\\.[^.]*$" "" _FILENAMENOEXT ${_FILENAME})
|
||||
set(_DEP_NAME "${target}-${_FILENAMENOEXT}")
|
||||
|
||||
install(
|
||||
FILES "${dep}"
|
||||
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
|
||||
COMPONENT ${_DEP_NAME}_Runtime
|
||||
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
|
||||
COMPONENT ${_DEP_NAME}_Runtime
|
||||
NAMELINK_COMPONENT ${_DEP_NAME}_Development)
|
||||
|
||||
install(
|
||||
FILES "${dep}"
|
||||
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
|
||||
COMPONENT obs_${_DEP_NAME}
|
||||
EXCLUDE_FROM_ALL
|
||||
DESTINATION "${ADVSS_BUNDLE_PLUGIN_DIR}"
|
||||
COMPONENT obs_${_DEP_NAME}
|
||||
EXCLUDE_FROM_ALL)
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${target}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" --install ${CMAKE_CURRENT_BINARY_DIR} --config
|
||||
$<CONFIG> --prefix ${CMAKE_INSTALL_PREFIX} --component obs_${_DEP_NAME}
|
||||
COMMENT "Installing ${_DEP_NAME} to OBS rundir\n"
|
||||
VERBATIM)
|
||||
endfunction()
|
||||
|
||||
# --- End of section ---
|
||||
@@ -128,8 +151,8 @@ else()
|
||||
|
||||
function(install_advss_plugin target)
|
||||
plugin_install_helper(
|
||||
"${target}" "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
||||
"${_PLUGIN_FOLDER}")
|
||||
"${target}" "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||
"${ADVSS_PLUGIN_FOLDER}")
|
||||
if(NOT OS_WINDOWS)
|
||||
set_target_properties(${target} PROPERTIES INSTALL_RPATH
|
||||
"$ORIGIN:$ORIGIN/..")
|
||||
@@ -143,12 +166,12 @@ else()
|
||||
${dep}
|
||||
RUNTIME
|
||||
DESTINATION
|
||||
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
||||
"${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||
COMPONENT
|
||||
${dep}_Runtime
|
||||
LIBRARY
|
||||
DESTINATION
|
||||
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
||||
"${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||
COMPONENT
|
||||
${dep}_Runtime
|
||||
NAMELINK_COMPONENT
|
||||
@@ -159,13 +182,13 @@ else()
|
||||
${dep}
|
||||
RUNTIME
|
||||
DESTINATION
|
||||
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
||||
"${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||
COMPONENT
|
||||
obs_${dep}
|
||||
EXCLUDE_FROM_ALL
|
||||
LIBRARY
|
||||
DESTINATION
|
||||
"${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
||||
"${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||
COMPONENT
|
||||
obs_${dep}
|
||||
EXCLUDE_FROM_ALL)
|
||||
@@ -188,18 +211,18 @@ else()
|
||||
|
||||
install(
|
||||
FILES "${dep}"
|
||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||
COMPONENT ${_DEP_NAME}_Runtime
|
||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||
COMPONENT ${_DEP_NAME}_Runtime
|
||||
NAMELINK_COMPONENT ${_DEP_NAME}_Development)
|
||||
|
||||
install(
|
||||
FILES "${dep}"
|
||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||
COMPONENT obs_${_DEP_NAME}
|
||||
EXCLUDE_FROM_ALL
|
||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${_PLUGIN_FOLDER}"
|
||||
DESTINATION "${OBS_PLUGIN_DESTINATION}/${ADVSS_PLUGIN_FOLDER}"
|
||||
COMPONENT obs_${_DEP_NAME}
|
||||
EXCLUDE_FROM_ALL)
|
||||
|
||||
@@ -309,7 +332,6 @@ function(install_advss_plugin_dependency)
|
||||
if(NOT PARSED_ARGS_TARGET)
|
||||
message(FATAL_ERROR "You must provide a target")
|
||||
endif()
|
||||
set(_PLUGIN_FOLDER "adv-ss-plugins")
|
||||
foreach(_DEPENDENCY ${PARSED_ARGS_DEPENDENCIES})
|
||||
if(EXISTS ${_DEPENDENCY})
|
||||
install_advss_plugin_dependency_file(${PARSED_ARGS_TARGET} ${_DEPENDENCY})
|
||||
|
||||
@@ -12,8 +12,14 @@ if(NOT DEFINED PLUGIN_BUILD_NUMBER AND EXISTS "${_BUILD_NUMBER_CACHE}")
|
||||
file(READ "${_BUILD_NUMBER_CACHE}" PLUGIN_BUILD_NUMBER)
|
||||
math(EXPR PLUGIN_BUILD_NUMBER "${PLUGIN_BUILD_NUMBER}+1")
|
||||
elseif(NOT DEFINED PLUGIN_BUILD_NUMBER)
|
||||
if($ENV{CI} AND $ENV{GITHUB_RUN_ID})
|
||||
set(PLUGIN_BUILD_NUMBER "$ENV{GITHUB_RUN_ID}")
|
||||
if($ENV{CI})
|
||||
if($ENV{GITHUB_RUN_ID})
|
||||
set(PLUGIN_BUILD_NUMBER "$ENV{GITHUB_RUN_ID}")
|
||||
elseif($ENV{GITLAB_RUN_ID})
|
||||
set(PLUGIN_BUILD_NUMBER "$ENV{GITLAB_RUN_ID}")
|
||||
else()
|
||||
set(PLUGIN_BUILD_NUMBER "1")
|
||||
endif()
|
||||
else()
|
||||
set(PLUGIN_BUILD_NUMBER "1")
|
||||
endif()
|
||||
|
||||
@@ -68,13 +68,21 @@ else()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
# Disable warning for https://github.com/WarmUpTill/SceneSwitcher/issues/1091
|
||||
add_compile_options(-Wno-error=psabi)
|
||||
|
||||
# Disable false-positive warning in GCC 12.1.0 and later
|
||||
add_compile_options(-Wno-error=maybe-uninitialized)
|
||||
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1.0)
|
||||
add_compile_options(-Wno-error=maybe-uninitialized)
|
||||
add_compile_options(-Wno-error=stringop-overflow)
|
||||
endif()
|
||||
|
||||
# Add warning for infinite recursion (added in GCC 12)
|
||||
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0)
|
||||
add_compile_options(-Winfinite-recursion)
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
add_compile_options(-Wno-error=null-pointer-subtraction)
|
||||
endif()
|
||||
|
||||
# Enable compiler and build tracing (requires Ninja generator)
|
||||
|
||||
@@ -19,11 +19,11 @@ 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"
|
||||
@@ -75,13 +75,12 @@ AdvSceneSwitcher.macroTab.edit.action="Aktions-Typ:"
|
||||
AdvSceneSwitcher.macroTab.add="Neues Makro hinzufügen"
|
||||
AdvSceneSwitcher.macroTab.name="Name:"
|
||||
AdvSceneSwitcher.macroTab.run="Makro ausführen"
|
||||
AdvSceneSwitcher.macroTab.runFail="Ausführen von \"%1\" fehlgeschlagen!\nEntweder ist eine der Aktionen fehlgeschlagen oder das Makro wird bereits ausgeführt."
|
||||
AdvSceneSwitcher.macroTab.runFail="Ausführen von \"%1\" fehlgeschlagen!\nEntweder ist eine der Aktionen fehlgeschlagen oder das Makro wird bereits ausgeführt.\nSoll die aktuelle Ausführung gestoppt werden?"
|
||||
AdvSceneSwitcher.macroTab.runInParallel="Parallel zu anderen Makros ausführen"
|
||||
AdvSceneSwitcher.macroTab.onChange="Nur bei Änderung ausführen"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Makro %1"
|
||||
AdvSceneSwitcher.macroTab.defaultGroupName="Gruppe %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Makro-Name existiert bereits"
|
||||
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?"
|
||||
AdvSceneSwitcher.macroTab.removeGroupPopup.text="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?"
|
||||
AdvSceneSwitcher.macroTab.copy="Kopie des aktuellen Makros erstellen"
|
||||
AdvSceneSwitcher.macroTab.group="Ausgewählte Elemente gruppieren"
|
||||
AdvSceneSwitcher.macroTab.ungroup="Gruppierung ausgewählter Gruppen aufheben"
|
||||
@@ -95,7 +94,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"
|
||||
@@ -143,8 +142,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"
|
||||
@@ -160,7 +157,7 @@ AdvSceneSwitcher.condition.media.source="Quelle"
|
||||
AdvSceneSwitcher.condition.media.anyOnScene="Beliebige Medienquelle in"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="Alle Medienquellen in"
|
||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="Leider verhalten sich nicht alle Medien-Quell-Typen gleich (z.B. Medien-Quelle vs. VLC-Video-Quelle \"Gestoppt\"-Status).\nBitte experimentieren, was in eurem Setup funktioniert!"
|
||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}Status ist{{states}}und{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}Status ist{{states}}und{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.video="Video"
|
||||
AdvSceneSwitcher.condition.video.condition.match="entspricht genau"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="stimmt nicht überein"
|
||||
@@ -288,8 +285,8 @@ AdvSceneSwitcher.condition.sceneOrder.entry="Auf{{scenes}}{{sources}}{{condition
|
||||
AdvSceneSwitcher.condition.hotkey="Hotkey"
|
||||
AdvSceneSwitcher.condition.hotkey.name="Makro-Trigger-Hotkey"
|
||||
AdvSceneSwitcher.condition.hotkey.tip="Hinweis: Die Tastenkombinationen für diesen Hotkey können in den OBS-Einstellungen konfiguriert werden"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line1="Hotkey ist gedrückt"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line2="Name: {{name}}"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.keyState="Hotkey ist{{keyState}}"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.name="Name:{{name}}"
|
||||
AdvSceneSwitcher.condition.replay="Replay Buffer"
|
||||
AdvSceneSwitcher.condition.replay.state.stopped="Replay Buffer gestoppt"
|
||||
AdvSceneSwitcher.condition.replay.state.started="Replay Buffer gestartet"
|
||||
@@ -322,11 +319,8 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Nächster Treffer bei: %1"
|
||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Bei Wiederholung ausgewähltes Datum auf Wiederholungsdatum aktualisieren"
|
||||
AdvSceneSwitcher.condition.sceneTransform="Szenenelement transformieren"
|
||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Transformation erhalten"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Auf{{scenes}}{{sources}}{{types}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="entspricht Transformation"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.changed="hat sich geändert"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.condition.match="entspricht Transformation"
|
||||
AdvSceneSwitcher.condition.sceneTransform.condition.changed="hat sich geändert"
|
||||
AdvSceneSwitcher.condition.transition="Übergang"
|
||||
AdvSceneSwitcher.condition.transition.type.current="Aktueller Übergangstyp ist"
|
||||
AdvSceneSwitcher.condition.transition.type.duration="Aktuelle Übergangsdauer beträgt"
|
||||
@@ -428,7 +422,6 @@ AdvSceneSwitcher.action.replay.saveWarn="Warnung: Ein zu häufiges Speichern kan
|
||||
AdvSceneSwitcher.action.replay.type.stop="Replay Buffer stoppen"
|
||||
AdvSceneSwitcher.action.replay.type.start="Replay Buffer starten"
|
||||
AdvSceneSwitcher.action.replay.type.save="Replay Buffer speichern"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Stream"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Stream stoppen"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Stream starten"
|
||||
@@ -445,7 +438,7 @@ AdvSceneSwitcher.action.filter="Filter"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Aktivieren"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Deaktivieren"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Einstellungen festlegen"
|
||||
AdvSceneSwitcher.action.filter.entry="Auf{{sources}}{{actions}}{{filters}}{{filters}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.filter.entry="Auf{{sources}}{{actions}}{{filters}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Aktuelle Einstellungen abfragen"
|
||||
AdvSceneSwitcher.action.source="Quelle"
|
||||
AdvSceneSwitcher.action.source.type.enable="Aktivieren"
|
||||
@@ -454,7 +447,6 @@ AdvSceneSwitcher.action.source.type.settings="Einstellungen festlegen"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings="Aktualisieren der Quelleneinstellungen"
|
||||
AdvSceneSwitcher.action.source.type.pressSettingsButton="Drücken der Einstellungstaste"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Kann verwendet werden, um Browser-, Medien- usw. Quellen zu aktualisieren"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="Keine Buttons gefunden!"
|
||||
AdvSceneSwitcher.action.source.warning="Warnung: Das globale Aktivieren und Deaktivieren von Quellen kann nicht über die OBS-Benutzeroberfläche gesteuert werden"
|
||||
AdvSceneSwitcher.action.source.getSettings="Aktuelle Einstellungen abfragen"
|
||||
AdvSceneSwitcher.action.media="Medien"
|
||||
@@ -471,7 +463,6 @@ AdvSceneSwitcher.action.macro.type.unpause="Nicht mehr pausieren"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Zähler zurücksetzen"
|
||||
AdvSceneSwitcher.action.macro.type.run="Aktionen ausführen"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Aktionen stoppen"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Plugin-Status"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Erweiterten Automatischen Szenenwechsler stoppen"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Ändern des Nichtübereinstimmungsverhaltens:"
|
||||
@@ -502,7 +493,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="An Position verschieben"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||
AdvSceneSwitcher.action.file="Datei"
|
||||
AdvSceneSwitcher.action.file.type.write="Schreiben"
|
||||
AdvSceneSwitcher.action.file.type.append="Anhängen"
|
||||
@@ -538,14 +529,14 @@ 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"
|
||||
AdvSceneSwitcher.action.sceneCollection.entry="Aktive Szenensammlung umschalten auf {{sceneCollections}}"
|
||||
AdvSceneSwitcher.action.sceneCollection.warning="Hinweis: Alle danach folgenden Aktionen werden nicht ausgeführt, da durch die Änderung der Szenensammlung auch die Einstellungen des Szenenwechslers neu geladen werden.\nDie Szenensammlungsaktion wird ignoriert, solange das Einstellungsfenster geöffnet ist."
|
||||
AdvSceneSwitcher.action.sequence="Sequenz"
|
||||
AdvSceneSwitcher.action.sequence.entry="Jedes Mal, wenn diese Aktion ausgeführt wird, wird das nächste Makro in der Liste ausgeführt (pausierte Makros werden ignoriert)"
|
||||
; AdvSceneSwitcher.action.sequence.entry="Jedes Mal, wenn diese Aktion ausgeführt wird, wird das nächste Makro in der Liste ausgeführt (pausierte Makros werden ignoriert)"
|
||||
AdvSceneSwitcher.action.sequence.status="Zuletzt ausgeführtes Makro: %1 - Nächstes auszuführendes Makro: %2"
|
||||
AdvSceneSwitcher.action.sequence.status.none="keines"
|
||||
AdvSceneSwitcher.action.sequence.restart="Neustart am Anfang, wenn das Ende der Liste erreicht ist"
|
||||
@@ -571,6 +562,7 @@ AdvSceneSwitcher.action.variable.actionNoVariableSupport="Das Abrufen von Variab
|
||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Bedingungen wird nicht unterstützt!"
|
||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Aktueller Wert:"
|
||||
|
||||
AdvSceneSwitcher.noSettingsButtons="Keine Buttons gefunden!"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Szenenübergänge"
|
||||
@@ -736,31 +728,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"
|
||||
@@ -781,30 +748,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"
|
||||
@@ -901,7 +844,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
@@ -19,11 +19,11 @@ 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"
|
||||
@@ -72,11 +72,9 @@ AdvSceneSwitcher.macroTab.edit.action="Tipo de acción:"
|
||||
AdvSceneSwitcher.macroTab.add="Agregar nueva macro"
|
||||
AdvSceneSwitcher.macroTab.name="Nombre:"
|
||||
AdvSceneSwitcher.macroTab.run="Ejecutar macro"
|
||||
AdvSceneSwitcher.macroTab.runFail="Error al ejecutar \"%1\" si se ha producido un error en una de las acciones o si la macro ya se está ejecutando."
|
||||
AdvSceneSwitcher.macroTab.runInParallel="Ejecutar macro en paralelo a otras macros"
|
||||
AdvSceneSwitcher.macroTab.onChange="Realizar acciones solo en el cambio de condición"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
|
||||
AdvSceneSwitcher.macroTab.exists="El nombre de la macro ya existe"
|
||||
AdvSceneSwitcher.macroTab.copy="Crear copia"
|
||||
AdvSceneSwitcher.macroTab.expandAll="Expandir todo"
|
||||
AdvSceneSwitcher.macroTab.collapseAll="Contraer todo"
|
||||
@@ -119,8 +117,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}}"
|
||||
@@ -130,7 +126,7 @@ AdvSceneSwitcher.condition.media="Medios"
|
||||
AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activada"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas"
|
||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="Desafortunadamente, no todos los tipos de fuentes de medios se comportan de la misma manera (p. ej., fuente de medios frente a estado \"Detenido\" de fuente de video VLC).\n¡Así que experimente lo que funciona para su configuración!"
|
||||
AdvSceneSwitcher.condition.media.entry="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}es{{states}}y{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.media.layout.legacy="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}es{{states}}y{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.video="Video"
|
||||
AdvSceneSwitcher.condition.video.condition.match="coincide exactamente"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="no coincide"
|
||||
@@ -240,8 +236,7 @@ AdvSceneSwitcher.condition.sceneOrder.entry="En{{scenes}}{{sources}}{{conditions
|
||||
AdvSceneSwitcher.condition.hotkey="Tecla de acceso rápido"
|
||||
AdvSceneSwitcher.condition.hotkey.name="Tecla de acceso directo de activación de macro"
|
||||
AdvSceneSwitcher.condition.hotkey.tip="Nota: puede configurar las combinaciones de teclas para esta tecla de acceso rápido en la ventana de configuración de OBS"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line1="Se presiona la tecla de acceso rápido"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line2="Nombre: {{name}}"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.name="Nombre:{{name}}"
|
||||
AdvSceneSwitcher.condition.replay="Búfer de reproducción"
|
||||
AdvSceneSwitcher.condition.replay.state.stopped="Búfer de reproducción detenido"
|
||||
AdvSceneSwitcher.condition.replay.state.started="Búfer de reproducción iniciado"
|
||||
@@ -272,10 +267,7 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Próxima coincidencia en: %
|
||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Al repetir actualizar la fecha seleccionada para repetir la fecha"
|
||||
AdvSceneSwitcher.condition.sceneTransform="Transformar elemento de escena"
|
||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtener transformación"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="En{{scenes}}{{sources}}{{types}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="coincide con la transformación"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.condition.match="coincide con la transformación"
|
||||
AdvSceneSwitcher.condition.transition="Transición"
|
||||
AdvSceneSwitcher.condition.transition.type.current="El tipo de transición actual es"
|
||||
AdvSceneSwitcher.condition.transition.type.duration="La duración de la transición actual es"
|
||||
@@ -351,7 +343,6 @@ AdvSceneSwitcher.action.replay.saveWarn="Advertencia: ¡Guardar con demasiada fr
|
||||
AdvSceneSwitcher.action.replay.type.stop="Detener el búfer de reproducción"
|
||||
AdvSceneSwitcher.action.replay.type.start="Iniciar búfer de reproducción"
|
||||
AdvSceneSwitcher.action.replay.type.save="Guardar búfer de reproducción"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Transmisión"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Detener transmisión"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Iniciar transmisión"
|
||||
@@ -367,7 +358,7 @@ AdvSceneSwitcher.action.filter="Filtro"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Habilitar"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Deshabilitar"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Establecer configuración"
|
||||
AdvSceneSwitcher.action.filter.entry="En{{sources}}{{actions}}{{filters}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.filter.entry="En{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Obtener la configuración actual"
|
||||
AdvSceneSwitcher.action.source="Fuente"
|
||||
AdvSceneSwitcher.action.source.type.enable="Habilitar"
|
||||
@@ -389,7 +380,6 @@ AdvSceneSwitcher.action.macro.type.unpause="Reanudar"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Reiniciar contador"
|
||||
AdvSceneSwitcher.action.macro.type.run="Ejecutar"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Detener"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Estado del complemento"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Detener el complemento Advanced Scene Switcher"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Cambiar el comportamiento de no coincidencia:"
|
||||
@@ -420,7 +410,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||
AdvSceneSwitcher.action.file="Archivo"
|
||||
AdvSceneSwitcher.action.file.type.write="Escribir"
|
||||
AdvSceneSwitcher.action.file.type.append="Agregar"
|
||||
@@ -456,7 +446,7 @@ AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
|
||||
AdvSceneSwitcher.action.sceneCollection.entry="Cambiar la colección de escenas activa a {{sceneCollections}}"
|
||||
AdvSceneSwitcher.action.sceneCollection.warning="Nota: Cualquier acción posterior a esta no se ejecutará, ya que la colección de escenas cambiante también volverá a cargar la configuración del conmutador de escenas.\nLa acción de la colección de escenas se ignorará mientras se abra la ventana de configuración".
|
||||
AdvSceneSwitcher.action.sequence="Secuencia"
|
||||
AdvSceneSwitcher.action.sequence.entry="Cada vez que se realiza esta acción, ejecute la siguiente macro de la lista (las macros en pausa se ignoran)"
|
||||
; AdvSceneSwitcher.action.sequence.entry="Cada vez que se realiza esta acción, ejecute la siguiente macro de la lista (las macros en pausa se ignoran)"
|
||||
AdvSceneSwitcher.action.sequence.status="Última macro ejecutada: %1 - Siguiente macro a ejecutar: %2"
|
||||
AdvSceneSwitcher.action.sequence.status.none="ninguno"
|
||||
AdvSceneSwitcher.action.sequence.restart="Reiniciar desde el principio una vez que se alcance el final de la lista"
|
||||
@@ -626,31 +616,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"
|
||||
@@ -671,30 +636,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"
|
||||
@@ -748,7 +689,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"
|
||||
|
||||
@@ -19,11 +19,11 @@ 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"
|
||||
@@ -78,15 +78,13 @@ AdvSceneSwitcher.macroTab.edit.action="Type d'action :"
|
||||
AdvSceneSwitcher.macroTab.add="Ajouter une nouvelle macro"
|
||||
AdvSceneSwitcher.macroTab.name="Nom :"
|
||||
AdvSceneSwitcher.macroTab.run="Exécuter la macro"
|
||||
AdvSceneSwitcher.macroTab.runFail="Échec de l'exécution de \"%1\" !\nSoit l'une des actions a échoué, soit la macro est déjà en cours d'exécution."
|
||||
AdvSceneSwitcher.macroTab.runInParallel="Exécuter la macro en parallèle avec d'autres macros"
|
||||
AdvSceneSwitcher.macroTab.onChange="Exécuter des actions uniquement en cas de changement de condition"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
|
||||
AdvSceneSwitcher.macroTab.defaultGroupName="Groupe %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Le nom de la macro existe déjà"
|
||||
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Êtes-vous sûr de vouloir supprimer \"%1\" et tous ses éléments ?"
|
||||
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="Êtes-vous sûr de vouloir supprimer %1 macros ?"
|
||||
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="Êtes-vous sûr de vouloir supprimer \"%1\" ?"
|
||||
AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Êtes-vous sûr de vouloir supprimer \"%1\" ?"
|
||||
AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text="Êtes-vous sûr de vouloir supprimer %1 macros ?"
|
||||
AdvSceneSwitcher.macroTab.removeGroupPopup.text="Êtes-vous sûr de vouloir supprimer \"%1\" et tous ses éléments ?"
|
||||
AdvSceneSwitcher.macroTab.contextMenuAdd="Ajouter"
|
||||
AdvSceneSwitcher.macroTab.copy="Dupliquer la macro"
|
||||
AdvSceneSwitcher.macroTab.group="Grouper les macros sélectionnées"
|
||||
@@ -111,7 +109,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"
|
||||
@@ -198,7 +196,7 @@ AdvSceneSwitcher.condition.media.source="Source"
|
||||
AdvSceneSwitcher.condition.media.anyOnScene="Toute source média sur la scène"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="Toutes les sources média sur la scène"
|
||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="Malheureusement, toutes les sources média ne se comportent pas de la même manière (par exemple, l'état \"Arrêté\" de la source Media par rapport à la source Vidéo VLC).\nPar conséquent, veuillez expérimenter ce qui fonctionne le mieux pour votre configuration !"
|
||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}l'état est{{states}}et{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}l'état est{{states}}et{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.video="Vidéo"
|
||||
AdvSceneSwitcher.condition.video.condition.match="correspond exactement à"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="ne correspond pas à"
|
||||
@@ -353,8 +351,7 @@ AdvSceneSwitcher.condition.sceneOrder.entry="Sur{{scenes}}{{sources}}{{condition
|
||||
AdvSceneSwitcher.condition.hotkey="Raccourci clavier"
|
||||
AdvSceneSwitcher.condition.hotkey.name="Raccourci clavier de déclenchement de macro"
|
||||
AdvSceneSwitcher.condition.hotkey.tip="Remarque : Vous pouvez configurer les raccourcis clavier pour ce raccourci dans la fenêtre de paramètres d'OBS"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line1="Raccourci clavier est enfoncé"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line2="Nom :{{name}}"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.name="Nom:{{name}}"
|
||||
AdvSceneSwitcher.condition.replay="Tampon de répétition"
|
||||
AdvSceneSwitcher.condition.replay.state.stopped="Tampon de répétition arrêté"
|
||||
AdvSceneSwitcher.condition.replay.state.started="Tampon de répétition démarré"
|
||||
@@ -385,8 +382,7 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Prochaine correspondance à
|
||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}À la répétition, mettre à jour la date sélectionnée pour la date de répétition"
|
||||
AdvSceneSwitcher.condition.sceneTransform="Transformation de l'élément de la scène"
|
||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtenir la transformation"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Sur{{scenes}}{{sources}}{{types}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="correspond à la transformation"
|
||||
AdvSceneSwitcher.condition.sceneTransform.condition.match="correspond à la transformation"
|
||||
AdvSceneSwitcher.condition.transition="Transition"
|
||||
AdvSceneSwitcher.condition.transition.type.current="Le type de transition actuel est"
|
||||
AdvSceneSwitcher.condition.transition.type.duration="La durée de la transition actuelle est"
|
||||
@@ -458,7 +454,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"
|
||||
@@ -523,7 +519,7 @@ AdvSceneSwitcher.action.filter.type.enable="Activer"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Désactiver"
|
||||
AdvSceneSwitcher.action.filter.type.toggle="Basculer"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Définir les paramètres"
|
||||
AdvSceneSwitcher.action.filter.entry="Sur{{sources}}{{actions}}{{filters}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.filter.entry="Sur{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Obtenir les paramètres actuels"
|
||||
AdvSceneSwitcher.action.source="Source"
|
||||
AdvSceneSwitcher.action.source.type.enable="Activer"
|
||||
@@ -535,7 +531,6 @@ AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Peut être utilisé
|
||||
AdvSceneSwitcher.action.source.type.deinterlaceMode="Définir le mode de désentrelacement"
|
||||
AdvSceneSwitcher.action.source.type.deinterlaceOrder="Définir l'ordre de désentrelacement des champs"
|
||||
AdvSceneSwitcher.action.source.type.openInteractionDialog="Ouvrir la boîte de dialogue d'interaction"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="Aucun bouton trouvé !"
|
||||
AdvSceneSwitcher.action.source.warning="Avertissement : Activer et désactiver les sources globalement ne peut pas être contrôlé par l'interface utilisateur d'OBS\nVous pourriez rechercher \"Visibilité de l'élément de scène\""
|
||||
AdvSceneSwitcher.action.source.getSettings="Obtenir les paramètres actuels"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.disable="Désactiver"
|
||||
@@ -615,7 +610,7 @@ AdvSceneSwitcher.action.sceneTransform.type.centerToScreen="Centrer à l'écran"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Centrer verticalement"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Centrer horizontalement"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtenir la transformation"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Sur{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Sur{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||
AdvSceneSwitcher.action.file="Fichier"
|
||||
AdvSceneSwitcher.action.file.type.write="Écrire"
|
||||
AdvSceneSwitcher.action.file.type.append="Ajouter"
|
||||
@@ -654,14 +649,14 @@ 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"
|
||||
AdvSceneSwitcher.action.sceneCollection.entry="Changer la collection de scènes active vers{{sceneCollections}}"
|
||||
AdvSceneSwitcher.action.sceneCollection.warning="Note : Toutes les actions suivantes ne seront pas exécutées car le changement de collection de scènes rechargera également les paramètres du commutateur de scènes. L'action de collection de scènes sera ignorée lorsque la fenêtre des paramètres est ouverte."
|
||||
AdvSceneSwitcher.action.sequence="Séquence"
|
||||
AdvSceneSwitcher.action.sequence.entry="Chaque fois que cette action est effectuée, exécutez le macro suivant dans la liste (les macros en pause sont ignorés)"
|
||||
; AdvSceneSwitcher.action.sequence.entry="Chaque fois que cette action est effectuée, exécutez le macro suivant dans la liste (les macros en pause sont ignorés)"
|
||||
AdvSceneSwitcher.action.sequence.status="Dernier macro exécuté : %1 - Prochain macro à exécuter : %2"
|
||||
AdvSceneSwitcher.action.sequence.status.none="aucun"
|
||||
AdvSceneSwitcher.action.sequence.restart="Recommencer depuis le début une fois la fin de la liste atteinte"
|
||||
@@ -740,6 +735,8 @@ AdvSceneSwitcher.action.twitch.type.channel.info.category.set="Définir la caté
|
||||
AdvSceneSwitcher.action.twitch.type.commercial.start="Démarrer une publicité d'une durée de"
|
||||
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Impossible de sélectionner une catégorie sans avoir d'abord sélectionné un compte Twitch !"
|
||||
|
||||
AdvSceneSwitcher.noSettingsButtons="Aucun bouton trouvé !"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Utiliser une transition pour le passage automatisé de la scène A à la scène B"
|
||||
@@ -900,31 +897,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"
|
||||
@@ -945,30 +917,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é"
|
||||
@@ -1164,8 +1112,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"
|
||||
@@ -1185,7 +1133,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"
|
||||
|
||||
2157
data/locale/ja-JP.ini
Normal file
2157
data/locale/ja-JP.ini
Normal file
File diff suppressed because it is too large
Load Diff
2087
data/locale/pt-BR.ini
Normal file
2087
data/locale/pt-BR.ini
Normal file
File diff suppressed because it is too large
Load Diff
@@ -19,11 +19,11 @@ 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="Сохранять положение и размер окна"
|
||||
@@ -69,7 +69,6 @@ AdvSceneSwitcher.macroTab.edit.action="Тип действия:"
|
||||
AdvSceneSwitcher.macroTab.add="Добавить новый макрос"
|
||||
AdvSceneSwitcher.macroTab.name="Имя:"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Макрос %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Имя макроса уже существует"
|
||||
AdvSceneSwitcher.macroTab.copy="Создать копию"
|
||||
; Macro Logic
|
||||
AdvSceneSwitcher.logic.none="Игнорировать вход"
|
||||
@@ -138,7 +137,6 @@ AdvSceneSwitcher.action.replay="Буфер воспроизведения"
|
||||
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="Остановить потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание"
|
||||
@@ -309,28 +307,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="Группы сцен"
|
||||
@@ -351,28 +327,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"
|
||||
@@ -401,6 +355,6 @@ AdvSceneSwitcher.status.active="Активный"
|
||||
AdvSceneSwitcher.status.inactive="Неактивен"
|
||||
|
||||
AdvSceneSwitcher.unit.milliseconds="миллисекунды"
|
||||
AdvSceneSwitcher.unit.secends="секунды"
|
||||
AdvSceneSwitcher.unit.seconds="секунды"
|
||||
AdvSceneSwitcher.unit.minutes="минуты"
|
||||
AdvSceneSwitcher.unit.hours="часы"
|
||||
|
||||
@@ -19,11 +19,11 @@ 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"
|
||||
@@ -72,11 +72,9 @@ AdvSceneSwitcher.macroTab.edit.action="Eylem türü:"
|
||||
AdvSceneSwitcher.macroTab.add="Yeni Makro ekle"
|
||||
AdvSceneSwitcher.macroTab.name="İsim:"
|
||||
AdvSceneSwitcher.macroTab.run="Makro Çalıştırma"
|
||||
AdvSceneSwitcher.macroTab.runFail="\"%1\" çalıştırılamadı!\n Eylemlerden biri başarısız oldu veya makro zaten çalışıyor."
|
||||
AdvSceneSwitcher.macroTab.runInParallel="Makroyu diğer makrolara paralel olarak çalıştırın"
|
||||
AdvSceneSwitcher.macroTab.onChange="Eylemleri yalnızca koşul değişikliğinde gerçekleştirin"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Makro %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Macro adı zaten mevcut"
|
||||
AdvSceneSwitcher.macroTab.copy="Kopya oluştur"
|
||||
AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet"
|
||||
AdvSceneSwitcher.macroTab.collapseAll="Hepsini Küçült"
|
||||
@@ -109,8 +107,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}}"
|
||||
@@ -119,7 +115,7 @@ AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFil
|
||||
AdvSceneSwitcher.condition.media="Medya"
|
||||
AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları "
|
||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}durumu{{states}}ve{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}durumu{{states}}ve{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.video="Video"
|
||||
AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="Eşleştirme"
|
||||
@@ -228,10 +224,7 @@ AdvSceneSwitcher.condition.date.state.before="Önce"
|
||||
AdvSceneSwitcher.condition.date.state.between="Arasında"
|
||||
AdvSceneSwitcher.condition.sceneTransform="Sahne öğesi dönüşümü"
|
||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Dönüşümü al"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Açık{{scenes}}{{sources}}{{types}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="dönüşümle eşleşir"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.condition.match="dönüşümle eşleşir"
|
||||
AdvSceneSwitcher.condition.transition="Geçiş"
|
||||
AdvSceneSwitcher.condition.transition.type.current="Geçerli geçiş türü"
|
||||
AdvSceneSwitcher.condition.transition.type.duration="Mevcut geçiş süresi"
|
||||
@@ -281,7 +274,6 @@ AdvSceneSwitcher.action.replay="Tekrar arabelleği"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Tekrar arabelleğini durdur"
|
||||
AdvSceneSwitcher.action.replay.type.start="Tekrar arabelleğini başlat"
|
||||
AdvSceneSwitcher.action.replay.type.save="Tekrar arabelleğini kaydet"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Yayın"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Yayın durdur"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Yayın başlat"
|
||||
@@ -297,7 +289,7 @@ AdvSceneSwitcher.action.filter="Filtrele"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Etkin"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Ayarları yap"
|
||||
AdvSceneSwitcher.action.filter.entry="Açık{{sources}}{{actions}}{{filters}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.filter.entry="Açık{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Mevcut ayarları al"
|
||||
AdvSceneSwitcher.action.source="Kaynak"
|
||||
AdvSceneSwitcher.action.source.type.enable="Etkin"
|
||||
@@ -317,7 +309,6 @@ AdvSceneSwitcher.action.macro.type.pause="Duraklat"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Duraklatma"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Sayacı sıfırla"
|
||||
AdvSceneSwitcher.action.macro.type.run="Çalıştır"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Eklenti durumu"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcher eklentisini durdurun"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Eşleşmeme davranışını değiştirin:"
|
||||
@@ -348,7 +339,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||
AdvSceneSwitcher.action.file="Dosya"
|
||||
AdvSceneSwitcher.action.file.type.write="Yaz"
|
||||
AdvSceneSwitcher.action.file.type.append="Ekle"
|
||||
@@ -536,31 +527,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ı"
|
||||
@@ -581,30 +547,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"
|
||||
@@ -652,7 +594,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"
|
||||
|
||||
@@ -19,11 +19,11 @@ 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.verboseLogging="详细日志输出"
|
||||
@@ -77,15 +77,13 @@ AdvSceneSwitcher.macroTab.edit.action="动作类型:"
|
||||
AdvSceneSwitcher.macroTab.add="添加新宏"
|
||||
AdvSceneSwitcher.macroTab.name="名称:"
|
||||
AdvSceneSwitcher.macroTab.run="运行宏"
|
||||
AdvSceneSwitcher.macroTab.runFail="运行 \"%1\" 失败!\n其中一个操作失败,或者宏已在运行."
|
||||
AdvSceneSwitcher.macroTab.runInParallel="与其他宏同时运行宏"
|
||||
AdvSceneSwitcher.macroTab.onChange="仅在条件更改时执行操作"
|
||||
AdvSceneSwitcher.macroTab.defaultname="宏 %1"
|
||||
AdvSceneSwitcher.macroTab.defaultGroupName="组 %1"
|
||||
AdvSceneSwitcher.macroTab.exists="宏名称已存在"
|
||||
AdvSceneSwitcher.macroTab.groupDeleteConfirm="您确定要删除 \"%1\"及其所有元素吗?"
|
||||
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="您确定要删除 %1 宏吗?"
|
||||
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="你确定你要删除 \"%1\"?"
|
||||
AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="你确定你要删除 \"%1\"?"
|
||||
AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text="您确定要删除 %1 宏吗?"
|
||||
AdvSceneSwitcher.macroTab.removeGroupPopup.text="您确定要删除 \"%1\"及其所有元素吗?"
|
||||
AdvSceneSwitcher.macroTab.contextMenuAdd="增加"
|
||||
AdvSceneSwitcher.macroTab.copy="创建副本"
|
||||
AdvSceneSwitcher.macroTab.group="对选定的宏进行分组"
|
||||
@@ -103,7 +101,7 @@ AdvSceneSwitcher.macroTab.highlightExecutedMacros="突出显示最近执行的
|
||||
AdvSceneSwitcher.macroTab.highlightTrueConditions="突出显示最近运算为 真 的当前选定宏的条件"
|
||||
AdvSceneSwitcher.macroTab.highlightPerformedActions="突出显示当前选定宏的最近执行的操作"
|
||||
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="使用快捷键控制新增宏的暂停状态"
|
||||
AdvSceneSwitcher.macroTab.currentDisableHotkeys="使用快捷键控制所选宏的暂停状态"
|
||||
AdvSceneSwitcher.macroTab.currentRegisterHotkeys="使用快捷键控制所选宏的暂停状态"
|
||||
AdvSceneSwitcher.macroTab.currentRegisterDock="注册停靠小部件以控制所选宏的暂停状态或手动运行它"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddRunButton="添加按钮以运行宏"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddPauseButton="添加按钮以暂停或取消暂停宏"
|
||||
@@ -161,8 +159,6 @@ AdvSceneSwitcher.condition.scene.type.currentPattern="当前场景匹配"
|
||||
AdvSceneSwitcher.condition.scene.type.previousPattern="上一个场景匹配"
|
||||
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="在转场过程中检查转场源场景"
|
||||
AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="在转场过程中检查转场源场景"
|
||||
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
|
||||
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
|
||||
AdvSceneSwitcher.condition.window="窗口"
|
||||
AdvSceneSwitcher.condition.window.entry.window="{{checkTitle}}窗口标题匹配{{windowRegex}}{{windows}}"
|
||||
AdvSceneSwitcher.condition.window.entry.fullscreen="{{fullscreen}}窗口为全屏"
|
||||
@@ -186,7 +182,7 @@ AdvSceneSwitcher.condition.media.source="源"
|
||||
AdvSceneSwitcher.condition.media.anyOnScene="任何媒体源"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="所有媒体源"
|
||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="不幸的是,并非所有媒体源类型的行为都相同(例如,媒体源与VLC视频源“停止”状态)。\n所以,请尝试适合您的设置!"
|
||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}} 的状态是 {{states}} 和 {{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}的状态是{{states}}和{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.video="视频"
|
||||
AdvSceneSwitcher.condition.video.condition.match="完全匹配"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="不匹配"
|
||||
@@ -334,8 +330,7 @@ AdvSceneSwitcher.condition.sceneOrder.entry="在 {{scenes}} {{sources}} {{condit
|
||||
AdvSceneSwitcher.condition.hotkey="热键"
|
||||
AdvSceneSwitcher.condition.hotkey.name="宏触发热键"
|
||||
AdvSceneSwitcher.condition.hotkey.tip="注意:您可以在OBS设置窗口中为此热键配置按键绑定"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line1="热键被按下"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line2="名称: {{name}}"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.name="名称:{{name}}"
|
||||
AdvSceneSwitcher.condition.replay="回放缓存"
|
||||
AdvSceneSwitcher.condition.replay.state.stopped="回放缓存已停止"
|
||||
AdvSceneSwitcher.condition.replay.state.started="回放缓存已启动"
|
||||
@@ -368,10 +363,7 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="下一次匹配 在: %1"
|
||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} 重复时,将选定的日期更新为重复日期"
|
||||
AdvSceneSwitcher.condition.sceneTransform="场景项目被改变"
|
||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="内容或者设置被改变了"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="在 {{scenes}} {{sources}} {{types}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="匹配变换"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.condition.match="匹配变换"
|
||||
AdvSceneSwitcher.condition.transition="转场"
|
||||
AdvSceneSwitcher.condition.transition.type.current="当前转场特效类型为"
|
||||
AdvSceneSwitcher.condition.transition.type.duration="当前转场特效持续时间为"
|
||||
@@ -484,7 +476,6 @@ AdvSceneSwitcher.action.replay.saveWarn="警告:保存过于频繁可能会导
|
||||
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="停止推流"
|
||||
AdvSceneSwitcher.action.streaming.type.start="开始推流"
|
||||
@@ -502,7 +493,7 @@ AdvSceneSwitcher.action.filter="滤镜"
|
||||
AdvSceneSwitcher.action.filter.type.enable="开启"
|
||||
AdvSceneSwitcher.action.filter.type.disable="关闭"
|
||||
AdvSceneSwitcher.action.filter.type.settings="参数设置"
|
||||
AdvSceneSwitcher.action.filter.entry="置{{sources}}{{actions}}{{filters}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.filter.entry="置{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.action.source="源"
|
||||
AdvSceneSwitcher.action.source.type.enable="启用"
|
||||
@@ -511,7 +502,6 @@ AdvSceneSwitcher.action.source.type.settings="配置设置"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings="刷新源设置"
|
||||
AdvSceneSwitcher.action.source.type.pressSettingsButton="按下设置按钮"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="可用于刷新浏览器、媒体、源、etc"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="未找到按钮!"
|
||||
AdvSceneSwitcher.action.source.warning="警告:OBS UI无法控制全局启用和禁用源,注意,通过这个设置禁用,我测试,你必须通过这个在启用才行,不然点击小眼睛你无法显示!"
|
||||
AdvSceneSwitcher.action.source.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.action.media="媒体"
|
||||
@@ -528,7 +518,6 @@ AdvSceneSwitcher.action.macro.type.unpause="取消暂停"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="复位计数器"
|
||||
AdvSceneSwitcher.action.macro.type.run="运行操作"
|
||||
AdvSceneSwitcher.action.macro.type.stop="停止操作"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="插件状态"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项时:"
|
||||
@@ -561,7 +550,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="移动到数值位置"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="在 {{scenes}} {{actions}} {{sources}} {{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="场景项目转换"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="获得转换"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="在 {{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="在{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||
AdvSceneSwitcher.action.file="文件"
|
||||
AdvSceneSwitcher.action.file.type.write="写入"
|
||||
AdvSceneSwitcher.action.file.type.append="追加写入"
|
||||
@@ -601,14 +590,14 @@ AdvSceneSwitcher.action.screenshot.type.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="场景集合"
|
||||
AdvSceneSwitcher.action.sceneCollection.entry="将活动场景集合切换到 {{sceneCollections}}"
|
||||
AdvSceneSwitcher.action.sceneCollection.warning="注意:在此之后的任何操作都不会执行,因为更改场景集合也会重新加载场景切换器设置。\n打开“设置”窗口时,场景采集操作将被忽略."
|
||||
AdvSceneSwitcher.action.sequence="场景序列"
|
||||
AdvSceneSwitcher.action.sequence.entry="每次执行此操作时,运行列表中的下一个宏(暂停的宏将被忽略)"
|
||||
; AdvSceneSwitcher.action.sequence.entry="每次执行此操作时,运行列表中的下一个宏(暂停的宏将被忽略)"
|
||||
AdvSceneSwitcher.action.sequence.status="上次执行的宏:%1 - 要执行的下一个宏: %2"
|
||||
AdvSceneSwitcher.action.sequence.status.none="无"
|
||||
AdvSceneSwitcher.action.sequence.restart="到达列表末尾后,从开头重新启动"
|
||||
@@ -663,6 +652,8 @@ AdvSceneSwitcher.action.midi.entry="发送消息到 {{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="将MIDI消息选择设置为 {{listenDevices}} 上传入的消息: {{listenButton}}"
|
||||
AdvSceneSwitcher.action.osc="打开声音控制"
|
||||
|
||||
AdvSceneSwitcher.noSettingsButtons="未找到按钮!"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="转场特效"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效"
|
||||
@@ -827,31 +818,6 @@ AdvSceneSwitcher.videoTab.entry="当 {{videoSources}} {{condition}} {{filePath}}
|
||||
AdvSceneSwitcher.videoTab.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.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="在Studio模式下运行时发送预览场景更改消息"
|
||||
AdvSceneSwitcher.networkTab.startFailed.message="WebSockets服务器无法启动,可能是因为:\n-TCP端口%1当前可能正在该系统的其他位置使用,可能是由其他应用程序使用。请尝试在WebSocket服务器设置中设置其他TCP端口,或停止可能正在使用此端口的任何应用程序。\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="场景组"
|
||||
@@ -872,30 +838,6 @@ AdvSceneSwitcher.sceneGroupTab.exists="场景组或场景名称已存在"
|
||||
AdvSceneSwitcher.sceneGroupTab.help="可以选择场景组作为目标,就像选择常规场景一样。\n\n名称表明场景组是多个场景的集合。\n场景组将根据配置的设置在其指定场景的列表中前进,可以在右侧找到。\n\n您可以将场景组配置为前进到列表中的下一个场景:\n在多次选择场景组作为目标后。\n在经过一定时间后。\n也可以是随机的。\n\n例如,包含场景的场景组…\n场景1\n场景2\n场景3\n。。。将在第一次选择“场景1”作为目标时激活它。\n第二次将激活“场景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="当 {{scenes}} {{triggers}} {{actions}} {{audioSources}} 在 {{duration}} 后"
|
||||
AdvSceneSwitcher.sceneTriggerTab.help="此选项卡允许您在场景更改时触发操作,如停止录制或流式处理."
|
||||
|
||||
; Hotkey
|
||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="启动高级场景切换器"
|
||||
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="停止高级场景切换器"
|
||||
@@ -1055,7 +997,7 @@ AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>这似乎是第一次
|
||||
AdvSceneSwitcher.deprecatedTabWarning="此选项卡的开发已停止!请考虑转换为使用宏来代替。\n可以在“常规”选项卡上禁用此提示."
|
||||
|
||||
AdvSceneSwitcher.unit.milliseconds="毫秒"
|
||||
AdvSceneSwitcher.unit.secends="秒"
|
||||
AdvSceneSwitcher.unit.seconds="秒"
|
||||
AdvSceneSwitcher.unit.minutes="分钟"
|
||||
AdvSceneSwitcher.unit.hours="小时"
|
||||
AdvSceneSwitcher.duration.condition.none="没有时间限制"
|
||||
|
||||
1
deps/libusb
vendored
Submodule
1
deps/libusb
vendored
Submodule
Submodule deps/libusb added at d52e355daa
73
deps/obs-websocket/lib/obs-websocket-api.h
vendored
73
deps/obs-websocket/lib/obs-websocket-api.h
vendored
@@ -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;
|
||||
|
||||
2
deps/opencv
vendored
2
deps/opencv
vendored
Submodule deps/opencv updated: b0dc474160...71d3237a09
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "backup.hpp"
|
||||
#include "curl-helper.hpp"
|
||||
#include "log-helper.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
@@ -10,14 +11,14 @@
|
||||
#include "status-control.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
#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 <QFileDialog>
|
||||
#include <QMainWindow>
|
||||
#include <QTextStream>
|
||||
#include <regex>
|
||||
@@ -44,7 +45,7 @@ AdvSceneSwitcher::~AdvSceneSwitcher()
|
||||
{
|
||||
if (switcher) {
|
||||
switcher->settingsWindowOpened = false;
|
||||
switcher->lastOpenedTab = ui->tabWidget->currentIndex();
|
||||
SaveLastOpenedTab(ui->tabWidget);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +70,9 @@ static void DisplayMissingDependencyWarning()
|
||||
QString warning(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage"));
|
||||
DisplayMessage(warning.arg(failedLibsString));
|
||||
|
||||
// Only display the warning once per plugin load
|
||||
switcher->loadFailureLibs.clear();
|
||||
}
|
||||
|
||||
static void DisplayMissingDataDirWarning()
|
||||
@@ -81,18 +85,32 @@ 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());
|
||||
DisplayMessage(msg);
|
||||
}
|
||||
|
||||
bool CanCreateDefaultAction();
|
||||
bool CanCreateDefaultCondition();
|
||||
|
||||
static void DisplayCorruptedInstallWarning()
|
||||
{
|
||||
if (CanCreateDefaultAction() && CanCreateDefaultCondition()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage"));
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::LoadUI()
|
||||
{
|
||||
DisplayMissingDataDirWarning();
|
||||
DisplayMissingDependencyWarning();
|
||||
DisplayCorruptedInstallWarning();
|
||||
|
||||
SetupGeneralTab();
|
||||
SetupTitleTab();
|
||||
@@ -108,15 +126,13 @@ void AdvSceneSwitcher::LoadUI()
|
||||
SetupTimeTab();
|
||||
SetupAudioTab();
|
||||
SetupVideoTab();
|
||||
SetupNetworkTab();
|
||||
SetupSceneGroupTab();
|
||||
SetupTriggerTab();
|
||||
SetupMacroTab();
|
||||
SetupVariableTab();
|
||||
SetupOtherTabs(ui->tabWidget);
|
||||
|
||||
SetDeprecationWarnings();
|
||||
SetTabOrder();
|
||||
SetCurrentTab();
|
||||
SetTabOrder(ui->tabWidget);
|
||||
SetCurrentTab(ui->tabWidget);
|
||||
RestoreWindowGeo();
|
||||
CheckFirstTimeSetup();
|
||||
|
||||
@@ -135,6 +151,17 @@ bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
|
||||
}
|
||||
|
||||
SetElseActionsStateToVisible();
|
||||
} else if (eventType == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
auto pressedKey = keyEvent->key();
|
||||
|
||||
if (obj == ui->macros && ui->macros->isVisible()) {
|
||||
if (pressedKey == Qt::Key_F2) {
|
||||
RenameSelectedMacro();
|
||||
} else if (pressedKey == Qt::Key_Delete) {
|
||||
RemoveSelectedMacros();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QDialog::eventFilter(obj, event);
|
||||
@@ -143,7 +170,6 @@ bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
|
||||
/******************************************************************************
|
||||
* Saving and loading
|
||||
******************************************************************************/
|
||||
static void AskForBackup(const QString &json);
|
||||
|
||||
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
{
|
||||
@@ -192,34 +218,6 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
}
|
||||
}
|
||||
|
||||
static void AskForBackup(const QString &json)
|
||||
{
|
||||
const bool backupWasConfirmed = DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.askBackup"), true, false);
|
||||
|
||||
if (!backupWasConfirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getSaveFileName(
|
||||
nullptr,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle"),
|
||||
GetDefaultSettingsSaveLocation(),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType"));
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
return;
|
||||
}
|
||||
auto out = QTextStream(&file);
|
||||
out << json;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Main switcher thread
|
||||
******************************************************************************/
|
||||
@@ -255,9 +253,9 @@ void SwitcherData::Thread()
|
||||
duration = std::chrono::milliseconds(interval) +
|
||||
std::chrono::milliseconds(linger) - runTime;
|
||||
if (duration.count() < 1) {
|
||||
blog(LOG_INFO,
|
||||
"detected busy loop - refusing to sleep less than 1ms");
|
||||
duration = std::chrono::milliseconds(50);
|
||||
vblog(LOG_INFO,
|
||||
"detected busy loop - refusing to sleep less than 1ms");
|
||||
duration = std::chrono::milliseconds(10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,15 +455,8 @@ void SwitcherData::Start()
|
||||
|
||||
// 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());
|
||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
|
||||
nullptr);
|
||||
}
|
||||
|
||||
if (showSystemTrayNotifications) {
|
||||
@@ -493,11 +484,12 @@ void SwitcherData::Stop()
|
||||
delete th;
|
||||
th = nullptr;
|
||||
writeToStatusFile("Advanced Scene Switcher stopped");
|
||||
if (!obsIsShuttingDown) {
|
||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
server.stop();
|
||||
client.disconnect();
|
||||
|
||||
if (showSystemTrayNotifications) {
|
||||
DisplayTrayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||
@@ -539,9 +531,7 @@ bool SwitcherData::AnySceneTransitionStarted()
|
||||
extern "C" EXPORT void FreeSceneSwitcher()
|
||||
{
|
||||
PlatformCleanup();
|
||||
for (const auto &cleanupStep : switcher->pluginCleanupSteps) {
|
||||
cleanupStep();
|
||||
}
|
||||
RunPluginCleanupSteps();
|
||||
|
||||
delete switcher;
|
||||
switcher = nullptr;
|
||||
@@ -567,12 +557,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()
|
||||
@@ -601,17 +586,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();
|
||||
@@ -635,6 +609,8 @@ static void handleShutdown()
|
||||
// TODO: Look for a way to possibly resolve this
|
||||
obs_frontend_save();
|
||||
}
|
||||
|
||||
BackupSettingsOfCurrentVersion();
|
||||
}
|
||||
|
||||
static void handleSceneCollectionChanging()
|
||||
@@ -687,9 +663,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();
|
||||
@@ -722,7 +695,7 @@ static void LoadPlugins()
|
||||
{
|
||||
QFileInfo libPath(
|
||||
QString(obs_get_module_binary_path(obs_current_module())));
|
||||
QString pluginDir(libPath.absolutePath() + "/adv-ss-plugins");
|
||||
QString pluginDir(libPath.absolutePath() + "/" ADVSS_PLUGIN_FOLDER);
|
||||
#ifdef _WIN32
|
||||
QString libPattern = "*.dll";
|
||||
SetDllDirectory(pluginDir.toStdWString().c_str());
|
||||
@@ -762,20 +735,38 @@ void OpenSettingsWindow()
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *GetSettingsWindow()
|
||||
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
|
||||
{
|
||||
return SettingsWindowIsOpened() ? AdvSceneSwitcher::window : nullptr;
|
||||
static QObject *highlight = nullptr;
|
||||
if ((highlight && enable) || (!highlight && !enable)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (highlight && !enable) {
|
||||
highlight->deleteLater();
|
||||
highlight = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HighlightUIElementsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
highlight = HighlightWidget(ui->macroSettings, Qt::green);
|
||||
}
|
||||
|
||||
void HighligthMacroSettingsButton(bool enable)
|
||||
{
|
||||
auto window = GetSettingsWindow();
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
static_cast<AdvSceneSwitcher *>(window)->HighligthMacroSettingsButton(
|
||||
enable);
|
||||
}
|
||||
|
||||
void SetupActionQueues();
|
||||
|
||||
extern "C" EXPORT void RunPluginPostLoadSteps()
|
||||
{
|
||||
for (const auto &postLoadStep : switcher->pluginPostLoadSteps) {
|
||||
postLoadStep();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" EXPORT void InitSceneSwitcher(obs_module_t *module,
|
||||
translateFunc translate)
|
||||
{
|
||||
@@ -789,9 +780,7 @@ extern "C" EXPORT void InitSceneSwitcher(obs_module_t *module,
|
||||
SetupDock();
|
||||
SetupActionQueues();
|
||||
|
||||
for (const auto &initStep : switcher->pluginInitSteps) {
|
||||
initStep();
|
||||
}
|
||||
RunPluginInitSteps();
|
||||
|
||||
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
|
||||
obs_frontend_add_event_callback(OBSEvent, switcher);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "macro-segment-list.hpp"
|
||||
#include "condition-logic.hpp"
|
||||
#include "log-helper.hpp"
|
||||
|
||||
#include <ui_advanced-scene-switcher.h>
|
||||
@@ -34,8 +35,6 @@ public:
|
||||
|
||||
void LoadUI();
|
||||
|
||||
void SetTabOrder();
|
||||
void SetCurrentTab();
|
||||
void RestoreWindowGeo();
|
||||
void CheckFirstTimeSetup();
|
||||
|
||||
@@ -54,15 +53,15 @@ public slots:
|
||||
void on_noMatchRandomSwitch_clicked();
|
||||
void NoMatchDelayDurationChanged(const Duration &);
|
||||
void CooldownDurationChanged(const Duration &);
|
||||
void on_enableCooldown_stateChanged(int state);
|
||||
void on_startupBehavior_currentIndexChanged(int index);
|
||||
void on_logLevel_currentIndexChanged(int index);
|
||||
void on_autoStartEvent_currentIndexChanged(int index);
|
||||
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
|
||||
void on_checkInterval_valueChanged(int value);
|
||||
void on_tabMoved(int from, int to);
|
||||
void on_tabWidget_currentChanged(int index);
|
||||
void on_exportSettings_clicked();
|
||||
void on_importSettings_clicked();
|
||||
void on_verboseLogging_stateChanged(int state);
|
||||
void on_saveWindowGeo_stateChanged(int state);
|
||||
void on_showTrayNotifications_stateChanged(int state);
|
||||
void on_uiHintsDisable_stateChanged(int state);
|
||||
@@ -81,6 +80,7 @@ public:
|
||||
bool AddNewMacro(std::shared_ptr<Macro> &res, std::string &name,
|
||||
std::string format = "");
|
||||
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();
|
||||
@@ -97,6 +97,7 @@ public:
|
||||
void SetConditionData(Macro &m);
|
||||
void SwapActions(Macro *m, int pos1, int pos2);
|
||||
void SwapConditions(Macro *m, int pos1, int pos2);
|
||||
void HighligthMacroSettingsButton(bool enable = true);
|
||||
|
||||
public slots:
|
||||
void on_macroAdd_clicked();
|
||||
@@ -135,7 +136,7 @@ public slots:
|
||||
void ShowMacroElseActionsContextMenu(const QPoint &);
|
||||
void ShowMacroConditionsContextMenu(const QPoint &);
|
||||
void CopyMacro();
|
||||
void RenameCurrentMacro();
|
||||
void RenameSelectedMacro();
|
||||
void ExportMacros();
|
||||
void ImportMacros();
|
||||
void ExpandAllActions();
|
||||
@@ -154,12 +155,16 @@ public slots:
|
||||
void SetElseActionsStateToVisible();
|
||||
void MacroActionSelectionChanged(int idx);
|
||||
void MacroActionReorder(int to, int target);
|
||||
void AddMacroAction(Macro *macro, int idx, const std::string &id,
|
||||
obs_data_t *data);
|
||||
void AddMacroAction(int idx);
|
||||
void RemoveMacroAction(int idx);
|
||||
void MoveMacroActionUp(int idx);
|
||||
void MoveMacroActionDown(int idx);
|
||||
void MacroElseActionSelectionChanged(int idx);
|
||||
void MacroElseActionReorder(int to, int target);
|
||||
void AddMacroElseAction(Macro *macro, int idx, const std::string &id,
|
||||
obs_data_t *data);
|
||||
void AddMacroElseAction(int idx);
|
||||
void RemoveMacroElseAction(int idx);
|
||||
void SwapElseActions(Macro *m, int pos1, int pos2);
|
||||
@@ -168,16 +173,16 @@ public slots:
|
||||
void MacroConditionSelectionChanged(int idx);
|
||||
void MacroConditionReorder(int to, int target);
|
||||
void AddMacroCondition(int idx);
|
||||
void AddMacroCondition(Macro *macro, int idx, const std::string &id,
|
||||
obs_data_t *data, Logic::Type logic);
|
||||
void RemoveMacroCondition(int idx);
|
||||
void MoveMacroConditionUp(int idx);
|
||||
void MoveMacroConditionDown(int idx);
|
||||
void FadeOutActionControls();
|
||||
void FadeOutConditionControls();
|
||||
void ResetOpacityActionControls();
|
||||
void ResetOpacityConditionControls();
|
||||
void HighlightControls();
|
||||
void HighlightOnChange();
|
||||
void on_macroProperties_clicked();
|
||||
void on_macroSettings_clicked();
|
||||
void CopyMacroSegment();
|
||||
void PasteMacroSegment();
|
||||
|
||||
signals:
|
||||
void MacroAdded(const QString &name);
|
||||
@@ -186,9 +191,6 @@ signals:
|
||||
void MacroSegmentOrderChanged();
|
||||
void SegmentTempVarsChanged();
|
||||
void HighlightMacrosChanged(bool value);
|
||||
void HighlightActionsChanged(bool value);
|
||||
void HighlightElseActionsChanged(bool value);
|
||||
void HighlightConditionsChanged(bool value);
|
||||
|
||||
void ConnectionAdded(const QString &);
|
||||
void ConnectionRenamed(const QString &oldName, const QString &newName);
|
||||
@@ -208,16 +210,6 @@ private:
|
||||
|
||||
/* --- End of macro tab section --- */
|
||||
|
||||
/* --- Begin of variable tab section --- */
|
||||
public:
|
||||
void SetupVariableTab();
|
||||
|
||||
public slots:
|
||||
void on_variableAdd_clicked();
|
||||
void on_variableRemove_clicked();
|
||||
|
||||
/* --- End of variable tab section --- */
|
||||
|
||||
/* --- Begin of legacy tab section --- */
|
||||
public:
|
||||
void ClearFrames(QListWidget *list);
|
||||
@@ -367,24 +359,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();
|
||||
@@ -406,19 +380,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);
|
||||
|
||||
} // namespace advss
|
||||
|
||||
229
lib/general.cpp
229
lib/general.cpp
@@ -9,6 +9,7 @@
|
||||
#include "splitter-helpers.hpp"
|
||||
#include "status-control.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "tab-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "variable.hpp"
|
||||
@@ -19,17 +20,6 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
static constexpr std::array<const char *, 19> tabNames = {
|
||||
"generalTab", "macroTab", "variableTab",
|
||||
"windowTitleTab", "executableTab", "screenRegionTab",
|
||||
"mediaTab", "fileTab", "randomTab",
|
||||
"timeTab", "idleTab", "sceneSequenceTab",
|
||||
"audioTab", "videoTab", "networkTab",
|
||||
"sceneGroupTab", "transitionsTab", "pauseTab",
|
||||
"sceneTriggerTab"};
|
||||
|
||||
static std::vector<int> tabOrder = std::vector<int>(tabNames.size());
|
||||
|
||||
void AdvSceneSwitcher::reject()
|
||||
{
|
||||
close();
|
||||
@@ -101,6 +91,17 @@ void AdvSceneSwitcher::CooldownDurationChanged(const Duration &dur)
|
||||
switcher->cooldown = dur;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_enableCooldown_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->enableCooldown = state;
|
||||
ui->cooldownTime->setEnabled(state);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
||||
{
|
||||
if (loading) {
|
||||
@@ -112,6 +113,14 @@ void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
||||
static_cast<SwitcherData::StartupBehavior>(index);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_logLevel_currentIndexChanged(int value)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
switcher->logLevel = static_cast<SwitcherData::LogLevel>(value);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_autoStartEvent_currentIndexChanged(int index)
|
||||
{
|
||||
if (loading) {
|
||||
@@ -141,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 *)
|
||||
@@ -157,15 +168,6 @@ void AdvSceneSwitcher::closeEvent(QCloseEvent *)
|
||||
obs_frontend_save();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_verboseLogging_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
switcher->verbose = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_saveWindowGeo_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
@@ -216,7 +218,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 +235,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)
|
||||
@@ -245,16 +244,13 @@ void AdvSceneSwitcher::on_hideLegacyTabs_stateChanged(int state)
|
||||
|
||||
for (int idx = 0; idx < ui->tabWidget->count(); idx++) {
|
||||
if (isLegacyTab(ui->tabWidget->tabText(idx))) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
// TODO: Switch to setTabVisible() once QT 5.15 is more wide spread
|
||||
ui->tabWidget->setTabEnabled(idx, !state);
|
||||
ui->tabWidget->setStyleSheet(
|
||||
"QTabBar::tab::disabled {width: 0; height: 0; margin: 0; padding: 0; border: none;} ");
|
||||
#else
|
||||
ui->tabWidget->setTabVisible(idx, !state);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
@@ -364,67 +360,6 @@ void AdvSceneSwitcher::on_importSettings_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
static int findTabIndex(QTabWidget *tabWidget, int pos)
|
||||
{
|
||||
int at = -1;
|
||||
QString tabName = tabNames.at(pos);
|
||||
QWidget *page = tabWidget->findChild<QWidget *>(tabName);
|
||||
if (page) {
|
||||
at = tabWidget->indexOf(page);
|
||||
}
|
||||
if (at == -1) {
|
||||
blog(LOG_INFO, "failed to find tab %s",
|
||||
tabName.toUtf8().constData());
|
||||
}
|
||||
|
||||
return at;
|
||||
}
|
||||
|
||||
static bool tabWidgetOrderValid()
|
||||
{
|
||||
auto tmp = std::vector<int>(tabNames.size());
|
||||
std::iota(tmp.begin(), tmp.end(), 0);
|
||||
|
||||
for (auto &p : tmp) {
|
||||
auto it = std::find(tabOrder.begin(), tabOrder.end(), p);
|
||||
if (it == tabOrder.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void resetTabWidgetOrder()
|
||||
{
|
||||
tabOrder = std::vector<int>(tabNames.size());
|
||||
std::iota(tabOrder.begin(), tabOrder.end(), 0);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetTabOrder()
|
||||
{
|
||||
if (!tabWidgetOrderValid()) {
|
||||
resetTabWidgetOrder();
|
||||
}
|
||||
|
||||
QTabBar *bar = ui->tabWidget->tabBar();
|
||||
for (int i = 0; i < bar->count(); ++i) {
|
||||
int curPos = findTabIndex(ui->tabWidget, tabOrder[i]);
|
||||
|
||||
if (i != curPos && curPos != -1) {
|
||||
bar->moveTab(curPos, i);
|
||||
}
|
||||
}
|
||||
|
||||
connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetCurrentTab()
|
||||
{
|
||||
if (switcher->lastOpenedTab >= 0) {
|
||||
ui->tabWidget->setCurrentIndex(switcher->lastOpenedTab);
|
||||
}
|
||||
}
|
||||
|
||||
static bool windowPosValid(QPoint pos)
|
||||
{
|
||||
return !!QGuiApplication::screenAt(pos);
|
||||
@@ -447,17 +382,12 @@ void AdvSceneSwitcher::CheckFirstTimeSetup()
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_tabMoved(int from, int to)
|
||||
void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::swap(tabOrder[from], tabOrder[to]);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
|
||||
{
|
||||
switcher->showFrame = false;
|
||||
ClearFrames(ui->screenRegionSwitches);
|
||||
SetShowFrames();
|
||||
@@ -515,7 +445,7 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
|
||||
}
|
||||
|
||||
LoadMacros(obj);
|
||||
LoadGlobalMacroProperties(obj);
|
||||
LoadGlobalMacroSettings(obj);
|
||||
loadWindowTitleSwitches(obj);
|
||||
loadScreenRegionSwitches(obj);
|
||||
loadPauseSwitches(obj);
|
||||
@@ -529,8 +459,6 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
|
||||
loadTimeSwitches(obj);
|
||||
loadAudioSwitches(obj);
|
||||
loadVideoSwitches(obj);
|
||||
loadNetworkSettings(obj);
|
||||
loadSceneTriggers(obj);
|
||||
LoadGeneralSettings(obj);
|
||||
LoadHotkeys(obj);
|
||||
LoadUISettings(obj);
|
||||
@@ -538,7 +466,7 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
|
||||
RunPostLoadSteps();
|
||||
|
||||
// Reset on startup and scene collection change
|
||||
switcher->lastOpenedTab = -1;
|
||||
ResetLastOpenedTab();
|
||||
startupLoadDone = true;
|
||||
}
|
||||
|
||||
@@ -550,7 +478,7 @@ void SwitcherData::SaveSettings(obs_data_t *obj)
|
||||
|
||||
saveSceneGroups(obj);
|
||||
SaveMacros(obj);
|
||||
SaveGlobalMacroProperties(obj);
|
||||
SaveGlobalMacroSettings(obj);
|
||||
SaveVariables(obj);
|
||||
saveWindowTitleSwitches(obj);
|
||||
saveScreenRegionSwitches(obj);
|
||||
@@ -565,8 +493,6 @@ void SwitcherData::SaveSettings(obs_data_t *obj)
|
||||
saveTimeSwitches(obj);
|
||||
saveAudioSwitches(obj);
|
||||
saveVideoSwitches(obj);
|
||||
saveNetworkSwitches(obj);
|
||||
saveSceneTriggers(obj);
|
||||
SaveGeneralSettings(obj);
|
||||
SaveHotkeys(obj);
|
||||
SaveUISettings(obj);
|
||||
@@ -589,6 +515,7 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
|
||||
noMatchDelay.Save(obj, "noMatchDelay");
|
||||
|
||||
cooldown.Save(obj, "cooldown");
|
||||
obs_data_set_bool(obj, "enableCooldown", enableCooldown);
|
||||
|
||||
obs_data_set_bool(obj, "active", sceneColletionStop ? true : !stop);
|
||||
sceneColletionStop = false;
|
||||
@@ -598,7 +525,8 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
|
||||
obs_data_set_int(obj, "autoStartEvent",
|
||||
static_cast<int>(autoStartEvent));
|
||||
|
||||
obs_data_set_bool(obj, "verbose", verbose);
|
||||
obs_data_set_int(obj, "logLevel", static_cast<int>(logLevel));
|
||||
obs_data_set_int(obj, "logLevelVersion", 1);
|
||||
obs_data_set_bool(obj, "showSystemTrayNotifications",
|
||||
showSystemTrayNotifications);
|
||||
obs_data_set_bool(obj, "disableHints", disableHints);
|
||||
@@ -634,6 +562,11 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
|
||||
noMatchDelay.Load(obj, "noMatchDelay");
|
||||
|
||||
cooldown.Load(obj, "cooldown");
|
||||
if (!obs_data_has_user_value(obj, "enableCooldown")) {
|
||||
enableCooldown = cooldown.Seconds() != 0;
|
||||
} else {
|
||||
enableCooldown = obs_data_get_bool(obj, "enableCooldown");
|
||||
}
|
||||
|
||||
obs_data_set_default_bool(obj, "active", true);
|
||||
stop = !obs_data_get_bool(obj, "active");
|
||||
@@ -649,7 +582,26 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
|
||||
autoStartEvent =
|
||||
static_cast<AutoStart>(obs_data_get_int(obj, "autoStartEvent"));
|
||||
|
||||
verbose = obs_data_get_bool(obj, "verbose");
|
||||
logLevel = static_cast<LogLevel>(obs_data_get_int(obj, "logLevel"));
|
||||
if (obs_data_get_int(obj, "logLevelVersion") < 1) {
|
||||
enum OldLogLevel { DEFAULT, LOG_ACTION, VERBOSE };
|
||||
OldLogLevel oldLogLevel = static_cast<OldLogLevel>(
|
||||
obs_data_get_int(obj, "logLevel"));
|
||||
switch (oldLogLevel) {
|
||||
case DEFAULT:
|
||||
logLevel = LogLevel::DEFAULT;
|
||||
break;
|
||||
case LOG_ACTION:
|
||||
logLevel = LogLevel::LOG_ACTION;
|
||||
break;
|
||||
case VERBOSE:
|
||||
logLevel = LogLevel::VERBOSE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
showSystemTrayNotifications =
|
||||
obs_data_get_bool(obj, "showSystemTrayNotifications");
|
||||
disableHints = obs_data_get_bool(obj, "disableHints");
|
||||
@@ -686,13 +638,7 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::SaveUISettings(obs_data_t *obj)
|
||||
{
|
||||
OBSDataArrayAutoRelease tabWidgetOrder = obs_data_array_create();
|
||||
for (size_t i = 0; i < tabNames.size(); i++) {
|
||||
OBSDataAutoRelease entry = obs_data_create();
|
||||
obs_data_set_int(entry, tabNames[i], tabOrder[i]);
|
||||
obs_data_array_push_back(tabWidgetOrder, entry);
|
||||
}
|
||||
obs_data_set_array(obj, "tabWidgetOrder", tabWidgetOrder);
|
||||
SaveTabOrder(obj);
|
||||
|
||||
obs_data_set_bool(obj, "saveWindowGeo", saveWindowGeo);
|
||||
obs_data_set_int(obj, "windowPosX", windowPos.x());
|
||||
@@ -706,28 +652,7 @@ void SwitcherData::SaveUISettings(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::LoadUISettings(obs_data_t *obj)
|
||||
{
|
||||
OBSDataArrayAutoRelease defaultTabWidgetOrder = obs_data_array_create();
|
||||
for (size_t i = 0; i < tabNames.size(); i++) {
|
||||
OBSDataAutoRelease entry = obs_data_create();
|
||||
obs_data_set_default_int(entry, tabNames[i], i);
|
||||
obs_data_array_push_back(defaultTabWidgetOrder, entry);
|
||||
}
|
||||
obs_data_set_default_array(obj, "tabWidgetOrder",
|
||||
defaultTabWidgetOrder);
|
||||
|
||||
tabOrder.clear();
|
||||
OBSDataArrayAutoRelease tabWidgetOrder =
|
||||
obs_data_get_array(obj, "tabWidgetOrder");
|
||||
for (size_t i = 0; i < tabNames.size(); i++) {
|
||||
OBSDataAutoRelease entry =
|
||||
obs_data_array_item(tabWidgetOrder, i);
|
||||
tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(entry, tabNames[i])));
|
||||
}
|
||||
|
||||
if (!tabWidgetOrderValid()) {
|
||||
resetTabWidgetOrder();
|
||||
}
|
||||
LoadTabOrder(obj);
|
||||
|
||||
saveWindowGeo = obs_data_get_bool(obj, "saveWindowGeo");
|
||||
windowPos = {(int)obs_data_get_int(obj, "windowPosX"),
|
||||
@@ -764,7 +689,7 @@ void SwitcherData::CheckNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||
|
||||
void SwitcherData::checkSwitchCooldown(bool &match)
|
||||
{
|
||||
if (!match) {
|
||||
if (!match || !enableCooldown) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -904,6 +829,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);
|
||||
@@ -925,14 +872,17 @@ 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);
|
||||
ui->cooldownTime->SetDuration(switcher->cooldown);
|
||||
ui->cooldownTime->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint"));
|
||||
@@ -940,7 +890,8 @@ void AdvSceneSwitcher::SetupGeneralTab()
|
||||
SIGNAL(DurationChanged(const Duration &)), this,
|
||||
SLOT(CooldownDurationChanged(const Duration &)));
|
||||
|
||||
ui->verboseLogging->setChecked(switcher->verbose);
|
||||
ui->logLevel->setCurrentIndex(static_cast<int>(switcher->logLevel));
|
||||
|
||||
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
|
||||
ui->showTrayNotifications->setChecked(
|
||||
switcher->showSystemTrayNotifications);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
SceneGroupEditWidget *typeEdit = nullptr;
|
||||
|
||||
std::deque<SceneGroup> &GetSceneGroups()
|
||||
@@ -141,7 +141,7 @@ void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
|
||||
placeHolderText = format.arg(++i);
|
||||
}
|
||||
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
bool accepted = NameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.sceneGroupTab.add"),
|
||||
obs_module_text("AdvSceneSwitcher.sceneGroupTab.add"), name,
|
||||
placeHolderText);
|
||||
@@ -170,7 +170,10 @@ void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
|
||||
item->setData(Qt::UserRole, text);
|
||||
ui->sceneGroups->setCurrentItem(item);
|
||||
|
||||
ui->sceneGroupAdd->disconnect(addPulse);
|
||||
if (addPulse) {
|
||||
addPulse->deleteLater();
|
||||
addPulse = nullptr;
|
||||
}
|
||||
ui->sceneGroupHelp->setVisible(false);
|
||||
|
||||
emit SceneGroupAdded(QString::fromStdString(name));
|
||||
@@ -512,8 +515,8 @@ void AdvSceneSwitcher::SetupSceneGroupTab()
|
||||
|
||||
if (switcher->sceneGroups.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse = PulseWidget(ui->sceneGroupAdd,
|
||||
QColor(Qt::green));
|
||||
addPulse = HighlightWidget(ui->sceneGroupAdd,
|
||||
QColor(Qt::green));
|
||||
}
|
||||
ui->sceneGroupHelp->setVisible(true);
|
||||
} else {
|
||||
|
||||
@@ -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 QMetaObject::Connection addPulse;
|
||||
|
||||
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()),
|
||||
ui->triggerAdd, &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 =
|
||||
PulseWidget(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
|
||||
@@ -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
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace advss {
|
||||
|
||||
bool AudioSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
|
||||
void AdvSceneSwitcher::on_audioAdd_clicked()
|
||||
{
|
||||
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_audioAdd_clicked()
|
||||
AudioSwitchWidget *sw =
|
||||
new AudioSwitchWidget(this, &switcher->audioSwitches.back());
|
||||
|
||||
listAddClicked(ui->audioSwitches, sw, ui->audioAdd, &addPulse);
|
||||
listAddClicked(ui->audioSwitches, sw, &addPulse);
|
||||
|
||||
ui->audioHelp->setVisible(false);
|
||||
}
|
||||
@@ -104,7 +104,7 @@ void SwitcherData::checkAudioSwitchFallback(OBSWeakSource &scene,
|
||||
scene = audioFallback.getScene();
|
||||
transition = audioFallback.transition;
|
||||
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
audioFallback.logMatch();
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,7 @@ bool SwitcherData::checkAudioSwitch(OBSWeakSource &scene,
|
||||
transition = s.transition;
|
||||
match = true;
|
||||
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
s.logMatch();
|
||||
}
|
||||
|
||||
@@ -232,7 +232,8 @@ void AdvSceneSwitcher::SetupAudioTab()
|
||||
|
||||
if (switcher->audioSwitches.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse = PulseWidget(ui->audioAdd, QColor(Qt::green));
|
||||
addPulse = HighlightWidget(ui->audioAdd,
|
||||
QColor(Qt::green));
|
||||
}
|
||||
ui->audioHelp->setVisible(true);
|
||||
} else {
|
||||
@@ -437,10 +438,8 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
|
||||
audioVolumeThreshold->setMaximum(100);
|
||||
audioVolumeThreshold->setMinimum(0);
|
||||
|
||||
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
|
||||
audioVolumeThreshold, SLOT(setValue(int)));
|
||||
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
|
||||
volMeter->GetSlider(), SLOT(setValue(int)));
|
||||
QWidget::connect(volMeter->GetSlider(), &DoubleSlider::DoubleValChanged,
|
||||
[=](double) { SyncSliderAndValueSelection(true); });
|
||||
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(VolumeThresholdChanged(int)));
|
||||
QWidget::connect(condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
@@ -487,6 +486,7 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
|
||||
switchData = s;
|
||||
|
||||
loading = false;
|
||||
SyncSliderAndValueSelection(false);
|
||||
}
|
||||
|
||||
AudioSwitch *AudioSwitchWidget::getSwitchData()
|
||||
@@ -509,6 +509,21 @@ void AudioSwitchWidget::swapSwitchData(AudioSwitchWidget *s1,
|
||||
s2->setSwitchData(t);
|
||||
}
|
||||
|
||||
void AudioSwitchWidget::SyncSliderAndValueSelection(bool sliderMoved)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sliderMoved) {
|
||||
auto sliderPosition = volMeter->GetSlider()->DoubleValue();
|
||||
audioVolumeThreshold->setValue(sliderPosition);
|
||||
} else {
|
||||
volMeter->GetSlider()->SetDoubleVal(
|
||||
switchData->volumeThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSwitchWidget::UpdateVolmeterSource()
|
||||
{
|
||||
delete volMeter;
|
||||
@@ -520,10 +535,8 @@ void AudioSwitchWidget::UpdateVolmeterSource()
|
||||
QLayout *layout = this->layout();
|
||||
layout->addWidget(volMeter);
|
||||
|
||||
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
|
||||
audioVolumeThreshold, SLOT(setValue(int)));
|
||||
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
|
||||
volMeter->GetSlider(), SLOT(setValue(int)));
|
||||
QWidget::connect(volMeter->GetSlider(), &DoubleSlider::DoubleValChanged,
|
||||
[=](double) { SyncSliderAndValueSelection(true); });
|
||||
|
||||
// Slider will default to 0 so set it manually once
|
||||
volMeter->GetSlider()->setValue(switchData->volumeThreshold);
|
||||
@@ -547,8 +560,11 @@ void AudioSwitchWidget::VolumeThresholdChanged(int vol)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->volumeThreshold = vol;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->volumeThreshold = vol;
|
||||
}
|
||||
SyncSliderAndValueSelection(false);
|
||||
}
|
||||
|
||||
void AudioSwitchWidget::ConditionChanged(int cond)
|
||||
|
||||
@@ -79,6 +79,7 @@ private slots:
|
||||
void ConditionChanged(int cond);
|
||||
void DurationChanged(const Duration &);
|
||||
void IgnoreInactiveChanged(int state);
|
||||
void SyncSliderAndValueSelection(bool sliderMoved);
|
||||
|
||||
private:
|
||||
QComboBox *audioSources;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace advss {
|
||||
|
||||
bool ExecutableSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
|
||||
void AdvSceneSwitcher::on_executableAdd_clicked()
|
||||
{
|
||||
@@ -19,7 +19,7 @@ void AdvSceneSwitcher::on_executableAdd_clicked()
|
||||
listAddClicked(ui->executables,
|
||||
new ExecutableSwitchWidget(
|
||||
this, &switcher->executableSwitches.back()),
|
||||
ui->executableAdd, &addPulse);
|
||||
&addPulse);
|
||||
|
||||
ui->exeHelp->setVisible(false);
|
||||
}
|
||||
@@ -119,7 +119,7 @@ bool SwitcherData::checkExeSwitch(OBSWeakSource &scene,
|
||||
scene = s.getScene();
|
||||
transition = s.transition;
|
||||
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
s.logMatch();
|
||||
}
|
||||
break;
|
||||
@@ -177,8 +177,8 @@ void AdvSceneSwitcher::SetupExecutableTab()
|
||||
|
||||
if (switcher->executableSwitches.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse = PulseWidget(ui->executableAdd,
|
||||
QColor(Qt::green));
|
||||
addPulse = HighlightWidget(ui->executableAdd,
|
||||
QColor(Qt::green));
|
||||
}
|
||||
ui->exeHelp->setVisible(true);
|
||||
} else {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
namespace advss {
|
||||
|
||||
bool FileSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
static std::hash<std::string> strHash;
|
||||
|
||||
void AdvSceneSwitcher::on_browseButton_clicked()
|
||||
@@ -274,7 +274,7 @@ bool SwitcherData::checkFileContent(OBSWeakSource &scene,
|
||||
transition = s.transition;
|
||||
match = true;
|
||||
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
s.logMatch();
|
||||
}
|
||||
break;
|
||||
@@ -291,7 +291,7 @@ void AdvSceneSwitcher::on_fileAdd_clicked()
|
||||
listAddClicked(ui->fileSwitches,
|
||||
new FileSwitchWidget(this,
|
||||
&switcher->fileSwitches.back()),
|
||||
ui->fileAdd, &addPulse);
|
||||
&addPulse);
|
||||
|
||||
ui->fileHelp->setVisible(false);
|
||||
}
|
||||
@@ -441,7 +441,8 @@ void AdvSceneSwitcher::SetupFileTab()
|
||||
|
||||
if (switcher->fileSwitches.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse = PulseWidget(ui->fileAdd, QColor(Qt::green));
|
||||
addPulse =
|
||||
HighlightWidget(ui->fileAdd, QColor(Qt::green));
|
||||
}
|
||||
ui->fileHelp->setVisible(true);
|
||||
} else {
|
||||
|
||||
@@ -53,7 +53,7 @@ bool SwitcherData::checkIdleSwitch(OBSWeakSource &scene,
|
||||
match = true;
|
||||
idleData.alreadySwitched = true;
|
||||
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
idleData.logMatch();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace advss {
|
||||
|
||||
bool MediaSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
|
||||
constexpr auto media_played_to_end_idx = 8;
|
||||
constexpr auto media_any_idx = 9;
|
||||
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_mediaAdd_clicked()
|
||||
listAddClicked(ui->mediaSwitches,
|
||||
new MediaSwitchWidget(this,
|
||||
&switcher->mediaSwitches.back()),
|
||||
ui->mediaAdd, &addPulse);
|
||||
&addPulse);
|
||||
|
||||
ui->mediaHelp->setVisible(false);
|
||||
}
|
||||
@@ -193,7 +193,7 @@ bool SwitcherData::checkMediaSwitch(OBSWeakSource &scene,
|
||||
scene = mediaSwitch.getScene();
|
||||
transition = mediaSwitch.transition;
|
||||
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
mediaSwitch.logMatch();
|
||||
}
|
||||
}
|
||||
@@ -252,7 +252,8 @@ void AdvSceneSwitcher::SetupMediaTab()
|
||||
|
||||
if (switcher->mediaSwitches.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse = PulseWidget(ui->mediaAdd, QColor(Qt::green));
|
||||
addPulse = HighlightWidget(ui->mediaAdd,
|
||||
QColor(Qt::green));
|
||||
}
|
||||
ui->mediaHelp->setVisible(true);
|
||||
} else {
|
||||
|
||||
@@ -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 (switcher->verbose) {
|
||||
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 (switcher->verbose && !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 (switcher->verbose) {
|
||||
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
|
||||
@@ -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 advss
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
|
||||
void AdvSceneSwitcher::on_pauseAdd_clicked()
|
||||
{
|
||||
@@ -20,7 +20,7 @@ void AdvSceneSwitcher::on_pauseAdd_clicked()
|
||||
listAddClicked(ui->pauseEntries,
|
||||
new PauseEntryWidget(this,
|
||||
&switcher->pauseEntries.back()),
|
||||
ui->pauseAdd, &addPulse);
|
||||
&addPulse);
|
||||
|
||||
ui->pauseHelp->setVisible(false);
|
||||
}
|
||||
@@ -264,7 +264,8 @@ void AdvSceneSwitcher::SetupPauseTab()
|
||||
|
||||
if (switcher->pauseEntries.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse = PulseWidget(ui->pauseAdd, QColor(Qt::green));
|
||||
addPulse = HighlightWidget(ui->pauseAdd,
|
||||
QColor(Qt::green));
|
||||
}
|
||||
ui->pauseHelp->setVisible(true);
|
||||
} else {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace advss {
|
||||
|
||||
bool RandomSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
|
||||
void AdvSceneSwitcher::on_randomAdd_clicked()
|
||||
{
|
||||
@@ -19,7 +19,7 @@ void AdvSceneSwitcher::on_randomAdd_clicked()
|
||||
listAddClicked(ui->randomSwitches,
|
||||
new RandomSwitchWidget(this,
|
||||
&switcher->randomSwitches.back()),
|
||||
ui->randomAdd, &addPulse);
|
||||
&addPulse);
|
||||
|
||||
ui->randomHelp->setVisible(false);
|
||||
}
|
||||
@@ -76,7 +76,7 @@ bool SwitcherData::checkRandom(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
lastRandomScene = r.scene;
|
||||
lastRandomSceneGroup = r.group;
|
||||
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
r.logMatch();
|
||||
}
|
||||
break;
|
||||
@@ -131,8 +131,8 @@ void AdvSceneSwitcher::SetupRandomTab()
|
||||
|
||||
if (switcher->randomSwitches.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse =
|
||||
PulseWidget(ui->randomAdd, QColor(Qt::green));
|
||||
addPulse = HighlightWidget(ui->randomAdd,
|
||||
QColor(Qt::green));
|
||||
}
|
||||
ui->randomHelp->setVisible(true);
|
||||
} else {
|
||||
@@ -143,12 +143,12 @@ 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) {
|
||||
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
|
||||
QColor(0, 0, 0, 0));
|
||||
HighlightWidget(ui->randomDisabledWarning,
|
||||
QColor(Qt::red), QColor(0, 0, 0, 0));
|
||||
}
|
||||
} else {
|
||||
ui->randomDisabledWarning->setVisible(false);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace advss {
|
||||
|
||||
bool ScreenRegionSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
|
||||
void AdvSceneSwitcher::ClearFrames(QListWidget *list)
|
||||
{
|
||||
@@ -79,7 +79,7 @@ void AdvSceneSwitcher::on_screenRegionAdd_clicked()
|
||||
listAddClicked(ui->screenRegionSwitches,
|
||||
new ScreenRegionWidget(
|
||||
this, &switcher->screenRegionSwitches.back()),
|
||||
ui->screenRegionAdd, &addPulse);
|
||||
&addPulse);
|
||||
|
||||
ui->regionHelp->setVisible(false);
|
||||
}
|
||||
@@ -183,7 +183,7 @@ bool SwitcherData::checkScreenRegionSwitch(OBSWeakSource &scene,
|
||||
transition = s.transition;
|
||||
minRegionSize = regionSize;
|
||||
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
s.logMatch();
|
||||
}
|
||||
break;
|
||||
@@ -248,8 +248,8 @@ void AdvSceneSwitcher::SetupRegionTab()
|
||||
|
||||
if (switcher->screenRegionSwitches.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse = PulseWidget(ui->screenRegionAdd,
|
||||
QColor(Qt::green));
|
||||
addPulse = HighlightWidget(ui->screenRegionAdd,
|
||||
QColor(Qt::green));
|
||||
}
|
||||
ui->regionHelp->setVisible(true);
|
||||
} else {
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace advss {
|
||||
constexpr auto max_extend_text_size = 150;
|
||||
|
||||
bool SceneSequenceSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
|
||||
void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
|
||||
{
|
||||
@@ -26,7 +26,7 @@ void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
|
||||
listAddClicked(ui->sceneSequenceSwitches,
|
||||
new SequenceWidget(
|
||||
this, &switcher->sceneSequenceSwitches.back()),
|
||||
ui->sceneSequenceAdd, &addPulse);
|
||||
&addPulse);
|
||||
|
||||
ui->sequenceHelp->setVisible(false);
|
||||
}
|
||||
@@ -225,13 +225,13 @@ bool SwitcherData::checkSceneSequence(OBSWeakSource &scene,
|
||||
scene = s.getScene();
|
||||
transition = s.transition;
|
||||
setPrevSceneAfterLinger = s.usePreviousScene;
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
s.logMatch();
|
||||
}
|
||||
}
|
||||
|
||||
s.advanceActiveSequence();
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
s.logAdvanceSequence();
|
||||
}
|
||||
|
||||
@@ -299,8 +299,8 @@ void AdvSceneSwitcher::SetupSequenceTab()
|
||||
|
||||
if (switcher->sceneSequenceSwitches.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse = PulseWidget(ui->sceneSequenceAdd,
|
||||
QColor(Qt::green));
|
||||
addPulse = HighlightWidget(ui->sceneSequenceAdd,
|
||||
QColor(Qt::green));
|
||||
}
|
||||
ui->sequenceHelp->setVisible(true);
|
||||
} else {
|
||||
@@ -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);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace advss {
|
||||
|
||||
bool TimeSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
|
||||
void AdvSceneSwitcher::on_timeAdd_clicked()
|
||||
{
|
||||
@@ -17,7 +17,7 @@ void AdvSceneSwitcher::on_timeAdd_clicked()
|
||||
listAddClicked(ui->timeSwitches,
|
||||
new TimeSwitchWidget(this,
|
||||
&switcher->timeSwitches.back()),
|
||||
ui->timeAdd, &addPulse);
|
||||
&addPulse);
|
||||
|
||||
ui->timeHelp->setVisible(false);
|
||||
}
|
||||
@@ -142,7 +142,7 @@ bool SwitcherData::checkTimeSwitch(OBSWeakSource &scene,
|
||||
transition = s.transition;
|
||||
match = true;
|
||||
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
s.logMatch();
|
||||
}
|
||||
break;
|
||||
@@ -197,7 +197,8 @@ void AdvSceneSwitcher::SetupTimeTab()
|
||||
|
||||
if (switcher->timeSwitches.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse = PulseWidget(ui->timeAdd, QColor(Qt::green));
|
||||
addPulse =
|
||||
HighlightWidget(ui->timeAdd, QColor(Qt::green));
|
||||
}
|
||||
ui->timeHelp->setVisible(true);
|
||||
} else {
|
||||
|
||||
@@ -168,7 +168,7 @@ void SwitcherData::checkDefaultSceneTransitions()
|
||||
|
||||
for (auto &t : defaultSceneTransitions) {
|
||||
if (t.checkMatch(currentScene)) {
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
t.logMatch();
|
||||
}
|
||||
t.setTransition();
|
||||
@@ -427,7 +427,8 @@ DefTransitionSwitchWidget::DefTransitionSwitchWidget(QWidget *parent,
|
||||
{
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", scenes}, {"{{transitions}}", transitions}};
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions}};
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.transitionTab.defaultTransitionEntry"),
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace advss {
|
||||
|
||||
bool VideoSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
|
||||
void AdvSceneSwitcher::on_videoAdd_clicked()
|
||||
{
|
||||
@@ -24,7 +24,7 @@ void AdvSceneSwitcher::on_videoAdd_clicked()
|
||||
VideoSwitchWidget *sw =
|
||||
new VideoSwitchWidget(this, &switcher->videoSwitches.back());
|
||||
|
||||
listAddClicked(ui->videoSwitches, sw, ui->videoAdd, &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());
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ bool SwitcherData::checkVideoSwitch(OBSWeakSource &scene,
|
||||
match = true;
|
||||
scene = s.getScene();
|
||||
transition = s.transition;
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
s.logMatch();
|
||||
}
|
||||
}
|
||||
@@ -202,7 +202,8 @@ void AdvSceneSwitcher::SetupVideoTab()
|
||||
|
||||
if (switcher->videoSwitches.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse = PulseWidget(ui->videoAdd, QColor(Qt::green));
|
||||
addPulse = HighlightWidget(ui->videoAdd,
|
||||
QColor(Qt::green));
|
||||
}
|
||||
ui->videoHelp->setVisible(true);
|
||||
} else {
|
||||
@@ -261,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);
|
||||
}
|
||||
|
||||
@@ -292,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace advss {
|
||||
|
||||
bool WindowSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
static QObject *addPulse = nullptr;
|
||||
|
||||
void AdvSceneSwitcher::on_windowAdd_clicked()
|
||||
{
|
||||
@@ -21,7 +21,7 @@ void AdvSceneSwitcher::on_windowAdd_clicked()
|
||||
listAddClicked(ui->windowSwitches,
|
||||
new WindowSwitchWidget(this,
|
||||
&switcher->windowSwitches.back()),
|
||||
ui->windowAdd, &addPulse);
|
||||
&addPulse);
|
||||
|
||||
ui->windowHelp->setVisible(false);
|
||||
}
|
||||
@@ -249,7 +249,7 @@ bool SwitcherData::checkWindowTitleSwitch(OBSWeakSource &scene,
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if (verbose) {
|
||||
if (VerboseLoggingEnabled()) {
|
||||
s.logMatch();
|
||||
}
|
||||
break;
|
||||
@@ -335,8 +335,8 @@ void AdvSceneSwitcher::SetupTitleTab()
|
||||
|
||||
if (switcher->windowSwitches.size() == 0) {
|
||||
if (!switcher->disableHints) {
|
||||
addPulse =
|
||||
PulseWidget(ui->windowAdd, QColor(Qt::green));
|
||||
addPulse = HighlightWidget(ui->windowAdd,
|
||||
QColor(Qt::green));
|
||||
}
|
||||
ui->windowHelp->setVisible(true);
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "macro-properties.hpp"
|
||||
#include "macro-settings.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
#include "section.hpp"
|
||||
#include "switch-button.hpp"
|
||||
|
||||
@@ -28,8 +29,7 @@ static inline void populateActionSelection(QComboBox *list)
|
||||
MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> *entryData,
|
||||
const std::string &id)
|
||||
: MacroSegmentEdit(GetGlobalMacroProperties()._highlightActions,
|
||||
parent),
|
||||
: MacroSegmentEdit(parent),
|
||||
_actionSelection(new FilterComboBox()),
|
||||
_enable(new SwitchButton()),
|
||||
_entryData(entryData)
|
||||
@@ -39,8 +39,6 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
SLOT(ActionSelectionChanged(const QString &)));
|
||||
QWidget::connect(_enable, SIGNAL(checked(bool)), this,
|
||||
SLOT(ActionEnableChanged(bool)));
|
||||
QWidget::connect(window(), SIGNAL(HighlightActionsChanged(bool)), this,
|
||||
SLOT(EnableHighlight(bool)));
|
||||
QWidget::connect(&_actionStateTimer, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateActionState()));
|
||||
|
||||
@@ -88,6 +86,7 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
|
||||
*_entryData = MacroActionFactory::Create(id, macro);
|
||||
(*_entryData)->SetIndex(idx);
|
||||
(*_entryData)->PostLoad();
|
||||
RunPostLoadSteps();
|
||||
}
|
||||
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
|
||||
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
||||
@@ -159,6 +158,34 @@ std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
|
||||
return *_entryData;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::AddMacroAction(Macro *macro, int idx,
|
||||
const std::string &id, obs_data_t *data)
|
||||
{
|
||||
if (idx < 0 || idx > (int)macro->Actions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->Actions().emplace(macro->Actions().begin() + idx,
|
||||
MacroActionFactory::Create(id, macro));
|
||||
if (data) {
|
||||
macro->Actions().at(idx)->Load(data);
|
||||
}
|
||||
macro->Actions().at(idx)->PostLoad();
|
||||
RunPostLoadSteps();
|
||||
macro->UpdateActionIndices();
|
||||
ui->actionsList->Insert(
|
||||
idx,
|
||||
new MacroActionEdit(this, ¯o->Actions()[idx], id));
|
||||
SetActionData(*macro);
|
||||
}
|
||||
HighlightAction(idx);
|
||||
ui->actionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::AddMacroAction(int idx)
|
||||
{
|
||||
auto macro = GetSelectedMacro();
|
||||
@@ -167,6 +194,7 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
|
||||
}
|
||||
|
||||
if (idx < 0 || idx > (int)macro->Actions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -176,26 +204,13 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
|
||||
} else {
|
||||
id = MacroAction::GetDefaultID();
|
||||
}
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->Actions().emplace(
|
||||
macro->Actions().begin() + idx,
|
||||
MacroActionFactory::Create(id, macro.get()));
|
||||
if (idx - 1 >= 0) {
|
||||
auto data = obs_data_create();
|
||||
macro->Actions().at(idx - 1)->Save(data);
|
||||
macro->Actions().at(idx)->Load(data);
|
||||
macro->Actions().at(idx)->PostLoad();
|
||||
obs_data_release(data);
|
||||
}
|
||||
macro->UpdateActionIndices();
|
||||
ui->actionsList->Insert(
|
||||
idx,
|
||||
new MacroActionEdit(this, ¯o->Actions()[idx], id));
|
||||
SetActionData(*macro);
|
||||
|
||||
OBSDataAutoRelease data;
|
||||
if (idx - 1 >= 0) {
|
||||
data = obs_data_create();
|
||||
macro->Actions().at(idx - 1)->Save(data);
|
||||
}
|
||||
HighlightAction(idx);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
AddMacroAction(macro.get(), idx, id, data);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionAdd_clicked()
|
||||
@@ -213,7 +228,6 @@ void AdvSceneSwitcher::on_actionAdd_clicked()
|
||||
if (currentActionIdx != -1) {
|
||||
MacroActionSelectionChanged(currentActionIdx + 1);
|
||||
}
|
||||
ui->actionsList->SetHelpMsgVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::RemoveMacroAction(int idx)
|
||||
@@ -308,7 +322,6 @@ void AdvSceneSwitcher::on_elseActionAdd_clicked()
|
||||
if (currentElseActionIdx != -1) {
|
||||
MacroElseActionSelectionChanged(currentElseActionIdx + 1);
|
||||
}
|
||||
ui->elseActionsList->SetHelpMsgVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_elseActionRemove_clicked()
|
||||
@@ -447,6 +460,36 @@ void AdvSceneSwitcher::MacroElseActionReorder(int to, int from)
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::AddMacroElseAction(Macro *macro, int idx,
|
||||
const std::string &id,
|
||||
obs_data_t *data)
|
||||
{
|
||||
if (idx < 0 || idx > (int)macro->ElseActions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->ElseActions().emplace(macro->ElseActions().begin() + idx,
|
||||
MacroActionFactory::Create(id,
|
||||
macro));
|
||||
if (data) {
|
||||
macro->ElseActions().at(idx)->Load(data);
|
||||
}
|
||||
macro->ElseActions().at(idx)->PostLoad();
|
||||
RunPostLoadSteps();
|
||||
macro->UpdateElseActionIndices();
|
||||
ui->elseActionsList->Insert(
|
||||
idx, new MacroActionEdit(
|
||||
this, ¯o->ElseActions()[idx], id));
|
||||
SetElseActionData(*macro);
|
||||
}
|
||||
HighlightElseAction(idx);
|
||||
ui->elseActionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::AddMacroElseAction(int idx)
|
||||
{
|
||||
auto macro = GetSelectedMacro();
|
||||
@@ -455,6 +498,7 @@ void AdvSceneSwitcher::AddMacroElseAction(int idx)
|
||||
}
|
||||
|
||||
if (idx < 0 || idx > (int)macro->ElseActions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -464,25 +508,13 @@ void AdvSceneSwitcher::AddMacroElseAction(int idx)
|
||||
} else {
|
||||
id = MacroAction::GetDefaultID();
|
||||
}
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->ElseActions().emplace(
|
||||
macro->ElseActions().begin() + idx,
|
||||
MacroActionFactory::Create(id, macro.get()));
|
||||
if (idx - 1 >= 0) {
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
macro->ElseActions().at(idx - 1)->Save(data);
|
||||
macro->ElseActions().at(idx)->Load(data);
|
||||
}
|
||||
macro->ElseActions().at(idx)->PostLoad();
|
||||
macro->UpdateElseActionIndices();
|
||||
ui->elseActionsList->Insert(
|
||||
idx, new MacroActionEdit(
|
||||
this, ¯o->ElseActions()[idx], id));
|
||||
SetElseActionData(*macro);
|
||||
|
||||
OBSDataAutoRelease data;
|
||||
if (idx - 1 >= 0) {
|
||||
data = obs_data_create();
|
||||
macro->ElseActions().at(idx - 1)->Save(data);
|
||||
}
|
||||
HighlightElseAction(idx);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
AddMacroElseAction(macro.get(), idx, id, data);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
|
||||
@@ -503,7 +535,7 @@ void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
|
||||
SetMacroAbortWait(true);
|
||||
GetMacroWaitCV().notify_all();
|
||||
macro->UpdateElseActionIndices();
|
||||
SetActionData(*macro);
|
||||
SetElseActionData(*macro);
|
||||
}
|
||||
MacroElseActionSelectionChanged(-1);
|
||||
lastInteracted = MacroSection::ELSE_ACTIONS;
|
||||
|
||||
@@ -1,7 +1,28 @@
|
||||
#include "macro-action-factory.hpp"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace advss {
|
||||
|
||||
namespace {
|
||||
|
||||
class MacroActionUnknown : public MacroAction {
|
||||
public:
|
||||
MacroActionUnknown(Macro *m) : MacroAction(m) {}
|
||||
std::shared_ptr<MacroAction> Copy() const
|
||||
{
|
||||
return std::make_shared<MacroActionUnknown>(GetMacro());
|
||||
}
|
||||
bool PerformAction() { return true; };
|
||||
bool Save(obs_data_t *obj) const { return MacroAction::Save(obj); };
|
||||
bool Load(obs_data_t *obj) { return MacroAction::Load(obj); };
|
||||
std::string GetId() const { return "unknown"; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
static std::recursive_mutex mutex;
|
||||
|
||||
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
|
||||
{
|
||||
static std::map<std::string, MacroActionInfo> _methods;
|
||||
@@ -10,6 +31,7 @@ std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
|
||||
|
||||
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (auto it = GetMap().find(id); it == GetMap().end()) {
|
||||
GetMap()[id] = info;
|
||||
return true;
|
||||
@@ -17,35 +39,61 @@ bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroActionFactory::Deregister(const std::string &id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (GetMap().count(id) == 0) {
|
||||
return false;
|
||||
}
|
||||
GetMap().erase(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::shared_ptr<MacroAction> createUnknownAction(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionUnknown>(m);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
|
||||
Macro *m)
|
||||
{
|
||||
if (auto it = GetMap().find(id); it != GetMap().end())
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||
return it->second._create(m);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return createUnknownAction(m);
|
||||
}
|
||||
|
||||
static QWidget *createUnknownActionWidget()
|
||||
{
|
||||
return new QLabel(obs_module_text("AdvSceneSwitcher.action.unknown"));
|
||||
}
|
||||
|
||||
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
if (auto it = GetMap().find(id); it != GetMap().end())
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||
return it->second._createWidget(parent, action);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return createUnknownActionWidget();
|
||||
}
|
||||
|
||||
std::string MacroActionFactory::GetActionName(const std::string &id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||
return it->second._name;
|
||||
}
|
||||
return "unknown action";
|
||||
return obs_module_text("AdvSceneSwitcher.action.unknown");
|
||||
}
|
||||
|
||||
std::string MacroActionFactory::GetIdByName(const QString &name)
|
||||
{
|
||||
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;
|
||||
@@ -54,4 +102,14 @@ std::string MacroActionFactory::GetIdByName(const QString &name)
|
||||
return "";
|
||||
}
|
||||
|
||||
bool CanCreateDefaultAction()
|
||||
{
|
||||
const auto action = MacroActionFactory::Create(
|
||||
MacroAction::GetDefaultID().data(), nullptr);
|
||||
if (!action) {
|
||||
return false;
|
||||
}
|
||||
return action->GetId() == MacroAction::GetDefaultID().data();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
namespace advss {
|
||||
|
||||
struct MacroActionInfo {
|
||||
using CreateAction = std::shared_ptr<MacroAction> (*)(Macro *m);
|
||||
using CreateActionWidget = QWidget *(*)(QWidget *parent,
|
||||
std::shared_ptr<MacroAction>);
|
||||
CreateAction _create = nullptr;
|
||||
|
||||
std::function<std::shared_ptr<MacroAction>(Macro *m)> _create = nullptr;
|
||||
CreateActionWidget _createWidget = nullptr;
|
||||
std::string _name;
|
||||
};
|
||||
@@ -19,6 +19,7 @@ public:
|
||||
MacroActionFactory() = delete;
|
||||
|
||||
EXPORT static bool Register(const std::string &id, MacroActionInfo);
|
||||
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,
|
||||
@@ -31,4 +32,6 @@ private:
|
||||
static std::map<std::string, MacroActionInfo> &GetMap();
|
||||
};
|
||||
|
||||
bool CanCreateDefaultAction();
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "macro-action-macro.hpp"
|
||||
#include "help-icon.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "macro.hpp"
|
||||
|
||||
@@ -48,9 +49,7 @@ bool MacroActionMacro::PerformAction()
|
||||
macro->ResetRunCount();
|
||||
break;
|
||||
case Action::RUN:
|
||||
if (!macro->Paused()) {
|
||||
macro->PerformActions(true, false, true);
|
||||
}
|
||||
RunActions(macro.get());
|
||||
break;
|
||||
case Action::STOP:
|
||||
macro->Stop();
|
||||
@@ -88,32 +87,32 @@ void MacroActionMacro::LogAction() const
|
||||
}
|
||||
switch (_action) {
|
||||
case Action::PAUSE:
|
||||
vblog(LOG_INFO, "paused \"%s\"", macro->Name().c_str());
|
||||
ablog(LOG_INFO, "paused \"%s\"", macro->Name().c_str());
|
||||
break;
|
||||
case Action::UNPAUSE:
|
||||
vblog(LOG_INFO, "unpaused \"%s\"", macro->Name().c_str());
|
||||
ablog(LOG_INFO, "unpaused \"%s\"", macro->Name().c_str());
|
||||
break;
|
||||
case Action::RESET_COUNTER:
|
||||
vblog(LOG_INFO, "reset counter for \"%s\"",
|
||||
ablog(LOG_INFO, "reset counter for \"%s\"",
|
||||
macro->Name().c_str());
|
||||
break;
|
||||
case Action::RUN:
|
||||
vblog(LOG_INFO, "run nested macro \"%s\"",
|
||||
ablog(LOG_INFO, "run nested macro \"%s\"",
|
||||
macro->Name().c_str());
|
||||
break;
|
||||
case Action::STOP:
|
||||
vblog(LOG_INFO, "stopped macro \"%s\"", macro->Name().c_str());
|
||||
ablog(LOG_INFO, "stopped macro \"%s\"", macro->Name().c_str());
|
||||
break;
|
||||
case Action::DISABLE_ACTION:
|
||||
vblog(LOG_INFO, "disabled action %d of macro \"%s\"",
|
||||
ablog(LOG_INFO, "disabled action %d of macro \"%s\"",
|
||||
_actionIndex.GetValue(), macro->Name().c_str());
|
||||
break;
|
||||
case Action::ENABLE_ACTION:
|
||||
vblog(LOG_INFO, "enabled action %d of macro \"%s\"",
|
||||
ablog(LOG_INFO, "enabled action %d of macro \"%s\"",
|
||||
_actionIndex.GetValue(), macro->Name().c_str());
|
||||
break;
|
||||
case Action::TOGGLE_ACTION:
|
||||
vblog(LOG_INFO, "toggled action %d of macro \"%s\"",
|
||||
ablog(LOG_INFO, "toggled action %d of macro \"%s\"",
|
||||
_actionIndex.GetValue(), macro->Name().c_str());
|
||||
break;
|
||||
default:
|
||||
@@ -127,6 +126,7 @@ bool MacroActionMacro::Save(obs_data_t *obj) const
|
||||
_macro.Save(obj);
|
||||
_actionIndex.Save(obj, "actionIndex");
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
_runOptions.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -137,6 +137,15 @@ bool MacroActionMacro::Load(obs_data_t *obj)
|
||||
_actionIndex.Load(obj, "actionIndex");
|
||||
_action = static_cast<MacroActionMacro::Action>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
_runOptions.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionMacro::PostLoad()
|
||||
{
|
||||
MacroRefAction::PostLoad();
|
||||
MacroAction::PostLoad();
|
||||
_runOptions.macro.PostLoad();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -145,11 +154,83 @@ std::string MacroActionMacro::GetShortDesc() const
|
||||
return _macro.Name();
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
std::shared_ptr<MacroAction> MacroActionMacro::Create(Macro *m)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
return std::make_shared<MacroActionMacro>(m);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionMacro::Copy() const
|
||||
{
|
||||
return std::make_shared<MacroActionMacro>(*this);
|
||||
}
|
||||
|
||||
void MacroActionMacro::ResolveVariablesToFixedValues()
|
||||
{
|
||||
_actionIndex.ResolveVariables();
|
||||
}
|
||||
|
||||
static void runActionsHelper(Macro *macro, bool runElseActions, bool setInputs,
|
||||
const StringList &inputs)
|
||||
{
|
||||
if (setInputs) {
|
||||
macro->GetInputVariables().SetValues(inputs);
|
||||
}
|
||||
macro->PerformActions(!runElseActions, false, true);
|
||||
}
|
||||
|
||||
void MacroActionMacro::RunActions(Macro *actionMacro) const
|
||||
{
|
||||
if (_runOptions.skipWhenPaused && actionMacro->Paused()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_runOptions.logic == RunOptions::Logic::IGNORE_CONDITIONS) {
|
||||
runActionsHelper(actionMacro, _runOptions.runElseActions,
|
||||
_runOptions.setInputs, _runOptions.inputs);
|
||||
return;
|
||||
}
|
||||
|
||||
auto conditionMacro = _runOptions.macro.GetMacro();
|
||||
if (!conditionMacro) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_runOptions.reevaluateConditionState) {
|
||||
conditionMacro->CheckConditions(true);
|
||||
}
|
||||
|
||||
if ((_runOptions.logic == RunOptions::Logic::CONDITIONS &&
|
||||
conditionMacro->ConditionsMatched()) ||
|
||||
(_runOptions.logic == RunOptions::Logic::INVERT_CONDITIONS &&
|
||||
!conditionMacro->ConditionsMatched())) {
|
||||
runActionsHelper(actionMacro, _runOptions.runElseActions,
|
||||
_runOptions.setInputs, _runOptions.inputs);
|
||||
}
|
||||
}
|
||||
|
||||
static void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : actionTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static void populateConditionBehaviorSelection(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.conditions.ignore"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.conditions.true"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.conditions.false"));
|
||||
}
|
||||
|
||||
static void populateActionTypeSelection(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.actionType.regular"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.actionType.else"));
|
||||
}
|
||||
|
||||
MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
@@ -158,9 +239,28 @@ MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
_macros(new MacroSelection(parent)),
|
||||
_actionIndex(new MacroSegmentSelection(
|
||||
this, MacroSegmentSelection::Type::ACTION)),
|
||||
_actions(new QComboBox())
|
||||
_actions(new QComboBox()),
|
||||
_conditionMacros(new MacroSelection(parent)),
|
||||
_conditionBehaviors(new QComboBox()),
|
||||
_reevaluateConditionState(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.macro.type.run."
|
||||
"updateConditionMatchState"))),
|
||||
_actionTypes(new QComboBox()),
|
||||
_skipWhenPaused(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.skipWhenPaused"))),
|
||||
_setInputs(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.setInputs"))),
|
||||
_inputs(new MacroInputEdit()),
|
||||
_entryLayout(new QHBoxLayout()),
|
||||
_conditionLayout(new QHBoxLayout()),
|
||||
_reevaluateConditionStateLayout(new QHBoxLayout()),
|
||||
_setInputsLayout(new QHBoxLayout())
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
populateConditionBehaviorSelection(_conditionBehaviors);
|
||||
populateActionTypeSelection(_actionTypes);
|
||||
|
||||
_conditionMacros->HideSelectedMacro();
|
||||
|
||||
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(MacroChanged(const QString &)));
|
||||
@@ -169,20 +269,53 @@ MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
QWidget::connect(_actionIndex,
|
||||
SIGNAL(SelectionChanged(const IntVariable &)), this,
|
||||
SLOT(ActionIndexChanged(const IntVariable &)));
|
||||
QWidget::connect(_conditionMacros,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ConditionMacroChanged(const QString &)));
|
||||
QWidget::connect(_conditionBehaviors, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(ConditionBehaviorChanged(int)));
|
||||
QWidget::connect(_actionTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionTypeChanged(int)));
|
||||
QWidget::connect(_skipWhenPaused, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(SkipWhenPausedChanged(int)));
|
||||
QWidget::connect(_setInputs, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(SetInputsChanged(int)));
|
||||
QWidget::connect(_inputs,
|
||||
SIGNAL(MacroInputValuesChanged(const StringList &)),
|
||||
this, SLOT(InputsChanged(const StringList &)));
|
||||
QWidget::connect(_reevaluateConditionState, SIGNAL(stateChanged(int)),
|
||||
this, SLOT(ReevaluateConditionStateChanged(int)));
|
||||
|
||||
auto mainLayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"),
|
||||
mainLayout,
|
||||
{{"{{actions}}", _actions},
|
||||
{"{{actionIndex}}", _actionIndex},
|
||||
{"{{macros}}", _macros}});
|
||||
setLayout(mainLayout);
|
||||
_setInputsLayout->addWidget(_setInputs);
|
||||
_setInputsLayout->addWidget(new HelpIcon(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.setInputs.description")));
|
||||
_setInputsLayout->addStretch();
|
||||
|
||||
_reevaluateConditionStateLayout->addWidget(_reevaluateConditionState);
|
||||
_reevaluateConditionStateLayout->addWidget(new HelpIcon(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState.help")));
|
||||
_reevaluateConditionStateLayout->addStretch();
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addLayout(_entryLayout);
|
||||
layout->addLayout(_conditionLayout);
|
||||
layout->addLayout(_reevaluateConditionStateLayout);
|
||||
layout->addLayout(_setInputsLayout);
|
||||
layout->addWidget(_inputs);
|
||||
layout->addWidget(_skipWhenPaused);
|
||||
setLayout(layout);
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void HighligthMacroSettingsButton(bool enable);
|
||||
|
||||
MacroActionMacroEdit::~MacroActionMacroEdit()
|
||||
{
|
||||
HighligthMacroSettingsButton(false);
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
@@ -192,45 +325,132 @@ void MacroActionMacroEdit::UpdateEntryData()
|
||||
_actionIndex->SetValue(_entryData->_actionIndex);
|
||||
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
|
||||
_macros->SetCurrentMacro(_entryData->_macro);
|
||||
_conditionMacros->SetCurrentMacro(_entryData->_runOptions.macro);
|
||||
_conditionBehaviors->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_runOptions.logic));
|
||||
_reevaluateConditionState->setChecked(
|
||||
_entryData->_runOptions.reevaluateConditionState);
|
||||
_actionTypes->setCurrentIndex(
|
||||
_entryData->_runOptions.runElseActions ? 1 : 0);
|
||||
_skipWhenPaused->setChecked(_entryData->_runOptions.skipWhenPaused);
|
||||
_setInputs->setChecked(_entryData->_runOptions.setInputs);
|
||||
SetupMacroInput(_entryData->_macro.GetMacro().get());
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::MacroChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_macro = text;
|
||||
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
|
||||
const auto ¯o = _entryData->_macro.GetMacro();
|
||||
_actionIndex->SetMacro(macro);
|
||||
SetupMacroInput(macro.get());
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_action = static_cast<MacroActionMacro::Action>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::ActionIndexChanged(const IntVariable &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_actionIndex = value;
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::ConditionMacroChanged(const QString &text)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_runOptions.macro = text;
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::ConditionBehaviorChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_runOptions.logic =
|
||||
static_cast<MacroActionMacro::RunOptions::Logic>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::ReevaluateConditionStateChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_runOptions.reevaluateConditionState = value;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::ActionTypeChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_runOptions.runElseActions = value;
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::SkipWhenPausedChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_runOptions.skipWhenPaused = value;
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::SetInputsChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_runOptions.setInputs = value;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::InputsChanged(const StringList &inputs)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_runOptions.inputs = inputs;
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
{
|
||||
_entryLayout->removeWidget(_actions);
|
||||
_entryLayout->removeWidget(_actionIndex);
|
||||
_entryLayout->removeWidget(_macros);
|
||||
_entryLayout->removeWidget(_actionTypes);
|
||||
_conditionLayout->removeWidget(_conditionBehaviors);
|
||||
_conditionLayout->removeWidget(_conditionMacros);
|
||||
|
||||
ClearLayout(_entryLayout);
|
||||
ClearLayout(_conditionLayout);
|
||||
|
||||
const std::unordered_map<std::string, QWidget *> placeholders = {
|
||||
{"{{actions}}", _actions},
|
||||
{"{{actionIndex}}", _actionIndex},
|
||||
{"{{macros}}", _macros},
|
||||
{"{{actionTypes}}", _actionTypes},
|
||||
{"{{conditionBehaviors}}", _conditionBehaviors},
|
||||
{"{{conditionMacros}}", _conditionMacros},
|
||||
|
||||
};
|
||||
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
_entryData->_action == MacroActionMacro::Action::RUN
|
||||
? "AdvSceneSwitcher.action.macro.entry.run"
|
||||
: "AdvSceneSwitcher.action.macro.entry.other"),
|
||||
_entryLayout, placeholders);
|
||||
|
||||
if (_entryData->_runOptions.logic ==
|
||||
MacroActionMacro::RunOptions::Logic::IGNORE_CONDITIONS) {
|
||||
_conditionLayout->addWidget(_conditionBehaviors);
|
||||
_conditionLayout->addStretch();
|
||||
} else {
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.entry.run.condition"),
|
||||
_conditionLayout, placeholders);
|
||||
}
|
||||
|
||||
if (_entryData->_action == MacroActionMacro::Action::RUN ||
|
||||
_entryData->_action == MacroActionMacro::Action::STOP) {
|
||||
_macros->HideSelectedMacro();
|
||||
@@ -245,6 +465,73 @@ void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
MacroActionMacro::Action::ENABLE_ACTION ||
|
||||
_entryData->_action == MacroActionMacro::Action::TOGGLE_ACTION;
|
||||
_actionIndex->setVisible(isModifyingActionState);
|
||||
|
||||
SetLayoutVisible(_conditionLayout,
|
||||
_entryData->_action == MacroActionMacro::Action::RUN);
|
||||
const bool needsAdditionalConditionWidgets =
|
||||
_entryData->_action == MacroActionMacro::Action::RUN &&
|
||||
_entryData->_runOptions.logic !=
|
||||
MacroActionMacro::RunOptions::Logic::IGNORE_CONDITIONS;
|
||||
_conditionMacros->setVisible(needsAdditionalConditionWidgets);
|
||||
SetLayoutVisible(_reevaluateConditionStateLayout,
|
||||
needsAdditionalConditionWidgets);
|
||||
SetLayoutVisible(_setInputsLayout,
|
||||
_entryData->_action == MacroActionMacro::Action::RUN);
|
||||
_inputs->setVisible(_entryData->_action ==
|
||||
MacroActionMacro::Action::RUN &&
|
||||
_entryData->_runOptions.setInputs);
|
||||
HighligthMacroSettingsButton(_entryData->_action ==
|
||||
MacroActionMacro::Action::RUN &&
|
||||
_entryData->_runOptions.setInputs &&
|
||||
!_inputs->HasInputsToSet());
|
||||
_actionTypes->setVisible(_entryData->_action ==
|
||||
MacroActionMacro::Action::RUN);
|
||||
_skipWhenPaused->setVisible(_entryData->_action ==
|
||||
MacroActionMacro::Action::RUN);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::SetupMacroInput(Macro *macro) const
|
||||
{
|
||||
if (macro) {
|
||||
_inputs->SetInputVariablesAndValues(
|
||||
macro->GetInputVariables(),
|
||||
_entryData->_runOptions.inputs);
|
||||
} else {
|
||||
_inputs->SetInputVariablesAndValues({}, {});
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionMacro::RunOptions::Save(obs_data_t *obj) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_int(data, "logic", static_cast<int>(logic));
|
||||
obs_data_set_bool(data, "reevaluateConditionState",
|
||||
reevaluateConditionState);
|
||||
obs_data_set_bool(data, "runElseActions", runElseActions);
|
||||
obs_data_set_bool(data, "skipWhenPaused", skipWhenPaused);
|
||||
obs_data_set_bool(data, "setInputs", setInputs);
|
||||
inputs.Save(data, "inputs");
|
||||
macro.Save(data);
|
||||
obs_data_set_obj(obj, "runOptions", data);
|
||||
}
|
||||
|
||||
void MacroActionMacro::RunOptions::Load(obs_data_t *obj)
|
||||
{
|
||||
if (!obs_data_has_user_value(obj, "runOptions")) {
|
||||
return;
|
||||
}
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, "runOptions");
|
||||
logic = static_cast<Logic>(obs_data_get_int(data, "logic"));
|
||||
reevaluateConditionState =
|
||||
obs_data_get_bool(data, "reevaluateConditionState");
|
||||
runElseActions = obs_data_get_bool(data, "runElseActions");
|
||||
skipWhenPaused = obs_data_get_bool(data, "skipWhenPaused");
|
||||
setInputs = obs_data_get_bool(data, "setInputs");
|
||||
inputs.Load(data, "inputs");
|
||||
macro.Load(data);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,25 +1,44 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-input.hpp"
|
||||
#include "macro-selection.hpp"
|
||||
#include "macro-segment-selection.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroActionMacro : public MacroRefAction {
|
||||
class MacroActionMacro final : public MacroRefAction {
|
||||
public:
|
||||
MacroActionMacro(Macro *m) : MacroAction(m), MacroRefAction(m) {}
|
||||
bool PerformAction();
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
bool PostLoad();
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionMacro>(m);
|
||||
}
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m);
|
||||
std::shared_ptr<MacroAction> Copy() const;
|
||||
void ResolveVariablesToFixedValues();
|
||||
|
||||
struct RunOptions {
|
||||
void Save(obs_data_t *obj) const;
|
||||
void Load(obs_data_t *obj);
|
||||
enum class Logic {
|
||||
IGNORE_CONDITIONS,
|
||||
CONDITIONS,
|
||||
INVERT_CONDITIONS,
|
||||
};
|
||||
Logic logic;
|
||||
bool reevaluateConditionState = false;
|
||||
bool runElseActions = false;
|
||||
bool skipWhenPaused = true;
|
||||
bool setInputs = false;
|
||||
StringList inputs;
|
||||
MacroRef macro;
|
||||
};
|
||||
|
||||
enum class Action {
|
||||
PAUSE,
|
||||
@@ -31,21 +50,25 @@ public:
|
||||
ENABLE_ACTION,
|
||||
TOGGLE_ACTION,
|
||||
};
|
||||
Action _action = Action::PAUSE;
|
||||
Action _action = Action::RUN;
|
||||
IntVariable _actionIndex = 1;
|
||||
RunOptions _runOptions = {};
|
||||
|
||||
private:
|
||||
void RunActions(Macro *actionMacro) const;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionMacroEdit : public QWidget {
|
||||
class MacroActionMacroEdit final : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionMacroEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionMacro> entryData = nullptr);
|
||||
~MacroActionMacroEdit();
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
@@ -59,18 +82,37 @@ private slots:
|
||||
void MacroChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
void ActionIndexChanged(const IntVariable &value);
|
||||
void ConditionMacroChanged(const QString &text);
|
||||
void ConditionBehaviorChanged(int value);
|
||||
void ReevaluateConditionStateChanged(int value);
|
||||
void ActionTypeChanged(int value);
|
||||
void SkipWhenPausedChanged(int value);
|
||||
void SetInputsChanged(int value);
|
||||
void InputsChanged(const StringList &);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
void SetupMacroInput(Macro *) const;
|
||||
|
||||
MacroSelection *_macros;
|
||||
MacroSegmentSelection *_actionIndex;
|
||||
QComboBox *_actions;
|
||||
MacroSelection *_conditionMacros;
|
||||
QComboBox *_conditionBehaviors;
|
||||
QCheckBox *_reevaluateConditionState;
|
||||
QComboBox *_actionTypes;
|
||||
QCheckBox *_skipWhenPaused;
|
||||
QCheckBox *_setInputs;
|
||||
MacroInputEdit *_inputs;
|
||||
QHBoxLayout *_entryLayout;
|
||||
QHBoxLayout *_conditionLayout;
|
||||
QHBoxLayout *_reevaluateConditionStateLayout;
|
||||
QHBoxLayout *_setInputsLayout;
|
||||
|
||||
std::shared_ptr<MacroActionMacro> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@ void MacroActionQueue::AddActions(ActionQueue *queue)
|
||||
|
||||
auto actions = *GetMacroActions(macro.get());
|
||||
for (const auto &action : actions) {
|
||||
queue->Add(action);
|
||||
if (action->Enabled()) {
|
||||
queue->Add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,20 +71,20 @@ void MacroActionQueue::LogAction() const
|
||||
}
|
||||
switch (_action) {
|
||||
case Action::ADD_TO_QUEUE:
|
||||
vblog(LOG_INFO, "queued actions of \"%s\" to \"%s\"",
|
||||
ablog(LOG_INFO, "queued actions of \"%s\" to \"%s\"",
|
||||
GetMacroName(macro.get()).c_str(),
|
||||
GetActionQueueName(_queue).c_str());
|
||||
break;
|
||||
case Action::START_QUEUE:
|
||||
vblog(LOG_INFO, "start queue \"%s\"",
|
||||
ablog(LOG_INFO, "start queue \"%s\"",
|
||||
GetActionQueueName(_queue).c_str());
|
||||
break;
|
||||
case Action::STOP_QUEUE:
|
||||
vblog(LOG_INFO, "stop queue \"%s\"",
|
||||
ablog(LOG_INFO, "stop queue \"%s\"",
|
||||
GetActionQueueName(_queue).c_str());
|
||||
break;
|
||||
case Action::CLEAR_QUEUE:
|
||||
vblog(LOG_INFO, "cleared queue \"%s\"",
|
||||
ablog(LOG_INFO, "cleared queue \"%s\"",
|
||||
GetActionQueueName(_queue).c_str());
|
||||
break;
|
||||
default:
|
||||
@@ -114,6 +116,16 @@ std::string MacroActionQueue::GetShortDesc() const
|
||||
return GetActionQueueName(_queue);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionQueue::Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionQueue>(m);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionQueue::Copy() const
|
||||
{
|
||||
return std::make_shared<MacroActionQueue>(*this);
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : actionTypes) {
|
||||
|
||||
@@ -16,10 +16,8 @@ public:
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionQueue>(m);
|
||||
}
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m);
|
||||
std::shared_ptr<MacroAction> Copy() const;
|
||||
|
||||
enum class Action {
|
||||
ADD_TO_QUEUE,
|
||||
|
||||
92
lib/macro/macro-action-script.cpp
Normal file
92
lib/macro/macro-action-script.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "macro-action-script.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "properties-view.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
MacroActionScript::MacroActionScript(Macro *m, const std::string &id,
|
||||
const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignal,
|
||||
const std::string &completionSignal)
|
||||
: MacroAction(m),
|
||||
MacroSegmentScript(defaultSettings, propertiesSignalName,
|
||||
triggerSignal, completionSignal),
|
||||
_id(id)
|
||||
{
|
||||
}
|
||||
|
||||
MacroActionScript::MacroActionScript(const advss::MacroActionScript &other)
|
||||
: MacroAction(other.GetMacro()),
|
||||
MacroSegmentScript(other),
|
||||
_id(other._id)
|
||||
{
|
||||
}
|
||||
|
||||
bool MacroActionScript::PerformAction()
|
||||
{
|
||||
if (!ScriptHandler::ActionIdIsValid(_id)) {
|
||||
blog(LOG_WARNING, "skipping unknown script action \"%s\"",
|
||||
_id.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
(void)SendTriggerSignal();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionScript::LogAction() const
|
||||
{
|
||||
ablog(LOG_INFO, "performing script action \"%s\"", GetId().c_str());
|
||||
}
|
||||
|
||||
bool MacroActionScript::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
MacroSegmentScript::Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionScript::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
MacroSegmentScript::Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionScript::Copy() const
|
||||
{
|
||||
return std::make_shared<MacroActionScript>(*this);
|
||||
}
|
||||
|
||||
void MacroActionScript::WaitForCompletion() const
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
start - start);
|
||||
const auto timeoutMs = GetTimeoutSeconds() * 1000.0;
|
||||
|
||||
std::unique_lock<std::mutex> lock(*GetMutex());
|
||||
while (!TriggerIsCompleted()) {
|
||||
if (MacroWaitShouldAbort() || MacroIsStopped(GetMacro())) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((double)timePassed.count() > timeoutMs) {
|
||||
blog(LOG_INFO, "script action timeout (%s)",
|
||||
_id.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
GetMacroWaitCV().wait_for(lock, 10ms);
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
timePassed =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - start);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
29
lib/macro/macro-action-script.hpp
Normal file
29
lib/macro/macro-action-script.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-script-handler.hpp"
|
||||
#include "macro-segment-script.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroActionScript : public MacroAction, public MacroSegmentScript {
|
||||
public:
|
||||
MacroActionScript(Macro *m, const std::string &id,
|
||||
const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignal,
|
||||
const std::string &signalComplete);
|
||||
MacroActionScript(const advss::MacroActionScript &);
|
||||
bool PerformAction();
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() const { return _id; };
|
||||
std::shared_ptr<MacroAction> Copy() const;
|
||||
|
||||
private:
|
||||
void WaitForCompletion() const;
|
||||
|
||||
std::string _id = "";
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -54,6 +54,27 @@ const static std::map<MacroActionVariable::Type, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.variable.type.setToTempvar"},
|
||||
{MacroActionVariable::Type::SCENE_ITEM_NAME,
|
||||
"AdvSceneSwitcher.action.variable.type.sceneItemName"},
|
||||
{MacroActionVariable::Type::PAD,
|
||||
"AdvSceneSwitcher.action.variable.type.padValue"},
|
||||
{MacroActionVariable::Type::TRUNCATE,
|
||||
"AdvSceneSwitcher.action.variable.type.truncateValue"},
|
||||
{MacroActionVariable::Type::SWAP_VALUES,
|
||||
"AdvSceneSwitcher.action.variable.type.swapValues"},
|
||||
{MacroActionVariable::Type::TRIM,
|
||||
"AdvSceneSwitcher.action.variable.type.trim"},
|
||||
{MacroActionVariable::Type::CHANGE_CASE,
|
||||
"AdvSceneSwitcher.action.variable.type.changeCase"},
|
||||
};
|
||||
|
||||
const static std::map<MacroActionVariable::CaseType, std::string> caseTypes = {
|
||||
{MacroActionVariable::CaseType::LOWER_CASE,
|
||||
"AdvSceneSwitcher.action.variable.case.type.lowerCase"},
|
||||
{MacroActionVariable::CaseType::UPPER_CASE,
|
||||
"AdvSceneSwitcher.action.variable.case.type.upperCase"},
|
||||
{MacroActionVariable::CaseType::CAPITALIZED,
|
||||
"AdvSceneSwitcher.action.variable.case.type.capitalized"},
|
||||
{MacroActionVariable::CaseType::START_CASE,
|
||||
"AdvSceneSwitcher.action.variable.case.type.startCase"},
|
||||
};
|
||||
|
||||
static void apppend(Variable &var, const std::string &value)
|
||||
@@ -129,18 +150,12 @@ void MacroActionVariable::HandleFindAndReplace(Variable *var)
|
||||
var->SetValue(value);
|
||||
return;
|
||||
}
|
||||
QString resultString = QString::fromStdString(value);
|
||||
|
||||
QString replacement = QString::fromStdString(_replaceStr);
|
||||
auto regex = _findRegex.GetRegularExpression(_findStr);
|
||||
auto matchIterator = regex.globalMatch(QString::fromStdString(value));
|
||||
int offset = 0;
|
||||
while (matchIterator.hasNext()) {
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
resultString.replace(match.capturedStart() + offset,
|
||||
match.capturedLength(), replacement);
|
||||
offset += replacement.length() - match.capturedLength();
|
||||
}
|
||||
var->SetValue(resultString.toStdString());
|
||||
auto result = QString::fromStdString(value);
|
||||
result = result.replace(regex, replacement);
|
||||
var->SetValue(result.toStdString());
|
||||
}
|
||||
|
||||
void MacroActionVariable::HandleMathExpression(Variable *var)
|
||||
@@ -153,6 +168,43 @@ void MacroActionVariable::HandleMathExpression(Variable *var)
|
||||
var->SetValue(std::get<double>(result));
|
||||
}
|
||||
|
||||
void MacroActionVariable::HandleCaseChange(Variable *var)
|
||||
{
|
||||
auto value = QString::fromStdString(var->Value());
|
||||
|
||||
switch (_caseType) {
|
||||
case CaseType::LOWER_CASE:
|
||||
var->SetValue(value.toLower().toStdString());
|
||||
break;
|
||||
case CaseType::UPPER_CASE:
|
||||
var->SetValue(value.toUpper().toStdString());
|
||||
break;
|
||||
case CaseType::CAPITALIZED: {
|
||||
if (value.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
value[0] = value[0].toUpper();
|
||||
var->SetValue(value.toStdString());
|
||||
break;
|
||||
}
|
||||
case CaseType::START_CASE: {
|
||||
auto partList = value.split(QRegularExpression("\\b"));
|
||||
|
||||
for (auto &part : partList) {
|
||||
if (part.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
part[0] = part[0].toUpper();
|
||||
}
|
||||
|
||||
var->SetValue(partList.join("").toStdString());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
struct GetSceneItemNameHelper {
|
||||
int curIdx = 0;
|
||||
int targetIdx = 0;
|
||||
@@ -198,7 +250,8 @@ void MacroActionVariable::SetToSceneItemName(Variable *var)
|
||||
|
||||
struct AskForInputParams {
|
||||
AskForInputParams(const QString &prompt_, const QString &placeholder_)
|
||||
: prompt(prompt_), placeholder(placeholder_){};
|
||||
: prompt(prompt_),
|
||||
placeholder(placeholder_){};
|
||||
QString prompt;
|
||||
QString placeholder;
|
||||
std::optional<std::string> result;
|
||||
@@ -216,6 +269,38 @@ static void askForInput(void *param)
|
||||
parameters->resultReady = true;
|
||||
}
|
||||
|
||||
static std::string truncate(const std::string &input,
|
||||
MacroActionVariable::Direction direction,
|
||||
size_t length)
|
||||
{
|
||||
if (input.length() <= length) {
|
||||
return input;
|
||||
}
|
||||
|
||||
if (direction == MacroActionVariable::Direction::RIGHT) {
|
||||
return input.substr(0, length);
|
||||
} else {
|
||||
return input.substr(input.length() - length);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string pad(const std::string &input,
|
||||
MacroActionVariable::Direction direction, size_t length,
|
||||
char paddingChar)
|
||||
{
|
||||
if (input.length() >= length) {
|
||||
return input;
|
||||
}
|
||||
|
||||
if (direction == MacroActionVariable::Direction::RIGHT) {
|
||||
return input +
|
||||
std::string(length - input.length(), paddingChar);
|
||||
} else {
|
||||
return std::string(length - input.length(), paddingChar) +
|
||||
input;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionVariable::PerformAction()
|
||||
{
|
||||
auto var = _variable.lock();
|
||||
@@ -310,7 +395,10 @@ bool MacroActionVariable::PerformAction()
|
||||
return true;
|
||||
}
|
||||
case Type::ENV_VARIABLE: {
|
||||
var->SetValue(std::getenv(_envVariableName.c_str()));
|
||||
auto value = std::getenv(_envVariableName.c_str());
|
||||
if (value) {
|
||||
var->SetValue(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Type::SCENE_ITEM_COUNT: {
|
||||
@@ -344,6 +432,34 @@ bool MacroActionVariable::PerformAction()
|
||||
case Type::SCENE_ITEM_NAME:
|
||||
SetToSceneItemName(var.get());
|
||||
return true;
|
||||
case Type::PAD:
|
||||
var->SetValue(pad(var->Value(), _direction, _stringLength,
|
||||
_paddingChar));
|
||||
return true;
|
||||
case Type::TRUNCATE:
|
||||
var->SetValue(
|
||||
truncate(var->Value(), _direction, _stringLength));
|
||||
return true;
|
||||
case Type::SWAP_VALUES: {
|
||||
auto var2 = _variable2.lock();
|
||||
if (!var2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto tempValue = var->Value();
|
||||
var->SetValue(var2->Value());
|
||||
var2->SetValue(tempValue);
|
||||
return true;
|
||||
}
|
||||
case Type::TRIM: {
|
||||
var->SetValue(QString::fromStdString(var->Value())
|
||||
.trimmed()
|
||||
.toStdString());
|
||||
return true;
|
||||
}
|
||||
case Type::CHANGE_CASE:
|
||||
HandleCaseChange(var.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -377,6 +493,11 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
|
||||
_scene.Save(obj);
|
||||
_tempVar.Save(obj);
|
||||
_sceneItemIndex.Save(obj, "sceneItemIndex");
|
||||
obs_data_set_int(obj, "direction", static_cast<int>(_direction));
|
||||
_stringLength.Save(obj, "stringLength");
|
||||
obs_data_set_int(obj, "paddingChar", _paddingChar);
|
||||
obs_data_set_int(obj, "caseType", static_cast<int>(_caseType));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -408,6 +529,15 @@ bool MacroActionVariable::Load(obs_data_t *obj)
|
||||
_scene.Load(obj);
|
||||
_tempVar.Load(obj, GetMacro());
|
||||
_sceneItemIndex.Load(obj, "sceneItemIndex");
|
||||
_direction = static_cast<Direction>(obs_data_get_int(obj, "direction"));
|
||||
_stringLength.Load(obj, "stringLength");
|
||||
if (obs_data_has_user_value(obj, "paddingChar")) {
|
||||
_paddingChar = obs_data_get_int(obj, "paddingChar");
|
||||
} else {
|
||||
_paddingChar = ' ';
|
||||
}
|
||||
_caseType = static_cast<CaseType>(obs_data_get_int(obj, "caseType"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -422,6 +552,16 @@ std::string MacroActionVariable::GetShortDesc() const
|
||||
return GetWeakVariableName(_variable);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionVariable::Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionVariable>(m);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionVariable::Copy() const
|
||||
{
|
||||
return std::make_shared<MacroActionVariable>(*this);
|
||||
}
|
||||
|
||||
void MacroActionVariable::SetSegmentIndexValue(int value)
|
||||
{
|
||||
DecrementCurrentSegmentVariableRef();
|
||||
@@ -485,6 +625,19 @@ int MacroActionVariable::GetSegmentIndexValue() const
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MacroActionVariable::ResolveVariablesToFixedValues()
|
||||
{
|
||||
_strValue.ResolveVariables();
|
||||
_findStr.ResolveVariables();
|
||||
_replaceStr.ResolveVariables();
|
||||
_mathExpression.ResolveVariables();
|
||||
_inputPrompt.ResolveVariables();
|
||||
_inputPlaceholder.ResolveVariables();
|
||||
_envVariableName.ResolveVariables();
|
||||
_scene.ResolveVariables();
|
||||
_sceneItemIndex.ResolveVariables();
|
||||
}
|
||||
|
||||
void MacroActionVariable::DecrementCurrentSegmentVariableRef()
|
||||
{
|
||||
auto segment = _macroSegment.lock();
|
||||
@@ -503,6 +656,24 @@ static inline void populateTypeSelection(QComboBox *list)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateCaseTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[type, name] : caseTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()),
|
||||
static_cast<int>(type));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateDirectionSelection(QComboBox *list)
|
||||
{
|
||||
list->addItems(
|
||||
QStringList()
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.truncateOrPadDirection.left")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.truncateOrPadDirection.right"));
|
||||
}
|
||||
|
||||
MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionVariable> entryData)
|
||||
: QWidget(parent),
|
||||
@@ -539,7 +710,13 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
_scenes(new SceneSelectionWidget(this, true, false, true, true,
|
||||
true)),
|
||||
_tempVars(new TempVariableSelection(this)),
|
||||
_tempVarsHelp(new HelpIcon(obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.type.setToTempvar.help"))),
|
||||
_sceneItemIndex(new VariableSpinBox()),
|
||||
_direction(new QComboBox()),
|
||||
_stringLength(new VariableSpinBox()),
|
||||
_paddingCharSelection(new SingleCharSelection()),
|
||||
_caseType(new FilterComboBox(this)),
|
||||
_entryLayout(new QHBoxLayout())
|
||||
{
|
||||
_numValue->setMinimum(-9999999999);
|
||||
@@ -556,10 +733,15 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
_regexMatchIdx->setMinimum(1);
|
||||
_regexMatchIdx->setMaximum(999);
|
||||
_regexMatchIdx->setSuffix(".");
|
||||
_replaceStr->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.findAndReplace.replace.tooltip"));
|
||||
_inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Preferred);
|
||||
_sceneItemIndex->setMinimum(1);
|
||||
_stringLength->setMaximum(999);
|
||||
populateTypeSelection(_actions);
|
||||
populateDirectionSelection(_direction);
|
||||
populateCaseTypeSelection(_caseType);
|
||||
|
||||
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
||||
this, SLOT(VariableChanged(const QString &)));
|
||||
@@ -615,8 +797,19 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
_sceneItemIndex,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(SceneItemIndexChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(_direction, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(DirectionChanged(int)));
|
||||
QWidget::connect(
|
||||
_stringLength,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(StringLengthChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(_paddingCharSelection,
|
||||
SIGNAL(CharChanged(const QString &)), this,
|
||||
SLOT(CharSelectionChanged(const QString &)));
|
||||
QWidget::connect(_caseType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(CaseTypeChanged(int)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{variables}}", _variables},
|
||||
{"{{variables2}}", _variables2},
|
||||
{"{{actions}}", _actions},
|
||||
@@ -637,10 +830,16 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
{"{{envVariableName}}", _envVariable},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{tempVars}}", _tempVars},
|
||||
{"{{tempVarsHelp}}", _tempVarsHelp},
|
||||
{"{{sceneItemIndex}}", _sceneItemIndex},
|
||||
{"{{direction}}", _direction},
|
||||
{"{{stringLength}}", _stringLength},
|
||||
{"{{paddingCharSelection}}", _paddingCharSelection},
|
||||
{"{{caseType}}", _caseType},
|
||||
};
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.variable.entry"),
|
||||
_entryLayout, widgetPlaceholders);
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.variable.entry.other"),
|
||||
_entryLayout, widgetPlaceholders);
|
||||
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
@@ -733,6 +932,13 @@ void MacroActionVariableEdit::UpdateEntryData()
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_tempVars->SetVariable(_entryData->_tempVar);
|
||||
_sceneItemIndex->SetValue(_entryData->_sceneItemIndex);
|
||||
_direction->setCurrentIndex(static_cast<int>(_entryData->_direction));
|
||||
_stringLength->SetValue(_entryData->_stringLength);
|
||||
_paddingCharSelection->setText(
|
||||
QChar::fromLatin1(_entryData->_paddingChar));
|
||||
_caseType->setCurrentIndex(
|
||||
_caseType->findData(static_cast<int>(_entryData->_caseType)));
|
||||
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -1089,6 +1295,7 @@ void MacroActionVariableEdit::SelectionChanged(const TempVariableRef &var)
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_tempVar = var;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::SceneItemIndexChanged(
|
||||
@@ -1102,12 +1309,97 @@ void MacroActionVariableEdit::SceneItemIndexChanged(
|
||||
_entryData->_sceneItemIndex = value;
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::DirectionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_direction =
|
||||
static_cast<MacroActionVariable::Direction>(value);
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::StringLengthChanged(
|
||||
const NumberVariable<int> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_stringLength = value;
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::CharSelectionChanged(const QString &character)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
if (character.isEmpty()) {
|
||||
_entryData->_paddingChar = ' ';
|
||||
} else {
|
||||
_entryData->_paddingChar = character.toStdString().at(0);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::CaseTypeChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_caseType = static_cast<MacroActionVariable::CaseType>(
|
||||
_caseType->itemData(index).toInt());
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{variables}}", _variables},
|
||||
{"{{variables2}}", _variables2},
|
||||
{"{{actions}}", _actions},
|
||||
{"{{strValue}}", _strValue},
|
||||
{"{{numValue}}", _numValue},
|
||||
{"{{segmentIndex}}", _segmentIdx},
|
||||
{"{{mathExpression}}", _mathExpression},
|
||||
{"{{envVariableName}}", _envVariable},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{tempVars}}", _tempVars},
|
||||
{"{{tempVarsHelp}}", _tempVarsHelp},
|
||||
{"{{sceneItemIndex}}", _sceneItemIndex},
|
||||
{"{{direction}}", _direction},
|
||||
{"{{stringLength}}", _stringLength},
|
||||
{"{{paddingCharSelection}}", _paddingCharSelection},
|
||||
{"{{caseType}}", _caseType},
|
||||
};
|
||||
|
||||
const char *layoutString = "";
|
||||
if (_entryData->_type == MacroActionVariable::Type::PAD) {
|
||||
layoutString = obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.entry.pad");
|
||||
} else if (_entryData->_type == MacroActionVariable::Type::TRUNCATE) {
|
||||
layoutString = obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.entry.truncate");
|
||||
} else {
|
||||
layoutString = obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.entry.other");
|
||||
}
|
||||
|
||||
for (const auto &[_, widget] : widgetPlaceholders) {
|
||||
_entryLayout->removeWidget(widget);
|
||||
}
|
||||
|
||||
ClearLayout(_entryLayout);
|
||||
PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders);
|
||||
|
||||
if (_entryData->_type == MacroActionVariable::Type::SET_FIXED_VALUE ||
|
||||
_entryData->_type == MacroActionVariable::Type::APPEND ||
|
||||
_entryData->_type == MacroActionVariable::Type::MATH_EXPRESSION ||
|
||||
@@ -1119,8 +1411,9 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
AddStretchIfNecessary(_entryLayout);
|
||||
}
|
||||
|
||||
_variables2->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::APPEND_VAR);
|
||||
_variables2->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::APPEND_VAR ||
|
||||
_entryData->_type == MacroActionVariable::Type::SWAP_VALUES);
|
||||
_strValue->setVisible(
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_FIXED_VALUE ||
|
||||
@@ -1196,8 +1489,23 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
MacroActionVariable::Type::SCENE_ITEM_NAME);
|
||||
_tempVars->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_TO_TEMPVAR);
|
||||
_tempVarsHelp->setVisible(
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_TO_TEMPVAR &&
|
||||
!_entryData->_tempVar.HasValidID());
|
||||
_sceneItemIndex->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::SCENE_ITEM_NAME);
|
||||
_direction->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::PAD ||
|
||||
_entryData->_type == MacroActionVariable::Type::TRUNCATE);
|
||||
_stringLength->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::PAD ||
|
||||
_entryData->_type == MacroActionVariable::Type::TRUNCATE);
|
||||
_paddingCharSelection->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::PAD);
|
||||
_caseType->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::CHANGE_CASE);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "help-icon.hpp"
|
||||
#include "macro-segment-selection.hpp"
|
||||
#include "regex-config.hpp"
|
||||
#include "resizing-text-edit.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "single-char-selection.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
#include "variable-spinbox.hpp"
|
||||
@@ -17,15 +19,14 @@ public:
|
||||
bool PerformAction();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
bool PostLoad() override;
|
||||
bool PostLoad();
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionVariable>(m);
|
||||
}
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m);
|
||||
std::shared_ptr<MacroAction> Copy() const;
|
||||
void SetSegmentIndexValue(int);
|
||||
int GetSegmentIndexValue() const;
|
||||
void ResolveVariablesToFixedValues();
|
||||
|
||||
enum class Type {
|
||||
SET_FIXED_VALUE,
|
||||
@@ -46,6 +47,11 @@ public:
|
||||
EXTRACT_JSON,
|
||||
SET_TO_TEMPVAR,
|
||||
SCENE_ITEM_NAME,
|
||||
PAD,
|
||||
TRUNCATE,
|
||||
SWAP_VALUES,
|
||||
TRIM,
|
||||
CHANGE_CASE,
|
||||
};
|
||||
|
||||
Type _type = Type::SET_FIXED_VALUE;
|
||||
@@ -80,12 +86,27 @@ public:
|
||||
TempVariableRef _tempVar;
|
||||
IntVariable _sceneItemIndex = 1;
|
||||
|
||||
enum class Direction { LEFT, RIGHT };
|
||||
Direction _direction = Direction::LEFT;
|
||||
IntVariable _stringLength = 1;
|
||||
char _paddingChar = '0';
|
||||
|
||||
enum class CaseType {
|
||||
LOWER_CASE,
|
||||
UPPER_CASE,
|
||||
CAPITALIZED,
|
||||
START_CASE,
|
||||
};
|
||||
|
||||
CaseType _caseType = CaseType::LOWER_CASE;
|
||||
|
||||
private:
|
||||
void DecrementCurrentSegmentVariableRef();
|
||||
void HandleIndexSubString(Variable *);
|
||||
void HandleRegexSubString(Variable *);
|
||||
void HandleFindAndReplace(Variable *);
|
||||
void HandleMathExpression(Variable *);
|
||||
void HandleCaseChange(Variable *);
|
||||
void SetToSceneItemName(Variable *);
|
||||
|
||||
std::weak_ptr<MacroSegment> _macroSegment;
|
||||
@@ -136,6 +157,10 @@ private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SelectionChanged(const TempVariableRef &var);
|
||||
void SceneItemIndexChanged(const NumberVariable<int> &);
|
||||
void DirectionChanged(int);
|
||||
void StringLengthChanged(const NumberVariable<int> &);
|
||||
void CharSelectionChanged(const QString &);
|
||||
void CaseTypeChanged(int index);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
@@ -175,7 +200,12 @@ private:
|
||||
VariableLineEdit *_envVariable;
|
||||
SceneSelectionWidget *_scenes;
|
||||
TempVariableSelection *_tempVars;
|
||||
HelpIcon *_tempVarsHelp;
|
||||
VariableSpinBox *_sceneItemIndex;
|
||||
QComboBox *_direction;
|
||||
VariableSpinBox *_stringLength;
|
||||
SingleCharSelection *_paddingCharSelection;
|
||||
FilterComboBox *_caseType;
|
||||
QHBoxLayout *_entryLayout;
|
||||
|
||||
std::shared_ptr<MacroActionVariable> _entryData;
|
||||
|
||||
@@ -25,7 +25,7 @@ bool MacroAction::Load(obs_data_t *obj)
|
||||
|
||||
void MacroAction::LogAction() const
|
||||
{
|
||||
vblog(LOG_INFO, "performed action %s", GetId().c_str());
|
||||
ablog(LOG_INFO, "performed action %s", GetId().c_str());
|
||||
}
|
||||
|
||||
void MacroAction::SetEnabled(bool value)
|
||||
@@ -38,6 +38,8 @@ bool MacroAction::Enabled() const
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
void MacroAction::ResolveVariablesToFixedValues() {}
|
||||
|
||||
std::string_view MacroAction::GetDefaultID()
|
||||
{
|
||||
return "scene_switch";
|
||||
|
||||
@@ -8,10 +8,17 @@ class EXPORT MacroAction : public MacroSegment {
|
||||
public:
|
||||
MacroAction(Macro *m, bool supportsVariableValue = false);
|
||||
virtual ~MacroAction() = default;
|
||||
virtual std::shared_ptr<MacroAction> Copy() const = 0;
|
||||
|
||||
virtual bool PerformAction() = 0;
|
||||
virtual void LogAction() const;
|
||||
|
||||
virtual bool Save(obs_data_t *obj) const = 0;
|
||||
virtual bool Load(obs_data_t *obj) = 0;
|
||||
virtual void LogAction() const;
|
||||
|
||||
// Used to resolve variables before actions are added to action queues
|
||||
virtual void ResolveVariablesToFixedValues();
|
||||
|
||||
void SetEnabled(bool);
|
||||
bool Enabled() const;
|
||||
|
||||
|
||||
@@ -1,34 +1,15 @@
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "macro-properties.hpp"
|
||||
#include "macro-settings.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "path-helpers.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
#include "section.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
static inline void populateLogicSelection(QComboBox *list, bool root = false)
|
||||
{
|
||||
if (root) {
|
||||
for (const auto &entry : MacroCondition::logicTypes) {
|
||||
if (static_cast<int>(entry.first) < logic_root_offset) {
|
||||
list->addItem(obs_module_text(
|
||||
entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto &entry : MacroCondition::logicTypes) {
|
||||
if (static_cast<int>(entry.first) >=
|
||||
logic_root_offset) {
|
||||
list->addItem(obs_module_text(
|
||||
entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, condition] :
|
||||
@@ -119,14 +100,13 @@ void DurationModifierEdit::Collapse(bool collapse)
|
||||
|
||||
MacroConditionEdit::MacroConditionEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
|
||||
const std::string &id, bool root)
|
||||
: MacroSegmentEdit(GetGlobalMacroProperties()._highlightConditions,
|
||||
parent),
|
||||
const std::string &id, bool isRootCondition)
|
||||
: MacroSegmentEdit(parent),
|
||||
_logicSelection(new QComboBox()),
|
||||
_conditionSelection(new FilterComboBox()),
|
||||
_dur(new DurationModifierEdit()),
|
||||
_entryData(entryData),
|
||||
_isRoot(root)
|
||||
_isRoot(isRootCondition)
|
||||
{
|
||||
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(LogicSelectionChanged(int)));
|
||||
@@ -138,10 +118,8 @@ MacroConditionEdit::MacroConditionEdit(
|
||||
QWidget::connect(_dur, SIGNAL(ModifierChanged(DurationModifier::Type)),
|
||||
this,
|
||||
SLOT(DurationModifierChanged(DurationModifier::Type)));
|
||||
QWidget::connect(window(), SIGNAL(HighlightConditionsChanged(bool)),
|
||||
this, SLOT(EnableHighlight(bool)));
|
||||
|
||||
populateLogicSelection(_logicSelection, root);
|
||||
Logic::PopulateLogicTypeSelection(_logicSelection, isRootCondition);
|
||||
populateConditionSelection(_conditionSelection);
|
||||
|
||||
_section->AddHeaderWidget(_logicSelection);
|
||||
@@ -170,15 +148,10 @@ void MacroConditionEdit::LogicSelectionChanged(int idx)
|
||||
return;
|
||||
}
|
||||
|
||||
LogicType type;
|
||||
if (IsRootNode()) {
|
||||
type = static_cast<LogicType>(idx);
|
||||
} else {
|
||||
type = static_cast<LogicType>(idx + logic_root_offset);
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
(*_entryData)->SetLogicType(type);
|
||||
const auto logic = static_cast<Logic::Type>(
|
||||
_logicSelection->itemData(idx).toInt());
|
||||
(*_entryData)->SetLogicType(logic);
|
||||
}
|
||||
|
||||
bool MacroConditionEdit::IsRootNode()
|
||||
@@ -188,13 +161,9 @@ bool MacroConditionEdit::IsRootNode()
|
||||
|
||||
void MacroConditionEdit::SetLogicSelection()
|
||||
{
|
||||
auto logic = (*_entryData)->GetLogicType();
|
||||
if (IsRootNode()) {
|
||||
_logicSelection->setCurrentIndex(static_cast<int>(logic));
|
||||
} else {
|
||||
_logicSelection->setCurrentIndex(static_cast<int>(logic) -
|
||||
logic_root_offset);
|
||||
}
|
||||
const auto logic = (*_entryData)->GetLogicType();
|
||||
_logicSelection->setCurrentIndex(
|
||||
_logicSelection->findData(static_cast<int>(logic)));
|
||||
}
|
||||
|
||||
void MacroConditionEdit::SetRootNode(bool root)
|
||||
@@ -202,7 +171,7 @@ void MacroConditionEdit::SetRootNode(bool root)
|
||||
_isRoot = root;
|
||||
const QSignalBlocker blocker(_logicSelection);
|
||||
_logicSelection->clear();
|
||||
populateLogicSelection(_logicSelection, root);
|
||||
Logic::PopulateLogicTypeSelection(_logicSelection, root);
|
||||
SetLogicSelection();
|
||||
}
|
||||
|
||||
@@ -254,6 +223,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
|
||||
(*_entryData)->SetIndex(idx);
|
||||
(*_entryData)->SetLogicType(logic);
|
||||
(*_entryData)->PostLoad();
|
||||
RunPostLoadSteps();
|
||||
}
|
||||
auto widget =
|
||||
MacroConditionFactory::CreateWidget(id, this, *_entryData);
|
||||
@@ -297,34 +267,51 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
||||
}
|
||||
|
||||
if (idx < 0 || idx > (int)macro->Conditions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string id;
|
||||
LogicType logic;
|
||||
Logic::Type logic;
|
||||
if (idx >= 1) {
|
||||
id = macro->Conditions().at(idx - 1)->GetId();
|
||||
if (idx == 1) {
|
||||
logic = LogicType::OR;
|
||||
logic = Logic::Type::OR;
|
||||
} else {
|
||||
logic = macro->Conditions().at(idx - 1)->GetLogicType();
|
||||
}
|
||||
} else {
|
||||
id = MacroCondition::GetDefaultID();
|
||||
logic = LogicType::ROOT_NONE;
|
||||
logic = Logic::Type::ROOT_NONE;
|
||||
}
|
||||
|
||||
OBSDataAutoRelease data;
|
||||
if (idx - 1 >= 0) {
|
||||
data = obs_data_create();
|
||||
macro->Conditions().at(idx - 1)->Save(data);
|
||||
}
|
||||
AddMacroCondition(macro.get(), idx, id, data.Get(), logic);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx,
|
||||
const std::string &id,
|
||||
obs_data_t *data, Logic::Type logic)
|
||||
{
|
||||
if (idx < 0 || idx > (int)macro->Conditions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto cond = macro->Conditions().emplace(
|
||||
macro->Conditions().begin() + idx,
|
||||
MacroConditionFactory::Create(id, macro.get()));
|
||||
if (idx - 1 >= 0) {
|
||||
auto data = obs_data_create();
|
||||
macro->Conditions().at(idx - 1)->Save(data);
|
||||
MacroConditionFactory::Create(id, macro));
|
||||
if (data) {
|
||||
macro->Conditions().at(idx)->Load(data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
macro->Conditions().at(idx)->PostLoad();
|
||||
RunPostLoadSteps();
|
||||
(*cond)->SetLogicType(logic);
|
||||
macro->UpdateConditionIndices();
|
||||
ui->conditionsList->Insert(
|
||||
@@ -334,6 +321,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
||||
SetConditionData(*macro);
|
||||
}
|
||||
HighlightCondition(idx);
|
||||
ui->conditionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
@@ -352,7 +340,6 @@ void AdvSceneSwitcher::on_conditionAdd_clicked()
|
||||
if (currentConditionIdx != -1) {
|
||||
MacroConditionSelectionChanged(currentConditionIdx + 1);
|
||||
}
|
||||
ui->conditionsList->SetHelpMsgVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::RemoveMacroCondition(int idx)
|
||||
@@ -373,7 +360,7 @@ void AdvSceneSwitcher::RemoveMacroCondition(int idx)
|
||||
macro->UpdateConditionIndices();
|
||||
if (idx == 0 && macro->Conditions().size() > 0) {
|
||||
auto newRoot = macro->Conditions().at(0);
|
||||
newRoot->SetLogicType(LogicType::ROOT_NONE);
|
||||
newRoot->SetLogicType(Logic::Type::ROOT_NONE);
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(0))
|
||||
->SetRootNode(true);
|
||||
@@ -524,22 +511,23 @@ void AdvSceneSwitcher::MacroConditionReorder(int to, int from)
|
||||
auto lock = LockContext();
|
||||
auto condition = macro->Conditions().at(from);
|
||||
if (to == 0) {
|
||||
condition->SetLogicType(LogicType::ROOT_NONE);
|
||||
condition->SetLogicType(Logic::Type::ROOT_NONE);
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(from))
|
||||
->SetRootNode(true);
|
||||
macro->Conditions().at(0)->SetLogicType(LogicType::AND);
|
||||
macro->Conditions().at(0)->SetLogicType(
|
||||
Logic::Type::AND);
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(0))
|
||||
->SetRootNode(false);
|
||||
}
|
||||
if (from == 0) {
|
||||
condition->SetLogicType(LogicType::AND);
|
||||
condition->SetLogicType(Logic::Type::AND);
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(from))
|
||||
->SetRootNode(false);
|
||||
macro->Conditions().at(1)->SetLogicType(
|
||||
LogicType::ROOT_NONE);
|
||||
Logic::Type::ROOT_NONE);
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(1))
|
||||
->SetRootNode(true);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "macro-condition.hpp"
|
||||
#include "macro-condition-factory.hpp"
|
||||
#include "filter-combo-box.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
#include "macro-condition-factory.hpp"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace advss {
|
||||
|
||||
namespace {
|
||||
|
||||
class MacroConditionUnknown : public MacroCondition {
|
||||
public:
|
||||
MacroConditionUnknown(Macro *m) : MacroCondition(m) {}
|
||||
bool CheckCondition() { return false; }
|
||||
bool Save(obs_data_t *obj) const { return MacroCondition::Save(obj); };
|
||||
bool Load(obs_data_t *obj) { return MacroCondition::Load(obj); };
|
||||
std::string GetId() const { return "unknown"; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
static std::recursive_mutex mutex;
|
||||
|
||||
std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
|
||||
{
|
||||
static std::map<std::string, MacroConditionInfo> _methods;
|
||||
@@ -11,6 +28,7 @@ std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
|
||||
bool MacroConditionFactory::Register(const std::string &id,
|
||||
MacroConditionInfo info)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (auto it = GetMap().find(id); it == GetMap().end()) {
|
||||
GetMap()[id] = info;
|
||||
return true;
|
||||
@@ -18,35 +36,60 @@ bool MacroConditionFactory::Register(const std::string &id,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionFactory::Deregister(const std::string &id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (GetMap().count(id) == 0) {
|
||||
return false;
|
||||
}
|
||||
GetMap().erase(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::shared_ptr<MacroCondition> createUnknownCondition(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroConditionUnknown>(m);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroCondition>
|
||||
MacroConditionFactory::Create(const std::string &id, Macro *m)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||
return it->second._create(m);
|
||||
}
|
||||
return nullptr;
|
||||
return createUnknownCondition(m);
|
||||
}
|
||||
|
||||
static QWidget *createUnknownConditionWidget()
|
||||
{
|
||||
return new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.condition.unknown"));
|
||||
}
|
||||
|
||||
QWidget *
|
||||
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||
return it->second._createWidget(parent, cond);
|
||||
}
|
||||
return nullptr;
|
||||
return createUnknownConditionWidget();
|
||||
}
|
||||
|
||||
std::string MacroConditionFactory::GetConditionName(const std::string &id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||
return it->second._name;
|
||||
}
|
||||
return "unknown condition";
|
||||
return obs_module_text("AdvSceneSwitcher.condition.unknown");
|
||||
}
|
||||
|
||||
std::string MacroConditionFactory::GetIdByName(const QString &name)
|
||||
{
|
||||
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;
|
||||
@@ -57,10 +100,21 @@ std::string MacroConditionFactory::GetIdByName(const QString &name)
|
||||
|
||||
bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||
return it->second._useDurationModifier;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CanCreateDefaultCondition()
|
||||
{
|
||||
const auto condition = MacroConditionFactory::Create(
|
||||
MacroCondition::GetDefaultID().data(), nullptr);
|
||||
if (!condition) {
|
||||
return false;
|
||||
}
|
||||
return condition->GetId() == MacroCondition::GetDefaultID().data();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
namespace advss {
|
||||
|
||||
struct MacroConditionInfo {
|
||||
using CreateCondition = std::shared_ptr<MacroCondition> (*)(Macro *m);
|
||||
using CreateConditionWidget =
|
||||
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
|
||||
CreateCondition _create = nullptr;
|
||||
|
||||
std::function<std::shared_ptr<MacroCondition>(Macro *m)> _create =
|
||||
nullptr;
|
||||
CreateConditionWidget _createWidget = nullptr;
|
||||
std::string _name;
|
||||
bool _useDurationModifier = true;
|
||||
@@ -19,6 +20,7 @@ class MacroConditionFactory {
|
||||
public:
|
||||
MacroConditionFactory() = delete;
|
||||
EXPORT static bool Register(const std::string &, MacroConditionInfo);
|
||||
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,
|
||||
@@ -32,4 +34,6 @@ private:
|
||||
static std::map<std::string, MacroConditionInfo> &GetMap();
|
||||
};
|
||||
|
||||
bool CanCreateDefaultCondition();
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
bool PostLoad() override;
|
||||
bool PostLoad();
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create(Macro *m)
|
||||
|
||||
79
lib/macro/macro-condition-script.cpp
Normal file
79
lib/macro/macro-condition-script.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "macro-condition-script.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
MacroConditionScript::MacroConditionScript(
|
||||
Macro *m, const std::string &id, const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignal, const std::string &completionSignal)
|
||||
: MacroCondition(m),
|
||||
MacroSegmentScript(defaultSettings, propertiesSignalName,
|
||||
triggerSignal, completionSignal),
|
||||
_id(id)
|
||||
{
|
||||
}
|
||||
|
||||
MacroConditionScript::MacroConditionScript(
|
||||
const advss::MacroConditionScript &other)
|
||||
: MacroCondition(other.GetMacro()),
|
||||
MacroSegmentScript(other),
|
||||
_id(other._id)
|
||||
{
|
||||
}
|
||||
|
||||
bool MacroConditionScript::CheckCondition()
|
||||
{
|
||||
if (!ScriptHandler::ConditionIdIsValid(_id)) {
|
||||
blog(LOG_WARNING, "skipping unknown script condition \"%s\"",
|
||||
_id.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return SendTriggerSignal();
|
||||
}
|
||||
|
||||
bool MacroConditionScript::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
MacroSegmentScript::Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionScript::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
MacroSegmentScript::Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroConditionScript::WaitForCompletion() const
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
start - start);
|
||||
const auto timeoutMs = GetTimeoutSeconds() * 1000.0;
|
||||
|
||||
while (!TriggerIsCompleted()) {
|
||||
if (MacroWaitShouldAbort() || MacroIsStopped(GetMacro())) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((double)timePassed.count() > timeoutMs) {
|
||||
blog(LOG_INFO, "script condition timeout (%s)",
|
||||
_id.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
timePassed =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - start);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
27
lib/macro/macro-condition-script.hpp
Normal file
27
lib/macro/macro-condition-script.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "macro-script-handler.hpp"
|
||||
#include "macro-segment-script.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroConditionScript : public MacroCondition, public MacroSegmentScript {
|
||||
public:
|
||||
MacroConditionScript(Macro *m, const std::string &id,
|
||||
const OBSData &defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignal,
|
||||
const std::string &signalComplete);
|
||||
MacroConditionScript(const advss::MacroConditionScript &);
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() const { return _id; };
|
||||
|
||||
private:
|
||||
void WaitForCompletion() const;
|
||||
|
||||
std::string _id = "";
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -2,79 +2,6 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::map<LogicType, LogicTypeInfo> MacroCondition::logicTypes = {
|
||||
{LogicType::NONE, {"AdvSceneSwitcher.logic.none"}},
|
||||
{LogicType::AND, {"AdvSceneSwitcher.logic.and"}},
|
||||
{LogicType::OR, {"AdvSceneSwitcher.logic.or"}},
|
||||
{LogicType::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
|
||||
{LogicType::OR_NOT, {"AdvSceneSwitcher.logic.orNot"}},
|
||||
{LogicType::ROOT_NONE, {"AdvSceneSwitcher.logic.rootNone"}},
|
||||
{LogicType::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
|
||||
};
|
||||
|
||||
void DurationModifier::Save(obs_data_t *obj, const char *condName,
|
||||
const char *duration) const
|
||||
{
|
||||
obs_data_set_int(obj, condName, static_cast<int>(_type));
|
||||
_dur.Save(obj, duration);
|
||||
}
|
||||
|
||||
void DurationModifier::Load(obs_data_t *obj, const char *condName,
|
||||
const char *duration)
|
||||
{
|
||||
// For backwards compatibility check if duration value exist without
|
||||
// time constraint condition - if so assume DurationCondition::MORE
|
||||
if (!obs_data_has_user_value(obj, condName) &&
|
||||
obs_data_has_user_value(obj, duration)) {
|
||||
obs_data_set_int(obj, condName, static_cast<int>(Type::MORE));
|
||||
}
|
||||
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, condName));
|
||||
_dur.Load(obj, duration);
|
||||
|
||||
// TODO: remove this fallback
|
||||
if (obs_data_has_user_value(obj, "displayUnit")) {
|
||||
_dur.SetUnit(static_cast<Duration::Unit>(
|
||||
obs_data_get_int(obj, "displayUnit")));
|
||||
}
|
||||
}
|
||||
|
||||
bool DurationModifier::DurationReached()
|
||||
{
|
||||
switch (_type) {
|
||||
case DurationModifier::Type::NONE:
|
||||
return true;
|
||||
break;
|
||||
case DurationModifier::Type::MORE:
|
||||
return _dur.DurationReached();
|
||||
break;
|
||||
case DurationModifier::Type::EQUAL:
|
||||
if (_dur.DurationReached() && !_timeReached) {
|
||||
_timeReached = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case DurationModifier::Type::LESS:
|
||||
return !_dur.DurationReached();
|
||||
break;
|
||||
case DurationModifier::Type::WITHIN:
|
||||
if (_dur.IsReset()) {
|
||||
return false;
|
||||
}
|
||||
return !_dur.DurationReached();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DurationModifier::Reset()
|
||||
{
|
||||
_timeReached = false;
|
||||
_dur.Reset();
|
||||
}
|
||||
|
||||
MacroCondition::MacroCondition(Macro *m, bool supportsVariableValue)
|
||||
: MacroSegment(m, supportsVariableValue)
|
||||
{
|
||||
@@ -84,73 +11,64 @@ bool MacroCondition::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroSegment::Save(obj);
|
||||
obs_data_set_string(obj, "id", GetId().c_str());
|
||||
obs_data_set_int(obj, "logic", static_cast<int>(_logic));
|
||||
|
||||
// To avoid conflicts with conditions which also use the Duration class
|
||||
// save the duration modifier in a separate obj
|
||||
auto durObj = obs_data_create();
|
||||
_duration.Save(durObj);
|
||||
obs_data_set_obj(obj, "durationModifier", durObj);
|
||||
obs_data_release(durObj);
|
||||
|
||||
_logic.Save(obj, "logic");
|
||||
_durationModifier.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroCondition::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroSegment::Load(obj);
|
||||
_logic = static_cast<LogicType>(obs_data_get_int(obj, "logic"));
|
||||
if (obs_data_has_user_value(obj, "durationModifier")) {
|
||||
auto durObj = obs_data_get_obj(obj, "durationModifier");
|
||||
_duration.Load(durObj);
|
||||
obs_data_release(durObj);
|
||||
} else {
|
||||
// For backwards compatibility
|
||||
_duration.Load(obj);
|
||||
}
|
||||
_logic.Load(obj, "logic");
|
||||
_durationModifier.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroCondition::ValidateLogicSelection(bool isRootCondition,
|
||||
const char *context)
|
||||
{
|
||||
if (_logic.IsValidSelection(isRootCondition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_logic.IsRootType()) {
|
||||
_logic.SetType(Logic::Type::ROOT_NONE);
|
||||
blog(LOG_WARNING,
|
||||
"setting invalid logic selection to 'if' for macro %s",
|
||||
context);
|
||||
return;
|
||||
}
|
||||
|
||||
_logic.SetType(Logic::Type::NONE);
|
||||
blog(LOG_WARNING,
|
||||
"setting invalid logic selection to 'ignore' for macro %s",
|
||||
context);
|
||||
}
|
||||
|
||||
void MacroCondition::ResetDuration()
|
||||
{
|
||||
_duration.Reset();
|
||||
_durationModifier.ResetDuration();
|
||||
}
|
||||
|
||||
void MacroCondition::CheckDurationModifier(bool &val)
|
||||
bool MacroCondition::CheckDurationModifier(bool conditionValue)
|
||||
{
|
||||
if (_duration.GetType() != DurationModifier::Type::WITHIN && !val) {
|
||||
_duration.Reset();
|
||||
}
|
||||
if (_duration.GetType() == DurationModifier::Type::WITHIN && val) {
|
||||
_duration.Reset();
|
||||
}
|
||||
switch (_duration.GetType()) {
|
||||
case DurationModifier::Type::NONE:
|
||||
case DurationModifier::Type::MORE:
|
||||
case DurationModifier::Type::EQUAL:
|
||||
case DurationModifier::Type::LESS:
|
||||
val = val && _duration.DurationReached();
|
||||
return;
|
||||
case DurationModifier::Type::WITHIN:
|
||||
if (val) {
|
||||
_duration.SetTimeRemaining(
|
||||
_duration.GetDuration().Seconds());
|
||||
}
|
||||
val = val || _duration.DurationReached();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return _durationModifier.CheckConditionWithDurationModifier(
|
||||
conditionValue);
|
||||
}
|
||||
|
||||
DurationModifier MacroCondition::GetDurationModifier() const
|
||||
{
|
||||
return _durationModifier;
|
||||
}
|
||||
|
||||
void MacroCondition::SetDurationModifier(DurationModifier::Type m)
|
||||
{
|
||||
_duration.SetModifier(m);
|
||||
_durationModifier.SetModifier(m);
|
||||
}
|
||||
|
||||
void MacroCondition::SetDuration(const Duration &duration)
|
||||
{
|
||||
_duration.SetValue(duration);
|
||||
_durationModifier.SetDuration(duration);
|
||||
}
|
||||
|
||||
std::string_view MacroCondition::GetDefaultID()
|
||||
|
||||
@@ -1,63 +1,11 @@
|
||||
#pragma once
|
||||
#include "macro-segment.hpp"
|
||||
#include "condition-logic.hpp"
|
||||
#include "duration-modifier.hpp"
|
||||
#include "macro-ref.hpp"
|
||||
#include <duration-control.hpp>
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto logic_root_offset = 100;
|
||||
|
||||
enum class LogicType {
|
||||
ROOT_NONE = 0,
|
||||
ROOT_NOT,
|
||||
ROOT_LAST,
|
||||
// leave some space for potential expansion
|
||||
NONE = 100,
|
||||
AND,
|
||||
OR,
|
||||
AND_NOT,
|
||||
OR_NOT,
|
||||
LAST,
|
||||
};
|
||||
|
||||
static inline bool isRootLogicType(LogicType l)
|
||||
{
|
||||
return static_cast<int>(l) < logic_root_offset;
|
||||
}
|
||||
|
||||
struct LogicTypeInfo {
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
class DurationModifier {
|
||||
public:
|
||||
enum class Type {
|
||||
NONE,
|
||||
MORE,
|
||||
EQUAL,
|
||||
LESS,
|
||||
WITHIN, // Condition will remain true for a set a amount of time
|
||||
// regardless of current state
|
||||
};
|
||||
|
||||
void Save(obs_data_t *obj, const char *condName = "time_constraint",
|
||||
const char *duration = "seconds") const;
|
||||
void Load(obs_data_t *obj, const char *condName = "time_constraint",
|
||||
const char *duration = "seconds");
|
||||
void SetModifier(Type cond) { _type = cond; }
|
||||
void SetTimeRemaining(const double &val) { _dur.SetTimeRemaining(val); }
|
||||
void SetValue(const Duration &value) { _dur = value; }
|
||||
Type GetType() { return _type; }
|
||||
Duration GetDuration() { return _dur; }
|
||||
bool DurationReached();
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
Type _type = Type::NONE;
|
||||
Duration _dur;
|
||||
bool _timeReached = false;
|
||||
};
|
||||
|
||||
class EXPORT MacroCondition : public MacroSegment {
|
||||
public:
|
||||
MacroCondition(Macro *m, bool supportsVariableValue = false);
|
||||
@@ -65,20 +13,24 @@ public:
|
||||
virtual bool CheckCondition() = 0;
|
||||
virtual bool Save(obs_data_t *obj) const = 0;
|
||||
virtual bool Load(obs_data_t *obj) = 0;
|
||||
LogicType GetLogicType() { return _logic; }
|
||||
void SetLogicType(LogicType logic) { _logic = logic; }
|
||||
static const std::map<LogicType, LogicTypeInfo> logicTypes;
|
||||
void ResetDuration();
|
||||
void CheckDurationModifier(bool &val);
|
||||
DurationModifier GetDurationModifier() { return _duration; }
|
||||
|
||||
Logic::Type GetLogicType() const { return _logic.GetType(); }
|
||||
void SetLogicType(const Logic::Type &logic) { _logic.SetType(logic); }
|
||||
|
||||
void ValidateLogicSelection(bool isRootCondition, const char *context);
|
||||
|
||||
DurationModifier GetDurationModifier() const;
|
||||
void SetDurationModifier(DurationModifier::Type m);
|
||||
void SetDuration(const Duration &duration);
|
||||
|
||||
void ResetDuration();
|
||||
bool CheckDurationModifier(bool conditionValue);
|
||||
|
||||
static std::string_view GetDefaultID();
|
||||
|
||||
private:
|
||||
LogicType _logic = LogicType::ROOT_NONE;
|
||||
DurationModifier _duration;
|
||||
Logic _logic = Logic(Logic::Type::ROOT_NONE);
|
||||
DurationModifier _durationModifier;
|
||||
};
|
||||
|
||||
class EXPORT MacroRefCondition : virtual public MacroCondition {
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
MacroDock::MacroDock(std::weak_ptr<Macro> m, QWidget *parent,
|
||||
MacroDock::MacroDock(std::weak_ptr<Macro> m,
|
||||
const StringVariable &runButtonText,
|
||||
const StringVariable &pauseButtonText,
|
||||
const StringVariable &unpauseButtonText,
|
||||
const StringVariable &conditionsTrueText,
|
||||
const StringVariable &conditionsFalseText,
|
||||
bool enableHighlight)
|
||||
: OBSDock(parent),
|
||||
: QFrame(),
|
||||
_runButtonText(runButtonText),
|
||||
_pauseButtonText(pauseButtonText),
|
||||
_unpauseButtonText(unpauseButtonText),
|
||||
@@ -25,19 +25,16 @@ MacroDock::MacroDock(std::weak_ptr<Macro> m, QWidget *parent,
|
||||
_statusText(new QLabel(conditionsFalseText.c_str())),
|
||||
_macro(m)
|
||||
{
|
||||
setFrameShape(QFrame::StyledPanel);
|
||||
setFrameShadow(QFrame::Sunken);
|
||||
|
||||
auto macro = _macro.lock();
|
||||
if (macro) {
|
||||
setWindowTitle(QString::fromStdString(macro->Name()));
|
||||
_run->setVisible(macro->DockHasRunButton());
|
||||
_pauseToggle->setVisible(macro->DockHasPauseButton());
|
||||
_statusText->setVisible(macro->DockHasStatusLabel());
|
||||
} else {
|
||||
setWindowTitle("<deleted macro>");
|
||||
}
|
||||
|
||||
setFeatures(DockWidgetClosable | DockWidgetMovable |
|
||||
DockWidgetFloatable);
|
||||
|
||||
QWidget::connect(_run, SIGNAL(clicked()), this, SLOT(RunClicked()));
|
||||
QWidget::connect(_pauseToggle, SIGNAL(clicked()), this,
|
||||
SLOT(PauseToggleClicked()));
|
||||
@@ -52,21 +49,7 @@ MacroDock::MacroDock(std::weak_ptr<Macro> m, QWidget *parent,
|
||||
QWidget::connect(&_timer, SIGNAL(timeout()), this, SLOT(Highlight()));
|
||||
_timer.start(500);
|
||||
|
||||
// QFrame wrapper is necessary to avoid dock being partially
|
||||
// transparent
|
||||
QFrame *wrapper = new QFrame;
|
||||
wrapper->setFrameShape(QFrame::StyledPanel);
|
||||
wrapper->setFrameShadow(QFrame::Sunken);
|
||||
wrapper->setLayout(layout);
|
||||
setWidget(wrapper);
|
||||
|
||||
setFloating(true);
|
||||
hide();
|
||||
}
|
||||
|
||||
void MacroDock::SetName(const QString &name)
|
||||
{
|
||||
setWindowTitle(name);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void MacroDock::ShowRunButton(bool value)
|
||||
@@ -155,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()
|
||||
@@ -167,7 +151,7 @@ void MacroDock::Highlight()
|
||||
}
|
||||
if (_lastHighlightCheckTime.time_since_epoch().count() != 0 &&
|
||||
macro->WasExecutedSince(_lastHighlightCheckTime)) {
|
||||
PulseWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
|
||||
HighlightWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
|
||||
}
|
||||
_lastHighlightCheckTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
#include "obs-dock.hpp"
|
||||
#include "variable-string.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include <QLabel>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
@@ -12,18 +11,16 @@ namespace advss {
|
||||
|
||||
class Macro;
|
||||
|
||||
class MacroDock : public OBSDock {
|
||||
class MacroDock : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroDock(std::weak_ptr<Macro>, QWidget *parent,
|
||||
const StringVariable &runButtonText,
|
||||
MacroDock(std::weak_ptr<Macro>, const StringVariable &runButtonText,
|
||||
const StringVariable &pauseButtonText,
|
||||
const StringVariable &unpauseButtonText,
|
||||
const StringVariable &conditionsTrueText,
|
||||
const StringVariable &conditionsFalseText,
|
||||
bool enableHighlight);
|
||||
void SetName(const QString &);
|
||||
void ShowRunButton(bool);
|
||||
void SetRunButtonText(const StringVariable &);
|
||||
void ShowPauseButton(bool);
|
||||
|
||||
@@ -8,8 +8,13 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
static bool usePlainText = false;
|
||||
|
||||
MacroExportImportDialog::MacroExportImportDialog(Type type)
|
||||
: QDialog(nullptr), _importExportString(new QPlainTextEdit(this))
|
||||
: QDialog(nullptr),
|
||||
_importExportString(new QPlainTextEdit(this)),
|
||||
_usePlainText(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.export.usePlainText")))
|
||||
{
|
||||
_importExportString->setReadOnly(type == Type::EXPORT_MACRO);
|
||||
auto label = new QLabel(obs_module_text(
|
||||
@@ -29,9 +34,15 @@ MacroExportImportDialog::MacroExportImportDialog(Type type)
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
buttons->setCenterButtons(true);
|
||||
|
||||
_usePlainText->setChecked(usePlainText);
|
||||
_usePlainText->setVisible(type == Type::EXPORT_MACRO);
|
||||
connect(_usePlainText, &QCheckBox::stateChanged, this,
|
||||
&MacroExportImportDialog::UsePlainTextChanged);
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
layout->addWidget(label);
|
||||
layout->addWidget(_importExportString);
|
||||
layout->addWidget(_usePlainText);
|
||||
layout->addWidget(buttons);
|
||||
setLayout(layout);
|
||||
|
||||
@@ -64,6 +75,21 @@ void MacroExportImportDialog::ExportMacros(const QString &json)
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void MacroExportImportDialog::UsePlainTextChanged(int value)
|
||||
{
|
||||
if (usePlainText == (!!value)) {
|
||||
return;
|
||||
}
|
||||
const auto current = _importExportString->toPlainText();
|
||||
if (usePlainText) {
|
||||
_importExportString->setPlainText(compressMacroString(current));
|
||||
} else {
|
||||
_importExportString->setPlainText(
|
||||
decompressMacroString(current));
|
||||
}
|
||||
usePlainText = value;
|
||||
}
|
||||
|
||||
static bool isValidData(const QString &json)
|
||||
{
|
||||
OBSDataAutoRelease data =
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
@@ -13,8 +14,12 @@ public:
|
||||
static void ExportMacros(const QString &json);
|
||||
static bool ImportMacros(QString &json);
|
||||
|
||||
private slots:
|
||||
void UsePlainTextChanged(int);
|
||||
|
||||
private:
|
||||
QPlainTextEdit *_importExportString;
|
||||
QCheckBox *_usePlainText;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -85,9 +85,11 @@ std::string GetMacroName(Macro *macro)
|
||||
return macro ? macro->Name() : "";
|
||||
}
|
||||
|
||||
int64_t MillisecondsSinceMacroConditionCheck(Macro *macro)
|
||||
std::chrono::high_resolution_clock::time_point
|
||||
LastMacroConditionCheckTime(Macro *macro)
|
||||
{
|
||||
return macro ? macro->MsSinceLastCheck() : 0;
|
||||
return macro ? macro->LastConditionCheckTime()
|
||||
: std::chrono::high_resolution_clock::time_point{};
|
||||
}
|
||||
|
||||
bool MacroIsStopped(Macro *macro)
|
||||
|
||||
@@ -43,7 +43,8 @@ EXPORT bool MacroSwitchedScene();
|
||||
|
||||
EXPORT std::string GetMacroName(Macro *);
|
||||
|
||||
EXPORT int64_t MillisecondsSinceMacroConditionCheck(Macro *);
|
||||
EXPORT std::chrono::high_resolution_clock::time_point
|
||||
LastMacroConditionCheckTime(Macro *);
|
||||
|
||||
EXPORT bool MacroIsStopped(Macro *);
|
||||
EXPORT bool MacroIsPaused(Macro *);
|
||||
|
||||
241
lib/macro/macro-input.cpp
Normal file
241
lib/macro/macro-input.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
#include "macro-input.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "log-helper.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <obs.hpp>
|
||||
|
||||
namespace advss {
|
||||
|
||||
void MacroInputVariables::Save(obs_data_t *obj) const
|
||||
{
|
||||
OBSDataArrayAutoRelease array = obs_data_array_create();
|
||||
for (auto &var : _inputVariables) {
|
||||
OBSDataAutoRelease arrayObj = obs_data_create();
|
||||
auto variable = var.lock();
|
||||
if (variable) {
|
||||
obs_data_set_string(arrayObj, "variableName",
|
||||
variable->Name().c_str());
|
||||
} else {
|
||||
obs_data_set_bool(arrayObj, "invalidVariable", true);
|
||||
}
|
||||
obs_data_array_push_back(array, arrayObj);
|
||||
}
|
||||
obs_data_set_array(obj, "inputVariables", array);
|
||||
}
|
||||
|
||||
void MacroInputVariables::Load(obs_data_t *obj)
|
||||
{
|
||||
OBSDataArrayAutoRelease array =
|
||||
obs_data_get_array(obj, "inputVariables");
|
||||
const size_t count = obs_data_array_count(array);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
OBSDataAutoRelease arrayObj = obs_data_array_item(array, i);
|
||||
const bool isInvalid =
|
||||
obs_data_get_bool(arrayObj, "invalidVariable");
|
||||
if (isInvalid) {
|
||||
_inputVariables.emplace_back();
|
||||
} else {
|
||||
const auto name =
|
||||
obs_data_get_string(arrayObj, "variableName");
|
||||
_inputVariables.emplace_back(
|
||||
GetWeakVariableByName(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacroInputVariables::SetValues(const StringList &values)
|
||||
{
|
||||
if (_inputVariables.size() != (size_t)values.size()) {
|
||||
blog(LOG_WARNING,
|
||||
"%s - sizes do not match (variables: %d, values %d)",
|
||||
__func__, (int)_inputVariables.size(), (int)values.size());
|
||||
}
|
||||
|
||||
for (size_t index = 0;
|
||||
index < _inputVariables.size() && index < (size_t)values.size();
|
||||
index++) {
|
||||
auto variable = _inputVariables.at(index).lock();
|
||||
if (!variable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
variable->SetValue(values.at(index));
|
||||
}
|
||||
}
|
||||
|
||||
MacroInputSelection::MacroInputSelection(QWidget *parent) : ListEditor(parent)
|
||||
{
|
||||
_list->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
}
|
||||
|
||||
void MacroInputSelection::SetInputs(const MacroInputVariables &inputs)
|
||||
{
|
||||
_currentSelection = inputs;
|
||||
for (const auto &var : inputs._inputVariables) {
|
||||
auto variable = var.lock();
|
||||
const auto name =
|
||||
variable
|
||||
? QString::fromStdString(variable->Name())
|
||||
: obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.inputSettings.invalid");
|
||||
|
||||
QVariant v = QVariant::fromValue(name);
|
||||
auto item = new QListWidgetItem(name, _list);
|
||||
item->setData(Qt::UserRole, name);
|
||||
}
|
||||
UpdateListSize();
|
||||
}
|
||||
|
||||
void MacroInputSelection::Add()
|
||||
{
|
||||
std::string varName;
|
||||
bool accepted = VariableSelectionDialog::AskForVariable(varName);
|
||||
|
||||
if (!accepted || varName.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_currentSelection._inputVariables.emplace_back(
|
||||
GetWeakVariableByName(varName));
|
||||
|
||||
QVariant v = QVariant::fromValue(QString::fromStdString(varName));
|
||||
auto item = new QListWidgetItem(QString::fromStdString(varName), _list);
|
||||
item->setData(Qt::UserRole, QString::fromStdString(varName));
|
||||
UpdateListSize();
|
||||
}
|
||||
|
||||
void MacroInputSelection::Remove()
|
||||
{
|
||||
auto item = _list->currentItem();
|
||||
int idx = _list->currentRow();
|
||||
if (!item || idx == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
_currentSelection._inputVariables.erase(
|
||||
std::next(_currentSelection._inputVariables.begin(), idx));
|
||||
|
||||
delete item;
|
||||
UpdateListSize();
|
||||
}
|
||||
|
||||
void MacroInputSelection::Up()
|
||||
{
|
||||
int idx = _list->currentRow();
|
||||
if (idx < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
_list->insertItem(idx - 1, _list->takeItem(idx));
|
||||
_list->setCurrentRow(idx - 1);
|
||||
|
||||
std::iter_swap(
|
||||
std::next(_currentSelection._inputVariables.begin(), idx),
|
||||
std::next(_currentSelection._inputVariables.begin(), idx - 1));
|
||||
}
|
||||
|
||||
void MacroInputSelection::Down()
|
||||
{
|
||||
int idx = _list->currentRow();
|
||||
if (idx < 0 || idx >= _list->count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_list->insertItem(idx + 1, _list->takeItem(idx));
|
||||
_list->setCurrentRow(idx + 1);
|
||||
|
||||
std::iter_swap(
|
||||
std::next(_currentSelection._inputVariables.begin(), idx + 1),
|
||||
std::next(_currentSelection._inputVariables.begin(), idx));
|
||||
}
|
||||
|
||||
void MacroInputSelection::Clicked(QListWidgetItem *item)
|
||||
{
|
||||
std::string varName;
|
||||
bool accepted = VariableSelectionDialog::AskForVariable(varName);
|
||||
|
||||
if (!accepted || varName.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
item->setText(QString::fromStdString(varName));
|
||||
int idx = _list->currentRow();
|
||||
|
||||
_currentSelection._inputVariables.at(idx) =
|
||||
GetWeakVariableByName(varName);
|
||||
}
|
||||
|
||||
MacroInputEdit::MacroInputEdit(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_layout(new QGridLayout())
|
||||
{
|
||||
setLayout(_layout);
|
||||
}
|
||||
|
||||
void MacroInputEdit::SetInputVariablesAndValues(
|
||||
const MacroInputVariables &inputs, const StringList &values)
|
||||
{
|
||||
_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();
|
||||
}
|
||||
|
||||
bool MacroInputEdit::HasInputsToSet() const
|
||||
{
|
||||
return !_variables._inputVariables.empty();
|
||||
}
|
||||
|
||||
void HighligthMacroSettingsButton(bool enable);
|
||||
|
||||
void MacroInputEdit::SetupWidgets()
|
||||
{
|
||||
ClearLayout(_layout);
|
||||
|
||||
auto &vars = _variables._inputVariables;
|
||||
if (vars.empty()) {
|
||||
_layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.inputSettings.noInputs")));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < vars.size(); index++) {
|
||||
auto variable = vars.at(index).lock();
|
||||
QLabel *label = new QLabel(
|
||||
variable
|
||||
? QString::fromStdString(variable->Name())
|
||||
: obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.inputSettings.invalid"));
|
||||
_layout->addWidget(label, index, 0);
|
||||
auto valueEdit = new VariableLineEdit(this);
|
||||
valueEdit->setEnabled(!!variable);
|
||||
valueEdit->setText(_values.at(index));
|
||||
connect(valueEdit, &VariableLineEdit::editingFinished,
|
||||
[this, valueEdit, index]() {
|
||||
_values[index] =
|
||||
valueEdit->text().toStdString();
|
||||
emit MacroInputValuesChanged(_values);
|
||||
});
|
||||
_layout->addWidget(valueEdit, index, 1);
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
63
lib/macro/macro-input.hpp
Normal file
63
lib/macro/macro-input.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include "list-editor.hpp"
|
||||
#include "string-list.hpp"
|
||||
#include "variable.hpp"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroInputVariables {
|
||||
public:
|
||||
void Save(obs_data_t *obj) const;
|
||||
void Load(obs_data_t *obj);
|
||||
void SetValues(const StringList &values);
|
||||
|
||||
private:
|
||||
std::vector<std::weak_ptr<Variable>> _inputVariables;
|
||||
friend class MacroInputSelection;
|
||||
friend class MacroInputEdit;
|
||||
};
|
||||
|
||||
class MacroInputSelection final : public ListEditor {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroInputSelection(QWidget *parent = nullptr);
|
||||
void SetInputs(const MacroInputVariables &inputs);
|
||||
MacroInputVariables GetInputs() const { return _currentSelection; }
|
||||
|
||||
private slots:
|
||||
void Add();
|
||||
void Remove();
|
||||
void Up();
|
||||
void Down();
|
||||
void Clicked(QListWidgetItem *);
|
||||
|
||||
private:
|
||||
MacroInputVariables _currentSelection;
|
||||
};
|
||||
|
||||
class MacroInputEdit final : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroInputEdit(QWidget *parent = nullptr);
|
||||
void SetInputVariablesAndValues(const MacroInputVariables &inputs,
|
||||
const StringList &values);
|
||||
bool HasInputsToSet() const;
|
||||
|
||||
signals:
|
||||
void MacroInputValuesChanged(const StringList &);
|
||||
|
||||
private:
|
||||
void SetupWidgets();
|
||||
|
||||
MacroInputVariables _variables;
|
||||
StringList _values;
|
||||
QGridLayout *_layout;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -7,68 +7,16 @@
|
||||
namespace advss {
|
||||
|
||||
MacroList::MacroList(QWidget *parent, bool allowDuplicates, bool reorder)
|
||||
: QWidget(parent),
|
||||
_list(new QListWidget()),
|
||||
_add(new QPushButton()),
|
||||
_remove(new QPushButton()),
|
||||
_up(new QPushButton()),
|
||||
_down(new QPushButton()),
|
||||
_controlsLayout(new QHBoxLayout()),
|
||||
_allowDuplicates(allowDuplicates),
|
||||
_reorder(reorder)
|
||||
: ListEditor(parent, reorder),
|
||||
_allowDuplicates(allowDuplicates)
|
||||
{
|
||||
_add->setMaximumWidth(22);
|
||||
_add->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("addIconSmall")));
|
||||
_add->setFlat(true);
|
||||
_remove->setMaximumWidth(22);
|
||||
_remove->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("removeIconSmall")));
|
||||
_remove->setFlat(true);
|
||||
_up->setMaximumWidth(22);
|
||||
_up->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("upArrowIconSmall")));
|
||||
_up->setFlat(true);
|
||||
_down->setMaximumWidth(22);
|
||||
_down->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("downArrowIconSmall")));
|
||||
_down->setFlat(true);
|
||||
|
||||
QWidget::connect(_add, SIGNAL(clicked()), this, SLOT(Add()));
|
||||
QWidget::connect(_remove, SIGNAL(clicked()), this, SLOT(Remove()));
|
||||
QWidget::connect(_up, SIGNAL(clicked()), this, SLOT(Up()));
|
||||
QWidget::connect(_down, SIGNAL(clicked()), this, SLOT(Down()));
|
||||
QWidget::connect(_list, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
|
||||
this, SLOT(MacroItemClicked(QListWidgetItem *)));
|
||||
QWidget::connect(window(),
|
||||
SIGNAL(MacroRenamed(const QString &, const QString &)),
|
||||
this,
|
||||
SLOT(MacroRename(const QString &, const QString &)));
|
||||
QWidget::connect(window(), SIGNAL(MacroRemoved(const QString &)), this,
|
||||
SLOT(MacroRemove(const QString &)));
|
||||
|
||||
_controlsLayout->addWidget(_add);
|
||||
_controlsLayout->addWidget(_remove);
|
||||
if (reorder) {
|
||||
QFrame *line = new QFrame();
|
||||
line->setFrameShape(QFrame::VLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
_controlsLayout->addWidget(line);
|
||||
}
|
||||
_controlsLayout->addWidget(_up);
|
||||
_controlsLayout->addWidget(_down);
|
||||
_controlsLayout->addStretch();
|
||||
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->addWidget(_list);
|
||||
mainLayout->addLayout(_controlsLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_up->setVisible(reorder);
|
||||
_down->setVisible(reorder);
|
||||
|
||||
SetMacroListSize();
|
||||
UpdateListSize();
|
||||
}
|
||||
|
||||
void MacroList::SetContent(const std::vector<MacroRef> ¯os)
|
||||
@@ -89,12 +37,15 @@ void MacroList::SetContent(const std::vector<MacroRef> ¯os)
|
||||
new QListWidgetItem(listEntryName, _list);
|
||||
item->setData(Qt::UserRole, listEntryName);
|
||||
}
|
||||
SetMacroListSize();
|
||||
UpdateListSize();
|
||||
}
|
||||
|
||||
void MacroList::AddControl(QWidget *widget)
|
||||
QAction *MacroList::AddControl(QWidget *widget, bool addSeperator)
|
||||
{
|
||||
_controlsLayout->insertWidget(_controlsLayout->count() - 1, widget);
|
||||
if (addSeperator) {
|
||||
_controls->addSeparator();
|
||||
}
|
||||
return _controls->addWidget(widget);
|
||||
}
|
||||
|
||||
int MacroList::CurrentRow()
|
||||
@@ -122,13 +73,13 @@ void MacroList::MacroRemove(const QString &name)
|
||||
delete _list->item(idx);
|
||||
idx = FindEntry(name.toStdString());
|
||||
}
|
||||
SetMacroListSize();
|
||||
UpdateListSize();
|
||||
}
|
||||
|
||||
void MacroList::Add()
|
||||
{
|
||||
std::string macroName;
|
||||
bool accepted = MacroSelectionDialog::AskForMacro(this, macroName);
|
||||
bool accepted = MacroSelectionDialog::AskForMacro(macroName);
|
||||
|
||||
if (!accepted || macroName.empty()) {
|
||||
return;
|
||||
@@ -142,7 +93,7 @@ void MacroList::Add()
|
||||
auto item =
|
||||
new QListWidgetItem(QString::fromStdString(macroName), _list);
|
||||
item->setData(Qt::UserRole, QString::fromStdString(macroName));
|
||||
SetMacroListSize();
|
||||
UpdateListSize();
|
||||
emit Added(macroName);
|
||||
}
|
||||
|
||||
@@ -154,7 +105,7 @@ void MacroList::Remove()
|
||||
return;
|
||||
}
|
||||
delete item;
|
||||
SetMacroListSize();
|
||||
UpdateListSize();
|
||||
emit Removed(idx);
|
||||
}
|
||||
|
||||
@@ -178,10 +129,10 @@ void MacroList::Down()
|
||||
}
|
||||
}
|
||||
|
||||
void MacroList::MacroItemClicked(QListWidgetItem *item)
|
||||
void MacroList::Clicked(QListWidgetItem *item)
|
||||
{
|
||||
std::string macroName;
|
||||
bool accepted = MacroSelectionDialog::AskForMacro(this, macroName);
|
||||
bool accepted = MacroSelectionDialog::AskForMacro(macroName);
|
||||
|
||||
if (!accepted || macroName.empty()) {
|
||||
return;
|
||||
@@ -216,10 +167,4 @@ int MacroList::FindEntry(const std::string ¯o)
|
||||
return idx;
|
||||
}
|
||||
|
||||
void MacroList::SetMacroListSize()
|
||||
{
|
||||
SetHeightToContentHeight(_list);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
#pragma once
|
||||
#include "list-editor.hpp"
|
||||
#include "macro-ref.hpp"
|
||||
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class ADVSS_EXPORT MacroList : public QWidget {
|
||||
class ADVSS_EXPORT MacroList final : public ListEditor {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MacroList(QWidget *parent, bool allowDuplicates, bool reorder);
|
||||
void SetContent(const std::vector<MacroRef> &);
|
||||
void AddControl(QWidget *);
|
||||
QAction *AddControl(QWidget *, bool addSeperator = true);
|
||||
int CurrentRow();
|
||||
|
||||
private slots:
|
||||
@@ -22,7 +19,7 @@ private slots:
|
||||
void Remove();
|
||||
void Up();
|
||||
void Down();
|
||||
void MacroItemClicked(QListWidgetItem *);
|
||||
void Clicked(QListWidgetItem *);
|
||||
|
||||
signals:
|
||||
void Added(const std::string &);
|
||||
@@ -33,16 +30,8 @@ signals:
|
||||
|
||||
private:
|
||||
int FindEntry(const std::string ¯o);
|
||||
void SetMacroListSize();
|
||||
|
||||
QListWidget *_list;
|
||||
QPushButton *_add;
|
||||
QPushButton *_remove;
|
||||
QPushButton *_up;
|
||||
QPushButton *_down;
|
||||
QHBoxLayout *_controlsLayout;
|
||||
const bool _allowDuplicates;
|
||||
const bool _reorder;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -96,11 +96,15 @@ void MacroRunButton::Pressed()
|
||||
|
||||
bool ret = _elseStateActive ? macro->PerformActions(false, true, true)
|
||||
: macro->PerformActions(true, true, true);
|
||||
if (ret) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
QString err =
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.runFail");
|
||||
DisplayMessage(err.arg(QString::fromStdString(macro->Name())));
|
||||
QString err = obs_module_text("AdvSceneSwitcher.macroTab.runFail");
|
||||
const bool abortMacro = DisplayMessage(
|
||||
err.arg(QString::fromStdString(macro->Name())), true);
|
||||
if (abortMacro) {
|
||||
macro->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user