mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 04:36:21 -05:00
Compare commits
184 Commits
source-int
...
1.29.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa87911b71 | ||
|
|
1b05019acc | ||
|
|
78a5a2629d | ||
|
|
634270a978 | ||
|
|
78ba22e1e4 | ||
|
|
aba5737a60 | ||
|
|
4315f7f621 | ||
|
|
53c535962f | ||
|
|
1e718b78c7 | ||
|
|
b1d2156228 | ||
|
|
9c3c953c6b | ||
|
|
ae74f68db7 | ||
|
|
213f1bba36 | ||
|
|
5a2cb0bd68 | ||
|
|
23b461828b | ||
|
|
9944a1b03b | ||
|
|
eaad4d1bbd | ||
|
|
17d9b73b9a | ||
|
|
d6185a6099 | ||
|
|
41c06b9edb | ||
|
|
d8807077da | ||
|
|
34e125f82d | ||
|
|
d670b6d07e | ||
|
|
7b0f985f18 | ||
|
|
212be923f6 | ||
|
|
88fcb57e9f | ||
|
|
9b609c118f | ||
|
|
37c398d37a | ||
|
|
a51b7f6b13 | ||
|
|
808fd84b83 | ||
|
|
57bcea15f5 | ||
|
|
4e51b56b9b | ||
|
|
ef5cf41d34 | ||
|
|
911e79ea6a | ||
|
|
d586177de0 | ||
|
|
202c36646c | ||
|
|
ab5102f5ca | ||
|
|
e612fb99f6 | ||
|
|
691e77a69a | ||
|
|
930118c61f | ||
|
|
1e05f2f2ce | ||
|
|
ae25b4d023 | ||
|
|
af055a12a1 | ||
|
|
7de28eedd3 | ||
|
|
8dfe81f522 | ||
|
|
a1d8ae291d | ||
|
|
eb6989527d | ||
|
|
2986a5dd96 | ||
|
|
816ee9b244 | ||
|
|
51b53bf948 | ||
|
|
2c5121ee94 | ||
|
|
6a58684854 | ||
|
|
0b622fdbed | ||
|
|
3bce8e075f | ||
|
|
f598d84ea1 | ||
|
|
d7a244e80e | ||
|
|
408002e96a | ||
|
|
50da3d3210 | ||
|
|
9e13067769 | ||
|
|
821e768e76 | ||
|
|
4d2179448b | ||
|
|
40d9002470 | ||
|
|
7f1e310ca6 | ||
|
|
66d02f4683 | ||
|
|
0aae1b9978 | ||
|
|
db50822b05 | ||
|
|
63a545b293 | ||
|
|
bc0497d2c9 | ||
|
|
37226a3a4c | ||
|
|
9f15fbe47c | ||
|
|
0ee6feb529 | ||
|
|
3b82b55090 | ||
|
|
071c3309c2 | ||
|
|
ee7ee7a846 | ||
|
|
d10300bd4b | ||
|
|
e262672876 | ||
|
|
392e775c7b | ||
|
|
f641c20564 | ||
|
|
39327be88e | ||
|
|
ed0963799c | ||
|
|
f9e73a2b8f | ||
|
|
dfaa704b71 | ||
|
|
d54edddde4 | ||
|
|
37cc289204 | ||
|
|
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 |
@@ -6,15 +6,6 @@
|
||||
"autosort": true
|
||||
},
|
||||
"additional_commands": {
|
||||
"find_qt": {
|
||||
"flags": [],
|
||||
"kwargs": {
|
||||
"COMPONENTS": "+",
|
||||
"COMPONENTS_WIN": "+",
|
||||
"COMPONENTS_MACOS": "+",
|
||||
"COMPONENTS_LINUX": "+"
|
||||
}
|
||||
},
|
||||
"set_target_properties_obs": {
|
||||
"pargs": 1,
|
||||
"flags": [],
|
||||
|
||||
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.
|
||||
112
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
112
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
# Based on the OBS issue templates
|
||||
# https://github.com/obsproject/.github/tree/master/.github/ISSUE_TEMPLATE
|
||||
|
||||
name: Bug Report
|
||||
description: Report a bug or crash
|
||||
body:
|
||||
- type: markdown
|
||||
id: md_welcome
|
||||
attributes:
|
||||
value: This form is for reporting bugs for Advanced Scene Switcher!
|
||||
- type: dropdown
|
||||
id: os_info
|
||||
attributes:
|
||||
label: Operating System Info
|
||||
description: What Operating System are you running?
|
||||
options:
|
||||
- Windows 11
|
||||
- Windows 10
|
||||
- macOS 14
|
||||
- macOS 13
|
||||
- macOS 12
|
||||
- macOS 11
|
||||
- Ubuntu 24.04
|
||||
- Ubuntu 23.10
|
||||
- Ubuntu 22.04
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os_info_other
|
||||
attributes:
|
||||
label: Other OS
|
||||
description: "If \"Other\" was selected above, what OS are you using?"
|
||||
placeholder: "e.g., Arch Linux, FreeBSD"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: obs_version
|
||||
attributes:
|
||||
label: OBS Studio Version
|
||||
description: What version of OBS Studio are you using?
|
||||
placeholder: 30.2.2
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: advss_version
|
||||
attributes:
|
||||
label: Advanced Scene Switcher Version
|
||||
description: What version of the Advanced Scene Switcher are you using?
|
||||
placeholder: 1.27.2
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: settings
|
||||
attributes:
|
||||
label: Plugin settings
|
||||
description: |
|
||||
Please provide the plugin settings used.
|
||||
Either only [export the macros](https://github.com/WarmUpTill/SceneSwitcher/wiki/Exporting-and-importing-individual-macros) relevant to this issue or [export all settings](https://github.com/WarmUpTill/SceneSwitcher/wiki/Saving-and-loading-settings#how-to-create-and-import-a-backup-of-your-settings) on the General tab of the plugin.
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: obs_log_url
|
||||
attributes:
|
||||
label: OBS Studio Log URL
|
||||
description: Please provide the obsproject.com URL (from Help menu > Log Files > Upload Current/Previous Log File) to the OBS log file where this issue occurred.
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: obs_crash_log_url
|
||||
attributes:
|
||||
label: OBS Studio Crash Log URL
|
||||
description: If this is a crash report, please provide the obsproject.com URL to the OBS crash log file where this issue occurred.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: expected_behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: "What did you expect to happen?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: current_behavior
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: "What actually happened?"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps_to_reproduce
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: "How do you trigger this bug? Please walk us through it step by step."
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
...
|
||||
value: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional_notes
|
||||
attributes:
|
||||
label: Anything else we should know?
|
||||
validations:
|
||||
required: false
|
||||
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
|
||||
2
.github/scripts/.Aptfile
vendored
2
.github/scripts/.Aptfile
vendored
@@ -9,5 +9,5 @@ package 'libxtst-dev'
|
||||
package 'libxss-dev'
|
||||
package 'libopencv-dev'
|
||||
package 'libtesseract-dev'
|
||||
package 'libprocps-dev'
|
||||
package 'libproc2-dev'
|
||||
package 'libusb-1.0-0-dev'
|
||||
1
.github/scripts/.Brewfile
vendored
1
.github/scripts/.Brewfile
vendored
@@ -4,3 +4,4 @@ brew "cmake"
|
||||
brew "jq"
|
||||
brew "xcbeautify"
|
||||
brew "automake"
|
||||
brew "libtool"
|
||||
|
||||
78
.github/scripts/.build-deps.zsh
vendored
78
.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,28 +358,40 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
-DCMAKE_INSTALL_PREFIX="${advss_dep_path}"
|
||||
)
|
||||
|
||||
if [ "${target}" != "macos-x86_64" ]; then
|
||||
tesseract_cmake_args+=(
|
||||
-DCMAKE_SYSTEM_PROCESSOR=aarch64
|
||||
-DHAVE_AVX=FALSE
|
||||
-DHAVE_AVX2=FALSE
|
||||
-DHAVE_AVX512F=FALSE
|
||||
-DHAVE_FMA=FALSE
|
||||
-DHAVE_SSE4_1=FALSE
|
||||
-DHAVE_NEON=TRUE
|
||||
)
|
||||
sed -i'.original' 's/HAVE_NEON FALSE/HAVE_NEON TRUE/g' "${tesseract_dir}/CMakeLists.txt"
|
||||
fi
|
||||
cmake -S . -B ${tesseract_build_dir_x86_64} ${tesseract_cmake_args}
|
||||
|
||||
pushd ${tesseract_dir}
|
||||
log_info "Configure Tesseract ..."
|
||||
cmake -S . -B ${tesseract_build_dir} ${tesseract_cmake_args}
|
||||
log_info "Building Tesseract (x86_64) ..."
|
||||
cmake --build ${tesseract_build_dir_x86_64} --config Release
|
||||
|
||||
log_info "Building Tesseract ..."
|
||||
cmake --build ${tesseract_build_dir} --config Release
|
||||
log_info "Configure Tesseract (arm64) ..."
|
||||
|
||||
git checkout .
|
||||
|
||||
local tesseract_build_dir_arm64="${tesseract_dir}/build_arm64"
|
||||
local -a tesseract_cmake_args=(
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DCMAKE_SYSTEM_NAME="Darwin"
|
||||
-DCMAKE_OSX_ARCHITECTURES=arm64
|
||||
-DCMAKE_SYSTEM_PROCESSOR=arm64
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
|
||||
-DSW_BUILD=OFF
|
||||
-DBUILD_TRAINING_TOOLS=OFF
|
||||
-DCMAKE_PREFIX_PATH="${advss_dep_path};${_plugin_deps}"
|
||||
-DCMAKE_INSTALL_PREFIX="${advss_dep_path}"
|
||||
)
|
||||
|
||||
cmake -S . -B ${tesseract_build_dir_arm64} ${tesseract_cmake_args}
|
||||
|
||||
log_info "Building Tesseract (arm64) ..."
|
||||
cmake --build ${tesseract_build_dir_arm64} --config Release
|
||||
|
||||
log_info "Combine arm and x86 libtesseract binaries ..."
|
||||
mv ${tesseract_build_dir_arm64}/libtesseract.a ${tesseract_build_dir_arm64}/libtesseract_arm.a
|
||||
lipo -create ${tesseract_build_dir_x86_64}/libtesseract.a ${tesseract_build_dir_arm64}/libtesseract_arm.a -output ${tesseract_build_dir_arm64}/libtesseract.a
|
||||
|
||||
log_info "Installing Tesseract..."
|
||||
cmake --install ${tesseract_build_dir} --prefix "${advss_dep_path}" --config Release
|
||||
cmake --install ${tesseract_build_dir_arm64} --prefix "${advss_dep_path}" --config Release
|
||||
|
||||
popd
|
||||
|
||||
pushd ${advss_dep_path}
|
||||
@@ -402,22 +419,29 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
lipo -create openssl_x86/libcrypto.a openssl_arm/libcrypto.a -output openssl-combined/libcrypto.a
|
||||
lipo -create openssl_x86/libssl.a openssl_arm/libssl.a -output openssl-combined/libssl.a
|
||||
|
||||
log_info "Clean up openssl dir..."
|
||||
log_info "Clean up openssl dir ..."
|
||||
mv openssl_x86 openssl
|
||||
rm -rf openssl_arm
|
||||
popd
|
||||
|
||||
pushd ${project_root}/deps/libusb
|
||||
log_info "Prepare libusb ..."
|
||||
|
||||
log_info "Building libusb x86 (deps) ..."
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.9
|
||||
log_info "Configure libusb x86 ..."
|
||||
export SDKROOT=$(xcrun --sdk macosx --show-sdk-path)
|
||||
export CC=$(xcrun --sdk macosx --find clang)
|
||||
export CXX=$(xcrun --sdk macosx --find clang++)
|
||||
export CFLAGS="-arch x86_64 -isysroot $SDKROOT"
|
||||
export CXXFLAGS="-arch x86_64 -isysroot $SDKROOT"
|
||||
export LDFLAGS="-arch x86_64 -isysroot $SDKROOT"
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.15
|
||||
|
||||
log_info "Building libusb x86 ..."
|
||||
mkdir ${project_root}/deps/libusb/out_x86
|
||||
./autogen.sh
|
||||
./configure --host=x86_64-apple-darwin --prefix=${advss_dep_path}
|
||||
make && make install
|
||||
|
||||
log_info "Building libusb x86 ..."
|
||||
log_info "Configure libusb arm ..."
|
||||
make clean
|
||||
rm -r ${project_root}/deps/libusb/out_x86
|
||||
mkdir ${project_root}/deps/libusb/out_x86
|
||||
|
||||
0
.github/test
vendored
0
.github/test
vendored
4
.github/workflows/build-debian.yml
vendored
4
.github/workflows/build-debian.yml
vendored
@@ -43,14 +43,14 @@ jobs:
|
||||
# devscripts and libobs-dev are needed but they were already installed
|
||||
# from check_libobs_revision and install_frontend_header sections.
|
||||
sudo apt update
|
||||
sudo apt install cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qtbase5-dev libopencv-dev libprocps-dev
|
||||
sudo apt install build-essential cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qt6-base-dev libopencv-dev libproc2-dev
|
||||
- name: build
|
||||
run: |
|
||||
debuild --no-lintian --no-sign
|
||||
mv ../*.deb .
|
||||
- name: Publish
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v2.2.1
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "obs-scene-switcher.deb"
|
||||
path: ${{ github.workspace }}/*.deb
|
||||
|
||||
14
.github/workflows/build-project.yaml
vendored
14
.github/workflows/build-project.yaml
vendored
@@ -10,7 +10,7 @@ env:
|
||||
jobs:
|
||||
check-event:
|
||||
name: Check GitHub Event Data 🔎
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
|
||||
macos-build:
|
||||
name: Build for macOS 🍏
|
||||
runs-on: macos-13
|
||||
runs-on: macos-14
|
||||
needs: check-event
|
||||
defaults:
|
||||
run:
|
||||
@@ -107,8 +107,8 @@ jobs:
|
||||
print "pluginName=${product_name}" >> $GITHUB_OUTPUT
|
||||
print "pluginVersion=${git_tag}" >> $GITHUB_OUTPUT
|
||||
|
||||
print '::group::Select Xcode version'
|
||||
sudo xcode-select --switch /Applications/Xcode_14.3.1.app
|
||||
print '::group::Enable Xcode 15.2'
|
||||
sudo xcode-select --switch /Applications/Xcode_15.2.app/Contents/Developer
|
||||
print '::endgroup::'
|
||||
|
||||
- uses: actions/cache@v4
|
||||
@@ -177,7 +177,7 @@ jobs:
|
||||
|
||||
ubuntu-build:
|
||||
name: Build for Ubuntu 🐧
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-event
|
||||
defaults:
|
||||
run:
|
||||
@@ -241,14 +241,14 @@ jobs:
|
||||
- name: Upload Artifacts 📡
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}
|
||||
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-24.04-x86_64-${{ needs.check-event.outputs.commitHash }}
|
||||
path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*.*
|
||||
|
||||
- name: Upload debug symbol artifacts 🪲
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||
with:
|
||||
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym
|
||||
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-24.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym
|
||||
path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*-dbgsym.ddeb
|
||||
|
||||
windows-build:
|
||||
|
||||
4
.github/workflows/check-format.yaml
vendored
4
.github/workflows/check-format.yaml
vendored
@@ -3,7 +3,7 @@ on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
clang-format:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
failCondition: error
|
||||
|
||||
cmake-format:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
4
.github/workflows/push.yaml
vendored
4
.github/workflows/push.yaml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
create-release:
|
||||
name: Create Release 🛫
|
||||
if: github.ref_type == 'tag'
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-project
|
||||
defaults:
|
||||
run:
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
variants=(
|
||||
'windows-x64;zip|exe'
|
||||
'macos-universal;tar.xz|pkg'
|
||||
'ubuntu-22.04-x86_64;tar.xz|deb|ddeb'
|
||||
'ubuntu-24.04-x86_64;tar.xz|deb|ddeb'
|
||||
'sources;tar.xz'
|
||||
)
|
||||
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -7,6 +7,7 @@
|
||||
!/cmake
|
||||
!/data
|
||||
!/deps
|
||||
!/scripting
|
||||
!/forms
|
||||
!/lib
|
||||
!/module
|
||||
@@ -34,4 +35,7 @@
|
||||
.DS_Store
|
||||
|
||||
# Exclude CMake build number cache
|
||||
/cmake/.CMakeBuildNumber
|
||||
/cmake/.CMakeBuildNumber
|
||||
|
||||
# Exclude python caches
|
||||
/**/__pycache__
|
||||
|
||||
@@ -20,6 +20,12 @@ if(BUILD_OUT_OF_TREE)
|
||||
include(helpers)
|
||||
endif()
|
||||
|
||||
# OBS 31 no longer defines find_qt so check if we need to include it for in-tree
|
||||
# builds
|
||||
if(NOT COMMAND find_qt)
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/helpers_common.cmake")
|
||||
endif()
|
||||
|
||||
set(LIB_NAME "${PROJECT_NAME}-lib")
|
||||
add_library(${PROJECT_NAME} MODULE)
|
||||
add_library(${LIB_NAME} SHARED)
|
||||
@@ -27,6 +33,11 @@ add_library(${LIB_NAME} SHARED)
|
||||
include(cmake/common/get_git_revision_description.cmake)
|
||||
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||
git_describe(GIT_TAG)
|
||||
|
||||
if(${GIT_TAG} STREQUAL "GIT-NOTFOUND")
|
||||
set(GIT_TAG ${PROJECT_VERSION})
|
||||
endif()
|
||||
message(STATUS "${PROJECT_NAME} version: ${GIT_TAG}")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/version.cpp.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/lib/version.cpp" @ONLY)
|
||||
|
||||
@@ -52,8 +63,6 @@ target_sources(
|
||||
${LIB_NAME}
|
||||
PRIVATE lib/legacy/scene-group.cpp
|
||||
lib/legacy/scene-group.hpp
|
||||
lib/legacy/scene-trigger.cpp
|
||||
lib/legacy/scene-trigger.hpp
|
||||
lib/legacy/switch-audio.cpp
|
||||
lib/legacy/switch-audio.hpp
|
||||
lib/legacy/switch-executable.cpp
|
||||
@@ -66,8 +75,6 @@ target_sources(
|
||||
lib/legacy/switch-idle.hpp
|
||||
lib/legacy/switch-media.cpp
|
||||
lib/legacy/switch-media.hpp
|
||||
lib/legacy/switch-network.cpp
|
||||
lib/legacy/switch-network.hpp
|
||||
lib/legacy/switch-pause.cpp
|
||||
lib/legacy/switch-pause.hpp
|
||||
lib/legacy/switch-random.cpp
|
||||
@@ -96,6 +103,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 +117,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
|
||||
@@ -128,10 +139,14 @@ target_sources(
|
||||
lib/macro/macro-ref.hpp
|
||||
lib/macro/macro-run-button.cpp
|
||||
lib/macro/macro-run-button.hpp
|
||||
lib/macro/macro-script-handler.cpp
|
||||
lib/macro/macro-script-handler.hpp
|
||||
lib/macro/macro-segment-copy-paste.cpp
|
||||
lib/macro/macro-segment-copy-paste.hpp
|
||||
lib/macro/macro-segment-list.cpp
|
||||
lib/macro/macro-segment-list.hpp
|
||||
lib/macro/macro-segment-script.cpp
|
||||
lib/macro/macro-segment-script.hpp
|
||||
lib/macro/macro-segment-selection.cpp
|
||||
lib/macro/macro-segment-selection.hpp
|
||||
lib/macro/macro-segment.cpp
|
||||
@@ -153,8 +168,12 @@ target_sources(
|
||||
lib/queue/action-queue.hpp
|
||||
lib/queue/action-queue-tab.cpp
|
||||
lib/queue/action-queue-tab.hpp
|
||||
lib/utils/auto-update-tooltip-label.cpp
|
||||
lib/utils/auto-update-tooltip-label.hpp
|
||||
lib/utils/backup.cpp
|
||||
lib/utils/backup.hpp
|
||||
lib/utils/condition-logic.cpp
|
||||
lib/utils/condition-logic.hpp
|
||||
lib/utils/curl-helper.cpp
|
||||
lib/utils/curl-helper.hpp
|
||||
lib/utils/cursor-shape-changer.cpp
|
||||
@@ -163,6 +182,8 @@ target_sources(
|
||||
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
|
||||
@@ -192,7 +213,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
|
||||
@@ -201,6 +221,9 @@ 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
|
||||
@@ -241,12 +264,16 @@ target_sources(
|
||||
lib/utils/tab-helpers.hpp
|
||||
lib/utils/temp-variable.cpp
|
||||
lib/utils/temp-variable.hpp
|
||||
lib/utils/time-helpers.cpp
|
||||
lib/utils/time-helpers.hpp
|
||||
lib/utils/ui-helpers.cpp
|
||||
lib/utils/ui-helpers.hpp
|
||||
lib/utils/utility.cpp
|
||||
lib/utils/utility.hpp
|
||||
lib/utils/volume-control.cpp
|
||||
lib/utils/volume-control.hpp
|
||||
lib/utils/websocket-api.cpp
|
||||
lib/utils/websocket-api.hpp
|
||||
lib/variables/variable-line-edit.cpp
|
||||
lib/variables/variable-line-edit.hpp
|
||||
lib/variables/variable-number.hpp
|
||||
@@ -264,13 +291,25 @@ 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})
|
||||
|
||||
find_qt(COMPONENTS Widgets Core)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt::Core Qt::Widgets)
|
||||
target_link_libraries(${LIB_NAME} PRIVATE Qt::Core Qt::Widgets)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core Qt6::Widgets)
|
||||
target_link_libraries(${LIB_NAME} PRIVATE Qt6::Core Qt6::Widgets)
|
||||
|
||||
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported Qt
|
||||
# version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
|
||||
if(Qt6_VERSION VERSION_GREATER "6.0.0")
|
||||
target_compile_definitions(${LIB_NAME} PRIVATE QT_NO_DEPRECATED_WARNINGS)
|
||||
endif()
|
||||
|
||||
target_compile_options(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE
|
||||
@@ -307,13 +346,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")
|
||||
@@ -390,7 +425,12 @@ else()
|
||||
set(PROCESS_CONDITION_SUPPORTED 1)
|
||||
endif()
|
||||
if(PROCPS2_INCLUDE_DIR)
|
||||
message(STATUS "${PROJECT_NAME} using libproc2")
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(libproc2 IMPORTED_TARGET libproc2<4.0.5 QUIET)
|
||||
if(libproc2_FOUND)
|
||||
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_USE_INFO)
|
||||
endif()
|
||||
pkg_check_modules(libproc2 REQUIRED IMPORTED_TARGET libproc2)
|
||||
set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}")
|
||||
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE)
|
||||
set(PROCESS_CONDITION_SUPPORTED 1)
|
||||
@@ -403,14 +443,16 @@ else()
|
||||
endif()
|
||||
target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}")
|
||||
target_sources(${LIB_NAME} PRIVATE lib/linux/advanced-scene-switcher-nix.cpp)
|
||||
|
||||
# Don't include irrelevant folders into sources archive
|
||||
list(APPEND CPACK_SOURCE_IGNORE_FILES "\\.deps/.*")
|
||||
endif()
|
||||
|
||||
if(NOT OS_WINDOWS)
|
||||
target_compile_options(
|
||||
${LIB_NAME}
|
||||
PUBLIC -Wno-error=unused-parameter -Wno-error=conversion -Wno-error=shadow
|
||||
-Wno-error=float-conversion -Wno-error=enum-conversion
|
||||
-Wno-error=deprecated-declarations)
|
||||
-Wno-error=float-conversion -Wno-error=enum-conversion)
|
||||
endif()
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
@@ -22,5 +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`.
|
||||
|
||||
@@ -6,6 +6,7 @@ import re
|
||||
import sys
|
||||
|
||||
defaultLocaleFile = "en-US.ini"
|
||||
commentChars = ";#"
|
||||
|
||||
|
||||
class localeEntry:
|
||||
@@ -21,6 +22,13 @@ class localeEntry:
|
||||
self.lineNum = lineNum
|
||||
|
||||
|
||||
def isCommentLine(line):
|
||||
for char in commentChars:
|
||||
if line.startswith(char):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def getNonDefaultLocales(dir):
|
||||
files = []
|
||||
for filename in os.listdir(dir):
|
||||
@@ -38,7 +46,7 @@ def getAllLocaleEntries(file):
|
||||
widgetPlaceholders = []
|
||||
qStringArgs = []
|
||||
|
||||
if line.startswith(";"):
|
||||
if isCommentLine(line):
|
||||
continue
|
||||
|
||||
for word in line.split("{{"):
|
||||
|
||||
@@ -7,7 +7,7 @@ Build-Depends: cmake,
|
||||
libcurl4-openssl-dev,
|
||||
libobs-dev,
|
||||
libxtst-dev,
|
||||
qtbase5-dev,
|
||||
qt6-base-dev,
|
||||
libxss-dev,
|
||||
libopencv-dev
|
||||
Standards-Version: 4.6.0
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
export QT_SELECT = qt5
|
||||
export QT_SELECT = qt6
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
@@ -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(
|
||||
@@ -154,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/..")
|
||||
@@ -169,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
|
||||
@@ -185,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)
|
||||
@@ -214,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)
|
||||
|
||||
@@ -286,8 +283,14 @@ endfunction()
|
||||
|
||||
function(setup_advss_plugin target)
|
||||
setup_obs_lib_dependency(${target})
|
||||
find_qt(COMPONENTS Widgets Core)
|
||||
target_link_libraries(${target} PRIVATE Qt::Core Qt::Widgets)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
|
||||
target_link_libraries(${target} PRIVATE Qt6::Core Qt6::Widgets)
|
||||
|
||||
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported
|
||||
# Qt version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
|
||||
if(Qt6_VERSION VERSION_GREATER "6.7.0")
|
||||
target_compile_definitions(${target} PRIVATE QT_NO_DEPRECATED_WARNINGS)
|
||||
endif()
|
||||
|
||||
set_target_properties(
|
||||
${target}
|
||||
@@ -335,7 +338,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})
|
||||
|
||||
@@ -17,85 +17,6 @@ if(NOT QT_VERSION)
|
||||
set_property(CACHE QT_VERSION PROPERTY STRINGS AUTO 5 6)
|
||||
endif()
|
||||
|
||||
# find_qt: Macro to find best possible Qt version for use with the project:
|
||||
macro(find_qt)
|
||||
set(multiValueArgs COMPONENTS COMPONENTS_WIN COMPONENTS_MAC COMPONENTS_LINUX)
|
||||
cmake_parse_arguments(find_qt "" "${oneValueArgs}" "${multiValueArgs}"
|
||||
${ARGN})
|
||||
|
||||
# Do not use versionless targets in the first step to avoid Qt::Core being
|
||||
# clobbered by later opportunistic find_package runs
|
||||
set(QT_NO_CREATE_VERSIONLESS_TARGETS TRUE)
|
||||
|
||||
message(DEBUG "Start Qt version discovery...")
|
||||
# Loop until _QT_VERSION is set or FATAL_ERROR aborts script execution early
|
||||
while(NOT _QT_VERSION)
|
||||
message(DEBUG "QT_VERSION set to ${QT_VERSION}")
|
||||
if(QT_VERSION STREQUAL AUTO AND NOT qt_test_version)
|
||||
set(qt_test_version 6)
|
||||
elseif(NOT QT_VERSION STREQUAL AUTO)
|
||||
set(qt_test_version ${QT_VERSION})
|
||||
endif()
|
||||
message(DEBUG "Attempting to find Qt${qt_test_version}")
|
||||
|
||||
find_package(
|
||||
Qt${qt_test_version}
|
||||
COMPONENTS Core
|
||||
QUIET)
|
||||
|
||||
if(TARGET Qt${qt_test_version}::Core)
|
||||
set(_QT_VERSION
|
||||
${qt_test_version}
|
||||
CACHE INTERNAL "")
|
||||
message(STATUS "Qt version found: ${_QT_VERSION}")
|
||||
unset(qt_test_version)
|
||||
break()
|
||||
elseif(QT_VERSION STREQUAL AUTO)
|
||||
if(qt_test_version EQUAL 6)
|
||||
message(WARNING "Qt6 was not found, falling back to Qt5")
|
||||
set(qt_test_version 5)
|
||||
continue()
|
||||
endif()
|
||||
endif()
|
||||
message(FATAL_ERROR "Neither Qt6 nor Qt5 found.")
|
||||
endwhile()
|
||||
|
||||
# Enable versionless targets for the remaining Qt components
|
||||
set(QT_NO_CREATE_VERSIONLESS_TARGETS FALSE)
|
||||
|
||||
set(qt_components ${find_qt_COMPONENTS})
|
||||
if(OS_WINDOWS)
|
||||
list(APPEND qt_components ${find_qt_COMPONENTS_WIN})
|
||||
elseif(OS_MACOS)
|
||||
list(APPEND qt_components ${find_qt_COMPONENTS_MAC})
|
||||
else()
|
||||
list(APPEND qt_components ${find_qt_COMPONENTS_LINUX})
|
||||
endif()
|
||||
message(DEBUG "Trying to find Qt components ${qt_components}...")
|
||||
|
||||
find_package(Qt${_QT_VERSION} REQUIRED ${qt_components})
|
||||
|
||||
list(APPEND qt_components Core)
|
||||
|
||||
if("Gui" IN_LIST find_qt_COMPONENTS_LINUX)
|
||||
list(APPEND qt_components "GuiPrivate")
|
||||
endif()
|
||||
|
||||
# Check for versionless targets of each requested component and create if
|
||||
# necessary
|
||||
foreach(component IN LISTS qt_components)
|
||||
message(DEBUG "Checking for target Qt::${component}")
|
||||
if(NOT TARGET Qt::${component} AND TARGET Qt${_QT_VERSION}::${component})
|
||||
add_library(Qt::${component} INTERFACE IMPORTED)
|
||||
set_target_properties(
|
||||
Qt::${component} PROPERTIES INTERFACE_LINK_LIBRARIES
|
||||
Qt${_QT_VERSION}::${component})
|
||||
endif()
|
||||
set_property(TARGET Qt::${component} PROPERTY INTERFACE_COMPILE_FEATURES "")
|
||||
endforeach()
|
||||
|
||||
endmacro()
|
||||
|
||||
# check_uuid: Helper function to check for valid UUID
|
||||
function(check_uuid uuid_string return_value)
|
||||
set(valid_uuid TRUE)
|
||||
|
||||
@@ -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"
|
||||
@@ -94,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"
|
||||
@@ -142,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"
|
||||
@@ -159,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"
|
||||
@@ -214,7 +212,6 @@ AdvSceneSwitcher.condition.stream.state.start="Stream läuft"
|
||||
AdvSceneSwitcher.condition.stream.state.stop="Stream gestoppt"
|
||||
AdvSceneSwitcher.condition.stream.state.starting="Stream wird gestartet"
|
||||
AdvSceneSwitcher.condition.stream.state.stopping="Stream wird gestoppt"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
|
||||
AdvSceneSwitcher.condition.record="Aufnahme"
|
||||
AdvSceneSwitcher.condition.record.state.start="Aufnahme läuft"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Aufnahme pausiert"
|
||||
@@ -321,11 +318,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"
|
||||
@@ -495,10 +489,10 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Nach unten verschieben"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveTop="An erste Stelle verschieben"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="An letzte Stelle verschieben"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.movePosition="An Position verschieben"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
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"
|
||||
@@ -532,9 +526,8 @@ AdvSceneSwitcher.action.screenshot.save.default="Standard"
|
||||
AdvSceneSwitcher.action.screenshot.save.custom="Benutzerdefiniert"
|
||||
AdvSceneSwitcher.action.screenshot.type.source="Quelle"
|
||||
AdvSceneSwitcher.action.screenshot.type.scene="Szene"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's Haupt-Ausgabe"
|
||||
AdvSceneSwitcher.action.screenshot.blackscreenNote="Quellen oder Szenen, die nicht immer gerendert werden, können dazu führen, dass einige Teile der Screenshots leer bleiben."
|
||||
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}und speichere in{{saveType}}Pfad"
|
||||
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}und speichere in{{saveType}}{{variables}}Pfad"
|
||||
AdvSceneSwitcher.action.profile="Profil"
|
||||
AdvSceneSwitcher.action.profile.entry="Aktives Profil umschalten auf {{profiles}}"
|
||||
AdvSceneSwitcher.action.sceneCollection="Szenensammlung"
|
||||
@@ -733,31 +726,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"
|
||||
@@ -778,30 +746,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"
|
||||
@@ -898,7 +842,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"
|
||||
@@ -117,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}}"
|
||||
@@ -128,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"
|
||||
@@ -174,7 +172,6 @@ AdvSceneSwitcher.condition.stream.state.start="Transmisión en ejecución"
|
||||
AdvSceneSwitcher.condition.stream.state.stop="Transmisión detenida"
|
||||
AdvSceneSwitcher.condition.stream.state.starting="Inicio de transmisión"
|
||||
AdvSceneSwitcher.condition.stream.state.stopping="Detener transmisión"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
|
||||
AdvSceneSwitcher.condition.record="Grabación"
|
||||
AdvSceneSwitcher.condition.record.state.start="Grabación en ejecución"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Grabación en pausa"
|
||||
@@ -269,10 +266,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"
|
||||
@@ -412,10 +406,10 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover hacia abajo"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover al principio"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover al final"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
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"
|
||||
@@ -444,7 +438,6 @@ AdvSceneSwitcher.action.random="Aleatorio"
|
||||
AdvSceneSwitcher.action.random.entry="Ejecute aleatoriamente cualquiera de las siguientes macros (las macros en pausa se ignoran)"
|
||||
AdvSceneSwitcher.action.systray="Notificación de la bandeja del sistema"
|
||||
AdvSceneSwitcher.action.screenshot="Captura de pantalla"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="Salida principal de OBS"
|
||||
AdvSceneSwitcher.action.profile="Perfil"
|
||||
AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}"
|
||||
AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
|
||||
@@ -621,31 +614,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"
|
||||
@@ -666,30 +634,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"
|
||||
@@ -743,7 +687,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"
|
||||
@@ -109,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"
|
||||
@@ -196,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 à"
|
||||
@@ -382,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"
|
||||
@@ -455,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"
|
||||
@@ -598,7 +597,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Déplacer vers le bas"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Déplacer en haut"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Déplacer en bas"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Déplacer à la position"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="Sur{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="Sur{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Transformation de l'élément de la scène"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.manual="Transform"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.reset="Réinitialiser la transformation"
|
||||
@@ -611,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"
|
||||
@@ -648,9 +647,8 @@ AdvSceneSwitcher.action.screenshot.save.default="Par défaut"
|
||||
AdvSceneSwitcher.action.screenshot.save.custom="Personnalisé"
|
||||
AdvSceneSwitcher.action.screenshot.type.source="Source"
|
||||
AdvSceneSwitcher.action.screenshot.type.scene="Scène"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="Sortie principale d'OBS"
|
||||
AdvSceneSwitcher.action.screenshot.blackscreenNote="Les sources ou les scènes qui ne sont pas toujours rendues peuvent entraîner des parties de captures d'écran vides."
|
||||
AdvSceneSwitcher.action.screenshot.entry="Capturer{{targetType}}{{sources}}{{scenes}}et enregistrer à l'emplacement{{saveType}}"
|
||||
AdvSceneSwitcher.action.screenshot.entry="Capturer{{targetType}}{{sources}}{{scenes}}et enregistrer à l'emplacement{{saveType}}{{variables}}"
|
||||
AdvSceneSwitcher.action.profile="Profil"
|
||||
AdvSceneSwitcher.action.profile.entry="Changer le profil actif vers{{profiles}}"
|
||||
AdvSceneSwitcher.action.sceneCollection="Collection de scènes"
|
||||
@@ -719,8 +717,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivue"
|
||||
AdvSceneSwitcher.action.projector.display="Affichage"
|
||||
AdvSceneSwitcher.action.projector.windowed="Fenêtré"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="Plein écran"
|
||||
AdvSceneSwitcher.action.projector.entry="Ouvrir le projecteur{{windowTypes}}de{{types}}{{scenes}}{{sources}}"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="sur{{monitors}}"
|
||||
AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="Envoyer un message à{{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="Définir la sélection de messages MIDI sur les messages entrants de{{listenDevices}}:{{listenButton}}"
|
||||
@@ -898,31 +894,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"
|
||||
@@ -943,30 +914,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é"
|
||||
@@ -1162,8 +1109,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"
|
||||
@@ -1183,7 +1130,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"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
AdvSceneSwitcher.pluginName="高機能シーンスイッチャー"
|
||||
AdvSceneSwitcher.windowTitle="高機能シーンスイッチャー"
|
||||
|
||||
; General Tab
|
||||
# General Tab
|
||||
AdvSceneSwitcher.generalTab.title="全般"
|
||||
AdvSceneSwitcher.generalTab.status="ステータス"
|
||||
AdvSceneSwitcher.generalTab.status.hotkeytips="OBS設定でホットキーを定義可能"
|
||||
@@ -19,16 +19,16 @@ AdvSceneSwitcher.generalTab.status.autoStart.streaming="配信を開始した時
|
||||
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="録画開始時または配信開始時"
|
||||
AdvSceneSwitcher.generalTab.status.checkInterval="イベント条件を確認する頻度"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior="一般"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="何も操作を行わなかった場合"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="設定されたチェック間隔と同程度の精度のみが保証されます。"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="切り替えないでください"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="ランダムタブで任意のシーンに切り替え"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="切り替え先:"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch="何も操作を行わなかった場合"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatchDelay.tooltip="設定されたチェック間隔と同程度の精度のみが保証されます。"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="切り替えないでください"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="ランダムタブで任意のシーンに切り替え"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="切り替え先:"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="アクションを実行した後、次のアクションをスキップする時間"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="この間、潜在的な一致は無視されます!"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel="ログレベル:"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="デフォルト"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.printActions="実行されたアクションのログ"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction="実行されたアクションのログ"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="詳細ログ(Verbose)"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="詳細ログを有効にする(生成されるログファイルのサイズが増加します)"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="ウィンドウのサイズと位置を記憶する"
|
||||
@@ -37,6 +37,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="UIヒントを無効
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.comboBoxFilterDisable="ドロップダウンメニューでの文字入力によるフィルタリングを無効にする"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="プラグインが読み込めない場合に警告を表示"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>次のプラグイン ライブラリの読み込みに失敗したため、一部の高機能シーンスイッチャーが利用できなくなる可能性があります:%1詳細については、OBS ログを確認してください。<br>このメッセージは、[全般] タブで無効にすることができます。 </body></html>"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="プラグインが正しくインストールできておらずクラッシュする可能性があります!\nプラグインが正しくインストールされているか確認してください!"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="旧来型マクロ設定のタブを非表示にする(廃止予定タブ)"
|
||||
AdvSceneSwitcher.generalTab.matchBehavior="マッチした場合の動作(廃止予定の設定項目)"
|
||||
AdvSceneSwitcher.generalTab.priority="優先順位"
|
||||
@@ -46,11 +47,11 @@ AdvSceneSwitcher.generalTab.priority.threadPriorityNotice="(優先度を \"Norma
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings="設定の保存・読み込み"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="エクスポート"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="インポート"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="高度なシーンスイッチャーの設定をファイルにエクスポート..."
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="高度なシーンスイッチャーの設定をファイルからインポート..."
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="高機能シーンスイッチャーの設定をファイルにエクスポート..."
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="高機能シーンスイッチャーの設定をファイルからインポート..."
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="テキストファイル(*.txt)"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="高機能シーンスイッチャーの設定インポートに失敗しました"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="シーンスイッチャーの高度な設定が正常にインポートされました"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="高機能シーンスイッチャーの設定が正常にインポートされました"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportSensitiveDataWarning="警告:\nエクスポートされたデータにはセンシティブ情報が含まれている可能性があります!"
|
||||
AdvSceneSwitcher.generalTab.priority.fileContent="ファイル内容"
|
||||
AdvSceneSwitcher.generalTab.priority.sceneSequence="シーンシーケンス"
|
||||
@@ -69,7 +70,7 @@ AdvSceneSwitcher.generalTab.transitionOverride="トランジションオーバ
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="アクティブトランジションタイプを調整する"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="少なくとも1つのオプションが有効であること:\n\n - トランジションオーバーライドの使用\n\n - アクティブなトランジションタイプを変更する"
|
||||
|
||||
; Variables Tab
|
||||
# Variables Tab
|
||||
AdvSceneSwitcher.variableTab.title="変数"
|
||||
AdvSceneSwitcher.variableTab.help="変数はプラグイン内の様々な場所で使用できます。\n\n強調表示されたプラス記号をクリックすると、新しい変数を追加できます。"
|
||||
AdvSceneSwitcher.variableTab.variableAddButton.tooltip="新しい変数を追加する"
|
||||
@@ -81,14 +82,12 @@ AdvSceneSwitcher.variableTab.value.header="値"
|
||||
AdvSceneSwitcher.variableTab.saveLoadBehavior.header="保存・読み込みの動作"
|
||||
AdvSceneSwitcher.variableTab.saveLoadBehavior.text.default="デフォルト\"%1\""
|
||||
AdvSceneSwitcher.variableTab.lastUsed.header="最後に使用された"
|
||||
AdvSceneSwitcher.variableTab.lastUsed.text="%1 秒前"
|
||||
; AdvSceneSwitcher.variableTab.lastUsed.text.never="決して"
|
||||
; AdvSceneSwitcher.variableTab.lastUsed.text.never="Never"
|
||||
AdvSceneSwitcher.variableTab.lastChanged.header="最終変更"
|
||||
AdvSceneSwitcher.variableTab.lastChanged.text="%1 秒前"
|
||||
AdvSceneSwitcher.variableTab.lastChanged.text.none="起動以来変更なし"
|
||||
AdvSceneSwitcher.variableTab.lastChanged.tooltip="変更された時間: %1\n\n以前の値: %2"
|
||||
|
||||
; Action Queue Tab
|
||||
# Action Queue Tab
|
||||
AdvSceneSwitcher.actionQueueTab.title="アクションキュー"
|
||||
AdvSceneSwitcher.actionQueueTab.help="アクションキューは、マクロシステムのリセットと並行して順次実行されます。"
|
||||
AdvSceneSwitcher.actionQueueTab.queueAddButton.tooltip="新しいアクションキューを追加する"
|
||||
@@ -102,7 +101,7 @@ AdvSceneSwitcher.actionQueueTab.no="いいえ"
|
||||
AdvSceneSwitcher.actionQueueTab.removeSingleQueuePopup.text="本当に \"%1\"を削除しますか?"
|
||||
AdvSceneSwitcher.actionQueueTab.removeMultipleQueuesPopup.text="アクション・キュー %1 を削除しますか?"
|
||||
|
||||
; Websocket Connections Tab
|
||||
# Websocket Connections Tab
|
||||
AdvSceneSwitcher.websocketConnectionTab.title="Websocket接続"
|
||||
AdvSceneSwitcher.websocketConnectionTab.help="Websocket接続は、他のOBSインスタンスまたはプログラムと通信するために使用できます。"
|
||||
AdvSceneSwitcher.websocketConnectionTab.websocketConnectionAddButton.tooltip="新しいWebsocket接続を追加する"
|
||||
@@ -116,10 +115,10 @@ AdvSceneSwitcher.websocketConnectionTab.protocol.no="いいえ"
|
||||
AdvSceneSwitcher.websocketConnectionTab.removeSingleConnectionPopup.text="本当に\"%1\"を削除しますか?"
|
||||
AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text="本当に%1接続を削除しますか?"
|
||||
|
||||
; Twitch Connections Tab
|
||||
# Twitch Connections Tab
|
||||
AdvSceneSwitcher.twitchConnectionTab.title="Twitch接続"
|
||||
AdvSceneSwitcher.twitchConnectionTab.help="Twitch接続を使用すると、Twitchイベントをトリガーとしてアクションを実行したり、リンク先のTwitchアカウントでアクションを実行したりできます。"
|
||||
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip.tooltip="新しいTwitch接続を追加する"
|
||||
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip="新しいTwitch接続を追加する"
|
||||
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionRemoveButton.tooltip="選択したTwitch接続を削除する"
|
||||
AdvSceneSwitcher.twitchConnectionTab.name.header="名前"
|
||||
AdvSceneSwitcher.twitchConnectionTab.isValid.header="有効ですか?"
|
||||
@@ -129,7 +128,7 @@ AdvSceneSwitcher.twitchConnectionTab.no="いいえ"
|
||||
AdvSceneSwitcher.twitchConnectionTab.removeSingleConnectionPopup.text="本当に\"%1\"を削除しますか?"
|
||||
AdvSceneSwitcher.twitchConnectionTab.removeMultipleConnectionsPopup.text="本当に%1 Twitch接続を削除しますか?"
|
||||
|
||||
; Macro Tab
|
||||
# Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="マクロ"
|
||||
AdvSceneSwitcher.macroTab.macros="マクロ"
|
||||
AdvSceneSwitcher.macroTab.priorityWarning="注意: マクロを最優先機能に設定することをお勧めします。\nこの設定は [全般] タブで変更できます。"
|
||||
@@ -175,10 +174,13 @@ AdvSceneSwitcher.macroTab.collapseAll="すべて折りたたむ"
|
||||
AdvSceneSwitcher.macroTab.maximize="最大化"
|
||||
AdvSceneSwitcher.macroTab.minimize="最小化"
|
||||
AdvSceneSwitcher.macroTab.segment.useCustomLabel="カスタムラベルの使用"
|
||||
AdvSceneSwitcher.macroTab.segment.customLabelRename="カスタムラベルの名前を変更する"
|
||||
AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="マイ・ラベル"
|
||||
AdvSceneSwitcher.macroTab.segment.setCustomLabel="ラベルを設定 ..."
|
||||
AdvSceneSwitcher.macroTab.segment.copy="コピー"
|
||||
AdvSceneSwitcher.macroTab.segment.paste="貼り付け"
|
||||
AdvSceneSwitcher.macroTab.segment.pasteAction="アクションとして貼り付ける"
|
||||
AdvSceneSwitcher.macroTab.segment.pasteElseAction="elseアクションとして貼り付ける"
|
||||
AdvSceneSwitcher.macroTab.highlightSettings="ビジュアル設定"
|
||||
AdvSceneSwitcher.macroTab.hotkeySettings="ホットキー設定"
|
||||
AdvSceneSwitcher.macroTab.generalSettings="一般設定"
|
||||
@@ -191,7 +193,7 @@ AdvSceneSwitcher.macroTab.highlightExecutedMacros="最近実行したマクロ
|
||||
AdvSceneSwitcher.macroTab.highlightTrueConditions="現在選択されているマクロのうち、最近 true と評価された条件を強調表示します"
|
||||
AdvSceneSwitcher.macroTab.highlightPerformedActions="現在選択されているマクロの最近実行されたアクションを強調表示します"
|
||||
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="新規マクロの一時停止状態を制御するホットキーを登録"
|
||||
AdvSceneSwitcher.macroTab.currentDisableHotkeys="選択したマクロの一時停止状態を制御するホットキーを登録"
|
||||
AdvSceneSwitcher.macroTab.currentRegisterHotkeys="選択したマクロの一時停止状態を制御するホットキーを登録"
|
||||
AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="起動時に現在のマクロのアクションの実行をスキップします"
|
||||
AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone="新しい実行がトリガーされたときに、現在選択されているマクロのアクションがまだ実行中であれば、アクションを停止して再実行する。"
|
||||
AdvSceneSwitcher.macroTab.currentRegisterDock="ドックウィジェットを登録して、選択したマクロの一時停止状態を制御したり、手動で実行したりできます"
|
||||
@@ -238,11 +240,11 @@ AdvSceneSwitcher.macroDock.run="実行"
|
||||
AdvSceneSwitcher.macroDock.statusLabel.true="結果はturuでした。"
|
||||
AdvSceneSwitcher.macroDock.statusLabel.false="結果はfalseでした。"
|
||||
|
||||
; Macro List
|
||||
# Macro List
|
||||
AdvSceneSwitcher.macroList.deleted="削除"
|
||||
AdvSceneSwitcher.macroList.duplicate="\"%1\" はすでに選択されています!"
|
||||
|
||||
; Macro Logic
|
||||
# Macro Logic
|
||||
AdvSceneSwitcher.logic.none="入力無視"
|
||||
; AdvSceneSwitcher.logic.and="And"
|
||||
; AdvSceneSwitcher.logic.or="Or"
|
||||
@@ -251,7 +253,8 @@ AdvSceneSwitcher.logic.none="入力無視"
|
||||
; AdvSceneSwitcher.logic.rootNone="If"
|
||||
; AdvSceneSwitcher.logic.not="If not"
|
||||
|
||||
; Macro Conditions
|
||||
# Macro Conditions
|
||||
AdvSceneSwitcher.condition.unknown="不明な状態"
|
||||
AdvSceneSwitcher.condition.audio="音声"
|
||||
AdvSceneSwitcher.condition.audio.state.below="以下"
|
||||
AdvSceneSwitcher.condition.audio.state.exact="同じ"
|
||||
@@ -265,9 +268,9 @@ AdvSceneSwitcher.condition.audio.type.monitor="オーディオモニタリング
|
||||
AdvSceneSwitcher.condition.audio.type.balance="オーディオバランス"
|
||||
AdvSceneSwitcher.condition.audio.entry="{{audioSources}}の{{checkType}}は{{condition}}{{volume}}{{volumeDB}}{{percentDBToggle}}{{syncOffset}}{{monitorTypes}}です"
|
||||
AdvSceneSwitcher.condition.cursor="カーソル"
|
||||
AdvSceneSwitcher.condition.cursor.type.region="位置にあります"
|
||||
AdvSceneSwitcher.condition.cursor.type.moving="動いている"
|
||||
AdvSceneSwitcher.condition.cursor.type.click="クリックした"
|
||||
AdvSceneSwitcher.condition.cursor.type.region="が次の範囲内にある場合"
|
||||
AdvSceneSwitcher.condition.cursor.type.moving="が動いた場合"
|
||||
AdvSceneSwitcher.condition.cursor.type.click="をクリックした場合"
|
||||
AdvSceneSwitcher.condition.cursor.button.left="マウスの左ボタン"
|
||||
AdvSceneSwitcher.condition.cursor.button.middle="マウスの中ボタン"
|
||||
AdvSceneSwitcher.condition.cursor.button.right="マウスの右ボタン"
|
||||
@@ -307,11 +310,14 @@ AdvSceneSwitcher.condition.file.local="ローカルファイル"
|
||||
; AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
; AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="Media"
|
||||
AdvSceneSwitcher.condition.media.checkType.state="状態が一致する"
|
||||
AdvSceneSwitcher.condition.media.checkType.time="時間制限にマッチ"
|
||||
AdvSceneSwitcher.condition.media.source="ソース"
|
||||
AdvSceneSwitcher.condition.media.anyOnScene="あらゆるメディアソース"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="すべてのメディアソースはこちら"
|
||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="残念ながら、すべてのメディア ソース タイプが同じように動作するわけではありません (例: メディア ソースと VLC ビデオ ソースの \"Stopped\" 状態)。\nそのため、ご自身のセットアップに適したものを試してみてください。"
|
||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}の状態は{{states}}かつ{{timeRestrictions}}{{time}}"
|
||||
; AdvSceneSwitcher.condition.media.layout="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}{{states}}{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.media.layout.legacy="{{sourceTypes}}{{mediaSources}}{{scenes}}{{checkTypes}}の状態は{{states}}かつ{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.video="動画"
|
||||
AdvSceneSwitcher.condition.video.condition.match="完全一致"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="合わない"
|
||||
@@ -463,7 +469,7 @@ AdvSceneSwitcher.condition.source.sizeCompare.less="より小さい"
|
||||
AdvSceneSwitcher.condition.source.sizeCompare.equal="等しい"
|
||||
AdvSceneSwitcher.condition.source.sizeCompare.more="より大きい"
|
||||
AdvSceneSwitcher.condition.source.refresh="更新"
|
||||
AdvSceneSwitcher.condition.source.refreshTooltip="名前が変数値と一致するソースの設定でソース設定の選択を再入力します。"
|
||||
AdvSceneSwitcher.condition.source.refresh.tooltip="名前が変数値と一致するソースの設定でソース設定の選択を再入力します。"
|
||||
AdvSceneSwitcher.condition.source.sceneVisibilityHint="シーン固有の可視性は \"Scene item visibility\" 条件で確認可能"
|
||||
AdvSceneSwitcher.condition.source.getSettings="現在の設定を取得する"
|
||||
; AdvSceneSwitcher.condition.source.entry.line1="{{sources}}{{conditions}}{{settingSelection}}{{refresh}}{{sizeCompareMethods}}{{size}}"
|
||||
@@ -481,7 +487,7 @@ AdvSceneSwitcher.condition.filter.type.settingsChanged="設定変更"
|
||||
AdvSceneSwitcher.condition.filter.type.individualSettingMatches="設定値が一致"
|
||||
AdvSceneSwitcher.condition.filter.type.individualSettingChanged="設定値が変更されました"
|
||||
AdvSceneSwitcher.condition.filter.refresh="更新"
|
||||
AdvSceneSwitcher.condition.filter.refreshTooltip="変数値と一致する名前のフィルターの設定でフィルター設定の選択を再入力します。"
|
||||
AdvSceneSwitcher.condition.filter.refresh.tooltip="変数値と一致する名前のフィルターの設定でフィルター設定の選択を再入力します。"
|
||||
AdvSceneSwitcher.condition.filter.getSettings="現在の設定を取得する"
|
||||
; AdvSceneSwitcher.condition.filter.entry.line1="On{{sources}}{{filters}}{{conditions}}{{settingSelection}}{{refresh}}"
|
||||
; AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
|
||||
@@ -531,11 +537,17 @@ AdvSceneSwitcher.condition.date.entry.pattern="現在の日付「{{currentDate}}
|
||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}}繰り返しの場合、選択した日付を繰り返しの日付に更新します"
|
||||
; AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
||||
; AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
|
||||
; AdvSceneSwitcher.condition.sceneTransform.entry.line1="On{{scenes}}{{sources}}{{types}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.matches="マッチトランスフォーム"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.type.changed="変わりました"
|
||||
; AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
; AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}}{{getSettings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.getCurrentValue="現在の値を取得"
|
||||
AdvSceneSwitcher.condition.sceneTransform.settingsType.all="全て"
|
||||
AdvSceneSwitcher.condition.sceneTransform.settingsType.single="単一"
|
||||
AdvSceneSwitcher.condition.sceneTransform.condition.match="マッチトランスフォーム"
|
||||
AdvSceneSwitcher.condition.sceneTransform.condition.changed="変わりました"
|
||||
AdvSceneSwitcher.condition.sceneTransform.compare.less="より小さい"
|
||||
AdvSceneSwitcher.condition.sceneTransform.compare.equal="等しい"
|
||||
AdvSceneSwitcher.condition.sceneTransform.compare.more="より大きい"
|
||||
; AdvSceneSwitcher.condition.sceneTransform.entry="On{{scenes}}{{settingsType}}transform setting of{{sources}}{{conditions}}"
|
||||
; AdvSceneSwitcher.condition.sceneTransform.entry.singleSettingCompare="{{setting}}{{compare}}{{singleSettingValue}}"
|
||||
; AdvSceneSwitcher.condition.sceneTransform.entry.options="{{regex}}{{getSettings}}{{getCurrentValue}}"
|
||||
; AdvSceneSwitcher.condition.transition="Transition"
|
||||
; AdvSceneSwitcher.condition.transition.type.current="Current transition type is"
|
||||
AdvSceneSwitcher.condition.transition.type.duration="Current transition duration is"
|
||||
@@ -619,7 +631,7 @@ AdvSceneSwitcher.condition.slideshow="スライドショー"
|
||||
AdvSceneSwitcher.condition.slideshow.condition.slideChanged="スライド変更しました"
|
||||
AdvSceneSwitcher.condition.slideshow.condition.slideIndex="現在のスライド番号は"
|
||||
AdvSceneSwitcher.condition.slideshow.condition.slidePath="現在のスライドパスは"
|
||||
AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip="スライド ショーのステータスに関する情報は、設定されたスライド間の時間に基づいてのみ更新されます"
|
||||
AdvSceneSwitcher.condition.slideshow.updateInterval.tooltip="スライド ショーのステータスに関する情報は、設定されたスライド間の時間に基づいてのみ更新されます"
|
||||
; AdvSceneSwitcher.condition.slideshow.entry="{{sources}}{{conditions}}{{index}}{{path}}"
|
||||
; AdvSceneSwitcher.condition.twitch="Twitch"
|
||||
AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online="配信がオンラインになりました"
|
||||
@@ -672,6 +684,16 @@ AdvSceneSwitcher.condition.twitch.type.polling.channel.live="配信中です"
|
||||
AdvSceneSwitcher.condition.twitch.type.polling.channel.title="現在のタイトルマッチ"
|
||||
AdvSceneSwitcher.condition.twitch.type.polling.channel.category="現在のカテゴリーは"
|
||||
AdvSceneSwitcher.condition.twitch.type.chat.message="チャットメッセージ受信済み"
|
||||
AdvSceneSwitcher.condition.twitch.type.chat.properties="チャットメッセージプロパティ:"
|
||||
AdvSceneSwitcher.condition.twitch.type.chat.properties.select="チャットメッセージプロパティを選択します:"
|
||||
AdvSceneSwitcher.condition.twitch.type.chat.properties.firstMessage="最初のメッセージ"
|
||||
AdvSceneSwitcher.condition.twitch.type.chat.properties.emoteOnly="エモートのみ使用"
|
||||
AdvSceneSwitcher.condition.twitch.type.chat.properties.mod="モデレータ"
|
||||
AdvSceneSwitcher.condition.twitch.type.chat.properties.subscriber="サブスクライバー"
|
||||
AdvSceneSwitcher.condition.twitch.type.chat.properties.turbo="ターボ・ユーザー"
|
||||
AdvSceneSwitcher.condition.twitch.type.chat.properties.displayName="表示名が一致"
|
||||
AdvSceneSwitcher.condition.twitch.type.chat.properties.loginName="ユーザー名が一致"
|
||||
AdvSceneSwitcher.condition.twitch.type.chat.properties.badge="ユーザーがバッジを持っている"
|
||||
AdvSceneSwitcher.condition.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません!"
|
||||
AdvSceneSwitcher.condition.twitch.entry="チャンネル{{channel}}{{conditions}}{{pointsReward}}{{streamTitle}}{{regex}}{{category}}"
|
||||
AdvSceneSwitcher.condition.twitch.entry.account="アカウント{{account}}で確認してください"
|
||||
@@ -685,7 +707,7 @@ AdvSceneSwitcher.condition.queue.entry.startStop="キュー{{queues}}は{{condit
|
||||
AdvSceneSwitcher.condition.queue.entry.size="キュー{{queues}}{{conditions}}は{{size}}未満です"
|
||||
AdvSceneSwitcher.condition.clipboard="クリップボード"
|
||||
AdvSceneSwitcher.condition.clipboard.placeholder="クリップボードのテキスト"
|
||||
AdvSceneSwitcher.condition.clipboard.urlTooltip="OS によっては、ファイルが URL として表されることもあります"
|
||||
AdvSceneSwitcher.condition.clipboard.url.tooltip="OS によっては、ファイルが URL として表されることもあります"
|
||||
AdvSceneSwitcher.condition.clipboard.condition.changed="クリップボードの内容が変更されました"
|
||||
AdvSceneSwitcher.condition.clipboard.condition.isText="クリップボードにテキストが含まれています"
|
||||
AdvSceneSwitcher.condition.clipboard.condition.isImage="クリップボードに画像が含まれています"
|
||||
@@ -700,11 +722,22 @@ AdvSceneSwitcher.condition.folder.condition.fileChange="ファイルが変更さ
|
||||
AdvSceneSwitcher.condition.folder.condition.fileRemove="ファイルが削除されました"
|
||||
AdvSceneSwitcher.condition.folder.condition.folderAdd="ディレクトリが追加されました"
|
||||
AdvSceneSwitcher.condition.folder.condition.folderRemove="ディレクトリが削除されました"
|
||||
; ; AdvSceneSwitcher.condition.folder.entry="{{conditions}}in{{folder}}{{tooltip}}"
|
||||
; AdvSceneSwitcher.condition.folder.entry="{{conditions}}in{{folder}}{{tooltip}}"
|
||||
AdvSceneSwitcher.condition.folder.enableFilter="変更パスがパターンに一致する場合にのみtrueと評価されます"
|
||||
; ; AdvSceneSwitcher.condition.folder.entry.filter="{{filter}}{{regex}}"
|
||||
; AdvSceneSwitcher.condition.folder.entry.filter="{{filter}}{{regex}}"
|
||||
; AdvSceneSwitcher.condition.usb="USB"
|
||||
AdvSceneSwitcher.condition.usb.description="以下のプロパティに一致するUSBデバイスが接続されています:"
|
||||
; AdvSceneSwitcher.condition.usb.vendorID="Vendor ID:"
|
||||
; AdvSceneSwitcher.condition.usb.productID="Product ID:"
|
||||
; AdvSceneSwitcher.condition.usb.busNumber="Bus Number:"
|
||||
; AdvSceneSwitcher.condition.usb.deviceAddress="Device Address:"
|
||||
; AdvSceneSwitcher.condition.usb.vendorName="Vendor Name:"
|
||||
; AdvSceneSwitcher.condition.usb.productName="Product Name:"
|
||||
; AdvSceneSwitcher.condition.usb.serialNumber="Serial Number:"
|
||||
AdvSceneSwitcher.condition.noDevicesFoundWarning="USBデバイスが検出されません!\nプラグインがUSBデバイスをチェックするのに必要な権限を持っていない可能性があります。"
|
||||
|
||||
; Macro Actions
|
||||
# Macro Actions
|
||||
AdvSceneSwitcher.action.unknown="不明なアクション"
|
||||
AdvSceneSwitcher.action.scene="シーン切り替え"
|
||||
AdvSceneSwitcher.action.scene.type.program="プログラム"
|
||||
AdvSceneSwitcher.action.scene.type.preview="プレビュー"
|
||||
@@ -748,9 +781,11 @@ AdvSceneSwitcher.action.recording.split.hint="まずOBS設定で自動ファイ
|
||||
; AdvSceneSwitcher.action.recording.entry="{{actions}}{{recordFolder}}{{recordFileFormat}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.replay="リプレイバッファー"
|
||||
AdvSceneSwitcher.action.replay.saveWarn="警告: 頻繁に保存しすぎると、リプレイ バッファーが実際には保存されなくなる可能性があります。"
|
||||
AdvSceneSwitcher.action.replay.durationWarn="警告: 最大再生時間を変更すると、次に再生バッファが起動したときにのみ適用されます!"
|
||||
AdvSceneSwitcher.action.replay.type.stop="リプレイバッファ停止"
|
||||
AdvSceneSwitcher.action.replay.type.start="リプレイバッファ開始"
|
||||
AdvSceneSwitcher.action.replay.type.save="リプレイバッファの保存"
|
||||
|
||||
; AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="配信"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="配信停止"
|
||||
@@ -778,7 +813,7 @@ AdvSceneSwitcher.action.filter.type.toggle="切り替え"
|
||||
AdvSceneSwitcher.action.filter.type.settings="設定を行う"
|
||||
AdvSceneSwitcher.action.filter.type.pressSettingsButton="設定ボタンを押します"
|
||||
AdvSceneSwitcher.action.filter.refresh="更新"
|
||||
AdvSceneSwitcher.action.filter.refreshTooltip="変数値と一致する名前のフィルターの設定でフィルター設定の選択を再入力します。"
|
||||
AdvSceneSwitcher.action.filter.refresh.tooltip="変数値と一致する名前のフィルターの設定でフィルター設定の選択を再入力します。"
|
||||
; AdvSceneSwitcher.action.filter.entry="On{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
; AdvSceneSwitcher.action.filter.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="現在の設定を取得する"
|
||||
@@ -816,7 +851,7 @@ AdvSceneSwitcher.action.source.inputMethod.individualManual="固定値に設定"
|
||||
AdvSceneSwitcher.action.source.inputMethod.individualTempvar="マクロプロパティに設定"
|
||||
AdvSceneSwitcher.action.source.inputMethod.json="設定用JSON文字列を設定します"
|
||||
AdvSceneSwitcher.action.source.refresh="更新"
|
||||
AdvSceneSwitcher.action.source.refreshTooltip="名前が変数値と一致するソースの設定でソース設定の選択を再入力します。"
|
||||
AdvSceneSwitcher.action.source.refresh.tooltip="名前が変数値と一致するソースの設定でソース設定の選択を再入力します。"
|
||||
; AdvSceneSwitcher.action.media="Media"
|
||||
AdvSceneSwitcher.action.media.type.play="再生"
|
||||
AdvSceneSwitcher.action.media.type.pause="一時停止"
|
||||
@@ -853,7 +888,7 @@ AdvSceneSwitcher.action.macro.type.toggleAction="トグルアクション"
|
||||
; AdvSceneSwitcher.action.macro.entry.run.condition="{{conditionBehaviors}}of{{conditionMacros}}"
|
||||
; AdvSceneSwitcher.action.macro.entry.other="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="プラグインの状態"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcherプラグインの停止"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="高機能シーンスイッチャープラグインの停止"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="不一致の動作を変更します:"
|
||||
AdvSceneSwitcher.action.pluginState.type.import="設定のインポート元"
|
||||
AdvSceneSwitcher.action.pluginState.importWarning="注意:設定ウィンドウを開いている間はアクションは無視されます。"
|
||||
@@ -890,9 +925,10 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="下へ移動"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveTop="トップへ移動"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="一番下へ移動"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.movePosition="ポジションへの移動"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="オン{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="オン{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="シーンアイテムの変形"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.manual="変形"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="トランスフォーム設定を行う"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.reset="変形をリセット"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.rotate="回転させます"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal="左右反転"
|
||||
@@ -903,7 +939,8 @@ AdvSceneSwitcher.action.sceneTransform.type.centerToScreen="画面中央から"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="縦方向中央に配置"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="水平方向に中央揃え"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="変形を取得"
|
||||
; AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.getCurrentValue="現在の値を取得"
|
||||
; AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
|
||||
AdvSceneSwitcher.action.file="ファイル"
|
||||
; AdvSceneSwitcher.action.file.type.write="Write"
|
||||
; AdvSceneSwitcher.action.file.type.append="Append"
|
||||
@@ -942,9 +979,8 @@ AdvSceneSwitcher.action.screenshot.save.default="デフォルト"
|
||||
AdvSceneSwitcher.action.screenshot.save.custom="カスタム"
|
||||
; AdvSceneSwitcher.action.screenshot.type.source="Source"
|
||||
AdvSceneSwitcher.action.screenshot.type.scene="シーン"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBSの主な出力"
|
||||
AdvSceneSwitcher.action.screenshot.blackscreenNote="常にレンダリングされるわけではないソースやシーンにより、スクリーンショットの一部が空白のままになる場合があります。"
|
||||
AdvSceneSwitcher.action.screenshot.entry="スクリーンショット{{targetType}}{{sources}}{{scenes}}を作成し、{{saveType}}の場所に保存します"
|
||||
AdvSceneSwitcher.action.screenshot.entry="スクリーンショット{{targetType}}{{sources}}{{scenes}}を作成し、{{saveType}}{{variables}}の場所に保存します"
|
||||
AdvSceneSwitcher.action.profile="プロファイル"
|
||||
AdvSceneSwitcher.action.profile.entry="アクティブなプロファイルを{{profiles}}に切り替えます"
|
||||
; AdvSceneSwitcher.action.sceneCollection="Scene collection"
|
||||
@@ -1031,8 +1067,6 @@ AdvSceneSwitcher.action.projector.type.multiview="マルチビュー"
|
||||
AdvSceneSwitcher.action.projector.display="ディスプレイ"
|
||||
AdvSceneSwitcher.action.projector.windowed="ウィンドウ"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="フルスクリーン"
|
||||
AdvSceneSwitcher.action.projector.entry="{{types}}{{scenes}}{{sources}}の{{windowTypes}}プロジェクターを開きます"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="{{monitors}}上で"
|
||||
; AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="メッセージを{{device}}に送信します:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="MIDI メッセージの選択を {{listenDevices}} で受信するメッセージに設定します:{{listenButton}}"
|
||||
@@ -1052,10 +1086,10 @@ AdvSceneSwitcher.action.twitch.type.clip.create="ストリームクリップの
|
||||
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="チャット通知の送信"
|
||||
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="チャットのエモート専用モードを有効にする"
|
||||
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="チャットのエモート専用モードを無効にする"
|
||||
AdvSceneSwitcher.action.twitch.type.sendChatMessage="チャットメッセージを送る"
|
||||
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="チャットメッセージを送る"
|
||||
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません!"
|
||||
; AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
|
||||
AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}} でアカウント{{account}}{{actions}}を使用しています"
|
||||
AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
|
||||
AdvSceneSwitcher.action.twitch.title.title="タイトルを入力してください"
|
||||
AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明"
|
||||
AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します"
|
||||
@@ -1090,10 +1124,10 @@ AdvSceneSwitcher.action.log="ログ"
|
||||
AdvSceneSwitcher.action.log.placeholder="私のログメッセージ!"
|
||||
AdvSceneSwitcher.action.log.entry="OBS ログに書き込む:{{logMessage}}"
|
||||
|
||||
; Hotkey
|
||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="アドバンストシーンスイッチャーの起動"
|
||||
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="アドバンストシーンスイッチャーの停止"
|
||||
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="アドバンストシーンスイッチャーの開始/停止切り替え"
|
||||
# Hotkey
|
||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="高機能シーンスイッチャーの起動"
|
||||
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="高機能シーンスイッチャーの停止"
|
||||
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="高機能シーンスイッチャーの開始/停止切り替え"
|
||||
AdvSceneSwitcher.hotkey.macro.pause="マクロ %1 を一時停止します"
|
||||
AdvSceneSwitcher.hotkey.macro.unpause="マクロ %1 の一時停止を解除します"
|
||||
AdvSceneSwitcher.hotkey.macro.togglePause="マクロ %1 の一時停止を切り替えます"
|
||||
@@ -1101,7 +1135,7 @@ AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="マクロセグメント選択を
|
||||
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="マクロセグメント選択を下に移動"
|
||||
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="選択したマクロセグメントを削除"
|
||||
|
||||
AdvSceneSwitcher.askBackup="Advanced Scene Switcherの新しいバージョンが検出されました。\n古い設定のバックアップを作成する必要がありますか?"
|
||||
AdvSceneSwitcher.askBackup="高機能シーンスイッチャーの新しいバージョンが検出されました。\n古い設定のバックアップを作成する必要がありますか?"
|
||||
AdvSceneSwitcher.askForMacro="マクロ選択{{macroSelection}}"
|
||||
|
||||
AdvSceneSwitcher.close="閉じる"
|
||||
@@ -1117,6 +1151,11 @@ AdvSceneSwitcher.item.emptyName="空の名前は使用できません!"
|
||||
AdvSceneSwitcher.item.nameNotAvailable="名前がありません!"
|
||||
AdvSceneSwitcher.item.nameReserved="名前は使用済みです!"
|
||||
|
||||
AdvSceneSwitcher.listControls.add="追加"
|
||||
AdvSceneSwitcher.listControls.remove="削除"
|
||||
AdvSceneSwitcher.listControls.up="上へ"
|
||||
AdvSceneSwitcher.listControls.down="下へ"
|
||||
|
||||
AdvSceneSwitcher.macroSegmentSelection.invalid="選択が無効です!"
|
||||
|
||||
AdvSceneSwitcher.validation.text.maxLength="このフィールドには %1 文字のみを使用できます!"
|
||||
@@ -1440,7 +1479,7 @@ AdvSceneSwitcher.tempVar.twitch.status.prediction.description="チャネルポ
|
||||
|
||||
AdvSceneSwitcher.tempVar.twitch.id.goal="目標ID"
|
||||
AdvSceneSwitcher.tempVar.twitch.type.goal="ゴールタイプ"
|
||||
AdvSceneSwitcher.tempVar.twitch.type.goal.description="ゴールの種類。可能な値は次のとおりです:\n follow — 目標はフォロワーを増やすことです。\n subscription — 目標はサブスクリプションを増やすことです。このタイプは、サブスクリプションに関連付けられたティアポイントの純増または減少を示します。\n subscription_count — 目標はサブスクリプションを増やすことです。このタイプは、サブスクリプション数の純増または減少を示します。\n new_subscription — 目標はサブスクリプションを増やすことです。このタイプは、サブスクリプションに関連付けられたティアポイントの純増のみを示します (目標開始後にサブスクリプションを解除したユーザーは考慮されません)。\n new_subscription_count — 目標はサブスクリプションを増やすことです。このタイプは、サブスクリプション数の純増のみを示します (目標開始後にサブスクリプションを解除したユーザーは考慮されません)。"
|
||||
AdvSceneSwitcher.tempVar.twitch.type.goal.description="ゴールの種類。可能な値は次のとおりです:\n
|
||||
AdvSceneSwitcher.tempVar.twitch.description.goal="目標説明"
|
||||
AdvSceneSwitcher.tempVar.twitch.description.goal.description="指定されている場合、目標の説明。説明文は40文字以内です。"
|
||||
AdvSceneSwitcher.tempVar.twitch.is_achieved.goal="目標達成"
|
||||
@@ -1636,9 +1675,13 @@ AdvSceneSwitcher.tempVar.twitch.user_login.removeMod.description="削除され
|
||||
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod="ユーザー名"
|
||||
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod.description="削除されたモデレーターの表示名"
|
||||
|
||||
AdvSceneSwitcher.tempVar.twitch.chatter="ユーザーログイン"
|
||||
AdvSceneSwitcher.tempVar.twitch.chatter.description="チャットメッセージを送信した人のユーザーログイン情報"
|
||||
AdvSceneSwitcher.tempVar.twitch.chat_message="チャットメッセージ"
|
||||
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive="ユーザーログイン"
|
||||
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive.description="チャットメッセージを送信した人のユーザーログイン情報"
|
||||
AdvSceneSwitcher.tempVar.twitch.user_name.chatReceive="ユーザー名"
|
||||
AdvSceneSwitcher.tempVar.twitch.user_name.chatReceive.description="チャットメッセージを送信した人の表示名です。"
|
||||
AdvSceneSwitcher.tempVar.twitch.chat_message.chatReceive="チャットメッセージ"
|
||||
AdvSceneSwitcher.tempVar.twitch.badges.chatReceive="チャットバッジ"
|
||||
AdvSceneSwitcher.tempVar.twitch.badges.chatReceive.description="このユーザーが使用できるバッジとその状態を表す文字列。\n例: vip/1,subscriber/42,hype-train/1"
|
||||
|
||||
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid="RaidクリエイターユーザーID"
|
||||
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid.description="レイドを作成した配信者ID"
|
||||
@@ -1764,6 +1807,24 @@ AdvSceneSwitcher.tempVar.source.settings="設定文字列"
|
||||
AdvSceneSwitcher.tempVar.filter.setting="設定値"
|
||||
AdvSceneSwitcher.tempVar.filter.settings="設定文字列"
|
||||
|
||||
AdvSceneSwitcher.tempVar.slideShow.index="スライドインデックス"
|
||||
AdvSceneSwitcher.tempVar.slideShow.index.description="現在表示されているスライドのインデックス。\nインデックスを決定できない場合、-1が返されます。"
|
||||
AdvSceneSwitcher.tempVar.slideShow.path="スライドパス"
|
||||
AdvSceneSwitcher.tempVar.slideShow.path.description="現在表示されているスライドのファイルシステムパス。"
|
||||
AdvSceneSwitcher.tempVar.slideShow.fileName="スライドファイル名"
|
||||
AdvSceneSwitcher.tempVar.slideShow.fileName.description="現在表示されているスライドのファイル名です。"
|
||||
|
||||
; AdvSceneSwitcher.tempVar.usb.vendorID="ベンダーID"
|
||||
; AdvSceneSwitcher.tempVar.usb.productID="プロダクトID"
|
||||
; AdvSceneSwitcher.tempVar.usb.busNumber="バス番号"
|
||||
; AdvSceneSwitcher.tempVar.usb.deviceAddress="デバイスアドレス"
|
||||
; AdvSceneSwitcher.tempVar.usb.vendorName="ベンダー名"
|
||||
; AdvSceneSwitcher.tempVar.usb.productName="製品名"
|
||||
; AdvSceneSwitcher.tempVar.usb.serialNumber="シリアル番号"
|
||||
|
||||
AdvSceneSwitcher.tempVar.transform.setting="設定値"
|
||||
AdvSceneSwitcher.tempVar.transform.settings="設定文字列"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--シーンを選択--"
|
||||
AdvSceneSwitcher.selectPreviousScene="前のシーン"
|
||||
AdvSceneSwitcher.selectCurrentScene="現在のシーン"
|
||||
@@ -1808,6 +1869,20 @@ AdvSceneSwitcher.settings.suffix.type.frameRate="(フレーム レート)"
|
||||
AdvSceneSwitcher.settings.suffix.type.group="(グループ)"
|
||||
AdvSceneSwitcher.settings.suffix.type.colorAlpha="(カラーアルファ)"
|
||||
; AdvSceneSwitcher.setting.select="--select setting--"
|
||||
AdvSceneSwitcher.setting.transform.alignment="位置合わせ"
|
||||
AdvSceneSwitcher.setting.transform.cropBottom="[クロップ]下"
|
||||
AdvSceneSwitcher.setting.transform.cropLeft="[クロップ]左"
|
||||
AdvSceneSwitcher.setting.transform.cropRight="[クロップ]右"
|
||||
AdvSceneSwitcher.setting.transform.cropTop="[クロップ]上"
|
||||
AdvSceneSwitcher.setting.transform.boundingBoxType="バウンディングボックスの種類"
|
||||
AdvSceneSwitcher.setting.transform.boundingBoxAlign="バウンディングボックス内の配置"
|
||||
AdvSceneSwitcher.setting.transform.boundingBoxSize="バウンディングボックスのサイズ"
|
||||
AdvSceneSwitcher.setting.transform.rotation="回転"
|
||||
AdvSceneSwitcher.setting.transform.scale="拡大率"
|
||||
AdvSceneSwitcher.setting.transform.size="サイズ"
|
||||
AdvSceneSwitcher.setting.transform.position="位置"
|
||||
AdvSceneSwitcher.setting.transform.height="高さ"
|
||||
AdvSceneSwitcher.setting.transform.width="幅"
|
||||
|
||||
|
||||
AdvSceneSwitcher.sceneItemSelection.configure="シーン項目選択タイプの設定"
|
||||
@@ -1817,8 +1892,8 @@ AdvSceneSwitcher.sceneItemSelection.type.sourceVariable="変数名"
|
||||
; AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.entry="{{nameConflictIndex}}{{variable}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern="ソース名に一致するパターン"
|
||||
; AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry="{{nameConflictIndex}}matching{{pattern}}{{regex}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup="ソースの種類"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.entry="ソースタイプ{{nameConflictIndex}}{{sourceGroups}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceType="ソースの種類"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceType.entry="ソースタイプ{{nameConflictIndex}}{{sourceTypes}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.index="ソースインデックス(上から順)"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.index.entry="ソース{{index}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.indexRange="複数のソースインデックス(上から順)"
|
||||
@@ -1835,12 +1910,12 @@ AdvSceneSwitcher.status.inactive="停止中"
|
||||
AdvSceneSwitcher.running="プラグイン実行中"
|
||||
AdvSceneSwitcher.stopped="プラグイン停止しました"
|
||||
|
||||
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Advanced Scene Switcherが初めて起動したようです。<br>ガイドと例のリストについては <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> をご覧ください。<br>質問は <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">OBSフォーラムのプラグインのスレッド</span></a> で遠慮なくどうぞ!</p></body></html>"
|
||||
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>高機能シーンスイッチャーが初めて起動したようです。<br>ガイドと例のリストについては <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> をご覧ください。<br>質問は <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">OBSフォーラムのプラグインのスレッド</span></a> で遠慮なくどうぞ!</p></body></html>"
|
||||
|
||||
AdvSceneSwitcher.deprecatedTabWarning="このタブの開発は停止しました!\n代わりにマクロの使用に移行することを検討してください。\nこのヒントは [全般] タブで無効にすることができます。"
|
||||
|
||||
AdvSceneSwitcher.unit.milliseconds="ミリ秒"
|
||||
AdvSceneSwitcher.unit.secends="秒"
|
||||
AdvSceneSwitcher.unit.seconds="秒"
|
||||
AdvSceneSwitcher.unit.minutes="分"
|
||||
AdvSceneSwitcher.unit.hours="時間"
|
||||
AdvSceneSwitcher.duration.condition.none="期間修飾子なし"
|
||||
@@ -1855,8 +1930,46 @@ AdvSceneSwitcher.audio.monitor.both="モニターと出力"
|
||||
|
||||
AdvSceneSwitcher.noSettingsButtons="ボタンが見つかりません!"
|
||||
|
||||
; Legacy tabs below - please don't waste your time adding translations for these :)以下の従来のタブ - これらの翻訳の追加に時間を無駄にしないでください。
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.clearBufferOnMatch="マッチするメッセージが見つかったらメッセージバッファをクリアする"
|
||||
|
||||
AdvSceneSwitcher.script.settings="設定"
|
||||
AdvSceneSwitcher.script.timeout="スクリプトタイムアウト:{{timeout}}"
|
||||
|
||||
# This secion is copied from the OBS locale files
|
||||
|
||||
# commonly shared locale
|
||||
Browse="ブラウズ"
|
||||
Show="表示"
|
||||
Hide="隠す"
|
||||
|
||||
|
||||
# properties window
|
||||
; Basic.PropertiesWindow="Properties for '%1'"
|
||||
Basic.PropertiesWindow.AutoSelectFormat="%1 (自動選択: %2)"
|
||||
Basic.PropertiesWindow.SelectColor="色を選択"
|
||||
Basic.PropertiesWindow.SelectFont="フォントを選択"
|
||||
Basic.PropertiesWindow.SelectFont.WindowTitle="フォントを選ぶ"
|
||||
Basic.PropertiesWindow.ConfirmTitle="変更された設定"
|
||||
Basic.PropertiesWindow.Confirm="未保存の変更があります。保存しますか?"
|
||||
Basic.PropertiesWindow.NoProperties="プロパティがありません"
|
||||
Basic.PropertiesWindow.AddFiles="ファイルを追加"
|
||||
Basic.PropertiesWindow.AddDir="ディレクトリを追加"
|
||||
Basic.PropertiesWindow.AddURL="パス/URL を追加"
|
||||
Basic.PropertiesWindow.AddEditableListDir="ディレクトリを以下に追加 '%1'"
|
||||
Basic.PropertiesWindow.AddEditableListFiles="ファイルを以下に '%1'"
|
||||
Basic.PropertiesWindow.AddEditableListEntry="エントリを以下に '%1'"
|
||||
Basic.PropertiesWindow.EditEditableListEntry="エントリを以下に '%1'"
|
||||
|
||||
# properties view
|
||||
; Basic.PropertiesView.FPS.Simple="Simple FPS Values"
|
||||
; Basic.PropertiesView.FPS.Rational="Rational FPS Values"
|
||||
; Basic.PropertiesView.FPS.ValidFPSRanges="Valid FPS Ranges:"
|
||||
Basic.PropertiesView.UrlButton.Text="このリンクをデフォルトウェブブラウザで開きますか?"
|
||||
Basic.PropertiesView.UrlButton.Text.Url="URL: %1"
|
||||
Basic.PropertiesView.UrlButton.OpenUrl="URLを開く
|
||||
|
||||
# Legacy tabs below - please don't waste your time adding translations for these :)以下は従来のタブ - これらの翻訳の追加に時間を無駄にしないでください。
|
||||
# Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="トランジションの変更"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="シーンAからシーンBへの自動シーン切り替えにはトランジションを使用します"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>これらの設定は、シーン・スイッチャーによって引き起こされるトランジションに<span style=\"font-style:italic;\">のみ</span>影響します - マニュアルのシーン変更のために設定したい場合は、<a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a>をチェックしてください。<br/>ここで定義された設定は、シーン・スイッチャーの他の場所で設定されたトランジション設定よりも優先されます。<br/><br/>新しいエントリーを追加するには、下のプラスマークをクリックしてください。</p></body></html>"
|
||||
@@ -1867,7 +1980,7 @@ AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="プラス記号をクリ
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition.delay="シーン切り替え後のトランジション{{defTransitionDelay}}を切り替えます。"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="遅延は、トランジションの進行中にトランジション タイプが変更された場合に発生する可能性がある、キャンセルされたシーンの切り替えを回避するために使用されます。"
|
||||
|
||||
; Pause Scenes Tab
|
||||
# Pause Scenes Tab
|
||||
AdvSceneSwitcher.pauseTab.title="ポーズ"
|
||||
AdvSceneSwitcher.pauseTab.pauseOnScene="シーンスイッチャーをシーン上で一時停止する"
|
||||
AdvSceneSwitcher.pauseTab.pauseInFocus1="シーンスイッチャーを一時停止する場合"
|
||||
@@ -1878,11 +1991,11 @@ AdvSceneSwitcher.pauseTab.pauseTargetAll="すべて"
|
||||
AdvSceneSwitcher.pauseTab.pauseEntry="一時停止{{pauseTargets}}がチェックするタイミング{{pauseTypes}}{{scenes}}{{windows}}"
|
||||
AdvSceneSwitcher.pauseTab.help="このタブでは、シーンがアクティブであるかウィンドウにフォーカスがある場合に、個々の切り替え方法を一時停止するように設定できます。\n\n続行するには、強調表示されたプラス記号をクリックしてください。"
|
||||
|
||||
; Window Title Tab
|
||||
# Window Title Tab
|
||||
AdvSceneSwitcher.windowTitleTab.title="ウィンドウタイトル"
|
||||
AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>ウィンドウタイトルを直接入力するか、有効な正規表現を入力します。<a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span></a>を使って正規表現の構文とマッチをチェックすることができます。</p></body></html>"
|
||||
AdvSceneSwitcher.windowTitleTab.stayInFocus1="このウィンドウ名は無視してください"
|
||||
AdvSceneSwitcher.windowTitleTab.stayInFocus2=""
|
||||
AdvSceneSwitcher.windowTitleTab.stayInFocus2=" "
|
||||
AdvSceneSwitcher.windowTitleTab.fullscreen="フルスクリーン時にシーン転換"
|
||||
AdvSceneSwitcher.windowTitleTab.maximized="ウィンドウを最大化するとシーン転換"
|
||||
AdvSceneSwitcher.windowTitleTab.focused="ウィンドウにフォーカスするとシーン転換"
|
||||
@@ -1890,14 +2003,14 @@ AdvSceneSwitcher.windowTitleTab.focused="ウィンドウにフォーカスする
|
||||
AdvSceneSwitcher.windowTitleTab.windowsHelp="実行中のアプリケーションのウィンドウタイトルに基づいてシーンを切り替えます。\n次の追加条件を選択できます。\nウィンドウがフルスクリーンである\nウィンドウが最大化されている\nウィンドウがフォーカスされている\n\n強調表示されたプラス記号をクリックして続行します。"
|
||||
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="ウィンドウ タイトルが無視された場合、シーン スイッチャーは、以前に選択したウィンドウにまだフォーカスがあるかのように動作します。\n これにより、別のウィンドウに頻繁に切り替える場合に、シーンの切り替えを回避できます。これにより、シーンがトリガーされなくなります。変更します。\n\nウィンドウを選択するか、上にウィンドウのタイトルを入力し、下のプラス記号をクリックしてリストに追加します。"
|
||||
|
||||
; Executable Tab
|
||||
# Executable Tab
|
||||
AdvSceneSwitcher.executableTab.title="実行中のプログラム"
|
||||
AdvSceneSwitcher.executableTab.implemented="dasOvenによる実装"
|
||||
AdvSceneSwitcher.executableTab.requiresFocus="集中している場合のみ"
|
||||
AdvSceneSwitcher.executableTab.entry="{{processes}}が実行中の場合は、{{transitions}}{{requiresFocus}}を使用した{{scenes}}に切り替えます"
|
||||
AdvSceneSwitcher.executableTab.help="このタブを使用すると、プロセスが実行中の場合にシーンを自動的に切り替えることができます。\nこれは、ウィンドウ名が変更される可能性がある場合やウィンドウ名が不明な場合に役立ちます。\n\n続行するには、強調表示されたプラス記号をクリックしてください。 ."
|
||||
|
||||
; Screen Region Tab
|
||||
# Screen Region Tab
|
||||
AdvSceneSwitcher.screenRegionTab.title="マウスカーソルの位置"
|
||||
AdvSceneSwitcher.screenRegionTab.currentPosition="カーソルの現在位置:"
|
||||
AdvSceneSwitcher.screenRegionTab.showGuideFrames="ガイド枠を表示する"
|
||||
@@ -1906,7 +2019,7 @@ AdvSceneSwitcher.screenRegionTab.excludeScenes.None="選択なし"
|
||||
AdvSceneSwitcher.screenRegionTab.entry="カーソルが{{minX}}{{minY}}x{{maxX}}{{maxY}}にある場合、{{excludeScenes}}でない限り、{{transitions}}を使用して{{scenes}}に切り替えます"
|
||||
AdvSceneSwitcher.screenRegionTab.help="このタブでは、マウス カーソルの現在位置に基づいてシーンを自動的に切り替えることができます。\n\n次に進むには、強調表示されたプラス記号をクリックしてください。"
|
||||
|
||||
; Media Tab
|
||||
# Media Tab
|
||||
AdvSceneSwitcher.mediaTab.title="メディア"
|
||||
; AdvSceneSwitcher.mediaTab.implemented="Implemented by Exeldro"
|
||||
AdvSceneSwitcher.mediaTab.states.none="なし"
|
||||
@@ -1927,7 +2040,7 @@ AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="残り時間が長くな
|
||||
AdvSceneSwitcher.mediaTab.entry="{{mediaSources}}の状態が{{states}}で、{{timeRestrictions}}{{time}}が{{transitions}}を使用して{{scenes}}に切り替わるとき"
|
||||
AdvSceneSwitcher.mediaTab.help="このタブでは、メディア ソースの状態に基づいてシーンを切り替えることができます。\nたとえば、選択したメディア ソースの再生が終了すると、自動的に前のシーンに切り替えることができます。\n\n続行するにはプラス記号を強調表示します。"
|
||||
|
||||
; File Tab
|
||||
# File Tab
|
||||
AdvSceneSwitcher.fileTab.title="ファイルの内容"
|
||||
AdvSceneSwitcher.fileTab.readWriteSceneFile="シーンをファイルから読み書きする/ファイルに書き込む"
|
||||
AdvSceneSwitcher.fileTab.currentSceneOutputFile="現在のシーンの名前をこのファイルに書き込みます:"
|
||||
@@ -1952,13 +2065,13 @@ AdvSceneSwitcher.fileTab.entry="{{fileType}}{{filePath}}{{browseButton}}の内
|
||||
; AdvSceneSwitcher.fileTab.entry3="{{useRegex}}{{checkModificationDate}}{{checkFileContent}}"
|
||||
AdvSceneSwitcher.fileTab.help="このタブでは、リモートまたはローカル ファイルの内容に基づいてシーンを自動的に切り替えることができます。\n\n続行するには、強調表示されたプラス記号をクリックしてください。"
|
||||
|
||||
; Random Tab
|
||||
# Random Tab
|
||||
AdvSceneSwitcher.randomTab.title="ランダム"
|
||||
AdvSceneSwitcher.randomTab.randomDisabledWarning="機能無効 - 有効にするには、全般タブの「切り替え条件が満たされない場合はランダムタブの任意のシーンに切り替える」を選択してください"
|
||||
AdvSceneSwitcher.randomTab.entry="切り替え条件が満たされない場合は、{{delay}}の間、{{transitions}}を使用して{{scenes}}に切り替えます"
|
||||
AdvSceneSwitcher.randomTab.help="シーン スイッチャーは、設定された時間の間、このタブ上のエントリをランダムに選択して切り替えます。\n同じエントリが連続して 2 回選択されることはないことに注意してください。\n\n強調表示されたプラス記号をクリックすると、続けます。"
|
||||
|
||||
; Time Tab
|
||||
# Time Tab
|
||||
AdvSceneSwitcher.timeTab.title="時間指定"
|
||||
AdvSceneSwitcher.timeTab.anyDay="いつでも"
|
||||
AdvSceneSwitcher.timeTab.mondays="月曜日"
|
||||
@@ -1973,22 +2086,22 @@ AdvSceneSwitcher.timeTab.afterstart.tip="配信/録画開始からの相対時
|
||||
AdvSceneSwitcher.timeTab.entry="{{triggers}}時{{time}}で{{transitions}}を使用して{{scenes}}に切り替えます"
|
||||
AdvSceneSwitcher.timeTab.help="このタブを使用すると、現在の現地時間に基づいて別のシーンに自動的に切り替えることができます。\n\nシーン スイッチャーは、指定した正確な時刻にのみシーンを切り替えることに注意してください。\n設定が完了していることを確認してください。他の切り替え方法が優先されて選択した時点が失われることがないように、[全般] タブの優先順位を好みに合わせて設定します。\n\n強調表示されたプラス記号をクリックして続行します。"
|
||||
|
||||
; Idle Tab
|
||||
# Idle Tab
|
||||
AdvSceneSwitcher.idleTab.title="アイドル"
|
||||
AdvSceneSwitcher.idleTab.enable="アイドル検知を有効にする"
|
||||
AdvSceneSwitcher.idleTab.idleswitch="キーボードやマウスの入力がない状態が{{duration}}続いた後、{{transitions}}を使用してシーン{{scenes}}に切り替わります"
|
||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="次の場合は切り替えないでください"
|
||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="ピントが合っています"
|
||||
|
||||
; Scene Sequence Tab
|
||||
# Scene Sequence Tab
|
||||
AdvSceneSwitcher.sceneSequenceTab.title="シーケンス"
|
||||
AdvSceneSwitcher.sceneSequenceTab.description="一連の自動シーン切り替えは、シーンスイッチャーの一時停止/停止、または手動で別のシーンに切り替えることでキャンセルできます"
|
||||
AdvSceneSwitcher.sceneSequenceTab.save="シーンシーケンスをファイルに保存"
|
||||
AdvSceneSwitcher.sceneSequenceTab.load="ファイルからシーンシーケンスをロード"
|
||||
AdvSceneSwitcher.sceneSequenceTab.saveTitle="シーンシーケンスをファイルに保存…"
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadTitle="シーンシーケンスを読み込むファイルを選択してください…"
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadFail="Advanced Scene Switcherで設定のインポートに失敗しました!"
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Scene Switcherの高度な設定がインポートされました!"
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadFail="高機能シーンスイッチャーの設定インポートに失敗しました!"
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="高機能シーンスイッチャーの設定がインポートされました!"
|
||||
AdvSceneSwitcher.sceneSequenceTab.fileType="テキストファイル(*.txt)"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="中断可能"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="このシーンシーケンスを中断する他の切り替え方法が許可されます"
|
||||
@@ -1997,7 +2110,7 @@ AdvSceneSwitcher.sceneSequenceTab.extendEdit="拡張シーケンス"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="{{delay}}後、{{transitions}}を使用して{{scenes}}に切り替えてください"
|
||||
AdvSceneSwitcher.sceneSequenceTab.help="このタブを使用すると、シーンが設定された期間アクティブだった場合に、別のシーンに自動的に切り替えることができます。\nたとえば、2 つのシーンを自動的に行き来できます。\n\nクリック強調表示されたプラス記号をクリックして続行します。"
|
||||
|
||||
; Audio Tab
|
||||
# Audio Tab
|
||||
AdvSceneSwitcher.audioTab.title="音声"
|
||||
AdvSceneSwitcher.audioTab.condition.above="以上"
|
||||
AdvSceneSwitcher.audioTab.condition.below="以下"
|
||||
@@ -2007,7 +2120,7 @@ AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="複数のエントリが
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallback="...{{duration}}秒間、{{transitions}}を使用して{{scenes}}に切り替えます"
|
||||
AdvSceneSwitcher.audioTab.help="このタブでは、ソースの音量に基づいてシーンを切り替えることができます。\nたとえば、マイクの音量が特定のしきい値に達した場合に、自動的に別のシーンに切り替えることができます。\n\n続行するにはプラス記号を強調表示します。"
|
||||
|
||||
; Video Tab
|
||||
# Video Tab
|
||||
AdvSceneSwitcher.videoTab.title="Video"
|
||||
AdvSceneSwitcher.videoTab.getScreenshot="選択したエントリのスクリーンショットを取得"
|
||||
AdvSceneSwitcher.videoTab.getScreenshotHelp="現在選択されているエントリーのビデオソースのスクリーンショットを取得し、自動的に対象画像として設定します"
|
||||
@@ -2020,31 +2133,6 @@ AdvSceneSwitcher.videoTab.ignoreInactiveSource="ソースが非アクティブ
|
||||
AdvSceneSwitcher.videoTab.entry="{{videoSources}}{{condition}}{{filePath}}{{browseButton}}が{{duration}}の間、{{transitions}}{{ignoreInactiveSource}}を使用して{{scenes}}に切り替わるとき"
|
||||
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>このタブでは、選択したソースの現在のビデオ出力に基づいてシーンを切り替えることができます。<br/>この機能のさらに優れた実装については、<a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a>を確認してください。<br/><br/> このタブでは、選択したソースの現在のビデオ出力に基づいてシーンを切り替えることができます。</p></body></html>"
|
||||
|
||||
; Network Tab
|
||||
AdvSceneSwitcher.networkTab.title="Network"
|
||||
AdvSceneSwitcher.networkTab.description="このタブを使用すると、別の OBS インスタンスのアクティブなシーンをリモートで制御できます。\nシーン名はすべての OBS インスタンスで正確に一致する必要があることに注意してください。"
|
||||
AdvSceneSwitcher.networkTab.warning="ローカルネットワーク外でサーバーを実行すると、第三者がアクティブシーンを読み取ることができます。"
|
||||
AdvSceneSwitcher.networkTab.server="サーバー起動(接続中の全クライアントにシーン切り替えメッセージを送信)"
|
||||
AdvSceneSwitcher.networkTab.server.port="港"
|
||||
AdvSceneSwitcher.networkTab.server.lockToIPv4="サーバーをIPv4のみにロックする"
|
||||
AdvSceneSwitcher.networkTab.server.sendSceneChange="場面転換時のメッセージ送信"
|
||||
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="自動シーン切り替えのメッセージのみ送信"
|
||||
AdvSceneSwitcher.networkTab.server.sendPreview="スタジオモード実行時のプレビューシーン変更メッセージ送信"
|
||||
AdvSceneSwitcher.networkTab.startFailed.message="WebSocket サーバーの起動に失敗しました。おそらく次の理由が考えられます:\n - TCP ポート %1 は現在、このシステム上の別の場所 (おそらく別のアプリケーション) で使用されている可能性があります。 WebSocket サーバー設定で別の TCP ポートを設定してみるか、このポートを使用している可能性のあるアプリケーションを停止してください。\n - エラー メッセージ: %2"
|
||||
; AdvSceneSwitcher.networkTab.server.status.currentStatus="Current status"
|
||||
AdvSceneSwitcher.networkTab.server.status.notRunning="動いていない"
|
||||
; AdvSceneSwitcher.networkTab.server.status.starting="Starting"
|
||||
; AdvSceneSwitcher.networkTab.server.status.running="Running"
|
||||
AdvSceneSwitcher.networkTab.server.restart="サーバーの再起動"
|
||||
AdvSceneSwitcher.networkTab.client="クライアント起動(シーン切り替えメッセージ受信)"
|
||||
AdvSceneSwitcher.networkTab.client.address="ホスト名またはIPアドレス"
|
||||
; AdvSceneSwitcher.networkTab.client.port="Port"
|
||||
; AdvSceneSwitcher.networkTab.client.status.currentStatus="Current status"
|
||||
AdvSceneSwitcher.networkTab.client.status.disconnected="切断されました"
|
||||
AdvSceneSwitcher.networkTab.client.status.connecting="接続中"
|
||||
AdvSceneSwitcher.networkTab.client.status.connected="接続した"
|
||||
AdvSceneSwitcher.networkTab.client.reconnect="強制再接続"
|
||||
|
||||
; Scene Group Tab
|
||||
AdvSceneSwitcher.sceneGroupTab.title="シーンのグループ化"
|
||||
AdvSceneSwitcher.sceneGroupTab.list="シーングループ集"
|
||||
@@ -2064,27 +2152,3 @@ AdvSceneSwitcher.sceneGroupTab.defaultname="シーングループ %1"
|
||||
AdvSceneSwitcher.sceneGroupTab.exists="シーングループまたはシーン名が既に存在します"
|
||||
AdvSceneSwitcher.sceneGroupTab.help="シーン グループは、通常のシーンと同様に対象として選択できます。\n\nシーン グループは、名前が示すように、複数のシーンの集合です。\nシーン グループは、割り当てられたシーンのリストを順に進みます。構成された設定に応じて、右側に表示されます。\n\nリスト内の次のシーンに進むようにシーン グループを構成できます:\nシーン グループが何度も選択された後\n一定時間経過後。\nまたはランダム。\n\nたとえば、シーンを含むシーングループ...\nシーン1\nシーン2 \nシーン 3 \n... 初めてターゲットとして選択されると、「シーン 1」がアクティブになります。\n2 回目には、「シーン 2」がアクティブになります。\n残りの回「シーン 3」が有効になります。\n\n下の強調表示されたプラス記号をクリックして、新しいシーン グループを追加します。"
|
||||
AdvSceneSwitcher.sceneGroupTab.scenes.help="左側で変更したいシーングループを選択します。\n\n上のシーンを選択し、下のプラス記号をクリックして、このシーングループに追加するシーンを選択します。\n\nシーンは次のとおりです。同じシーングループに複数回追加しました。"
|
||||
|
||||
; Scene Trigger Tab
|
||||
AdvSceneSwitcher.sceneTriggerTab.title="シーントリガー"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--トリガーを選択--"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="アクティブです"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="活動していません"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="から切り替えた"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--アクションを選択--"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="録画開始"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="録画一時停止"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="録画の一時停止を解除"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="録画停止"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="配信停止"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="配信開始"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="リプレイバッファ開始"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="リプレイバッファ停止"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="ソースをミュート"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="ソースのミュートを解除"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="シーンスイッチャーを起動します"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="シーンスイッチャーを停止する"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera="仮想カメラ起動"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="仮想カメラ停止"
|
||||
; AdvSceneSwitcher.sceneTriggerTab.entry="When{{scenes}}{{triggers}}{{actions}}{{audioSources}}after{{duration}}"
|
||||
AdvSceneSwitcher.sceneTriggerTab.help="このタブでは、録画や配信の停止など、シーンの変更時にアクションをトリガーできます。"
|
||||
2083
data/locale/pt-BR.ini
Normal file
2083
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="Сохранять положение и размер окна"
|
||||
@@ -307,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="Группы сцен"
|
||||
@@ -349,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"
|
||||
@@ -399,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"
|
||||
@@ -107,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}}"
|
||||
@@ -117,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"
|
||||
@@ -151,7 +149,6 @@ AdvSceneSwitcher.condition.stream.state.start="Yayın çalışıyor"
|
||||
AdvSceneSwitcher.condition.stream.state.stop="Yayın durdu"
|
||||
AdvSceneSwitcher.condition.stream.state.starting="Yayın başlıyor"
|
||||
AdvSceneSwitcher.condition.stream.state.stopping="Yayın duruyor"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
|
||||
AdvSceneSwitcher.condition.record="Kayıt"
|
||||
AdvSceneSwitcher.condition.record.state.start="Kayıt Çalışıyor"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Kayıt durakladı"
|
||||
@@ -226,10 +223,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"
|
||||
@@ -341,10 +335,10 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Aşağı indir"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveTop="En üste taşı"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Aşağıya taşı"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
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"
|
||||
@@ -362,7 +356,6 @@ AdvSceneSwitcher.action.random="Rastgele"
|
||||
AdvSceneSwitcher.action.random.entry="Aşağıdaki makrolardan herhangi birini rastgele çalıştırın (duraklatılmış makrolar yoksayılır)"
|
||||
AdvSceneSwitcher.action.systray="Sistem tepsisi bildirimi"
|
||||
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
|
||||
AdvSceneSwitcher.action.profile="Profil"
|
||||
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
|
||||
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"
|
||||
@@ -532,31 +525,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ı"
|
||||
@@ -577,30 +545,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"
|
||||
@@ -648,7 +592,7 @@ AdvSceneSwitcher.stopped="Eklenti durdu"
|
||||
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Gelişmiş Sahne Değiştirici ilk kez başlatılıyor gibi görünüyor.<br>Lütfen <a href=\"https://github.com/ adresine bir göz atın. WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> için kılavuzlar ve örnekler listesi.<br>Yapmayın. eklentinin <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:# sayfasında soru sormaktan çekinmeyin OBS forumlarında 268bd2;\">konu</span></a>!</p></body></html>"
|
||||
|
||||
AdvSceneSwitcher.unit.milliseconds="millisaniye"
|
||||
AdvSceneSwitcher.unit.secends="saniye"
|
||||
AdvSceneSwitcher.unit.seconds="saniye"
|
||||
AdvSceneSwitcher.unit.minutes="dakika"
|
||||
AdvSceneSwitcher.unit.hours="saat"
|
||||
AdvSceneSwitcher.duration.condition.none="Zaman kısıtlaması yok"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
13156
deps/exprtk/exprtk.hpp
vendored
13156
deps/exprtk/exprtk.hpp
vendored
File diff suppressed because it is too large
Load Diff
2
deps/libremidi
vendored
2
deps/libremidi
vendored
Submodule deps/libremidi updated: d6d6557b5b...73e6ea40de
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;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "backup.hpp"
|
||||
#include "curl-helper.hpp"
|
||||
#include "log-helper.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
@@ -14,15 +13,20 @@
|
||||
#include "tab-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "version.h"
|
||||
#include "websocket-api.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <obs-frontend-api.h>
|
||||
#include <QAction>
|
||||
#include <QDirIterator>
|
||||
#include <QLibrary>
|
||||
#include <QMainWindow>
|
||||
#include <QTextStream>
|
||||
#include <regex>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace advss {
|
||||
|
||||
AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr;
|
||||
@@ -85,18 +89,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();
|
||||
@@ -112,9 +130,7 @@ void AdvSceneSwitcher::LoadUI()
|
||||
SetupTimeTab();
|
||||
SetupAudioTab();
|
||||
SetupVideoTab();
|
||||
SetupNetworkTab();
|
||||
SetupSceneGroupTab();
|
||||
SetupTriggerTab();
|
||||
SetupMacroTab();
|
||||
SetupOtherTabs(ui->tabWidget);
|
||||
|
||||
@@ -241,8 +257,8 @@ 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");
|
||||
vblog(LOG_INFO,
|
||||
"detected busy loop - refusing to sleep less than 1ms");
|
||||
duration = std::chrono::milliseconds(10);
|
||||
}
|
||||
}
|
||||
@@ -295,7 +311,7 @@ void SwitcherData::Thread()
|
||||
}
|
||||
}
|
||||
|
||||
ResetForNextInterval();
|
||||
RunIntervalResetSteps();
|
||||
|
||||
if (match) {
|
||||
if (macroMatch) {
|
||||
@@ -345,14 +361,6 @@ void SwitcherData::SetPreconditions()
|
||||
InvalidateMacroTempVarValues();
|
||||
}
|
||||
|
||||
void SwitcherData::ResetForNextInterval()
|
||||
{
|
||||
// Plugin reset functions
|
||||
for (const auto &func : resetIntervalSteps) {
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger,
|
||||
bool &setPrevSceneAfterLinger,
|
||||
@@ -433,7 +441,7 @@ void AutoStartActionQueues();
|
||||
void SwitcherData::Start()
|
||||
{
|
||||
if (!(th && th->isRunning())) {
|
||||
ResetForNextInterval();
|
||||
RunIntervalResetSteps();
|
||||
ResetMacros();
|
||||
AutoStartActionQueues();
|
||||
|
||||
@@ -441,17 +449,7 @@ void SwitcherData::Start()
|
||||
th = new SwitcherThread();
|
||||
th->start((QThread::Priority)threadPriority);
|
||||
|
||||
// Will be overwritten quickly but might be useful
|
||||
writeToStatusFile("Advanced Scene Switcher running");
|
||||
}
|
||||
|
||||
if (networkConfig.ServerEnabled) {
|
||||
server.start(networkConfig.ServerPort,
|
||||
networkConfig.LockToIPv4);
|
||||
}
|
||||
|
||||
if (networkConfig.ClientEnabled) {
|
||||
client.connect(networkConfig.GetClientUri());
|
||||
RunStartSteps();
|
||||
}
|
||||
|
||||
if (showSystemTrayNotifications) {
|
||||
@@ -478,12 +476,9 @@ void SwitcherData::Stop()
|
||||
th->wait();
|
||||
delete th;
|
||||
th = nullptr;
|
||||
writeToStatusFile("Advanced Scene Switcher stopped");
|
||||
RunStopSteps();
|
||||
}
|
||||
|
||||
server.stop();
|
||||
client.disconnect();
|
||||
|
||||
if (showSystemTrayNotifications) {
|
||||
DisplayTrayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||
@@ -551,12 +546,7 @@ static void handleSceneChange()
|
||||
GetWeakSourceName(switcher->previousScene).c_str());
|
||||
}
|
||||
|
||||
switcher->checkTriggers();
|
||||
switcher->checkDefaultSceneTransitions();
|
||||
|
||||
if (switcher->networkConfig.ShouldSendFrontendSceneChange()) {
|
||||
switcher->server.sendMessage({ws.Get(), nullptr, 0});
|
||||
}
|
||||
}
|
||||
|
||||
static void setLiveTime()
|
||||
@@ -585,17 +575,6 @@ static void checkAutoStartStreaming()
|
||||
switcher->Start();
|
||||
}
|
||||
|
||||
static void handlePeviewSceneChange()
|
||||
{
|
||||
if (switcher->networkConfig.ShouldSendPrviewSceneChange()) {
|
||||
OBSSourceAutoRelease source =
|
||||
obs_frontend_get_current_preview_scene();
|
||||
OBSWeakSourceAutoRelease weak =
|
||||
obs_source_get_weak_source(source);
|
||||
switcher->server.sendMessage({weak.Get(), nullptr, 0}, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void handleTransitionEnd()
|
||||
{
|
||||
GetMacroTransitionCV().notify_all();
|
||||
@@ -673,9 +652,6 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
||||
handleSceneChange();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
|
||||
handlePeviewSceneChange();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
|
||||
setLiveTime();
|
||||
checkAutoStartRecording();
|
||||
@@ -708,7 +684,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());
|
||||
@@ -748,12 +724,7 @@ void OpenSettingsWindow()
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *GetSettingsWindow()
|
||||
{
|
||||
return SettingsWindowIsOpened() ? AdvSceneSwitcher::window : nullptr;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
|
||||
void AdvSceneSwitcher::HighlightMacroSettingsButton(bool enable)
|
||||
{
|
||||
static QObject *highlight = nullptr;
|
||||
if ((highlight && enable) || (!highlight && !enable)) {
|
||||
@@ -773,13 +744,13 @@ void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
|
||||
highlight = HighlightWidget(ui->macroSettings, Qt::green);
|
||||
}
|
||||
|
||||
void HighligthMacroSettingsButton(bool enable)
|
||||
void HighlightMacroSettingsButton(bool enable)
|
||||
{
|
||||
auto window = GetSettingsWindow();
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
static_cast<AdvSceneSwitcher *>(window)->HighligthMacroSettingsButton(
|
||||
static_cast<AdvSceneSwitcher *>(window)->HighlightMacroSettingsButton(
|
||||
enable);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -12,7 +13,6 @@ class MacroActionEdit;
|
||||
class MacroConditionEdit;
|
||||
class Duration;
|
||||
class SequenceWidget;
|
||||
enum class LogicType;
|
||||
struct SceneGroup;
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -53,6 +53,7 @@ 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);
|
||||
@@ -96,7 +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);
|
||||
void HighlightMacroSettingsButton(bool enable = true);
|
||||
|
||||
public slots:
|
||||
void on_macroAdd_clicked();
|
||||
@@ -173,7 +174,7 @@ public slots:
|
||||
void MacroConditionReorder(int to, int target);
|
||||
void AddMacroCondition(int idx);
|
||||
void AddMacroCondition(Macro *macro, int idx, const std::string &id,
|
||||
obs_data_t *data, LogicType logic);
|
||||
obs_data_t *data, Logic::Type logic);
|
||||
void RemoveMacroCondition(int idx);
|
||||
void MoveMacroConditionUp(int idx);
|
||||
void MoveMacroConditionDown(int idx);
|
||||
@@ -358,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();
|
||||
@@ -397,20 +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);
|
||||
void HighlightMacroSettingsButton(bool enable);
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -91,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) {
|
||||
@@ -139,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 *)
|
||||
@@ -205,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(
|
||||
@@ -223,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)
|
||||
@@ -237,6 +247,10 @@ void AdvSceneSwitcher::on_hideLegacyTabs_stateChanged(int state)
|
||||
ui->tabWidget->setTabVisible(idx, !state);
|
||||
}
|
||||
}
|
||||
|
||||
// Changing priority of legacy tabs will very likely not be necessary if
|
||||
// the legacy tabs are hidden
|
||||
ui->priorityBox->setVisible(!switcher->hideLegacyTabs);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetDeprecationWarnings()
|
||||
@@ -330,6 +344,10 @@ void AdvSceneSwitcher::on_importSettings_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
// We have to make sure to that no macro is currently being edited while
|
||||
// the new settings are loaded
|
||||
ui->macros->clearSelection();
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->LoadSettings(obj);
|
||||
switcher->lastImportPath = path.toStdString();
|
||||
@@ -445,8 +463,6 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
|
||||
loadTimeSwitches(obj);
|
||||
loadAudioSwitches(obj);
|
||||
loadVideoSwitches(obj);
|
||||
loadNetworkSettings(obj);
|
||||
loadSceneTriggers(obj);
|
||||
LoadGeneralSettings(obj);
|
||||
LoadHotkeys(obj);
|
||||
LoadUISettings(obj);
|
||||
@@ -481,8 +497,6 @@ void SwitcherData::SaveSettings(obs_data_t *obj)
|
||||
saveTimeSwitches(obj);
|
||||
saveAudioSwitches(obj);
|
||||
saveVideoSwitches(obj);
|
||||
saveNetworkSwitches(obj);
|
||||
saveSceneTriggers(obj);
|
||||
SaveGeneralSettings(obj);
|
||||
SaveHotkeys(obj);
|
||||
SaveUISettings(obj);
|
||||
@@ -505,6 +519,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;
|
||||
@@ -515,6 +530,7 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
|
||||
static_cast<int>(autoStartEvent));
|
||||
|
||||
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);
|
||||
@@ -550,6 +566,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");
|
||||
@@ -566,6 +587,25 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
|
||||
static_cast<AutoStart>(obs_data_get_int(obj, "autoStartEvent"));
|
||||
|
||||
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");
|
||||
@@ -653,7 +693,7 @@ void SwitcherData::CheckNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||
|
||||
void SwitcherData::checkSwitchCooldown(bool &match)
|
||||
{
|
||||
if (!match) {
|
||||
if (!match || !enableCooldown) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -793,6 +833,28 @@ static void setupGeneralTabInactiveWarning(QTabWidget *tabs)
|
||||
inactiveTimer->start();
|
||||
}
|
||||
|
||||
void advss::AdvSceneSwitcher::SetCheckIntervalTooLowVisibility() const
|
||||
{
|
||||
auto macro = GetMacroWithInvalidConditionInterval();
|
||||
if (!macro) {
|
||||
ui->checkIntervalTooLowWarning->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString labelTextFormat(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.status.checkIntervalTooLow"));
|
||||
const QString labelTooltipFormat(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.status.checkIntervalTooLow.tooltip"));
|
||||
const QString name = QString::fromStdString(macro->Name());
|
||||
const QString duration = QString::fromStdString(
|
||||
macro->GetCustomConditionCheckInterval().ToString());
|
||||
|
||||
ui->checkIntervalTooLowWarning->setText(labelTextFormat.arg(name));
|
||||
ui->checkIntervalTooLowWarning->setToolTip(
|
||||
labelTooltipFormat.arg(name).arg(duration).arg(name));
|
||||
ui->checkIntervalTooLowWarning->show();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetupGeneralTab()
|
||||
{
|
||||
PopulateSceneSelection(ui->noMatchSwitchScene, false);
|
||||
@@ -814,14 +876,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"));
|
||||
|
||||
@@ -170,8 +170,10 @@ void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
|
||||
item->setData(Qt::UserRole, text);
|
||||
ui->sceneGroups->setCurrentItem(item);
|
||||
|
||||
addPulse->deleteLater();
|
||||
addPulse = nullptr;
|
||||
if (addPulse) {
|
||||
addPulse->deleteLater();
|
||||
addPulse = nullptr;
|
||||
}
|
||||
ui->sceneGroupHelp->setVisible(false);
|
||||
|
||||
emit SceneGroupAdded(QString::fromStdString(name));
|
||||
|
||||
@@ -1,581 +0,0 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "selection-helpers.hpp"
|
||||
#include "source-helpers.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <obs-frontend-api.h>
|
||||
#include <thread>
|
||||
|
||||
namespace advss {
|
||||
|
||||
bool SceneTrigger::pause = false;
|
||||
static QObject *addPulse = nullptr;
|
||||
|
||||
void AdvSceneSwitcher::on_triggerAdd_clicked()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->sceneTriggers.emplace_back();
|
||||
|
||||
listAddClicked(ui->sceneTriggers,
|
||||
new SceneTriggerWidget(this,
|
||||
&switcher->sceneTriggers.back()),
|
||||
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 = HighlightWidget(ui->triggerAdd,
|
||||
QColor(Qt::green));
|
||||
}
|
||||
ui->triggerHelp->setVisible(true);
|
||||
} else {
|
||||
ui->triggerHelp->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTrigger::save(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_string(obj, "scene", GetWeakSourceName(scene).c_str());
|
||||
obs_data_set_int(obj, "triggerType", static_cast<int>(triggerType));
|
||||
obs_data_set_int(obj, "triggerAction", static_cast<int>(triggerAction));
|
||||
duration.Save(obj, "duration");
|
||||
obs_data_set_string(obj, "audioSource",
|
||||
GetWeakSourceName(audioSource).c_str());
|
||||
}
|
||||
|
||||
void SceneTrigger::load(obs_data_t *obj)
|
||||
{
|
||||
const char *sceneName = obs_data_get_string(obj, "scene");
|
||||
scene = GetWeakSourceByName(sceneName);
|
||||
|
||||
triggerType = static_cast<sceneTriggerType>(
|
||||
obs_data_get_int(obj, "triggerType"));
|
||||
triggerAction = static_cast<sceneTriggerAction>(
|
||||
obs_data_get_int(obj, "triggerAction"));
|
||||
duration.Load(obj, "duration");
|
||||
|
||||
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
||||
audioSource = GetWeakSourceByName(audioSourceName);
|
||||
}
|
||||
|
||||
static inline void populateTriggers(QComboBox *list)
|
||||
{
|
||||
AddSelectionEntry(
|
||||
list,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none"));
|
||||
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave"));
|
||||
}
|
||||
|
||||
inline void populateActions(QComboBox *list)
|
||||
{
|
||||
AddSelectionEntry(
|
||||
list,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none"));
|
||||
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera"));
|
||||
}
|
||||
|
||||
SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
|
||||
: SwitchWidget(parent, s, false, false)
|
||||
{
|
||||
triggers = new QComboBox();
|
||||
actions = new QComboBox();
|
||||
duration = new DurationSelection();
|
||||
audioSources = new QComboBox();
|
||||
|
||||
QWidget::connect(triggers, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(TriggerTypeChanged(int)));
|
||||
QWidget::connect(actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(TriggerActionChanged(int)));
|
||||
QWidget::connect(duration, SIGNAL(DurationChanged(const Duration &)),
|
||||
this, SLOT(DurationChanged(const Duration &)));
|
||||
QWidget::connect(audioSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(AudioSourceChanged(const QString &)));
|
||||
|
||||
populateTriggers(triggers);
|
||||
populateActions(actions);
|
||||
PopulateAudioSelection(audioSources);
|
||||
|
||||
if (s) {
|
||||
triggers->setCurrentIndex(static_cast<int>(s->triggerType));
|
||||
actions->setCurrentIndex(static_cast<int>(s->triggerAction));
|
||||
duration->SetDuration(s->duration);
|
||||
|
||||
audioSources->setCurrentText(
|
||||
GetWeakSourceName(s->audioSource).c_str());
|
||||
|
||||
if (isAudioAction(s->triggerAction)) {
|
||||
audioSources->show();
|
||||
} else {
|
||||
audioSources->hide();
|
||||
}
|
||||
}
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{triggers}}", triggers},
|
||||
{"{{actions}}", actions},
|
||||
{"{{audioSources}}", audioSources},
|
||||
{"{{duration}}", duration},
|
||||
{"{{scenes}}", scenes}};
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.sceneTriggerTab.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
switchData = s;
|
||||
|
||||
loading = false;
|
||||
}
|
||||
|
||||
SceneTrigger *SceneTriggerWidget::getSwitchData()
|
||||
{
|
||||
return switchData;
|
||||
}
|
||||
|
||||
void SceneTriggerWidget::setSwitchData(SceneTrigger *s)
|
||||
{
|
||||
switchData = s;
|
||||
}
|
||||
|
||||
void SceneTriggerWidget::swapSwitchData(SceneTriggerWidget *s1,
|
||||
SceneTriggerWidget *s2)
|
||||
{
|
||||
SwitchWidget::swapSwitchData(s1, s2);
|
||||
|
||||
SceneTrigger *t = s1->getSwitchData();
|
||||
s1->setSwitchData(s2->getSwitchData());
|
||||
s2->setSwitchData(t);
|
||||
}
|
||||
|
||||
void SceneTriggerWidget::TriggerTypeChanged(int index)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->triggerType = static_cast<sceneTriggerType>(index);
|
||||
}
|
||||
|
||||
void SceneTriggerWidget::TriggerActionChanged(int index)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->triggerAction =
|
||||
static_cast<sceneTriggerAction>(index);
|
||||
}
|
||||
|
||||
if (isAudioAction(switchData->triggerAction)) {
|
||||
audioSources->show();
|
||||
} else {
|
||||
audioSources->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTriggerWidget::DurationChanged(const Duration &duration)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->duration = duration;
|
||||
}
|
||||
|
||||
void SceneTriggerWidget::AudioSourceChanged(const QString &text)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->audioSource = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,29 @@ bool FileSwitch::pause = false;
|
||||
static QObject *addPulse = nullptr;
|
||||
static std::hash<std::string> strHash;
|
||||
|
||||
static void writeToStatusFile(const QString &msg)
|
||||
{
|
||||
if (!GetSwitcher() || !GetSwitcher()->fileIO.writeEnabled ||
|
||||
GetSwitcher()->fileIO.writePath.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(QString::fromStdString(GetSwitcher()->fileIO.writePath));
|
||||
if (file.open(QIODevice::ReadWrite)) {
|
||||
QTextStream stream(&file);
|
||||
stream << msg << Qt::endl;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
static bool _ = []() {
|
||||
AddStartStep(
|
||||
[]() { writeToStatusFile("Advanced Scene Switcher running"); });
|
||||
AddStopStep(
|
||||
[]() { writeToStatusFile("Advanced Scene Switcher stopped"); });
|
||||
return true;
|
||||
}();
|
||||
|
||||
void AdvSceneSwitcher::on_browseButton_clicked()
|
||||
{
|
||||
QString path = QFileDialog::getOpenFileName(
|
||||
@@ -113,24 +136,6 @@ void SwitcherData::writeSceneInfoToFile()
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::writeToStatusFile(const QString &msg)
|
||||
{
|
||||
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(QString::fromStdString(fileIO.writePath));
|
||||
if (file.open(QIODevice::ReadWrite)) {
|
||||
QTextStream stream(&file);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
stream << msg << "\n";
|
||||
#else
|
||||
stream << msg << Qt::endl;
|
||||
#endif
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool SwitcherData::checkSwitchInfoFromFile(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
@@ -291,7 +296,7 @@ void AdvSceneSwitcher::on_fileAdd_clicked()
|
||||
listAddClicked(ui->fileSwitches,
|
||||
new FileSwitchWidget(this,
|
||||
&switcher->fileSwitches.back()),
|
||||
ui->fileAdd, addPulse);
|
||||
&addPulse);
|
||||
|
||||
ui->fileHelp->setVisible(false);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,717 +0,0 @@
|
||||
/*
|
||||
Most of this code is based on https://github.com/Palakis/obs-websocket
|
||||
*/
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "scene-switch-helpers.hpp"
|
||||
#include "source-helpers.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
|
||||
#include <obs-frontend-api.h>
|
||||
#include <QMessageBox>
|
||||
#include <QTime>
|
||||
#include <QMainWindow>
|
||||
|
||||
namespace advss {
|
||||
|
||||
#define PARAM_SERVER_ENABLE "ServerEnabled"
|
||||
#define PARAM_SERVER_PORT "ServerPort"
|
||||
#define PARAM_LOCKTOIPV4 "LockToIPv4"
|
||||
|
||||
#define PARAM_CLIENT_ENABLE "ClientEnabled"
|
||||
#define PARAM_CLIENT_PORT "ClientPort"
|
||||
#define PARAM_ADDRESS "Address"
|
||||
#define PARAM_CLIENT_SEND_SCENE_CHANGE "SendSceneChange"
|
||||
#define PARAM_CLIENT_SEND_SCENE_CHANGE_ALL "SendSceneChangeAll"
|
||||
#define PARAM_CLIENT_SENDPREVIEW "SendPreview"
|
||||
|
||||
#define RECONNECT_DELAY 10
|
||||
|
||||
#define SCENE_ENTRY "scene"
|
||||
#define TRANSITION_ENTRY "transition"
|
||||
#define TRANSITION_DURATION "duration"
|
||||
#define SET_PREVIEW "preview"
|
||||
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
using websocketpp::lib::bind;
|
||||
|
||||
NetworkConfig::NetworkConfig()
|
||||
: ServerEnabled(false),
|
||||
ServerPort(55555),
|
||||
LockToIPv4(false),
|
||||
ClientEnabled(false),
|
||||
Address(""),
|
||||
ClientPort(55555),
|
||||
SendSceneChange(true),
|
||||
SendSceneChangeAll(true),
|
||||
SendPreview(true)
|
||||
{
|
||||
}
|
||||
|
||||
void NetworkConfig::Load(obs_data_t *obj)
|
||||
{
|
||||
SetDefaults(obj);
|
||||
|
||||
ServerEnabled = obs_data_get_bool(obj, PARAM_SERVER_ENABLE);
|
||||
ServerPort = obs_data_get_int(obj, PARAM_SERVER_PORT);
|
||||
LockToIPv4 = obs_data_get_bool(obj, PARAM_LOCKTOIPV4);
|
||||
|
||||
ClientEnabled = obs_data_get_bool(obj, PARAM_CLIENT_ENABLE);
|
||||
Address = obs_data_get_string(obj, PARAM_ADDRESS);
|
||||
ClientPort = obs_data_get_int(obj, PARAM_CLIENT_PORT);
|
||||
SendSceneChange =
|
||||
obs_data_get_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE);
|
||||
SendSceneChangeAll =
|
||||
obs_data_get_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL);
|
||||
SendPreview = obs_data_get_bool(obj, PARAM_CLIENT_SENDPREVIEW);
|
||||
}
|
||||
|
||||
void NetworkConfig::Save(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_bool(obj, PARAM_SERVER_ENABLE, ServerEnabled);
|
||||
obs_data_set_int(obj, PARAM_SERVER_PORT, ServerPort);
|
||||
obs_data_set_bool(obj, PARAM_LOCKTOIPV4, LockToIPv4);
|
||||
|
||||
obs_data_set_bool(obj, PARAM_CLIENT_ENABLE, ClientEnabled);
|
||||
obs_data_set_string(obj, PARAM_ADDRESS, Address.c_str());
|
||||
obs_data_set_int(obj, PARAM_CLIENT_PORT, ClientPort);
|
||||
obs_data_set_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE, SendSceneChange);
|
||||
obs_data_set_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL,
|
||||
SendSceneChangeAll);
|
||||
obs_data_set_bool(obj, PARAM_CLIENT_SENDPREVIEW, SendPreview);
|
||||
}
|
||||
|
||||
void NetworkConfig::SetDefaults(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_default_bool(obj, PARAM_SERVER_ENABLE, false);
|
||||
obs_data_set_default_int(obj, PARAM_SERVER_PORT, ServerPort);
|
||||
obs_data_set_default_bool(obj, PARAM_LOCKTOIPV4, LockToIPv4);
|
||||
|
||||
obs_data_set_default_bool(obj, PARAM_CLIENT_ENABLE, false);
|
||||
obs_data_set_default_string(obj, PARAM_ADDRESS, Address.c_str());
|
||||
obs_data_set_default_int(obj, PARAM_CLIENT_PORT, ClientPort);
|
||||
obs_data_set_default_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE,
|
||||
SendSceneChange);
|
||||
obs_data_set_default_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL,
|
||||
SendSceneChangeAll);
|
||||
obs_data_set_default_bool(obj, PARAM_CLIENT_SENDPREVIEW, SendPreview);
|
||||
}
|
||||
|
||||
std::string NetworkConfig::GetClientUri()
|
||||
{
|
||||
return "ws://" + Address + ":" + std::to_string(ClientPort);
|
||||
}
|
||||
|
||||
bool NetworkConfig::ShouldSendSceneChange()
|
||||
{
|
||||
return ServerEnabled && SendSceneChange;
|
||||
}
|
||||
|
||||
bool NetworkConfig::ShouldSendFrontendSceneChange()
|
||||
{
|
||||
return ShouldSendSceneChange() && SendSceneChangeAll;
|
||||
}
|
||||
|
||||
bool NetworkConfig::ShouldSendPrviewSceneChange()
|
||||
{
|
||||
return ServerEnabled && SendPreview;
|
||||
}
|
||||
|
||||
WSServer::WSServer() : QObject(nullptr), _connections(), _clMutex()
|
||||
{
|
||||
_server.get_alog().clear_channels(
|
||||
websocketpp::log::alevel::frame_header |
|
||||
websocketpp::log::alevel::frame_payload |
|
||||
websocketpp::log::alevel::control);
|
||||
_server.init_asio();
|
||||
#ifndef _WIN32
|
||||
_server.set_reuse_addr(true);
|
||||
#endif
|
||||
|
||||
_server.set_open_handler(bind(&WSServer::onOpen, this, _1));
|
||||
_server.set_close_handler(bind(&WSServer::onClose, this, _1));
|
||||
_server.set_message_handler(bind(&WSServer::onMessage, this, _1, _2));
|
||||
}
|
||||
|
||||
WSServer::~WSServer()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void WSServer::start(quint16 port, bool lockToIPv4)
|
||||
{
|
||||
if (_server.is_listening() &&
|
||||
(port == _serverPort && _lockToIPv4 == lockToIPv4)) {
|
||||
blog(LOG_INFO,
|
||||
"WSServer::start: server already on this port and protocol mode. no restart needed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_server.is_listening()) {
|
||||
stop();
|
||||
}
|
||||
|
||||
_server.reset();
|
||||
|
||||
_serverPort = port;
|
||||
_lockToIPv4 = lockToIPv4;
|
||||
|
||||
websocketpp::lib::error_code errorCode;
|
||||
if (lockToIPv4) {
|
||||
blog(LOG_INFO, "WSServer::start: Locked to IPv4 bindings");
|
||||
_server.listen(websocketpp::lib::asio::ip::tcp::v4(),
|
||||
_serverPort, errorCode);
|
||||
} else {
|
||||
blog(LOG_INFO, "WSServer::start: Not locked to IPv4 bindings");
|
||||
_server.listen(_serverPort, errorCode);
|
||||
}
|
||||
|
||||
if (errorCode) {
|
||||
std::string errorCodeMessage = errorCode.message();
|
||||
blog(LOG_INFO, "server: listen failed: %s",
|
||||
errorCodeMessage.c_str());
|
||||
|
||||
QString errorTitle =
|
||||
obs_module_text("AdvSceneSwitcher.windowTitle");
|
||||
QString errorMessage =
|
||||
QString(obs_module_text(
|
||||
"AdvSceneSwitcher.networkTab.startFailed.message"))
|
||||
.arg(_serverPort)
|
||||
.arg(errorCodeMessage.c_str());
|
||||
|
||||
QMainWindow *mainWindow = reinterpret_cast<QMainWindow *>(
|
||||
obs_frontend_get_main_window());
|
||||
QMessageBox::warning(mainWindow, errorTitle, errorMessage);
|
||||
|
||||
return;
|
||||
}
|
||||
switcher->serverStatus = ServerStatus::STARTING;
|
||||
|
||||
_server.start_accept();
|
||||
|
||||
_threadPool.start(Compatability::CreateFunctionRunnable([=]() {
|
||||
blog(LOG_INFO, "WSServer::start: io thread started");
|
||||
_server.run();
|
||||
blog(LOG_INFO, "WSServer::start: io thread exited");
|
||||
}));
|
||||
|
||||
switcher->serverStatus = ServerStatus::RUNNING;
|
||||
blog(LOG_INFO,
|
||||
"WSServer::start: server started successfully on port %d",
|
||||
_serverPort);
|
||||
}
|
||||
|
||||
void WSServer::stop()
|
||||
{
|
||||
if (!_server.is_listening()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_server.stop_listening();
|
||||
for (connection_hdl hdl : _connections) {
|
||||
websocketpp::lib::error_code ec;
|
||||
_server.close(hdl, websocketpp::close::status::going_away,
|
||||
"Server stopping", ec);
|
||||
}
|
||||
|
||||
_threadPool.waitForDone();
|
||||
|
||||
while (_connections.size() > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
switcher->serverStatus = ServerStatus::NOT_RUNNING;
|
||||
blog(LOG_INFO, "server stopped successfully");
|
||||
}
|
||||
|
||||
void WSServer::sendMessage(SceneSwitchInfo sceneSwitch, bool preview)
|
||||
{
|
||||
if (!sceneSwitch.scene) {
|
||||
return;
|
||||
}
|
||||
|
||||
OBSData data = obs_data_create();
|
||||
obs_data_set_string(data, SCENE_ENTRY,
|
||||
GetWeakSourceName(sceneSwitch.scene).c_str());
|
||||
obs_data_set_string(data, TRANSITION_ENTRY,
|
||||
GetWeakSourceName(sceneSwitch.transition).c_str());
|
||||
obs_data_set_int(data, TRANSITION_DURATION, sceneSwitch.duration);
|
||||
obs_data_set_bool(data, SET_PREVIEW, preview);
|
||||
std::string message = obs_data_get_json(data);
|
||||
obs_data_release(data);
|
||||
|
||||
for (connection_hdl hdl : _connections) {
|
||||
websocketpp::lib::error_code ec;
|
||||
_server.send(hdl, message, websocketpp::frame::opcode::text,
|
||||
ec);
|
||||
if (ec) {
|
||||
std::string errorCodeMessage = ec.message();
|
||||
blog(LOG_INFO, "server: send failed: %s",
|
||||
errorCodeMessage.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (VerboseLoggingEnabled()) {
|
||||
blog(LOG_INFO, "server sent message:\n%s", message.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void WSServer::onOpen(connection_hdl hdl)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(_clMutex);
|
||||
_connections.insert(hdl);
|
||||
}
|
||||
|
||||
QString clientIp = getRemoteEndpoint(hdl);
|
||||
blog(LOG_INFO, "new client connection from %s",
|
||||
clientIp.toUtf8().constData());
|
||||
}
|
||||
|
||||
std::string processMessage(std::string payload)
|
||||
{
|
||||
auto config = switcher->networkConfig;
|
||||
std::string msgContainer(payload);
|
||||
const char *msg = msgContainer.c_str();
|
||||
|
||||
OBSData data = obs_data_create_from_json(msg);
|
||||
if (!data) {
|
||||
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
|
||||
return "invalid JSON payload";
|
||||
}
|
||||
|
||||
if (!obs_data_has_user_value(data, SCENE_ENTRY) ||
|
||||
!obs_data_has_user_value(data, TRANSITION_ENTRY) ||
|
||||
!obs_data_has_user_value(data, TRANSITION_DURATION) ||
|
||||
!obs_data_has_user_value(data, SET_PREVIEW)) {
|
||||
return "missing request parameters";
|
||||
}
|
||||
|
||||
std::string sceneName = obs_data_get_string(data, SCENE_ENTRY);
|
||||
std::string transitionName =
|
||||
obs_data_get_string(data, TRANSITION_ENTRY);
|
||||
int duration = obs_data_get_int(data, TRANSITION_DURATION);
|
||||
bool preview = obs_data_get_bool(data, SET_PREVIEW);
|
||||
|
||||
obs_data_release(data);
|
||||
|
||||
auto scene = GetWeakSourceByName(sceneName.c_str());
|
||||
if (!scene) {
|
||||
return "ignoring request - unknown scene '" + sceneName + "'";
|
||||
}
|
||||
|
||||
std::string ret = "message ok";
|
||||
|
||||
auto transition = GetWeakTransitionByName(transitionName.c_str());
|
||||
if (VerboseLoggingEnabled() && !transition) {
|
||||
ret += " - ignoring invalid transition: '" + transitionName +
|
||||
"'";
|
||||
}
|
||||
if (preview) {
|
||||
SwitchPreviewScene(scene);
|
||||
} else {
|
||||
SwitchScene({scene, transition, duration});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WSServer::onMessage(connection_hdl, server::message_ptr message)
|
||||
{
|
||||
auto opcode = message->get_opcode();
|
||||
if (opcode != websocketpp::frame::opcode::text) {
|
||||
return;
|
||||
}
|
||||
|
||||
_threadPool.start(Compatability::CreateFunctionRunnable([=]() {
|
||||
if (message->get_payload() != "message ok") {
|
||||
blog(LOG_WARNING, "received response: %s",
|
||||
message->get_payload().c_str());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void WSServer::onClose(connection_hdl hdl)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(_clMutex);
|
||||
_connections.erase(hdl);
|
||||
}
|
||||
|
||||
auto conn = _server.get_con_from_hdl(hdl);
|
||||
auto localCloseCode = conn->get_local_close_code();
|
||||
|
||||
if (localCloseCode != websocketpp::close::status::going_away) {
|
||||
QString clientIp = getRemoteEndpoint(hdl);
|
||||
blog(LOG_INFO, "client %s disconnected",
|
||||
clientIp.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
QString WSServer::getRemoteEndpoint(connection_hdl hdl)
|
||||
{
|
||||
auto conn = _server.get_con_from_hdl(hdl);
|
||||
return QString::fromStdString(conn->get_remote_endpoint());
|
||||
}
|
||||
|
||||
WSClient::WSClient() : QObject(nullptr)
|
||||
{
|
||||
_client.get_alog().clear_channels(
|
||||
websocketpp::log::alevel::frame_header |
|
||||
websocketpp::log::alevel::frame_payload |
|
||||
websocketpp::log::alevel::control);
|
||||
_client.init_asio();
|
||||
#ifndef _WIN32
|
||||
_client.set_reuse_addr(true);
|
||||
#endif
|
||||
|
||||
_client.set_open_handler(bind(&WSClient::onOpen, this, _1));
|
||||
_client.set_fail_handler(bind(&WSClient::onFail, this, _1));
|
||||
_client.set_message_handler(bind(&WSClient::onMessage, this, _1, _2));
|
||||
_client.set_close_handler(bind(&WSClient::onClose, this, _1));
|
||||
}
|
||||
|
||||
WSClient::~WSClient()
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
void WSClient::connectThread()
|
||||
{
|
||||
while (_retry) {
|
||||
_client.reset();
|
||||
switcher->clientStatus = ClientStatus::CONNECTING;
|
||||
// Create a connection to the given URI and queue it for connection once
|
||||
// the event loop starts
|
||||
websocketpp::lib::error_code ec;
|
||||
client::connection_ptr con = _client.get_connection(_uri, ec);
|
||||
if (ec) {
|
||||
_failMsg = ec.message();
|
||||
blog(LOG_INFO, "client: connect failed: %s",
|
||||
_failMsg.c_str());
|
||||
switcher->clientStatus = ClientStatus::FAIL;
|
||||
} else {
|
||||
_client.connect(con);
|
||||
_connection = connection_hdl(con);
|
||||
|
||||
// Start the ASIO io_service run loop
|
||||
blog(LOG_INFO, "WSClient::connect: io thread started");
|
||||
_connected = true;
|
||||
_client.run();
|
||||
_connected = false;
|
||||
blog(LOG_INFO, "WSClient::connect: io thread exited");
|
||||
}
|
||||
|
||||
if (_retry) {
|
||||
std::unique_lock<std::mutex> lck(_waitMtx);
|
||||
blog(LOG_INFO,
|
||||
"trying to reconnect to %s in %d seconds.",
|
||||
_uri.c_str(), RECONNECT_DELAY);
|
||||
_cv.wait_for(lck,
|
||||
std::chrono::seconds(RECONNECT_DELAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WSClient::connect(std::string uri)
|
||||
{
|
||||
disconnect();
|
||||
_uri = uri;
|
||||
_retry = true;
|
||||
|
||||
_thread = std::thread(&WSClient::connectThread, this);
|
||||
|
||||
switcher->clientStatus = ClientStatus::DISCONNECTED;
|
||||
blog(LOG_INFO, "WSClient::connect: exited");
|
||||
}
|
||||
|
||||
void WSClient::disconnect()
|
||||
{
|
||||
_retry = false;
|
||||
websocketpp::lib::error_code ec;
|
||||
_client.close(_connection, websocketpp::close::status::normal,
|
||||
"Client stopping", ec);
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> waitLck(_waitMtx);
|
||||
blog(LOG_INFO, "trying to reconnect to %s in %d seconds.",
|
||||
_uri.c_str(), RECONNECT_DELAY);
|
||||
_cv.notify_all();
|
||||
}
|
||||
|
||||
while (_connected) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
_client.close(_connection, websocketpp::close::status::normal,
|
||||
"Client stopping", ec);
|
||||
}
|
||||
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void WSClient::onOpen(connection_hdl)
|
||||
{
|
||||
blog(LOG_INFO, "connection to %s opened", _uri.c_str());
|
||||
switcher->clientStatus = ClientStatus::CONNECTED;
|
||||
}
|
||||
|
||||
void WSClient::onFail(connection_hdl)
|
||||
{
|
||||
blog(LOG_INFO, "connection to %s failed", _uri.c_str());
|
||||
}
|
||||
|
||||
void WSClient::onMessage(connection_hdl hdl, client::message_ptr message)
|
||||
{
|
||||
auto opcode = message->get_opcode();
|
||||
if (opcode != websocketpp::frame::opcode::text) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string payload = message->get_payload();
|
||||
std::string response = processMessage(payload);
|
||||
websocketpp::lib::error_code errorCode;
|
||||
_client.send(hdl, response, websocketpp::frame::opcode::text,
|
||||
errorCode);
|
||||
|
||||
if (errorCode) {
|
||||
std::string errorCodeMessage = errorCode.message();
|
||||
blog(LOG_INFO, "client(response): send failed: %s",
|
||||
errorCodeMessage.c_str());
|
||||
}
|
||||
|
||||
if (VerboseLoggingEnabled()) {
|
||||
blog(LOG_INFO, "client sent message:\n%s", response.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void WSClient::onClose(connection_hdl)
|
||||
{
|
||||
blog(LOG_INFO, "client-connection to %s closed.", _uri.c_str());
|
||||
switcher->clientStatus = ClientStatus::DISCONNECTED;
|
||||
}
|
||||
|
||||
void SwitcherData::loadNetworkSettings(obs_data_t *obj)
|
||||
{
|
||||
networkConfig.Load(obj);
|
||||
}
|
||||
|
||||
void SwitcherData::saveNetworkSwitches(obs_data_t *obj)
|
||||
{
|
||||
networkConfig.Save(obj);
|
||||
if (!networkConfig.ServerEnabled) {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetupNetworkTab()
|
||||
{
|
||||
ui->serverSettings->setChecked(switcher->networkConfig.ServerEnabled);
|
||||
ui->serverPort->setValue(switcher->networkConfig.ServerPort);
|
||||
ui->lockToIPv4->setChecked(switcher->networkConfig.LockToIPv4);
|
||||
|
||||
ui->clientSettings->setChecked(switcher->networkConfig.ClientEnabled);
|
||||
ui->clientHostname->setText(switcher->networkConfig.Address.c_str());
|
||||
ui->clientPort->setValue(switcher->networkConfig.ClientPort);
|
||||
ui->sendSceneChange->setChecked(
|
||||
switcher->networkConfig.SendSceneChange);
|
||||
ui->restrictSend->setChecked(
|
||||
!switcher->networkConfig.SendSceneChangeAll);
|
||||
ui->sendPreview->setChecked(switcher->networkConfig.SendPreview);
|
||||
ui->restrictSend->setDisabled(!switcher->networkConfig.SendSceneChange);
|
||||
|
||||
QTimer *statusTimer = new QTimer(this);
|
||||
connect(statusTimer, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateClientStatus()));
|
||||
connect(statusTimer, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateServerStatus()));
|
||||
statusTimer->start(500);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_serverSettings_toggled(bool on)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->networkConfig.ServerEnabled = on;
|
||||
if (on) {
|
||||
switcher->server.start(switcher->networkConfig.ServerPort,
|
||||
switcher->networkConfig.LockToIPv4);
|
||||
} else {
|
||||
switcher->server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_serverPort_valueChanged(int value)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->networkConfig.ServerPort = value;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_lockToIPv4_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->networkConfig.LockToIPv4 = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_serverRestart_clicked()
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->server.start(switcher->networkConfig.ServerPort,
|
||||
switcher->networkConfig.LockToIPv4);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::UpdateServerStatus()
|
||||
{
|
||||
switch (switcher->serverStatus) {
|
||||
case ServerStatus::NOT_RUNNING:
|
||||
ui->serverStatus->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.networkTab.server.status.notRunning"));
|
||||
break;
|
||||
case ServerStatus::STARTING:
|
||||
ui->serverStatus->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.networkTab.server.status.starting"));
|
||||
break;
|
||||
case ServerStatus::RUNNING:
|
||||
ui->serverStatus->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.networkTab.server.status.running"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_clientSettings_toggled(bool on)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->networkConfig.ClientEnabled = on;
|
||||
|
||||
if (on) {
|
||||
switcher->client.connect(
|
||||
switcher->networkConfig.GetClientUri());
|
||||
} else {
|
||||
switcher->client.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_clientHostname_textChanged(const QString &text)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->networkConfig.Address = text.toUtf8().constData();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_clientPort_valueChanged(int value)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->networkConfig.ClientPort = value;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_sendSceneChange_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->networkConfig.SendSceneChange = state;
|
||||
ui->restrictSend->setDisabled(!state);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_restrictSend_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->networkConfig.SendSceneChangeAll = !state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_sendPreview_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->networkConfig.SendPreview = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_clientReconnect_clicked()
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->client.connect(switcher->networkConfig.GetClientUri());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::UpdateClientStatus()
|
||||
{
|
||||
switch (switcher->clientStatus) {
|
||||
case ClientStatus::DISCONNECTED:
|
||||
ui->clientStatus->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.networkTab.client.status.disconnected"));
|
||||
break;
|
||||
case ClientStatus::CONNECTING:
|
||||
ui->clientStatus->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.networkTab.client.status.connecting"));
|
||||
break;
|
||||
case ClientStatus::CONNECTED:
|
||||
ui->clientStatus->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.networkTab.client.status.connected"));
|
||||
break;
|
||||
case ClientStatus::FAIL:
|
||||
ui->clientStatus->setText(QString("Error: ") +
|
||||
switcher->client.getFail().c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Compatability::StdFunctionRunnable::run()
|
||||
{
|
||||
cb();
|
||||
}
|
||||
|
||||
QRunnable *Compatability::CreateFunctionRunnable(std::function<void()> func)
|
||||
{
|
||||
return new Compatability::StdFunctionRunnable(std::move(func));
|
||||
}
|
||||
|
||||
Compatability::StdFunctionRunnable::StdFunctionRunnable(
|
||||
std::function<void()> func)
|
||||
: cb(std::move(func))
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Most of this code is based on https://github.com/Palakis/obs-websocket
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QVariantHash>
|
||||
#include <QtCore/QThreadPool>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <QRunnable>
|
||||
|
||||
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
#include <websocketpp/server.hpp>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
||||
namespace advss {
|
||||
|
||||
using websocketpp::connection_hdl;
|
||||
typedef websocketpp::server<websocketpp::config::asio> server;
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
|
||||
struct SceneSwitchInfo;
|
||||
|
||||
class NetworkConfig {
|
||||
public:
|
||||
NetworkConfig();
|
||||
void Load(obs_data_t *obj);
|
||||
void Save(obs_data_t *obj);
|
||||
void SetDefaults(obs_data_t *obj);
|
||||
|
||||
std::string GetClientUri();
|
||||
|
||||
bool ShouldSendSceneChange();
|
||||
bool ShouldSendFrontendSceneChange();
|
||||
bool ShouldSendPrviewSceneChange();
|
||||
|
||||
// Server
|
||||
bool ServerEnabled;
|
||||
uint64_t ServerPort;
|
||||
bool LockToIPv4;
|
||||
|
||||
// Client
|
||||
bool ClientEnabled;
|
||||
std::string Address;
|
||||
uint64_t ClientPort;
|
||||
bool SendSceneChange;
|
||||
bool SendSceneChangeAll;
|
||||
bool SendPreview;
|
||||
};
|
||||
|
||||
class WSServer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WSServer();
|
||||
virtual ~WSServer();
|
||||
void start(quint16 port, bool lockToIPv4);
|
||||
void stop();
|
||||
void sendMessage(SceneSwitchInfo sceneSwitch, bool preview = false);
|
||||
QThreadPool *threadPool() { return &_threadPool; }
|
||||
|
||||
private:
|
||||
void onOpen(connection_hdl hdl);
|
||||
void onMessage(connection_hdl hdl, server::message_ptr message);
|
||||
void onClose(connection_hdl hdl);
|
||||
|
||||
QString getRemoteEndpoint(connection_hdl hdl);
|
||||
|
||||
server _server;
|
||||
quint16 _serverPort = 55555;
|
||||
bool _lockToIPv4 = false;
|
||||
std::set<connection_hdl, std::owner_less<connection_hdl>> _connections;
|
||||
std::recursive_mutex _clMutex;
|
||||
QThreadPool _threadPool;
|
||||
};
|
||||
|
||||
enum class ServerStatus {
|
||||
NOT_RUNNING,
|
||||
STARTING,
|
||||
RUNNING,
|
||||
};
|
||||
|
||||
class WSClient : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WSClient();
|
||||
virtual ~WSClient();
|
||||
void connect(std::string uri);
|
||||
void disconnect();
|
||||
std::string getFail() { return _failMsg; }
|
||||
|
||||
private:
|
||||
void onOpen(connection_hdl hdl);
|
||||
void onFail(connection_hdl hdl);
|
||||
void onMessage(connection_hdl hdl, client::message_ptr message);
|
||||
void onClose(connection_hdl hdl);
|
||||
void connectThread();
|
||||
|
||||
client _client;
|
||||
std::string _uri;
|
||||
connection_hdl _connection;
|
||||
std::thread _thread;
|
||||
bool _retry = false;
|
||||
std::atomic_bool _connected = {false};
|
||||
std::mutex _waitMtx;
|
||||
std::condition_variable _cv;
|
||||
std::string _failMsg;
|
||||
};
|
||||
|
||||
enum class ClientStatus {
|
||||
DISCONNECTED,
|
||||
CONNECTING,
|
||||
CONNECTED,
|
||||
FAIL,
|
||||
};
|
||||
|
||||
namespace Compatability {
|
||||
// Reimplement QRunnable for std::function. Retrocompatability for Qt < 5.15
|
||||
class StdFunctionRunnable : public QRunnable {
|
||||
std::function<void()> cb;
|
||||
|
||||
public:
|
||||
StdFunctionRunnable(std::function<void()> func);
|
||||
void run() override;
|
||||
};
|
||||
|
||||
QRunnable *CreateFunctionRunnable(std::function<void()> func);
|
||||
} // namespace Compatability
|
||||
|
||||
} // namespace advss
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -143,7 +143,7 @@ void AdvSceneSwitcher::SetupRandomTab()
|
||||
border-style: outset; \
|
||||
border-width: 2px; \
|
||||
border-radius: 7px; \
|
||||
border-color: rgb(0,0,0,0) \
|
||||
border-color: rgb(0,0,0) \
|
||||
}");
|
||||
if (switcher->switchIfNotMatching != NoMatchBehavior::RANDOM_SWITCH) {
|
||||
if (!switcher->disableHints) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ void VideoSwitch::load(obs_data_t *obj)
|
||||
void VideoSwitch::getScreenshot()
|
||||
{
|
||||
auto source = obs_weak_source_get_source(videoSource);
|
||||
screenshotData = std::make_unique<ScreenshotHelper>(source);
|
||||
screenshotData = std::make_unique<Screenshot>(source);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
@@ -293,56 +293,55 @@ bool VideoSwitch::checkMatch()
|
||||
|
||||
bool match = false;
|
||||
|
||||
if (screenshotData) {
|
||||
if (screenshotData->done) {
|
||||
bool conditionMatch = false;
|
||||
|
||||
switch (condition) {
|
||||
case videoSwitchType::MATCH:
|
||||
conditionMatch = screenshotData->image ==
|
||||
matchImage;
|
||||
break;
|
||||
case videoSwitchType::DIFFER:
|
||||
conditionMatch = screenshotData->image !=
|
||||
matchImage;
|
||||
break;
|
||||
case videoSwitchType::HAS_NOT_CHANGED:
|
||||
conditionMatch = screenshotData->image ==
|
||||
matchImage;
|
||||
break;
|
||||
case videoSwitchType::HAS_CHANGED:
|
||||
conditionMatch = screenshotData->image !=
|
||||
matchImage;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (conditionMatch) {
|
||||
currentMatchDuration +=
|
||||
std::chrono::duration_cast<
|
||||
std::chrono::milliseconds>(
|
||||
screenshotData->time -
|
||||
previousTime);
|
||||
} else {
|
||||
currentMatchDuration = {};
|
||||
}
|
||||
|
||||
bool durationMatch = currentMatchDuration.count() >=
|
||||
duration * 1000;
|
||||
|
||||
if (conditionMatch && durationMatch) {
|
||||
match = true;
|
||||
}
|
||||
|
||||
if (!requiresFileInput(condition)) {
|
||||
matchImage = std::move(screenshotData->image);
|
||||
}
|
||||
previousTime = std::move(screenshotData->time);
|
||||
|
||||
screenshotData.reset(nullptr);
|
||||
}
|
||||
if (!screenshotData) {
|
||||
getScreenshot();
|
||||
return match;
|
||||
}
|
||||
if (!screenshotData->IsDone()) {
|
||||
getScreenshot();
|
||||
return match;
|
||||
}
|
||||
|
||||
bool conditionMatch = false;
|
||||
|
||||
switch (condition) {
|
||||
case videoSwitchType::MATCH:
|
||||
conditionMatch = screenshotData->GetImage() == matchImage;
|
||||
break;
|
||||
case videoSwitchType::DIFFER:
|
||||
conditionMatch = screenshotData->GetImage() != matchImage;
|
||||
break;
|
||||
case videoSwitchType::HAS_NOT_CHANGED:
|
||||
conditionMatch = screenshotData->GetImage() == matchImage;
|
||||
break;
|
||||
case videoSwitchType::HAS_CHANGED:
|
||||
conditionMatch = screenshotData->GetImage() != matchImage;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (conditionMatch) {
|
||||
currentMatchDuration +=
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
screenshotData->GetScreenshotTime() -
|
||||
previousTime);
|
||||
} else {
|
||||
currentMatchDuration = {};
|
||||
}
|
||||
|
||||
bool durationMatch = currentMatchDuration.count() >= duration * 1000;
|
||||
|
||||
if (conditionMatch && durationMatch) {
|
||||
match = true;
|
||||
}
|
||||
|
||||
if (!requiresFileInput(condition)) {
|
||||
matchImage = screenshotData->GetImage();
|
||||
}
|
||||
previousTime = screenshotData->GetScreenshotTime();
|
||||
|
||||
screenshotData.reset(nullptr);
|
||||
|
||||
getScreenshot();
|
||||
return match;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -384,7 +384,11 @@ static void getProcessListProcps2(QStringList &processes)
|
||||
return;
|
||||
}
|
||||
while ((stack = procps_pids_get_(info, PIDS_FETCH_TASKS_ONLY))) {
|
||||
#ifdef PROCPS2_USE_INFO
|
||||
auto cmd = PIDS_VAL(0, str, stack, info);
|
||||
#else
|
||||
auto cmd = PIDS_VAL(0, str, stack);
|
||||
#endif
|
||||
QString procName(cmd);
|
||||
if (!procName.isEmpty() && !processes.contains(procName)) {
|
||||
processes << procName;
|
||||
|
||||
@@ -17,6 +17,9 @@ static inline void populateActionSelection(QComboBox *list)
|
||||
QString entry(obs_module_text(action._name.c_str()));
|
||||
if (list->findText(entry) == -1) {
|
||||
list->addItem(entry);
|
||||
qobject_cast<QListView *>(list->view())
|
||||
->setRowHidden(list->count() - 1,
|
||||
action._hidden);
|
||||
} else {
|
||||
blog(LOG_WARNING,
|
||||
"did not insert duplicate action entry with name \"%s\"",
|
||||
|
||||
@@ -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,12 +6,13 @@
|
||||
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;
|
||||
bool _hidden = false;
|
||||
};
|
||||
|
||||
class MacroActionFactory {
|
||||
@@ -19,6 +20,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 +33,6 @@ private:
|
||||
static std::map<std::string, MacroActionInfo> &GetMap();
|
||||
};
|
||||
|
||||
bool CanCreateDefaultAction();
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -196,13 +196,13 @@ void MacroActionMacro::RunActions(Macro *actionMacro) const
|
||||
}
|
||||
|
||||
if (_runOptions.reevaluateConditionState) {
|
||||
conditionMacro->CeckMatch(true);
|
||||
conditionMacro->CheckConditions(true);
|
||||
}
|
||||
|
||||
if ((_runOptions.logic == RunOptions::Logic::CONDITIONS &&
|
||||
conditionMacro->Matched()) ||
|
||||
conditionMacro->ConditionsMatched()) ||
|
||||
(_runOptions.logic == RunOptions::Logic::INVERT_CONDITIONS &&
|
||||
!conditionMacro->Matched())) {
|
||||
!conditionMacro->ConditionsMatched())) {
|
||||
runActionsHelper(actionMacro, _runOptions.runElseActions,
|
||||
_runOptions.setInputs, _runOptions.inputs);
|
||||
}
|
||||
@@ -309,11 +309,11 @@ MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void HighligthMacroSettingsButton(bool enable);
|
||||
void HighlightMacroSettingsButton(bool enable);
|
||||
|
||||
MacroActionMacroEdit::~MacroActionMacroEdit()
|
||||
{
|
||||
HighligthMacroSettingsButton(false);
|
||||
HighlightMacroSettingsButton(false);
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::UpdateEntryData()
|
||||
@@ -480,7 +480,7 @@ void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
_inputs->setVisible(_entryData->_action ==
|
||||
MacroActionMacro::Action::RUN &&
|
||||
_entryData->_runOptions.setInputs);
|
||||
HighligthMacroSettingsButton(_entryData->_action ==
|
||||
HighlightMacroSettingsButton(_entryData->_action ==
|
||||
MacroActionMacro::Action::RUN &&
|
||||
_entryData->_runOptions.setInputs &&
|
||||
!_inputs->HasInputsToSet());
|
||||
|
||||
120
lib/macro/macro-action-script.cpp
Normal file
120
lib/macro/macro-action-script.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#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 &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName)
|
||||
: MacroAction(m),
|
||||
MacroSegmentScript(defaultSettings, propertiesSignalName,
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName),
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionScript::RegisterTempVarHelper(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText)
|
||||
{
|
||||
AddTempvar(variableId, name, helpText);
|
||||
}
|
||||
|
||||
void MacroActionScript::DeregisterAllTempVarsHelper()
|
||||
{
|
||||
MacroSegment::SetupTempVars();
|
||||
}
|
||||
|
||||
void MacroActionScript::SetTempVarValueHelper(const std::string &variableId,
|
||||
const std::string &value)
|
||||
{
|
||||
MacroAction::SetTempVarValue(variableId, value);
|
||||
}
|
||||
|
||||
void MacroActionScript::SetupTempVars()
|
||||
{
|
||||
// This just exists so MacroSegment::SetupTempVars() is not called.
|
||||
// We want the ScriptHandler to handle the registration and clearing of
|
||||
// the temp vars.
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
38
lib/macro/macro-action-script.hpp
Normal file
38
lib/macro/macro-action-script.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#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 &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName);
|
||||
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;
|
||||
void RegisterTempVarHelper(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText);
|
||||
void DeregisterAllTempVarsHelper();
|
||||
void SetTempVarValueHelper(const std::string &variableId,
|
||||
const std::string &value);
|
||||
void SetupTempVars();
|
||||
|
||||
std::string _id = "";
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,8 +28,8 @@ public:
|
||||
int GetSegmentIndexValue() const;
|
||||
void ResolveVariablesToFixedValues();
|
||||
|
||||
enum class Type {
|
||||
SET_FIXED_VALUE,
|
||||
enum class Action {
|
||||
SET_VALUE,
|
||||
APPEND,
|
||||
APPEND_VAR,
|
||||
INCREMENT,
|
||||
@@ -50,18 +50,21 @@ public:
|
||||
PAD,
|
||||
TRUNCATE,
|
||||
SWAP_VALUES,
|
||||
TRIM,
|
||||
CHANGE_CASE,
|
||||
RANDOM_NUMBER,
|
||||
};
|
||||
|
||||
Type _type = Type::SET_FIXED_VALUE;
|
||||
Action _action = Action::SET_VALUE;
|
||||
std::weak_ptr<Variable> _variable;
|
||||
std::weak_ptr<Variable> _variable2;
|
||||
StringVariable _strValue = "";
|
||||
double _numValue = 0;
|
||||
int _subStringStart = 0;
|
||||
int _subStringSize = 0;
|
||||
DoubleVariable _numValue = 0;
|
||||
IntVariable _subStringStart = 0;
|
||||
IntVariable _subStringSize = 0;
|
||||
RegexConfig _subStringRegex = RegexConfig::PartialMatchRegexConfig();
|
||||
std::string _regexPattern = ".*";
|
||||
int _regexMatchIdx = 0;
|
||||
IntVariable _regexMatchIdx = 0;
|
||||
RegexConfig _findRegex;
|
||||
StringVariable _findStr = obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.findAndReplace.find");
|
||||
@@ -89,13 +92,27 @@ public:
|
||||
IntVariable _stringLength = 1;
|
||||
char _paddingChar = '0';
|
||||
|
||||
enum class CaseType {
|
||||
LOWER_CASE,
|
||||
UPPER_CASE,
|
||||
CAPITALIZED,
|
||||
START_CASE,
|
||||
};
|
||||
|
||||
CaseType _caseType = CaseType::LOWER_CASE;
|
||||
DoubleVariable _randomNumberStart = 0;
|
||||
DoubleVariable _randomNumberEnd = 100;
|
||||
bool _generateInteger = true;
|
||||
|
||||
private:
|
||||
void DecrementCurrentSegmentVariableRef();
|
||||
void HandleIndexSubString(Variable *);
|
||||
void HandleRegexSubString(Variable *);
|
||||
void HandleFindAndReplace(Variable *);
|
||||
void HandleMathExpression(Variable *);
|
||||
void HandleCaseChange(Variable *);
|
||||
void SetToSceneItemName(Variable *);
|
||||
void GenerateRandomNumber(Variable *);
|
||||
|
||||
std::weak_ptr<MacroSegment> _macroSegment;
|
||||
int _segmentIdxLoadValue = -1;
|
||||
@@ -124,15 +141,15 @@ private slots:
|
||||
void Variable2Changed(const QString &);
|
||||
void ActionChanged(int);
|
||||
void StrValueChanged();
|
||||
void NumValueChanged(double);
|
||||
void NumValueChanged(const NumberVariable<double> &value);
|
||||
void SegmentIndexChanged(const IntVariable &);
|
||||
void UpdateSegmentVariableValue();
|
||||
void MacroSegmentOrderChanged();
|
||||
void SubStringStartChanged(int val);
|
||||
void SubStringSizeChanged(int val);
|
||||
void SubStringStartChanged(const NumberVariable<int> &start);
|
||||
void SubStringSizeChanged(const NumberVariable<int> &size);
|
||||
void SubStringRegexChanged(const RegexConfig &conf);
|
||||
void RegexPatternChanged();
|
||||
void RegexMatchIdxChanged(int val);
|
||||
void RegexMatchIdxChanged(const NumberVariable<int> &index);
|
||||
void FindStrValueChanged();
|
||||
void FindRegexChanged(const RegexConfig &conf);
|
||||
void ReplaceStrValueChanged();
|
||||
@@ -148,6 +165,10 @@ private slots:
|
||||
void DirectionChanged(int);
|
||||
void StringLengthChanged(const NumberVariable<int> &);
|
||||
void CharSelectionChanged(const QString &);
|
||||
void CaseTypeChanged(int index);
|
||||
void RandomNumberStartChanged(const NumberVariable<double> &);
|
||||
void RandomNumberEndChanged(const NumberVariable<double> &);
|
||||
void GenerateIntegerChanged(int);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
@@ -160,18 +181,18 @@ private:
|
||||
VariableSelection *_variables2;
|
||||
FilterComboBox *_actions;
|
||||
VariableTextEdit *_strValue;
|
||||
QDoubleSpinBox *_numValue;
|
||||
VariableDoubleSpinBox *_numValue;
|
||||
MacroSegmentSelection *_segmentIdx;
|
||||
QLabel *_segmentValueStatus;
|
||||
ResizingPlainTextEdit *_segmentValue;
|
||||
QVBoxLayout *_substringLayout;
|
||||
QHBoxLayout *_subStringIndexEntryLayout;
|
||||
QHBoxLayout *_subStringRegexEntryLayout;
|
||||
QSpinBox *_subStringStart;
|
||||
QSpinBox *_subStringSize;
|
||||
VariableSpinBox *_subStringStart;
|
||||
VariableSpinBox *_subStringSize;
|
||||
RegexConfigWidget *_substringRegex;
|
||||
ResizingPlainTextEdit *_regexPattern;
|
||||
QSpinBox *_regexMatchIdx;
|
||||
VariableSpinBox *_regexMatchIdx;
|
||||
QHBoxLayout *_findReplaceLayout;
|
||||
RegexConfigWidget *_findRegex;
|
||||
VariableTextEdit *_findStr;
|
||||
@@ -192,6 +213,11 @@ private:
|
||||
QComboBox *_direction;
|
||||
VariableSpinBox *_stringLength;
|
||||
SingleCharSelection *_paddingCharSelection;
|
||||
FilterComboBox *_caseType;
|
||||
VariableDoubleSpinBox *_randomNumberStart;
|
||||
VariableDoubleSpinBox *_randomNumberEnd;
|
||||
QCheckBox *_generateInteger;
|
||||
QVBoxLayout *_randomLayout;
|
||||
QHBoxLayout *_entryLayout;
|
||||
|
||||
std::shared_ptr<MacroActionVariable> _entryData;
|
||||
|
||||
@@ -10,26 +10,6 @@
|
||||
|
||||
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] :
|
||||
@@ -120,13 +100,13 @@ void DurationModifierEdit::Collapse(bool collapse)
|
||||
|
||||
MacroConditionEdit::MacroConditionEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
|
||||
const std::string &id, bool root)
|
||||
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)));
|
||||
@@ -139,7 +119,7 @@ MacroConditionEdit::MacroConditionEdit(
|
||||
this,
|
||||
SLOT(DurationModifierChanged(DurationModifier::Type)));
|
||||
|
||||
populateLogicSelection(_logicSelection, root);
|
||||
Logic::PopulateLogicTypeSelection(_logicSelection, isRootCondition);
|
||||
populateConditionSelection(_conditionSelection);
|
||||
|
||||
_section->AddHeaderWidget(_logicSelection);
|
||||
@@ -168,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()
|
||||
@@ -186,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)
|
||||
@@ -200,7 +171,7 @@ void MacroConditionEdit::SetRootNode(bool root)
|
||||
_isRoot = root;
|
||||
const QSignalBlocker blocker(_logicSelection);
|
||||
_logicSelection->clear();
|
||||
populateLogicSelection(_logicSelection, root);
|
||||
Logic::PopulateLogicTypeSelection(_logicSelection, root);
|
||||
SetLogicSelection();
|
||||
}
|
||||
|
||||
@@ -301,17 +272,17 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -324,7 +295,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
||||
|
||||
void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx,
|
||||
const std::string &id,
|
||||
obs_data_t *data, LogicType logic)
|
||||
obs_data_t *data, Logic::Type logic)
|
||||
{
|
||||
if (idx < 0 || idx > (int)macro->Conditions().size()) {
|
||||
assert(false);
|
||||
@@ -389,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);
|
||||
@@ -540,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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <QPushButton>
|
||||
#include <QHBoxLayout>
|
||||
#include <QTimer>
|
||||
#include <QListWidget>
|
||||
|
||||
namespace advss {
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ bool MacroConditionQueue::CheckCondition()
|
||||
return false;
|
||||
}
|
||||
|
||||
SetTempVarValue("size", std::to_string(queue->Size()));
|
||||
SetTempVarValue("running", queue->IsRunning());
|
||||
|
||||
switch (_condition) {
|
||||
case Condition::STARTED:
|
||||
return queue->IsRunning();
|
||||
@@ -63,6 +66,18 @@ std::string MacroConditionQueue::GetShortDesc() const
|
||||
return GetActionQueueName(_queue);
|
||||
}
|
||||
|
||||
void MacroConditionQueue::SetupTempVars()
|
||||
{
|
||||
MacroCondition::SetupTempVars();
|
||||
AddTempvar("size",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.queue.size"));
|
||||
AddTempvar(
|
||||
"running",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.queue.running"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.queue.running.description"));
|
||||
}
|
||||
|
||||
static inline void populateQueueTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : conditionTypes) {
|
||||
@@ -141,6 +156,7 @@ void MacroConditionQueueEdit::UpdateEntryData()
|
||||
}
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_queues->SetActionQueue(_entryData->_queue);
|
||||
_size->SetValue(_entryData->_size);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ public:
|
||||
IntVariable _size = 1;
|
||||
|
||||
private:
|
||||
void SetupTempVars();
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
108
lib/macro/macro-condition-script.cpp
Normal file
108
lib/macro/macro-condition-script.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#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 &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName)
|
||||
: MacroCondition(m),
|
||||
MacroSegmentScript(defaultSettings, propertiesSignalName,
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName),
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionScript::RegisterTempVarHelper(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText)
|
||||
{
|
||||
AddTempvar(variableId, name, helpText);
|
||||
}
|
||||
|
||||
void MacroConditionScript::DeregisterAllTempVarsHelper()
|
||||
{
|
||||
MacroSegment::SetupTempVars();
|
||||
}
|
||||
|
||||
void MacroConditionScript::SetTempVarValueHelper(const std::string &variableId,
|
||||
const std::string &value)
|
||||
{
|
||||
MacroCondition::SetTempVarValue(variableId, value);
|
||||
}
|
||||
|
||||
void MacroConditionScript::SetupTempVars()
|
||||
{
|
||||
// This just exists so MacroSegment::SetupTempVars() is not called.
|
||||
// We want the ScriptHandler to handle the registration and clearing of
|
||||
// the temp vars.
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
36
lib/macro/macro-condition-script.hpp
Normal file
36
lib/macro/macro-condition-script.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#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 &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName);
|
||||
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;
|
||||
void RegisterTempVarHelper(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText);
|
||||
void DeregisterAllTempVarsHelper();
|
||||
void SetTempVarValueHelper(const std::string &variableId,
|
||||
const std::string &value);
|
||||
void SetupTempVars();
|
||||
|
||||
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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -67,9 +67,7 @@ void SetMacroSwitchedScene(bool value)
|
||||
{
|
||||
static bool setupDone = false;
|
||||
if (!setupDone) {
|
||||
// Will always be called with switcher lock already held
|
||||
AddIntervalResetStep([]() { macroSceneSwitched = false; },
|
||||
false);
|
||||
AddIntervalResetStep([]() { macroSceneSwitched = false; });
|
||||
setupDone = true;
|
||||
}
|
||||
macroSceneSwitched = value;
|
||||
@@ -85,9 +83,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)
|
||||
@@ -107,6 +107,14 @@ bool MacroWasPausedSince(
|
||||
return macro ? macro->WasPausedSince(time) : false;
|
||||
}
|
||||
|
||||
bool MacroWasCheckedSinceLastStart(Macro *macro)
|
||||
{
|
||||
if (!macro) {
|
||||
return false;
|
||||
}
|
||||
return macro->LastConditionCheckTime().time_since_epoch().count() != 0;
|
||||
}
|
||||
|
||||
void AddMacroHelperThread(Macro *macro, std::thread &&newThread)
|
||||
{
|
||||
if (!macro) {
|
||||
|
||||
@@ -43,13 +43,15 @@ 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 *);
|
||||
EXPORT bool
|
||||
MacroWasPausedSince(Macro *,
|
||||
const std::chrono::high_resolution_clock::time_point &);
|
||||
EXPORT bool MacroWasCheckedSinceLastStart(Macro *);
|
||||
|
||||
EXPORT void AddMacroHelperThread(Macro *, std::thread &&);
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ void MacroInputSelection::SetInputs(const MacroInputVariables &inputs)
|
||||
void MacroInputSelection::Add()
|
||||
{
|
||||
std::string varName;
|
||||
bool accepted = VariableSelectionDialog::AskForVariable(this, varName);
|
||||
bool accepted = VariableSelectionDialog::AskForVariable(varName);
|
||||
|
||||
if (!accepted || varName.empty()) {
|
||||
return;
|
||||
@@ -155,7 +155,7 @@ void MacroInputSelection::Down()
|
||||
void MacroInputSelection::Clicked(QListWidgetItem *item)
|
||||
{
|
||||
std::string varName;
|
||||
bool accepted = VariableSelectionDialog::AskForVariable(this, varName);
|
||||
bool accepted = VariableSelectionDialog::AskForVariable(varName);
|
||||
|
||||
if (!accepted || varName.empty()) {
|
||||
return;
|
||||
@@ -181,15 +181,7 @@ void MacroInputEdit::SetInputVariablesAndValues(
|
||||
_variables = inputs;
|
||||
_values = values;
|
||||
if ((size_t)_values.size() < _variables._inputVariables.size()) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
for (int i = 0;
|
||||
_variables._inputVariables.size() - (size_t)_values.size();
|
||||
i++) {
|
||||
_values.push_back({});
|
||||
}
|
||||
#else
|
||||
_values.resize(_variables._inputVariables.size());
|
||||
#endif
|
||||
}
|
||||
SetupWidgets();
|
||||
}
|
||||
@@ -199,7 +191,7 @@ bool MacroInputEdit::HasInputsToSet() const
|
||||
return !_variables._inputVariables.empty();
|
||||
}
|
||||
|
||||
void HighligthMacroSettingsButton(bool enable);
|
||||
void HighlightMacroSettingsButton(bool enable);
|
||||
|
||||
void MacroInputEdit::SetupWidgets()
|
||||
{
|
||||
|
||||
@@ -79,7 +79,7 @@ void MacroList::MacroRemove(const QString &name)
|
||||
void MacroList::Add()
|
||||
{
|
||||
std::string macroName;
|
||||
bool accepted = MacroSelectionDialog::AskForMacro(this, macroName);
|
||||
bool accepted = MacroSelectionDialog::AskForMacro(macroName);
|
||||
|
||||
if (!accepted || macroName.empty()) {
|
||||
return;
|
||||
@@ -132,7 +132,7 @@ void MacroList::Down()
|
||||
void MacroList::Clicked(QListWidgetItem *item)
|
||||
{
|
||||
std::string macroName;
|
||||
bool accepted = MacroSelectionDialog::AskForMacro(this, macroName);
|
||||
bool accepted = MacroSelectionDialog::AskForMacro(macroName);
|
||||
|
||||
if (!accepted || macroName.empty()) {
|
||||
return;
|
||||
|
||||
667
lib/macro/macro-script-handler.cpp
Normal file
667
lib/macro/macro-script-handler.cpp
Normal file
@@ -0,0 +1,667 @@
|
||||
#include "macro-script-handler.hpp"
|
||||
#include "macro-action-script.hpp"
|
||||
#include "macro-condition-script.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
#include "log-helper.hpp"
|
||||
#include "variable.hpp"
|
||||
|
||||
#include <obs-properties.h>
|
||||
|
||||
namespace advss {
|
||||
|
||||
std::mutex ScriptHandler::_mutex = {};
|
||||
std::unordered_map<std::string, ScriptSegmentType> ScriptHandler::_actions = {};
|
||||
std::unordered_map<std::string, ScriptSegmentType> ScriptHandler::_conditions =
|
||||
{};
|
||||
|
||||
/* Procedure handler helpers */
|
||||
|
||||
#define RETURN_STATUS(status) \
|
||||
{ \
|
||||
calldata_set_bool(data, "success", status); \
|
||||
return; \
|
||||
}
|
||||
#define RETURN_SUCCESS() RETURN_STATUS(true);
|
||||
#define RETURN_FAILURE() RETURN_STATUS(false);
|
||||
|
||||
static constexpr std::string_view nameParam = "name";
|
||||
static constexpr std::string_view defaultSettingsParam = "default_settings";
|
||||
static constexpr std::string_view propertiesSignalParam =
|
||||
"properties_signal_name";
|
||||
static constexpr std::string_view triggerSignalParam = "trigger_signal_name";
|
||||
static constexpr std::string_view newInstanceSignalNameParam =
|
||||
"new_instance_signal_name";
|
||||
static constexpr std::string_view deletedInstanceSignalNameParam =
|
||||
"deleted_instance_signal_name";
|
||||
|
||||
static std::string getRegisterScriptSegmentDeclString(const char *funcName)
|
||||
{
|
||||
return std::string("bool ") + funcName + "(in string " +
|
||||
nameParam.data() + ", in ptr " + defaultSettingsParam.data() +
|
||||
", out string " + propertiesSignalParam.data() +
|
||||
", out string " + triggerSignalParam.data() + ")";
|
||||
}
|
||||
|
||||
static std::string getDeregisterScriptSegmentDeclString(const char *funcName)
|
||||
{
|
||||
return std::string("bool ") + funcName + "(in string " +
|
||||
nameParam.data() + ")";
|
||||
}
|
||||
|
||||
/* Script actions */
|
||||
|
||||
static constexpr std::string_view registerActionFuncName =
|
||||
"advss_register_script_action";
|
||||
static constexpr std::string_view deregisterActionFuncName =
|
||||
"advss_deregister_script_action";
|
||||
|
||||
static const std::string registerScriptActionDeclString =
|
||||
getRegisterScriptSegmentDeclString(registerActionFuncName.data());
|
||||
static const std::string deregisterScriptActionDeclString =
|
||||
getDeregisterScriptSegmentDeclString(deregisterActionFuncName.data());
|
||||
|
||||
/* Script conditions */
|
||||
|
||||
static constexpr std::string_view registerConditionFuncName =
|
||||
"advss_register_script_condition";
|
||||
static constexpr std::string_view deregisterConditionFuncName =
|
||||
"advss_deregister_script_condition";
|
||||
|
||||
static const std::string registerScriptConditionDeclString =
|
||||
getRegisterScriptSegmentDeclString(registerConditionFuncName.data());
|
||||
static const std::string deregisterScriptConditionDeclString =
|
||||
getDeregisterScriptSegmentDeclString(
|
||||
deregisterConditionFuncName.data());
|
||||
|
||||
/* Script variables */
|
||||
|
||||
static constexpr std::string_view valueParam = "value";
|
||||
static constexpr std::string_view tempVarIdParam = "temp_var_id";
|
||||
static constexpr std::string_view tempVarNameParam = "temp_var_name";
|
||||
static constexpr std::string_view tempVarHelpParam = "temp_var_help";
|
||||
static constexpr std::string_view getVariableValueFuncName =
|
||||
"advss_get_variable_value";
|
||||
static constexpr std::string_view setVariableValueFuncName =
|
||||
"advss_set_variable_value";
|
||||
static constexpr std::string_view registerTempVarFuncName =
|
||||
"advss_register_temp_var";
|
||||
static constexpr std::string_view deregisterAllTempVarsFuncName =
|
||||
"advss_deregister_temp_vars";
|
||||
static constexpr std::string_view setTempVarValueFuncName =
|
||||
"advss_set_temp_var_value";
|
||||
static const std::string getVariableValueDeclString =
|
||||
std::string("bool ") + getVariableValueFuncName.data() + "(in string " +
|
||||
nameParam.data() + ", out string " + valueParam.data() + ")";
|
||||
static const std::string setVariableValueDeclString =
|
||||
std::string("bool ") + setVariableValueFuncName.data() + "(in string " +
|
||||
nameParam.data() + ", in string " + valueParam.data() + ")";
|
||||
static const std::string registerTempVarDeclString =
|
||||
std::string("bool ") + registerTempVarFuncName.data() + "(in string " +
|
||||
tempVarIdParam.data() + ", in string " + tempVarNameParam.data() +
|
||||
", in string " + tempVarHelpParam.data() + ", in int " +
|
||||
GetInstanceIdParamName().data() + ")";
|
||||
static const std::string deregisterAllTempVarsDeclString =
|
||||
std::string("bool ") + deregisterAllTempVarsFuncName.data() +
|
||||
"(in int " + GetInstanceIdParamName().data() + ")";
|
||||
static const std::string setTempVarValueDeclString =
|
||||
std::string("bool ") + setTempVarValueFuncName.data() + "(in string " +
|
||||
tempVarIdParam.data() + ", in string " + valueParam.data() +
|
||||
", in int " + GetInstanceIdParamName().data() + ")";
|
||||
|
||||
/* Plugin status */
|
||||
|
||||
static constexpr std::string_view stopSignalName = "advss_plugin_stopped";
|
||||
static constexpr std::string_view startSignalName = "advss_plugin_started";
|
||||
static constexpr std::string_view getRunningStatusFuncName =
|
||||
"advss_plugin_running";
|
||||
static constexpr std::string_view resetIntervalSignalName =
|
||||
"advss_interval_reset";
|
||||
|
||||
static const std::string stopSignalDeclString =
|
||||
std::string("void ") + stopSignalName.data() + "()";
|
||||
static const std::string startSignalDeclString =
|
||||
std::string("void ") + startSignalName.data() + "()";
|
||||
static const std::string resetIntervalDeclString =
|
||||
std::string("void ") + resetIntervalSignalName.data() + "()";
|
||||
static const std::string getRunningStatusDeclString =
|
||||
std::string("bool ") + getRunningStatusFuncName.data() + "()";
|
||||
|
||||
static bool setup();
|
||||
static bool setupDone = setup();
|
||||
|
||||
static bool setup()
|
||||
{
|
||||
proc_handler_t *ph = obs_get_proc_handler();
|
||||
assert(ph != NULL);
|
||||
|
||||
proc_handler_add(ph, registerScriptActionDeclString.c_str(),
|
||||
&ScriptHandler::RegisterScriptAction, nullptr);
|
||||
proc_handler_add(ph, deregisterScriptActionDeclString.c_str(),
|
||||
&ScriptHandler::DeregisterScriptAction, nullptr);
|
||||
proc_handler_add(ph, registerScriptConditionDeclString.c_str(),
|
||||
&ScriptHandler::RegisterScriptCondition, nullptr);
|
||||
proc_handler_add(ph, deregisterScriptConditionDeclString.c_str(),
|
||||
&ScriptHandler::DeregisterScriptCondition, nullptr);
|
||||
proc_handler_add(ph, getVariableValueDeclString.c_str(),
|
||||
&ScriptHandler::GetVariableValue, nullptr);
|
||||
proc_handler_add(ph, setVariableValueDeclString.c_str(),
|
||||
&ScriptHandler::SetVariableValue, nullptr);
|
||||
proc_handler_add(ph, registerTempVarDeclString.c_str(),
|
||||
&ScriptHandler::RegisterTempVar, nullptr);
|
||||
proc_handler_add(ph, deregisterAllTempVarsDeclString.c_str(),
|
||||
&ScriptHandler::DeregisterAllTempVars, nullptr);
|
||||
proc_handler_add(ph, setTempVarValueDeclString.c_str(),
|
||||
&ScriptHandler::SetTempVarValue, nullptr);
|
||||
proc_handler_add(ph, getRunningStatusDeclString.c_str(),
|
||||
&ScriptHandler::GetRunningStatus, nullptr);
|
||||
|
||||
auto sh = obs_get_signal_handler();
|
||||
signal_handler_add(sh, stopSignalDeclString.c_str());
|
||||
signal_handler_add(sh, startSignalDeclString.c_str());
|
||||
signal_handler_add(sh, resetIntervalDeclString.c_str());
|
||||
|
||||
static constexpr auto triggerSignal = [](const std::string_view &name) {
|
||||
auto sh = obs_get_signal_handler();
|
||||
struct calldata data;
|
||||
calldata_init(&data);
|
||||
signal_handler_signal(sh, name.data(), &data);
|
||||
calldata_free(&data);
|
||||
};
|
||||
|
||||
AddIntervalResetStep([]() { triggerSignal(resetIntervalSignalName); });
|
||||
AddStartStep([]() { triggerSignal(startSignalName); });
|
||||
AddStopStep([]() { triggerSignal(stopSignalName); });
|
||||
return true;
|
||||
}
|
||||
|
||||
static void replaceProblematicChars(std::string &string)
|
||||
{
|
||||
std::transform(string.begin(), string.end(), string.begin(), [](char c) {
|
||||
if (std::isspace(static_cast<unsigned char>(c)) || c == '(' ||
|
||||
c == ')' || c == '[' || c == ']' || c == '{' || c == '}') {
|
||||
return '_';
|
||||
}
|
||||
return c;
|
||||
});
|
||||
}
|
||||
|
||||
static std::string nameToScriptID(const std::string &name)
|
||||
{
|
||||
return std::string("script_") + name;
|
||||
}
|
||||
|
||||
static std::string getTriggerSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
std::string signal = name;
|
||||
replaceProblematicChars(signal);
|
||||
signal += "_run";
|
||||
signal += isAction ? "_action" : "_condition";
|
||||
return signal;
|
||||
}
|
||||
|
||||
static std::string getCompletionSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
auto signal = getTriggerSignal(name, isAction);
|
||||
signal += "_complete";
|
||||
return signal;
|
||||
}
|
||||
|
||||
static std::string getPropertiesSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
std::string signal = name;
|
||||
replaceProblematicChars(signal);
|
||||
signal += isAction ? "_action" : "_condition";
|
||||
signal += "_get_properties";
|
||||
return signal;
|
||||
}
|
||||
|
||||
static std::string getNewInstanceSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
std::string signal = name;
|
||||
replaceProblematicChars(signal);
|
||||
signal += isAction ? "_action" : "_condition";
|
||||
signal += "_new_instance";
|
||||
return signal;
|
||||
}
|
||||
|
||||
static std::string getDeletedInstanceSignal(const std::string &name,
|
||||
const bool isAction)
|
||||
{
|
||||
std::string signal = name;
|
||||
replaceProblematicChars(signal);
|
||||
signal += isAction ? "_action" : "_condition";
|
||||
signal += "_deleted_instance";
|
||||
return signal;
|
||||
}
|
||||
|
||||
void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
|
||||
{
|
||||
const char *actionName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &actionName) ||
|
||||
strlen(actionName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerScriptActionDeclString.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
obs_data_t *defaultSettingsPtr = nullptr;
|
||||
if (!calldata_get_ptr(data, defaultSettingsParam.data(),
|
||||
&defaultSettingsPtr)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerActionFuncName.data(),
|
||||
defaultSettingsParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
OBSData defaultSettings(defaultSettingsPtr);
|
||||
obs_data_release(defaultSettingsPtr);
|
||||
|
||||
if (_actions.count(actionName) > 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! Action \"%s\" already exists!",
|
||||
registerActionFuncName.data(), actionName);
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
const std::string id = nameToScriptID(actionName);
|
||||
auto triggerSignalName = getTriggerSignal(actionName, true);
|
||||
auto completionSignalName = getCompletionSignal(actionName, true);
|
||||
auto propertiesSignalName = getPropertiesSignal(actionName, true);
|
||||
auto newInstanceSignalName = getNewInstanceSignal(actionName, true);
|
||||
auto deletedInstanceSignalName =
|
||||
getDeletedInstanceSignal(actionName, true);
|
||||
|
||||
const auto createScriptAction =
|
||||
[id, defaultSettings, propertiesSignalName, triggerSignalName,
|
||||
completionSignalName, newInstanceSignalName,
|
||||
deletedInstanceSignalName](
|
||||
Macro *m) -> std::shared_ptr<MacroAction> {
|
||||
return std::make_shared<MacroActionScript>(
|
||||
m, id, defaultSettings, propertiesSignalName,
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName);
|
||||
};
|
||||
if (!MacroActionFactory::Register(id, {createScriptAction,
|
||||
MacroSegmentScriptEdit::Create,
|
||||
actionName})) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Action id \"%s\" already exists!",
|
||||
registerActionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "[%s] successful for \"%s\"",
|
||||
registerActionFuncName.data(), actionName);
|
||||
|
||||
calldata_set_string(data, triggerSignalParam.data(),
|
||||
triggerSignalName.c_str());
|
||||
calldata_set_string(data, propertiesSignalParam.data(),
|
||||
propertiesSignalName.c_str());
|
||||
calldata_set_string(data, newInstanceSignalNameParam.data(),
|
||||
newInstanceSignalName.c_str());
|
||||
calldata_set_string(data, deletedInstanceSignalNameParam.data(),
|
||||
deletedInstanceSignalName.c_str());
|
||||
_actions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
|
||||
triggerSignalName,
|
||||
completionSignalName,
|
||||
newInstanceSignalName,
|
||||
deletedInstanceSignalName));
|
||||
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::DeregisterScriptAction(void *, calldata_t *data)
|
||||
{
|
||||
const char *actionName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &actionName) ||
|
||||
strlen(actionName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
deregisterActionFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
const std::string id = nameToScriptID(actionName);
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
if (_actions.count(id) == 0) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Action \"%s\" was never registered!",
|
||||
deregisterActionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
if (!MacroActionFactory::Deregister(id)) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Action id \"%s\" does not exist!",
|
||||
deregisterActionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
auto it = _actions.find(id);
|
||||
if (it != _actions.end()) {
|
||||
_actions.erase(it);
|
||||
}
|
||||
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
|
||||
{
|
||||
const char *conditionName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &conditionName) ||
|
||||
strlen(conditionName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerScriptConditionDeclString.data(),
|
||||
nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
obs_data_t *defaultSettingsPtr = nullptr;
|
||||
if (!calldata_get_ptr(data, defaultSettingsParam.data(),
|
||||
&defaultSettingsPtr)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerScriptConditionDeclString.data(),
|
||||
defaultSettingsParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
OBSData defaultSettings(defaultSettingsPtr);
|
||||
obs_data_release(defaultSettingsPtr);
|
||||
|
||||
if (_conditions.count(conditionName) > 0) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Condition \"%s\" already exists!",
|
||||
registerConditionFuncName.data(), conditionName);
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
const std::string id = nameToScriptID(conditionName);
|
||||
auto triggerSignalName = getTriggerSignal(conditionName, false);
|
||||
auto completionSignalName = getCompletionSignal(conditionName, false);
|
||||
auto propertiesSignalName = getPropertiesSignal(conditionName, false);
|
||||
auto newInstanceSignalName = getNewInstanceSignal(conditionName, false);
|
||||
auto deletedInstanceSignalName =
|
||||
getDeletedInstanceSignal(conditionName, false);
|
||||
|
||||
const auto createScriptCondition =
|
||||
[id, defaultSettings, propertiesSignalName, triggerSignalName,
|
||||
completionSignalName, newInstanceSignalName,
|
||||
deletedInstanceSignalName](
|
||||
Macro *m) -> std::shared_ptr<MacroCondition> {
|
||||
return std::make_shared<MacroConditionScript>(
|
||||
m, id, defaultSettings, propertiesSignalName,
|
||||
triggerSignalName, completionSignalName,
|
||||
newInstanceSignalName, deletedInstanceSignalName);
|
||||
};
|
||||
if (!MacroConditionFactory::Register(
|
||||
id, {createScriptCondition, MacroSegmentScriptEdit::Create,
|
||||
conditionName})) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Condition id \"%s\" already exists!",
|
||||
registerConditionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "[%s] successful for \"%s\"",
|
||||
registerConditionFuncName.data(), conditionName);
|
||||
|
||||
calldata_set_string(data, triggerSignalParam.data(),
|
||||
triggerSignalName.c_str());
|
||||
calldata_set_string(data, propertiesSignalParam.data(),
|
||||
propertiesSignalName.c_str());
|
||||
calldata_set_string(data, newInstanceSignalNameParam.data(),
|
||||
newInstanceSignalName.c_str());
|
||||
calldata_set_string(data, deletedInstanceSignalNameParam.data(),
|
||||
deletedInstanceSignalName.c_str());
|
||||
_conditions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
|
||||
triggerSignalName,
|
||||
completionSignalName,
|
||||
newInstanceSignalName,
|
||||
deletedInstanceSignalName));
|
||||
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::DeregisterScriptCondition(void *, calldata_t *data)
|
||||
{
|
||||
const char *conditionName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &conditionName) ||
|
||||
strlen(conditionName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
deregisterConditionFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
const std::string id = nameToScriptID(conditionName);
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
if (_conditions.count(id) == 0) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Condition \"%s\" was never registered!",
|
||||
deregisterConditionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
if (!MacroConditionFactory::Deregister(id)) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! Condition id \"%s\" does not exist!",
|
||||
deregisterConditionFuncName.data(), id.c_str());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
auto it = _conditions.find(id);
|
||||
if (it != _conditions.end()) {
|
||||
_conditions.erase(it);
|
||||
}
|
||||
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::GetVariableValue(void *, calldata_t *data)
|
||||
{
|
||||
const char *variableName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &variableName) ||
|
||||
strlen(variableName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
getVariableValueFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
auto weakVariable = GetWeakVariableByName(variableName);
|
||||
auto variable = weakVariable.lock();
|
||||
if (!variable) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! \"%s\" variable does not exist!",
|
||||
getVariableValueFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
calldata_set_string(data, valueParam.data(), variable->Value().c_str());
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::SetVariableValue(void *, calldata_t *data)
|
||||
{
|
||||
const char *variableName;
|
||||
if (!calldata_get_string(data, nameParam.data(), &variableName) ||
|
||||
strlen(variableName) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
getVariableValueFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
const char *variableValue;
|
||||
if (!calldata_get_string(data, valueParam.data(), &variableValue) ||
|
||||
strlen(variableValue) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
getVariableValueFuncName.data(), valueParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
auto weakVariable = GetWeakVariableByName(variableName);
|
||||
auto variable = weakVariable.lock();
|
||||
if (!variable) {
|
||||
blog(LOG_WARNING,
|
||||
"[%s] failed! \"%s\" variable does not exist!",
|
||||
getVariableValueFuncName.data(), nameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
variable->SetValue(variableValue);
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::GetRunningStatus(void *ctx, calldata_t *data)
|
||||
{
|
||||
calldata_set_bool(data, "is_running", PluginIsRunning());
|
||||
}
|
||||
|
||||
void ScriptHandler::RegisterTempVar(void *, calldata_t *data)
|
||||
{
|
||||
const char *variableId;
|
||||
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
|
||||
strlen(variableId) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(), tempVarIdParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
const char *name;
|
||||
if (!calldata_get_string(data, tempVarNameParam.data(), &name) ||
|
||||
strlen(name) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(), tempVarNameParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
const char *helpText;
|
||||
if (!calldata_get_string(data, tempVarHelpParam.data(), &helpText)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(), tempVarHelpParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
long long instanceId;
|
||||
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
|
||||
&instanceId)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
registerTempVarFuncName.data(),
|
||||
GetInstanceIdParamName().data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
MacroSegmentScript::RegisterTempVar(variableId, name, helpText,
|
||||
instanceId);
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::DeregisterAllTempVars(void *, calldata_t *data)
|
||||
{
|
||||
long long instanceId;
|
||||
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
|
||||
&instanceId)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
deregisterAllTempVarsFuncName.data(),
|
||||
GetInstanceIdParamName().data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
MacroSegmentScript::DeregisterAllTempVars(instanceId);
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::SetTempVarValue(void *, calldata_t *data)
|
||||
{
|
||||
const char *variableId;
|
||||
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
|
||||
strlen(variableId) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
setTempVarValueFuncName.data(), tempVarIdParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
const char *variableValue;
|
||||
if (!calldata_get_string(data, valueParam.data(), &variableValue) ||
|
||||
strlen(variableValue) == 0) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
setTempVarValueFuncName.data(), valueParam.data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
long long instanceId;
|
||||
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
|
||||
&instanceId)) {
|
||||
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
|
||||
setTempVarValueFuncName.data(),
|
||||
GetInstanceIdParamName().data());
|
||||
RETURN_FAILURE();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
MacroSegmentScript::SetTempVarValue(variableId, variableValue,
|
||||
instanceId);
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
bool ScriptHandler::ActionIdIsValid(const std::string &id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _actions.count(id) > 0;
|
||||
}
|
||||
|
||||
bool ScriptHandler::ConditionIdIsValid(const std::string &id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _conditions.count(id) > 0;
|
||||
}
|
||||
|
||||
static std::string signalNameToTriggerSignalDecl(const std::string &name)
|
||||
{
|
||||
return std::string("void ") + name + "()";
|
||||
}
|
||||
|
||||
static std::string signalNameToPropertiesSignalDecl(const std::string &name)
|
||||
{
|
||||
return std::string("void ") + name + "(in ptr " +
|
||||
GetPropertiesSignalParamName().data() + ")";
|
||||
}
|
||||
static std::string signalNameToCompletionSignalDecl(const std::string &name)
|
||||
{
|
||||
return std::string("bool ") + name + "(in int " +
|
||||
GetCompletionIdParamName().data() + ")";
|
||||
}
|
||||
|
||||
static std::string signalNameToInstanceSignalDecl(const std::string &name)
|
||||
{
|
||||
return std::string("void ") + name + "(out int " +
|
||||
GetInstanceIdParamName().data() + ")";
|
||||
}
|
||||
|
||||
ScriptSegmentType::ScriptSegmentType(
|
||||
const std::string &id, const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName)
|
||||
: _id(id)
|
||||
{
|
||||
auto sh = obs_get_signal_handler();
|
||||
|
||||
signal_handler_add(
|
||||
sh,
|
||||
signalNameToPropertiesSignalDecl(propertiesSignalName).c_str());
|
||||
signal_handler_add(
|
||||
sh, signalNameToTriggerSignalDecl(triggerSignalName).c_str());
|
||||
signal_handler_add(
|
||||
sh,
|
||||
signalNameToCompletionSignalDecl(completionSignalName).c_str());
|
||||
signal_handler_add(
|
||||
sh,
|
||||
signalNameToInstanceSignalDecl(newInstanceSignalName).c_str());
|
||||
signal_handler_add(
|
||||
sh, signalNameToInstanceSignalDecl(deletedInstanceSignalName)
|
||||
.c_str());
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
70
lib/macro/macro-script-handler.hpp
Normal file
70
lib/macro/macro-script-handler.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#include <obs.hpp>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class ScriptSegmentType {
|
||||
public:
|
||||
ScriptSegmentType() = delete;
|
||||
ScriptSegmentType(const std::string &id,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName);
|
||||
|
||||
private:
|
||||
std::string _id;
|
||||
};
|
||||
|
||||
class ScriptHandler {
|
||||
public:
|
||||
ScriptHandler() = delete;
|
||||
static void RegisterScriptAction(void *ctx, calldata_t *data);
|
||||
static void DeregisterScriptAction(void *ctx, calldata_t *data);
|
||||
static void RegisterScriptCondition(void *ctx, calldata_t *data);
|
||||
static void DeregisterScriptCondition(void *ctx, calldata_t *data);
|
||||
static void GetVariableValue(void *ctx, calldata_t *data);
|
||||
static void RegisterTempVar(void *ctx, calldata_t *data);
|
||||
static void DeregisterAllTempVars(void *ctx, calldata_t *data);
|
||||
static void SetTempVarValue(void *ctx, calldata_t *data);
|
||||
static void SetVariableValue(void *ctx, calldata_t *data);
|
||||
static void GetRunningStatus(void *ctx, calldata_t *data);
|
||||
static bool ActionIdIsValid(const std::string &id);
|
||||
static bool ConditionIdIsValid(const std::string &id);
|
||||
|
||||
private:
|
||||
static std::mutex _mutex;
|
||||
static std::unordered_map<std::string, ScriptSegmentType> _actions;
|
||||
static std::unordered_map<std::string, ScriptSegmentType> _conditions;
|
||||
};
|
||||
|
||||
static constexpr std::string_view GetPropertiesSignalParamName()
|
||||
{
|
||||
return "properties";
|
||||
}
|
||||
|
||||
static constexpr std::string_view GetActionCompletionSignalParamName()
|
||||
{
|
||||
return "completion_signal_name";
|
||||
}
|
||||
|
||||
static constexpr std::string_view GetCompletionIdParamName()
|
||||
{
|
||||
return "completion_id";
|
||||
}
|
||||
|
||||
static constexpr std::string_view GetResultSignalParamName()
|
||||
{
|
||||
return "result";
|
||||
}
|
||||
|
||||
static constexpr std::string_view GetInstanceIdParamName()
|
||||
{
|
||||
return "instance_id";
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "macro-segment-copy-paste.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "macro.hpp"
|
||||
|
||||
@@ -61,13 +62,13 @@ void AdvSceneSwitcher::PasteMacroSegment()
|
||||
const auto condition = std::static_pointer_cast<MacroCondition>(
|
||||
copyInfo.segment);
|
||||
auto logic = condition->GetLogicType();
|
||||
if (logic > LogicType::ROOT_LAST &&
|
||||
if (logic > Logic::Type::ROOT_LAST &&
|
||||
macro->Conditions().empty()) {
|
||||
logic = LogicType::ROOT_NONE;
|
||||
logic = Logic::Type::ROOT_NONE;
|
||||
}
|
||||
if (logic < LogicType::ROOT_LAST &&
|
||||
if (logic < Logic::Type::ROOT_LAST &&
|
||||
!macro->Conditions().empty()) {
|
||||
logic = LogicType::OR;
|
||||
logic = Logic::Type::OR;
|
||||
}
|
||||
AddMacroCondition(macro.get(), macro->Conditions().size(),
|
||||
copyInfo.segment->GetId(), data.Get(), logic);
|
||||
@@ -91,6 +92,27 @@ bool MacroSegmentIsInClipboard()
|
||||
return copyInfo.type != MacroSegmentCopyInfo::Type::NONE;
|
||||
}
|
||||
|
||||
bool MacroActionIsInClipboard()
|
||||
{
|
||||
return copyInfo.type == MacroSegmentCopyInfo::Type::ACTION ||
|
||||
copyInfo.type == MacroSegmentCopyInfo::Type::ELSE;
|
||||
}
|
||||
|
||||
void SetCopySegmentTargetActionType(bool setToElseAction)
|
||||
{
|
||||
if (copyInfo.type == MacroSegmentCopyInfo::Type::ACTION &&
|
||||
setToElseAction) {
|
||||
copyInfo.type = MacroSegmentCopyInfo::Type::ELSE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (copyInfo.type == MacroSegmentCopyInfo::Type::ELSE &&
|
||||
!setToElseAction) {
|
||||
copyInfo.type = MacroSegmentCopyInfo::Type::ACTION;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window)
|
||||
{
|
||||
auto copyShortcut = new QShortcut(QKeySequence("Ctrl+C"), window);
|
||||
|
||||
@@ -3,6 +3,8 @@ namespace advss {
|
||||
class AdvSceneSwitcher;
|
||||
|
||||
bool MacroSegmentIsInClipboard();
|
||||
bool MacroActionIsInClipboard();
|
||||
void SetCopySegmentTargetActionType(bool setToElseAction);
|
||||
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window);
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -59,7 +59,7 @@ static bool posIsInScrollbar(const QScrollBar *scrollbar, const QPoint &pos)
|
||||
return globalGeo.contains(pos);
|
||||
}
|
||||
|
||||
int MacroSegmentList::GetDragIndex(const QPoint &pos)
|
||||
int MacroSegmentList::GetDragIndex(const QPoint &pos) const
|
||||
{
|
||||
// Don't drag widget when interacting with the scrollbars
|
||||
if (posIsInScrollbar(horizontalScrollBar(), mapTo(this, pos)) ||
|
||||
@@ -88,12 +88,12 @@ int MacroSegmentList::GetDragIndex(const QPoint &pos)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MacroSegmentList::SetHelpMsg(const QString &msg)
|
||||
void MacroSegmentList::SetHelpMsg(const QString &msg) const
|
||||
{
|
||||
_helpMsg->setText(msg);
|
||||
}
|
||||
|
||||
void MacroSegmentList::SetHelpMsgVisible(bool visible)
|
||||
void MacroSegmentList::SetHelpMsgVisible(bool visible) const
|
||||
{
|
||||
_helpMsg->setVisible(visible);
|
||||
}
|
||||
@@ -110,12 +110,12 @@ void MacroSegmentList::Add(QWidget *widget)
|
||||
_contentLayout->addWidget(widget);
|
||||
}
|
||||
|
||||
void MacroSegmentList::Remove(int idx)
|
||||
void MacroSegmentList::Remove(int idx) const
|
||||
{
|
||||
DeleteLayoutItemWidget(_contentLayout->takeAt(idx));
|
||||
}
|
||||
|
||||
void MacroSegmentList::Clear(int idx)
|
||||
void MacroSegmentList::Clear(int idx) const
|
||||
{
|
||||
ClearLayout(_contentLayout, idx);
|
||||
}
|
||||
@@ -133,7 +133,7 @@ void MacroSegmentList::Highlight(int idx, QColor color)
|
||||
HighlightWidget(widget, color, QColor(0, 0, 0, 0), true);
|
||||
}
|
||||
|
||||
void MacroSegmentList::SetCollapsed(bool collapse)
|
||||
void MacroSegmentList::SetCollapsed(bool collapse) const
|
||||
{
|
||||
QLayoutItem *item = nullptr;
|
||||
for (int i = 0; i < _contentLayout->count(); i++) {
|
||||
@@ -145,7 +145,7 @@ void MacroSegmentList::SetCollapsed(bool collapse)
|
||||
}
|
||||
}
|
||||
|
||||
void MacroSegmentList::SetSelection(int idx)
|
||||
void MacroSegmentList::SetSelection(int idx) const
|
||||
{
|
||||
for (int i = 0; i < _contentLayout->count(); ++i) {
|
||||
auto widget = static_cast<MacroSegmentEdit *>(
|
||||
@@ -178,11 +178,7 @@ void MacroSegmentList::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton ||
|
||||
event->button() == Qt::RightButton) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
_dragPosition = GetDragIndex(event->globalPos());
|
||||
#else
|
||||
_dragPosition = GetDragIndex(event->globalPosition().toPoint());
|
||||
#endif
|
||||
emit SelectionChanged(_dragPosition);
|
||||
} else {
|
||||
_dragPosition = -1;
|
||||
@@ -235,7 +231,7 @@ void MacroSegmentList::dragEnterEvent(QDragEnterEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
MacroSegmentEdit *MacroSegmentList::WidgetAt(int idx)
|
||||
MacroSegmentEdit *MacroSegmentList::WidgetAt(int idx) const
|
||||
{
|
||||
if (idx < 0 || idx >= _contentLayout->count()) {
|
||||
return nullptr;
|
||||
@@ -247,11 +243,16 @@ MacroSegmentEdit *MacroSegmentList::WidgetAt(int idx)
|
||||
return static_cast<MacroSegmentEdit *>(item->widget());
|
||||
}
|
||||
|
||||
MacroSegmentEdit *MacroSegmentList::WidgetAt(const QPoint &pos)
|
||||
MacroSegmentEdit *MacroSegmentList::WidgetAt(const QPoint &pos) const
|
||||
{
|
||||
return WidgetAt(GetSegmentIndexFromPos(mapToGlobal(pos)));
|
||||
}
|
||||
|
||||
int MacroSegmentList::IndexAt(const QPoint &pos) const
|
||||
{
|
||||
return GetSegmentIndexFromPos(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void MacroSegmentList::HideLastDropLine()
|
||||
{
|
||||
if (_dropLineIdx >= 0 && _dropLineIdx < _contentLayout->count()) {
|
||||
@@ -295,15 +296,11 @@ void MacroSegmentList::dragMoveEvent(QDragMoveEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
_dragCursorPos = (mapToGlobal(event->pos()));
|
||||
#else
|
||||
_dragCursorPos = (mapToGlobal(event->position().toPoint()));
|
||||
#endif
|
||||
CheckDropLine(_dragCursorPos);
|
||||
}
|
||||
|
||||
QRect MacroSegmentList::GetContentItemRectWithPadding(int idx)
|
||||
QRect MacroSegmentList::GetContentItemRectWithPadding(int idx) const
|
||||
{
|
||||
auto item = _contentLayout->itemAt(idx);
|
||||
if (!item) {
|
||||
@@ -325,7 +322,7 @@ QRect MacroSegmentList::GetContentItemRectWithPadding(int idx)
|
||||
return rect;
|
||||
}
|
||||
|
||||
int MacroSegmentList::GetSegmentIndexFromPos(const QPoint &pos)
|
||||
int MacroSegmentList::GetSegmentIndexFromPos(const QPoint &pos) const
|
||||
{
|
||||
int idx = -1;
|
||||
for (int i = 0; i < _contentLayout->count(); ++i) {
|
||||
@@ -337,7 +334,7 @@ int MacroSegmentList::GetSegmentIndexFromPos(const QPoint &pos)
|
||||
return idx;
|
||||
}
|
||||
|
||||
void MacroSegmentList::CheckScroll()
|
||||
void MacroSegmentList::CheckScroll() const
|
||||
{
|
||||
while (_autoScroll) {
|
||||
const int scrollTrigger = 15;
|
||||
@@ -407,14 +404,14 @@ void MacroSegmentList::CheckDropLine(const QPoint &pos)
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroSegmentList::IsInListArea(const QPoint &pos)
|
||||
bool MacroSegmentList::IsInListArea(const QPoint &pos) const
|
||||
{
|
||||
const QRect layoutRect(mapToGlobal(_layout->contentsRect().topLeft()),
|
||||
_layout->contentsRect().size());
|
||||
return layoutRect.contains(pos);
|
||||
}
|
||||
|
||||
int MacroSegmentList::GetDropIndex(const QPoint &pos)
|
||||
int MacroSegmentList::GetDropIndex(const QPoint &pos) const
|
||||
{
|
||||
int idx = GetSegmentIndexFromPos(pos);
|
||||
if (idx == _dragPosition) {
|
||||
@@ -445,16 +442,6 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
|
||||
{
|
||||
HideLastDropLine();
|
||||
auto widget = qobject_cast<QWidget *>(event->source());
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
if (widget && !widget->geometry().contains(event->pos()) &&
|
||||
widgetIsInLayout(widget, _contentLayout)) {
|
||||
int dropPosition = GetDropIndex(mapToGlobal(event->pos()));
|
||||
if (dropPosition == -1) {
|
||||
return;
|
||||
}
|
||||
emit Reorder(dropPosition, _dragPosition);
|
||||
}
|
||||
#else
|
||||
if (widget &&
|
||||
!widget->geometry().contains(event->position().toPoint()) &&
|
||||
widgetIsInLayout(widget, _contentLayout)) {
|
||||
@@ -465,7 +452,6 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
|
||||
}
|
||||
emit Reorder(dropPosition, _dragPosition);
|
||||
}
|
||||
#endif
|
||||
_dragPosition = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,18 +15,19 @@ class MacroSegmentList : public QScrollArea {
|
||||
public:
|
||||
MacroSegmentList(QWidget *parent = nullptr);
|
||||
virtual ~MacroSegmentList();
|
||||
void SetHelpMsg(const QString &msg);
|
||||
void SetHelpMsgVisible(bool visible);
|
||||
MacroSegmentEdit *WidgetAt(int idx);
|
||||
MacroSegmentEdit *WidgetAt(const QPoint &);
|
||||
void SetHelpMsg(const QString &msg) const;
|
||||
void SetHelpMsgVisible(bool visible) const;
|
||||
MacroSegmentEdit *WidgetAt(int idx) const;
|
||||
MacroSegmentEdit *WidgetAt(const QPoint &) const;
|
||||
int IndexAt(const QPoint &) const;
|
||||
void Insert(int idx, MacroSegmentEdit *widget);
|
||||
void Add(QWidget *widget);
|
||||
void Remove(int idx);
|
||||
void Clear(int idx = 0); // Clear all elements >= idx
|
||||
void Remove(int idx) const;
|
||||
void Clear(int idx = 0) const; // Clear all elements >= idx
|
||||
void Highlight(int idx, QColor color = QColor(Qt::green));
|
||||
void SetCollapsed(bool);
|
||||
void SetSelection(int idx);
|
||||
QVBoxLayout *ContentLayout() { return _contentLayout; }
|
||||
void SetCollapsed(bool) const;
|
||||
void SetSelection(int idx) const;
|
||||
QVBoxLayout *ContentLayout() const { return _contentLayout; }
|
||||
|
||||
signals:
|
||||
void SelectionChanged(int idx);
|
||||
@@ -43,13 +44,13 @@ protected:
|
||||
void dropEvent(QDropEvent *event);
|
||||
|
||||
private:
|
||||
int GetDragIndex(const QPoint &);
|
||||
int GetDropIndex(const QPoint &);
|
||||
int GetSegmentIndexFromPos(const QPoint &);
|
||||
void CheckScroll();
|
||||
int GetSegmentIndexFromPos(const QPoint &) const;
|
||||
int GetDragIndex(const QPoint &) const;
|
||||
int GetDropIndex(const QPoint &) const;
|
||||
void CheckScroll() const;
|
||||
void CheckDropLine(const QPoint &);
|
||||
bool IsInListArea(const QPoint &);
|
||||
QRect GetContentItemRectWithPadding(int idx);
|
||||
bool IsInListArea(const QPoint &) const;
|
||||
QRect GetContentItemRectWithPadding(int idx) const;
|
||||
void HideLastDropLine();
|
||||
|
||||
int _dragPosition = -1;
|
||||
|
||||
307
lib/macro/macro-segment-script.cpp
Normal file
307
lib/macro/macro-segment-script.cpp
Normal file
@@ -0,0 +1,307 @@
|
||||
#include "macro-segment-script.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "macro-action.hpp"
|
||||
#include "macro-condition.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "macro-script-handler.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "properties-view.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
static std::atomic_int completionIdCounter = 0;
|
||||
static std::atomic_int instanceIdCounter = 0;
|
||||
static std::mutex instanceMtx;
|
||||
static std::vector<MacroSegmentScript *> instances{};
|
||||
|
||||
MacroSegmentScript::MacroSegmentScript(
|
||||
obs_data_t *defaultSettings, const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName)
|
||||
: _settings(obs_data_get_defaults(defaultSettings)),
|
||||
_propertiesSignal(propertiesSignalName),
|
||||
_triggerSignal(triggerSignalName),
|
||||
_completionSignal(completionSignalName),
|
||||
_newInstanceSignal(newInstanceSignalName),
|
||||
_deletedInstanceSignal(deletedInstanceSignalName),
|
||||
_instanceId(++instanceIdCounter)
|
||||
{
|
||||
signal_handler_connect(obs_get_signal_handler(),
|
||||
completionSignalName.c_str(),
|
||||
&MacroSegmentScript::CompletionSignalReceived,
|
||||
this);
|
||||
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
instances.emplace_back(this);
|
||||
|
||||
SignalNewInstance();
|
||||
}
|
||||
|
||||
MacroSegmentScript::MacroSegmentScript(const MacroSegmentScript &other)
|
||||
: _settings(obs_data_create()),
|
||||
_propertiesSignal(other._propertiesSignal),
|
||||
_triggerSignal(other._triggerSignal),
|
||||
_completionSignal(other._completionSignal),
|
||||
_newInstanceSignal(other._newInstanceSignal),
|
||||
_deletedInstanceSignal(other._deletedInstanceSignal),
|
||||
_instanceId(++instanceIdCounter)
|
||||
{
|
||||
signal_handler_connect(obs_get_signal_handler(),
|
||||
_completionSignal.c_str(),
|
||||
&MacroSegmentScript::CompletionSignalReceived,
|
||||
this);
|
||||
obs_data_apply(_settings.Get(), other._settings.Get());
|
||||
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
instances.emplace_back(this);
|
||||
|
||||
SignalNewInstance();
|
||||
}
|
||||
|
||||
MacroSegmentScript::~MacroSegmentScript()
|
||||
{
|
||||
auto data = calldata_create();
|
||||
calldata_set_int(data, GetInstanceIdParamName().data(), _instanceId);
|
||||
signal_handler_signal(obs_get_signal_handler(),
|
||||
_deletedInstanceSignal.c_str(), data);
|
||||
calldata_destroy(data);
|
||||
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
instances.erase(std::remove(instances.begin(), instances.end(), this),
|
||||
instances.end());
|
||||
}
|
||||
|
||||
void MacroSegmentScript::RegisterTempVar(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText,
|
||||
int instanceId)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
for (auto instance : instances) {
|
||||
if (instance->_instanceId != instanceId) {
|
||||
continue;
|
||||
}
|
||||
instance->RegisterTempVarHelper(variableId, name, helpText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MacroSegmentScript::DeregisterAllTempVars(int instanceId)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
for (auto instance : instances) {
|
||||
if (instance->_instanceId != instanceId) {
|
||||
continue;
|
||||
}
|
||||
instance->DeregisterAllTempVarsHelper();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroSegmentScript::SetTempVarValue(const std::string &variableId,
|
||||
const std::string &value,
|
||||
int instanceId)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(instanceMtx);
|
||||
for (auto instance : instances) {
|
||||
if (instance->_instanceId != instanceId) {
|
||||
continue;
|
||||
}
|
||||
instance->SetTempVarValueHelper(variableId, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroSegmentScript::Save(obs_data_t *obj) const
|
||||
{
|
||||
obs_data_set_obj(obj, "settings", _settings.Get());
|
||||
_timeout.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroSegmentScript::Load(obs_data_t *obj)
|
||||
{
|
||||
OBSDataAutoRelease settings = obs_data_get_obj(obj, "settings");
|
||||
obs_data_apply(_settings.Get(), settings);
|
||||
_timeout.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
obs_properties_t *MacroSegmentScript::GetProperties() const
|
||||
{
|
||||
auto data = calldata_create();
|
||||
signal_handler_signal(obs_get_signal_handler(),
|
||||
_propertiesSignal.c_str(), data);
|
||||
obs_properties_t *properties = nullptr;
|
||||
if (!calldata_get_ptr(data, GetPropertiesSignalParamName().data(),
|
||||
&properties)) {
|
||||
calldata_destroy(data);
|
||||
return nullptr;
|
||||
}
|
||||
calldata_destroy(data);
|
||||
return properties;
|
||||
}
|
||||
|
||||
void MacroSegmentScript::UpdateSettings(obs_data_t *newSettings) const
|
||||
{
|
||||
obs_data_clear(_settings.Get());
|
||||
obs_data_apply(_settings.Get(), newSettings);
|
||||
}
|
||||
|
||||
bool MacroSegmentScript::SendTriggerSignal()
|
||||
{
|
||||
_completionId = ++completionIdCounter;
|
||||
_triggerIsComplete = false;
|
||||
_triggerResult = false;
|
||||
|
||||
auto data = calldata_create();
|
||||
calldata_set_string(data, GetActionCompletionSignalParamName().data(),
|
||||
_completionSignal.c_str());
|
||||
calldata_set_int(data, GetCompletionIdParamName().data(),
|
||||
_completionId);
|
||||
calldata_set_string(data, "settings", obs_data_get_json(GetSettings()));
|
||||
calldata_set_int(data, GetInstanceIdParamName().data(), _instanceId);
|
||||
signal_handler_signal(obs_get_signal_handler(), _triggerSignal.c_str(),
|
||||
data);
|
||||
calldata_destroy(data);
|
||||
|
||||
SetMacroAbortWait(false);
|
||||
WaitForCompletion();
|
||||
|
||||
return _triggerResult;
|
||||
}
|
||||
|
||||
void MacroSegmentScript::CompletionSignalReceived(void *param, calldata_t *data)
|
||||
{
|
||||
auto segment = static_cast<MacroSegmentScript *>(param);
|
||||
long long int id;
|
||||
if (!calldata_get_int(data, GetCompletionIdParamName().data(), &id)) {
|
||||
blog(LOG_WARNING,
|
||||
"received completion signal without \"%s\" parameter",
|
||||
GetCompletionIdParamName().data());
|
||||
return;
|
||||
}
|
||||
bool result;
|
||||
if (!calldata_get_bool(data, GetResultSignalParamName().data(),
|
||||
&result)) {
|
||||
blog(LOG_WARNING,
|
||||
"received completion signal without \"%s\" parameter",
|
||||
GetResultSignalParamName().data());
|
||||
return;
|
||||
}
|
||||
if (id != segment->_completionId) {
|
||||
return;
|
||||
}
|
||||
segment->_triggerIsComplete = true;
|
||||
segment->_triggerResult = result;
|
||||
}
|
||||
|
||||
void MacroSegmentScript::SignalNewInstance() const
|
||||
{
|
||||
std::thread signalNewInstanceThread([this]() {
|
||||
auto data = calldata_create();
|
||||
calldata_set_int(data, GetInstanceIdParamName().data(),
|
||||
_instanceId);
|
||||
signal_handler_signal(obs_get_signal_handler(),
|
||||
_newInstanceSignal.c_str(), data);
|
||||
calldata_destroy(data);
|
||||
});
|
||||
signalNewInstanceThread.detach();
|
||||
}
|
||||
|
||||
obs_properties_t *MacroSegmentScriptEdit::GetProperties(void *obj)
|
||||
{
|
||||
auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj);
|
||||
if (!segmentEdit) {
|
||||
return nullptr;
|
||||
}
|
||||
return segmentEdit->_entryData->GetProperties();
|
||||
}
|
||||
|
||||
void MacroSegmentScriptEdit::UpdateSettings(void *obj, obs_data_t *settings)
|
||||
{
|
||||
auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj);
|
||||
if (!segmentEdit || segmentEdit->_loading || !segmentEdit->_entryData) {
|
||||
return;
|
||||
}
|
||||
auto lock = LockContext();
|
||||
segmentEdit->_entryData->UpdateSettings(settings);
|
||||
}
|
||||
|
||||
MacroSegmentScriptEdit::MacroSegmentScriptEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroSegmentScript> entryData)
|
||||
: QWidget(parent),
|
||||
_timeout(new DurationSelection(this))
|
||||
{
|
||||
QWidget::connect(_timeout, &DurationSelection::DurationChanged, this,
|
||||
&MacroSegmentScriptEdit::TimeoutChanged);
|
||||
|
||||
auto timeoutLayout = new QHBoxLayout();
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.script.timeout"),
|
||||
timeoutLayout, {{"{{timeout}}", _timeout}});
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
|
||||
auto properties = entryData->GetProperties();
|
||||
if (!!properties) {
|
||||
obs_properties_destroy(properties);
|
||||
|
||||
// We need a separate OBSData object here as we can't risk
|
||||
// entryData->_settings being modified while it is currently used
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_apply(data, entryData->GetSettings());
|
||||
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(30, 0, 0)
|
||||
auto propertiesView =
|
||||
new OBSPropertiesView(data.Get(), this, GetProperties,
|
||||
nullptr, UpdateSettings);
|
||||
layout->addWidget(propertiesView);
|
||||
connect(propertiesView, &OBSPropertiesView::PropertiesResized,
|
||||
this, [this]() {
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
});
|
||||
#else
|
||||
layout->addWidget(new QLabel(
|
||||
"Displaying script properties not supported when compiled for OBS 29!"));
|
||||
#endif
|
||||
}
|
||||
|
||||
layout->addLayout(timeoutLayout);
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroSegmentScriptEdit::UpdateEntryData()
|
||||
{
|
||||
_timeout->SetDuration(_entryData->_timeout);
|
||||
}
|
||||
|
||||
QWidget *MacroSegmentScriptEdit::Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> segment)
|
||||
{
|
||||
return new MacroSegmentScriptEdit(
|
||||
parent, std::dynamic_pointer_cast<MacroSegmentScript>(segment));
|
||||
}
|
||||
|
||||
QWidget *MacroSegmentScriptEdit::Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> segment)
|
||||
{
|
||||
return new MacroSegmentScriptEdit(
|
||||
parent, std::dynamic_pointer_cast<MacroSegmentScript>(segment));
|
||||
}
|
||||
|
||||
void MacroSegmentScriptEdit::TimeoutChanged(const Duration &timeout)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_timeout = timeout;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
101
lib/macro/macro-segment-script.hpp
Normal file
101
lib/macro/macro-segment-script.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
#include "duration-control.hpp"
|
||||
#include "macro-script-handler.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <obs-data.h>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class Macro;
|
||||
class MacroAction;
|
||||
class MacroCondition;
|
||||
|
||||
class MacroSegmentScript {
|
||||
public:
|
||||
MacroSegmentScript(obs_data_t *defaultSettings,
|
||||
const std::string &propertiesSignalName,
|
||||
const std::string &triggerSignalName,
|
||||
const std::string &completionSignalName,
|
||||
const std::string &newInstanceSignalName,
|
||||
const std::string &deletedInstanceSignalName);
|
||||
MacroSegmentScript(const advss::MacroSegmentScript &);
|
||||
virtual ~MacroSegmentScript();
|
||||
|
||||
static void RegisterTempVar(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText,
|
||||
int instanceId);
|
||||
static void DeregisterAllTempVars(int instanceId);
|
||||
static void SetTempVarValue(const std::string &variableId,
|
||||
const std::string &value, int instanceId);
|
||||
|
||||
protected:
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
|
||||
obs_properties_t *GetProperties() const;
|
||||
OBSData GetSettings() const { return _settings.Get(); }
|
||||
void UpdateSettings(obs_data_t *newSettings) const;
|
||||
|
||||
bool SendTriggerSignal();
|
||||
double GetTimeoutSeconds() const { return _timeout.Seconds(); };
|
||||
bool TriggerIsCompleted() const { return _triggerIsComplete; }
|
||||
|
||||
virtual void RegisterTempVarHelper(const std::string &variableId,
|
||||
const std::string &name,
|
||||
const std::string &helpText) = 0;
|
||||
virtual void DeregisterAllTempVarsHelper() = 0;
|
||||
virtual void SetTempVarValueHelper(const std::string &variableId,
|
||||
const std::string &value) = 0;
|
||||
|
||||
const int64_t _instanceId;
|
||||
|
||||
private:
|
||||
virtual void WaitForCompletion() const = 0;
|
||||
static void CompletionSignalReceived(void *param, calldata_t *data);
|
||||
void SignalNewInstance() const;
|
||||
|
||||
OBSDataAutoRelease _settings;
|
||||
std::string _propertiesSignal = "";
|
||||
|
||||
std::string _triggerSignal = "";
|
||||
std::string _completionSignal = "";
|
||||
std::string _newInstanceSignal = "";
|
||||
std::string _deletedInstanceSignal = "";
|
||||
std::atomic_bool _triggerIsComplete = {false};
|
||||
bool _triggerResult = false;
|
||||
int64_t _completionId = 0;
|
||||
|
||||
Duration _timeout = Duration(10.0);
|
||||
|
||||
friend class MacroSegmentScriptEdit;
|
||||
};
|
||||
|
||||
class MacroSegmentScriptEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroSegmentScriptEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroSegmentScript> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> segment);
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> segment);
|
||||
|
||||
private slots:
|
||||
void TimeoutChanged(const Duration &);
|
||||
|
||||
private:
|
||||
static obs_properties_t *GetProperties(void *obj);
|
||||
static void UpdateSettings(void *obj, obs_data_t *settings);
|
||||
|
||||
DurationSelection *_timeout;
|
||||
|
||||
std::shared_ptr<MacroSegmentScript> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -21,7 +21,7 @@ MacroSegmentSelection::MacroSegmentSelection(QWidget *parent, Type type,
|
||||
{
|
||||
_index->setMinimum(0);
|
||||
_index->setMaximum(99);
|
||||
_index->specialValueText("-");
|
||||
_index->setSpecialValueText("-");
|
||||
if (!allowVariables) {
|
||||
_index->DisableVariableSelection();
|
||||
}
|
||||
@@ -70,7 +70,8 @@ void MacroSegmentSelection::MacroSegmentOrderChanged()
|
||||
SetupDescription();
|
||||
}
|
||||
|
||||
QString GetMacroSegmentDescription(Macro *macro, int idx, bool isCondition)
|
||||
static QString GetMacroSegmentDescription(Macro *macro, int idx,
|
||||
bool isCondition)
|
||||
{
|
||||
if (!macro) {
|
||||
return "";
|
||||
|
||||
@@ -294,13 +294,8 @@ bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
|
||||
QMouseEvent *newEvent = new QMouseEvent(
|
||||
mouseEvent->type(),
|
||||
_headerInfo->mapTo(this, mouseEvent->pos()),
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
mouseEvent->globalPos(),
|
||||
#else
|
||||
mouseEvent->globalPosition(),
|
||||
#endif
|
||||
mouseEvent->button(), mouseEvent->buttons(),
|
||||
mouseEvent->modifiers());
|
||||
mouseEvent->globalPosition(), mouseEvent->button(),
|
||||
mouseEvent->buttons(), mouseEvent->modifiers());
|
||||
QApplication::sendEvent(parentWidget(), newEvent);
|
||||
}
|
||||
return QWidget::eventFilter(obj, ev);
|
||||
|
||||
@@ -51,8 +51,17 @@ protected:
|
||||
virtual void SetupTempVars();
|
||||
void AddTempvar(const std::string &id, const std::string &name,
|
||||
const std::string &description = "");
|
||||
|
||||
void SetTempVarValue(const std::string &id, const std::string &value);
|
||||
|
||||
template<typename T, typename = std::enable_if_t<
|
||||
std::is_same<std::decay_t<T>, bool>::value>>
|
||||
void SetTempVarValue(const std::string &id, T value)
|
||||
{
|
||||
SetTempVarValue(id, value ? std::string("true")
|
||||
: std::string("false"));
|
||||
}
|
||||
|
||||
private:
|
||||
void ClearAvailableTempvars();
|
||||
std::optional<const TempVariable>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QStandardItemModel>
|
||||
@@ -68,7 +69,7 @@ void MacroSelection::HideSelectedMacro()
|
||||
void MacroSelection::ShowAllMacros()
|
||||
{
|
||||
auto v = qobject_cast<QListView *>(view());
|
||||
for (int i = count(); i > 0; i--) {
|
||||
for (int i = count() - 1; i >= 0; i--) {
|
||||
v->setRowHidden(i, false);
|
||||
}
|
||||
}
|
||||
@@ -126,7 +127,7 @@ MacroSelectionDialog::MacroSelectionDialog(QWidget *parent)
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
bool MacroSelectionDialog::AskForMacro(QWidget *parent, std::string ¯oName)
|
||||
bool MacroSelectionDialog::AskForMacro(std::string ¯oName)
|
||||
{
|
||||
MacroSelectionDialog dialog(GetSettingsWindow());
|
||||
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
||||
|
||||
@@ -28,7 +28,7 @@ class MacroSelectionDialog : public QDialog {
|
||||
|
||||
public:
|
||||
MacroSelectionDialog(QWidget *parent);
|
||||
static bool AskForMacro(QWidget *parent, std::string ¯oName);
|
||||
static bool AskForMacro(std::string ¯oName);
|
||||
|
||||
private:
|
||||
MacroSelection *_macroSelection;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "macro-settings.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QScrollArea>
|
||||
@@ -20,6 +21,10 @@ void GlobalMacroSettings::Save(obs_data_t *obj) const
|
||||
obs_data_set_bool(data, "highlightActions", _highlightActions);
|
||||
obs_data_set_bool(data, "newMacroRegisterHotkey",
|
||||
_newMacroRegisterHotkeys);
|
||||
obs_data_set_bool(data, "newMacroUseShortCircuitEvaluation",
|
||||
_newMacroUseShortCircuitEvaluation);
|
||||
obs_data_set_bool(data, "saveSettingsOnMacroChange",
|
||||
_saveSettingsOnMacroChange);
|
||||
obs_data_set_obj(obj, "macroSettings", data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
@@ -38,6 +43,12 @@ void GlobalMacroSettings::Load(obs_data_t *obj)
|
||||
_highlightActions = obs_data_get_bool(data, "highlightActions");
|
||||
_newMacroRegisterHotkeys =
|
||||
obs_data_get_bool(data, "newMacroRegisterHotkey");
|
||||
_newMacroUseShortCircuitEvaluation =
|
||||
obs_data_get_bool(data, "newMacroUseShortCircuitEvaluation");
|
||||
_saveSettingsOnMacroChange =
|
||||
obs_data_has_user_value(data, "saveSettingsOnMacroChange")
|
||||
? obs_data_get_bool(data, "saveSettingsOnMacroChange")
|
||||
: true;
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
@@ -45,16 +56,29 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
||||
const GlobalMacroSettings &settings,
|
||||
Macro *macro)
|
||||
: QDialog(parent),
|
||||
_executed(new QCheckBox(obs_module_text(
|
||||
_highlightExecutedMacros(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.highlightExecutedMacros"))),
|
||||
_conditions(new QCheckBox(obs_module_text(
|
||||
_highlightConditions(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.highlightTrueConditions"))),
|
||||
_actions(new QCheckBox(obs_module_text(
|
||||
_highlightActions(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.highlightPerformedActions"))),
|
||||
_newMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))),
|
||||
_newMacroUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation"))),
|
||||
_saveSettingsOnMacroChange(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange"))),
|
||||
_currentMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentDisableHotkeys"))),
|
||||
"AdvSceneSwitcher.macroTab.currentRegisterHotkeys"))),
|
||||
_currentUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentUseShortCircuitEvaluation"))),
|
||||
_currentUseCustomConditionCheckInterval(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentUseCustomConditionCheckInterval"))),
|
||||
_currentCustomConditionCheckInterval(
|
||||
new DurationSelection(this, true, 0.01)),
|
||||
_currentCustomConditionCheckIntervalWarning(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentUseCustomConditionCheckIntervalWarning"))),
|
||||
_currentPauseSaveBehavior(new QComboBox(this)),
|
||||
_currentSkipOnStartup(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))),
|
||||
_currentStopActionsIfNotDone(new QCheckBox(obs_module_text(
|
||||
@@ -83,12 +107,32 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
||||
setWindowModality(Qt::WindowModality::WindowModal);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
_newMacroUseShortCircuitEvaluation->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.shortCircuit.tooltip"));
|
||||
_saveSettingsOnMacroChange->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange.tooltip"));
|
||||
_currentUseShortCircuitEvaluation->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.shortCircuit.tooltip"));
|
||||
|
||||
_currentPauseSaveBehavior->addItem(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.pauseStateSaveBehavior.persist"),
|
||||
static_cast<int>(Macro::PauseStateSaveBehavior::PERSIST));
|
||||
_currentPauseSaveBehavior->addItem(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.pauseStateSaveBehavior.pause"),
|
||||
static_cast<int>(Macro::PauseStateSaveBehavior::PAUSE));
|
||||
_currentPauseSaveBehavior->addItem(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.pauseStateSaveBehavior.unpause"),
|
||||
static_cast<int>(Macro::PauseStateSaveBehavior::UNPAUSE));
|
||||
|
||||
auto highlightOptions = new QGroupBox(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.highlightSettings"));
|
||||
auto highlightLayout = new QVBoxLayout;
|
||||
highlightLayout->addWidget(_executed);
|
||||
highlightLayout->addWidget(_conditions);
|
||||
highlightLayout->addWidget(_actions);
|
||||
highlightLayout->addWidget(_highlightExecutedMacros);
|
||||
highlightLayout->addWidget(_highlightConditions);
|
||||
highlightLayout->addWidget(_highlightActions);
|
||||
highlightOptions->setLayout(highlightLayout);
|
||||
|
||||
auto hotkeyOptions = new QGroupBox(
|
||||
@@ -101,8 +145,29 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
||||
auto generalOptions = new QGroupBox(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.generalSettings"));
|
||||
auto generalLayout = new QVBoxLayout;
|
||||
generalLayout->addWidget(_saveSettingsOnMacroChange);
|
||||
generalLayout->addWidget(_currentSkipOnStartup);
|
||||
generalLayout->addWidget(_currentStopActionsIfNotDone);
|
||||
generalLayout->addWidget(_currentUseShortCircuitEvaluation);
|
||||
generalLayout->addWidget(_newMacroUseShortCircuitEvaluation);
|
||||
|
||||
auto durationLayout = new QHBoxLayout();
|
||||
durationLayout->addWidget(_currentUseCustomConditionCheckInterval);
|
||||
durationLayout->addWidget(_currentCustomConditionCheckInterval);
|
||||
durationLayout->addStretch();
|
||||
auto customConditionIntervalLayout = new QVBoxLayout();
|
||||
customConditionIntervalLayout->addLayout(durationLayout);
|
||||
customConditionIntervalLayout->addWidget(
|
||||
_currentCustomConditionCheckIntervalWarning);
|
||||
generalLayout->addLayout(customConditionIntervalLayout);
|
||||
|
||||
auto pauseStateSaveBehavorLayout = new QHBoxLayout();
|
||||
pauseStateSaveBehavorLayout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.pauseStateSaveBehavior")));
|
||||
pauseStateSaveBehavorLayout->addWidget(_currentPauseSaveBehavior);
|
||||
pauseStateSaveBehavorLayout->addStretch();
|
||||
generalLayout->addLayout(pauseStateSaveBehavorLayout);
|
||||
|
||||
generalOptions->setLayout(generalLayout);
|
||||
|
||||
auto inputOptions = new QGroupBox(
|
||||
@@ -178,6 +243,13 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
||||
&MacroSettingsDialog::PauseButtonEnableChanged);
|
||||
connect(_currentMacroDockAddStatusLabel, &QCheckBox::stateChanged, this,
|
||||
&MacroSettingsDialog::StatusLabelEnableChanged);
|
||||
connect(_currentUseCustomConditionCheckInterval,
|
||||
&QCheckBox::stateChanged, this, [this](int state) {
|
||||
_currentCustomConditionCheckInterval->setEnabled(state);
|
||||
});
|
||||
connect(_currentCustomConditionCheckInterval,
|
||||
&DurationSelection::DurationChanged, this,
|
||||
[this]() { SetCustomConditionIntervalWarningVisibility(); });
|
||||
|
||||
auto scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
@@ -198,20 +270,43 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
||||
dialogLayout->addWidget(buttonbox);
|
||||
setLayout(dialogLayout);
|
||||
|
||||
_executed->setChecked(settings._highlightExecuted);
|
||||
_conditions->setChecked(settings._highlightConditions);
|
||||
_actions->setChecked(settings._highlightActions);
|
||||
_highlightExecutedMacros->setChecked(settings._highlightExecuted);
|
||||
_highlightConditions->setChecked(settings._highlightConditions);
|
||||
_highlightActions->setChecked(settings._highlightActions);
|
||||
_newMacroRegisterHotkeys->setChecked(settings._newMacroRegisterHotkeys);
|
||||
_newMacroUseShortCircuitEvaluation->setChecked(
|
||||
settings._newMacroUseShortCircuitEvaluation);
|
||||
_saveSettingsOnMacroChange->setChecked(
|
||||
settings._saveSettingsOnMacroChange);
|
||||
|
||||
if (!macro || macro->IsGroup()) {
|
||||
hotkeyOptions->hide();
|
||||
generalOptions->hide();
|
||||
|
||||
// General group
|
||||
_currentSkipOnStartup->hide();
|
||||
_currentStopActionsIfNotDone->hide();
|
||||
_currentUseShortCircuitEvaluation->hide();
|
||||
SetLayoutVisible(customConditionIntervalLayout, false);
|
||||
SetLayoutVisible(pauseStateSaveBehavorLayout, false);
|
||||
|
||||
inputOptions->hide();
|
||||
_dockOptions->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
|
||||
_currentUseShortCircuitEvaluation->setChecked(
|
||||
macro->ShortCircuitEvaluationEnabled());
|
||||
_currentUseCustomConditionCheckInterval->setChecked(
|
||||
macro->CustomConditionCheckIntervalEnabled());
|
||||
_currentCustomConditionCheckInterval->SetDuration(
|
||||
macro->GetCustomConditionCheckInterval());
|
||||
_currentCustomConditionCheckInterval->setEnabled(
|
||||
macro->CustomConditionCheckIntervalEnabled());
|
||||
SetCustomConditionIntervalWarningVisibility();
|
||||
_currentPauseSaveBehavior->setCurrentIndex(
|
||||
_currentPauseSaveBehavior->findData(
|
||||
static_cast<int>(macro->GetPauseStateSaveBehavior())));
|
||||
_currentSkipOnStartup->setChecked(macro->SkipExecOnStart());
|
||||
_currentStopActionsIfNotDone->setChecked(macro->StopActionsIfNotDone());
|
||||
_currentInputs->SetInputs(macro->GetInputVariables());
|
||||
@@ -313,6 +408,15 @@ void MacroSettingsDialog::Resize()
|
||||
_dockOptions->updateGeometry();
|
||||
}
|
||||
|
||||
void MacroSettingsDialog::SetCustomConditionIntervalWarningVisibility() const
|
||||
{
|
||||
_currentCustomConditionCheckIntervalWarning->setVisible(
|
||||
_currentUseCustomConditionCheckInterval->isChecked() &&
|
||||
GetIntervalValue() >
|
||||
_currentCustomConditionCheckInterval->GetDuration()
|
||||
.Milliseconds());
|
||||
}
|
||||
|
||||
bool MacroSettingsDialog::AskForSettings(QWidget *parent,
|
||||
GlobalMacroSettings &userInput,
|
||||
Macro *macro)
|
||||
@@ -322,17 +426,33 @@ bool MacroSettingsDialog::AskForSettings(QWidget *parent,
|
||||
if (dialog.exec() != DialogCode::Accepted) {
|
||||
return false;
|
||||
}
|
||||
userInput._highlightExecuted = dialog._executed->isChecked();
|
||||
userInput._highlightConditions = dialog._conditions->isChecked();
|
||||
userInput._highlightActions = dialog._actions->isChecked();
|
||||
userInput._highlightExecuted =
|
||||
dialog._highlightExecutedMacros->isChecked();
|
||||
userInput._highlightConditions =
|
||||
dialog._highlightConditions->isChecked();
|
||||
userInput._highlightActions = dialog._highlightActions->isChecked();
|
||||
userInput._newMacroRegisterHotkeys =
|
||||
dialog._newMacroRegisterHotkeys->isChecked();
|
||||
userInput._newMacroUseShortCircuitEvaluation =
|
||||
dialog._newMacroUseShortCircuitEvaluation->isChecked();
|
||||
userInput._saveSettingsOnMacroChange =
|
||||
dialog._saveSettingsOnMacroChange->isChecked();
|
||||
if (!macro) {
|
||||
return true;
|
||||
}
|
||||
|
||||
macro->EnablePauseHotkeys(
|
||||
dialog._currentMacroRegisterHotkeys->isChecked());
|
||||
macro->SetShortCircuitEvaluation(
|
||||
dialog._currentUseShortCircuitEvaluation->isChecked());
|
||||
macro->SetCustomConditionCheckIntervalEnabled(
|
||||
dialog._currentUseCustomConditionCheckInterval->isChecked());
|
||||
macro->SetCustomConditionCheckInterval(
|
||||
dialog._currentCustomConditionCheckInterval->GetDuration());
|
||||
macro->SetPauseStateSaveBehavior(
|
||||
static_cast<Macro::PauseStateSaveBehavior>(
|
||||
dialog._currentPauseSaveBehavior->currentData()
|
||||
.toInt()));
|
||||
macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked());
|
||||
macro->SetStopActionsIfNotDone(
|
||||
dialog._currentStopActionsIfNotDone->isChecked());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "variable-line-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "macro-input.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
@@ -23,6 +24,8 @@ public:
|
||||
bool _highlightConditions = false;
|
||||
bool _highlightActions = false;
|
||||
bool _newMacroRegisterHotkeys = true;
|
||||
bool _newMacroUseShortCircuitEvaluation = false;
|
||||
bool _saveSettingsOnMacroChange = true;
|
||||
};
|
||||
|
||||
// Dialog for configuring global and individual macro specific settings
|
||||
@@ -43,13 +46,22 @@ private slots:
|
||||
|
||||
private:
|
||||
void Resize();
|
||||
void SetCustomConditionIntervalWarningVisibility() const;
|
||||
|
||||
QCheckBox *_executed;
|
||||
QCheckBox *_conditions;
|
||||
QCheckBox *_actions;
|
||||
// Global macro settings
|
||||
QCheckBox *_highlightExecutedMacros;
|
||||
QCheckBox *_highlightConditions;
|
||||
QCheckBox *_highlightActions;
|
||||
QCheckBox *_newMacroRegisterHotkeys;
|
||||
QCheckBox *_newMacroUseShortCircuitEvaluation;
|
||||
QCheckBox *_saveSettingsOnMacroChange;
|
||||
// Current macro specific settings
|
||||
QCheckBox *_currentMacroRegisterHotkeys;
|
||||
QCheckBox *_currentUseShortCircuitEvaluation;
|
||||
QCheckBox *_currentUseCustomConditionCheckInterval;
|
||||
DurationSelection *_currentCustomConditionCheckInterval;
|
||||
QLabel *_currentCustomConditionCheckIntervalWarning;
|
||||
QComboBox *_currentPauseSaveBehavior;
|
||||
QCheckBox *_currentSkipOnStartup;
|
||||
QCheckBox *_currentStopActionsIfNotDone;
|
||||
MacroInputSelection *_currentInputs;
|
||||
|
||||
@@ -87,7 +87,8 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
|
||||
}
|
||||
|
||||
res = std::make_shared<Macro>(
|
||||
name, GetGlobalMacroSettings()._newMacroRegisterHotkeys);
|
||||
name, GetGlobalMacroSettings()._newMacroRegisterHotkeys,
|
||||
GetGlobalMacroSettings()._newMacroUseShortCircuitEvaluation);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -99,9 +100,33 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
ui->macros->Add(newMacro);
|
||||
addPulse->deleteLater();
|
||||
addPulse = nullptr;
|
||||
if (addPulse) {
|
||||
addPulse->deleteLater();
|
||||
addPulse = nullptr;
|
||||
}
|
||||
|
||||
auto selectedMacro = GetSelectedMacro();
|
||||
if (!selectedMacro) {
|
||||
ui->macros->Add(newMacro);
|
||||
emit MacroAdded(QString::fromStdString(name));
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedMacro->IsGroup()) {
|
||||
ui->macros->Add(newMacro, selectedMacro);
|
||||
emit MacroAdded(QString::fromStdString(name));
|
||||
return;
|
||||
}
|
||||
|
||||
auto selectedMacroGroup = selectedMacro->Parent();
|
||||
if (!selectedMacroGroup) {
|
||||
ui->macros->Add(newMacro, selectedMacro);
|
||||
emit MacroAdded(QString::fromStdString(name));
|
||||
return;
|
||||
}
|
||||
|
||||
Macro::PrepareMoveToGroup(selectedMacroGroup, newMacro);
|
||||
ui->macros->Add(newMacro, selectedMacro);
|
||||
emit MacroAdded(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
@@ -739,7 +764,10 @@ void AdvSceneSwitcher::MacroSelectionChanged()
|
||||
return;
|
||||
}
|
||||
SetEditMacro(*macro);
|
||||
obs_frontend_save();
|
||||
|
||||
if (GetGlobalMacroSettings()._saveSettingsOnMacroChange) {
|
||||
obs_frontend_save();
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::HighlightOnChange()
|
||||
@@ -760,6 +788,16 @@ void AdvSceneSwitcher::HighlightOnChange()
|
||||
}
|
||||
}
|
||||
|
||||
static void resetSegmentHighlights(MacroSegmentList *list)
|
||||
{
|
||||
MacroSegmentEdit *widget = nullptr;
|
||||
for (int i = 0; (widget = list->WidgetAt(i)); i++) {
|
||||
if (widget && widget->Data()) {
|
||||
(void)widget->Data()->GetHighlightAndReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_macroSettings_clicked()
|
||||
{
|
||||
GlobalMacroSettings prop = GetGlobalMacroSettings();
|
||||
@@ -768,8 +806,22 @@ void AdvSceneSwitcher::on_macroSettings_clicked()
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
GetGlobalMacroSettings() = prop;
|
||||
emit HighlightMacrosChanged(prop._highlightExecuted);
|
||||
|
||||
// Reset highlights to avoid all macro segments being highlighted, which
|
||||
// would have been highlighted at least once since the moment the
|
||||
// highlighting was disabled
|
||||
if (prop._highlightConditions) {
|
||||
resetSegmentHighlights(ui->conditionsList);
|
||||
}
|
||||
if (prop._highlightActions) {
|
||||
resetSegmentHighlights(ui->actionsList);
|
||||
resetSegmentHighlights(ui->elseActionsList);
|
||||
}
|
||||
|
||||
SetCheckIntervalTooLowVisibility();
|
||||
}
|
||||
|
||||
static void moveControlsToSplitter(QSplitter *splitter, int idx,
|
||||
@@ -1047,8 +1099,30 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
|
||||
menu.exec(globalPos);
|
||||
}
|
||||
|
||||
static void handleCustomLabelChange(MacroSegmentEdit *segmentEdit,
|
||||
QAction *contextMenuOption)
|
||||
static bool handleCustomLabelRename(MacroSegmentEdit *segmentEdit)
|
||||
{
|
||||
std::string label;
|
||||
auto segment = segmentEdit->Data();
|
||||
if (!segment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool accepted = NameDialog::AskForName(
|
||||
GetSettingsWindow(),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.segment.setCustomLabel"),
|
||||
"", label, QString::fromStdString(segment->GetCustomLabel()));
|
||||
if (!accepted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
segment->SetCustomLabel(label);
|
||||
segmentEdit->HeaderInfoChanged("");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handleCustomLabelEnableChange(MacroSegmentEdit *segmentEdit,
|
||||
QAction *contextMenuOption)
|
||||
{
|
||||
bool enable = contextMenuOption->isChecked();
|
||||
auto segment = segmentEdit->Data();
|
||||
@@ -1059,22 +1133,93 @@ static void handleCustomLabelChange(MacroSegmentEdit *segmentEdit,
|
||||
return;
|
||||
}
|
||||
|
||||
std::string label;
|
||||
bool accepted = NameDialog::AskForName(
|
||||
GetSettingsWindow(),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.segment.setCustomLabel"),
|
||||
"", label, QString::fromStdString(segment->GetCustomLabel()));
|
||||
if (!accepted) {
|
||||
if (!handleCustomLabelRename(segmentEdit)) {
|
||||
segment->SetUseCustomLabel(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void setupSegmentLabelContextMenuEntries(MacroSegmentEdit *segmentEdit,
|
||||
QMenu &menu)
|
||||
{
|
||||
if (!segmentEdit) {
|
||||
return;
|
||||
}
|
||||
|
||||
segment->SetCustomLabel(label);
|
||||
segmentEdit->HeaderInfoChanged("");
|
||||
auto segment = segmentEdit ? segmentEdit->Data() : nullptr;
|
||||
const bool customLabelIsEnabled = segment &&
|
||||
segment->GetUseCustomLabel();
|
||||
|
||||
auto enableCustomLabel = menu.addAction(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.segment.useCustomLabel"));
|
||||
enableCustomLabel->setCheckable(true);
|
||||
enableCustomLabel->setChecked(customLabelIsEnabled);
|
||||
QWidget::connect(enableCustomLabel, &QAction::triggered,
|
||||
[segmentEdit, enableCustomLabel]() {
|
||||
handleCustomLabelEnableChange(
|
||||
segmentEdit, enableCustomLabel);
|
||||
});
|
||||
|
||||
if (!customLabelIsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto customLabelRename = menu.addAction(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.segment.customLabelRename"));
|
||||
QWidget::connect(customLabelRename, &QAction::triggered,
|
||||
[segmentEdit]() {
|
||||
handleCustomLabelRename(segmentEdit);
|
||||
});
|
||||
}
|
||||
|
||||
static void setupCopyPasteContextMenuEnry(AdvSceneSwitcher *ss,
|
||||
MacroSegmentEdit *segmentEdit,
|
||||
QMenu &menu)
|
||||
{
|
||||
auto copy = menu.addAction(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.segment.copy"), ss,
|
||||
[ss]() { ss->CopyMacroSegment(); });
|
||||
copy->setEnabled(!!segmentEdit);
|
||||
|
||||
bool pasteAsElseAction = true;
|
||||
const char *pasteText = "AdvSceneSwitcher.macroTab.segment.paste";
|
||||
if (MacroActionIsInClipboard()) {
|
||||
if (IsCursorInWidgetArea(ss->ui->macroActions)) {
|
||||
pasteAsElseAction = false;
|
||||
pasteText =
|
||||
"AdvSceneSwitcher.macroTab.segment.pasteAction";
|
||||
} else if (IsCursorInWidgetArea(ss->ui->macroElseActions)) {
|
||||
pasteAsElseAction = true;
|
||||
pasteText =
|
||||
"AdvSceneSwitcher.macroTab.segment.pasteElseAction";
|
||||
}
|
||||
}
|
||||
auto paste = menu.addAction(
|
||||
obs_module_text(pasteText), ss, [ss, pasteAsElseAction]() {
|
||||
SetCopySegmentTargetActionType(pasteAsElseAction);
|
||||
ss->PasteMacroSegment();
|
||||
});
|
||||
paste->setEnabled(MacroSegmentIsInClipboard());
|
||||
}
|
||||
|
||||
static void setupRemoveContextMenuEnry(
|
||||
AdvSceneSwitcher *ss,
|
||||
const std::function<void(AdvSceneSwitcher *, int)> &remove,
|
||||
const QPoint &pos, MacroSegmentList *list, QMenu &menu)
|
||||
{
|
||||
const auto segmentEditIndex = list->IndexAt(pos);
|
||||
if (segmentEditIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
menu.addAction(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.segment.remove"), ss,
|
||||
[ss, remove, segmentEditIndex]() {
|
||||
remove(ss, segmentEditIndex);
|
||||
});
|
||||
}
|
||||
|
||||
static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
|
||||
std::function<void(AdvSceneSwitcher *, int)> remove,
|
||||
std::function<void(AdvSceneSwitcher *)> expand,
|
||||
std::function<void(AdvSceneSwitcher *)> collapse,
|
||||
std::function<void(AdvSceneSwitcher *)> maximize,
|
||||
@@ -1084,49 +1229,28 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
|
||||
QMenu menu;
|
||||
auto segmentEdit = list->WidgetAt(pos);
|
||||
|
||||
auto copy = menu.addAction(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.segment.copy"), ss,
|
||||
[ss]() { ss->CopyMacroSegment(); });
|
||||
copy->setEnabled(!!segmentEdit);
|
||||
auto paste = menu.addAction(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.segment.paste"), ss,
|
||||
[ss]() { ss->PasteMacroSegment(); });
|
||||
paste->setEnabled(MacroSegmentIsInClipboard());
|
||||
|
||||
setupCopyPasteContextMenuEnry(ss, segmentEdit, menu);
|
||||
menu.addSeparator();
|
||||
|
||||
if (segmentEdit) {
|
||||
auto customLabel = menu.addAction(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.segment.useCustomLabel"));
|
||||
customLabel->setCheckable(true);
|
||||
auto segment = segmentEdit ? segmentEdit->Data() : nullptr;
|
||||
customLabel->setChecked(segment &&
|
||||
segment->GetUseCustomLabel());
|
||||
QWidget::connect(customLabel, &QAction::triggered,
|
||||
std::bind(handleCustomLabelChange, segmentEdit,
|
||||
customLabel));
|
||||
}
|
||||
|
||||
setupRemoveContextMenuEnry(ss, remove, pos, list, menu);
|
||||
menu.addSeparator();
|
||||
setupSegmentLabelContextMenuEntries(segmentEdit, menu);
|
||||
menu.addSeparator();
|
||||
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
|
||||
ss, [ss, expand]() { expand(ss); });
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"),
|
||||
ss, [ss, collapse]() { collapse(ss); });
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"),
|
||||
ss, [ss, maximize]() { maximize(ss); });
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"),
|
||||
ss, [ss, minimize]() { minimize(ss); });
|
||||
|
||||
menu.exec(list->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ShowMacroActionsContextMenu(const QPoint &pos)
|
||||
{
|
||||
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllActions,
|
||||
setupConextMenu(this, pos, &AdvSceneSwitcher::RemoveMacroAction,
|
||||
&AdvSceneSwitcher::ExpandAllActions,
|
||||
&AdvSceneSwitcher::CollapseAllActions,
|
||||
&AdvSceneSwitcher::MaximizeActions,
|
||||
&AdvSceneSwitcher::MinimizeActions, ui->actionsList);
|
||||
@@ -1134,7 +1258,8 @@ void AdvSceneSwitcher::ShowMacroActionsContextMenu(const QPoint &pos)
|
||||
|
||||
void AdvSceneSwitcher::ShowMacroElseActionsContextMenu(const QPoint &pos)
|
||||
{
|
||||
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllElseActions,
|
||||
setupConextMenu(this, pos, &AdvSceneSwitcher::RemoveMacroElseAction,
|
||||
&AdvSceneSwitcher::ExpandAllElseActions,
|
||||
&AdvSceneSwitcher::CollapseAllElseActions,
|
||||
&AdvSceneSwitcher::MaximizeElseActions,
|
||||
&AdvSceneSwitcher::MinimizeElseActions,
|
||||
@@ -1143,7 +1268,8 @@ void AdvSceneSwitcher::ShowMacroElseActionsContextMenu(const QPoint &pos)
|
||||
|
||||
void AdvSceneSwitcher::ShowMacroConditionsContextMenu(const QPoint &pos)
|
||||
{
|
||||
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllConditions,
|
||||
setupConextMenu(this, pos, &AdvSceneSwitcher::RemoveMacroCondition,
|
||||
&AdvSceneSwitcher::ExpandAllConditions,
|
||||
&AdvSceneSwitcher::CollapseAllConditions,
|
||||
&AdvSceneSwitcher::MaximizeConditions,
|
||||
&AdvSceneSwitcher::MinimizeConditions,
|
||||
@@ -1165,7 +1291,7 @@ void AdvSceneSwitcher::CopyMacro()
|
||||
}
|
||||
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
macro->Save(data);
|
||||
macro->Save(data, true);
|
||||
newMacro->Load(data);
|
||||
newMacro->PostLoad();
|
||||
newMacro->SetName(name);
|
||||
@@ -1173,8 +1299,10 @@ void AdvSceneSwitcher::CopyMacro()
|
||||
Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
|
||||
|
||||
ui->macros->Add(newMacro, macro);
|
||||
addPulse->deleteLater();
|
||||
addPulse = nullptr;
|
||||
if (addPulse) {
|
||||
addPulse->deleteLater();
|
||||
addPulse = nullptr;
|
||||
}
|
||||
emit MacroAdded(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ void MacroTreeItem::Update(bool force)
|
||||
_spacer = nullptr;
|
||||
}
|
||||
|
||||
if (_type == Type::Group) {
|
||||
if (_type == Type::Group && _expand) {
|
||||
_boxLayout->removeWidget(_expand);
|
||||
_expand->deleteLater();
|
||||
_expand = nullptr;
|
||||
@@ -178,6 +178,7 @@ void MacroTreeItem::Update(bool force)
|
||||
QSizePolicy::Maximum);
|
||||
_expand->setMaximumSize(10, 16);
|
||||
_expand->setMinimumSize(10, 0);
|
||||
_expand->setProperty("class", "checkbox-icon indicator-expand");
|
||||
#ifdef __APPLE__
|
||||
_expand->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
#endif
|
||||
@@ -888,14 +889,7 @@ void MacroTree::dropEvent(QDropEvent *event)
|
||||
QModelIndexList indices = selectedIndexes();
|
||||
|
||||
DropIndicatorPosition indicator = dropIndicatorPosition();
|
||||
int row = indexAt(
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
event->position().toPoint()
|
||||
#else
|
||||
event->pos()
|
||||
#endif
|
||||
)
|
||||
.row();
|
||||
int row = indexAt(event->position().toPoint()).row();
|
||||
bool emptyDrop = row == -1;
|
||||
|
||||
if (emptyDrop) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user