Compare commits
372 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68c6492c3f | ||
|
|
0ba7ba77d8 | ||
|
|
04b2b3474d | ||
|
|
ecbb5ebbd7 | ||
|
|
b62757b65d | ||
|
|
b1a3ab5493 | ||
|
|
abc3357180 | ||
|
|
29f9cba236 | ||
|
|
9a62522140 | ||
|
|
666c52556e | ||
|
|
1c0734d1cb | ||
|
|
1304ac7336 | ||
|
|
884fb1b637 | ||
|
|
a82bce947f | ||
|
|
d5241f0700 | ||
|
|
f24f258c9c | ||
|
|
eddd9fa8ab | ||
|
|
6cc224aeec | ||
|
|
4b0a631987 | ||
|
|
dcae0e8e8b | ||
|
|
29f810a2a0 | ||
|
|
719dfa7b72 | ||
|
|
259aac8fcd | ||
|
|
5be2cd278f | ||
|
|
211eac5313 | ||
|
|
f92ed3139d | ||
|
|
bdff84c605 | ||
|
|
08e430396a | ||
|
|
df7fc7ec01 | ||
|
|
9eef581161 | ||
|
|
52811e44f4 | ||
|
|
7b1c56877e | ||
|
|
78d73af589 | ||
|
|
d9c1ef4c22 | ||
|
|
93f96ee660 | ||
|
|
d25a8d4efb | ||
|
|
1f22278496 | ||
|
|
70b705e0fd | ||
|
|
9e1dfdc3f0 | ||
|
|
47d6d4ac06 | ||
|
|
fe3586b6f4 | ||
|
|
650881204f | ||
|
|
8c0ba112e2 | ||
|
|
a17d8f7e09 | ||
|
|
371b4ae05d | ||
|
|
1d7ce510f7 | ||
|
|
d759ded64d | ||
|
|
0b2e1b88cc | ||
|
|
72a11eedfc | ||
|
|
1f02226620 | ||
|
|
ad8c29997c | ||
|
|
2c7c40c3f0 | ||
|
|
c9c0e4db2e | ||
|
|
dd880b13c0 | ||
|
|
76c759c8f4 | ||
|
|
284c8020b2 | ||
|
|
f506600b8c | ||
|
|
339418b5f3 | ||
|
|
2eca2cc2e9 | ||
|
|
4315d309f2 | ||
|
|
8d18bb363a | ||
|
|
592f31f059 | ||
|
|
300ad15ad0 | ||
|
|
91f93169eb | ||
|
|
5d95069291 | ||
|
|
e5bb9162ef | ||
|
|
6869cc8a01 | ||
|
|
55c7ac8a32 | ||
|
|
101a85b063 | ||
|
|
e7c6afbc6a | ||
|
|
18e19fb6c8 | ||
|
|
a7ccfc3282 | ||
|
|
85eb2a4235 | ||
|
|
3e3805b542 | ||
|
|
c59acd836a | ||
|
|
1e81181312 | ||
|
|
8a796efdb3 | ||
|
|
1a07a93552 | ||
|
|
bd74629420 | ||
|
|
dabb293cc0 | ||
|
|
b6031bd29a | ||
|
|
05fa3af2ac | ||
|
|
c52166bfec | ||
|
|
6d3964fa5d | ||
|
|
65545d85ba | ||
|
|
dea4ff47cd | ||
|
|
258a5f6ab7 | ||
|
|
2048ca2816 | ||
|
|
eb9e2e8062 | ||
|
|
3229bf0150 | ||
|
|
dfa12f9807 | ||
|
|
e0917a35fd | ||
|
|
686b00ffd1 | ||
|
|
78402f6546 | ||
|
|
a1b5fbb1fe | ||
|
|
b71f633fac | ||
|
|
dbb674aa8e | ||
|
|
018daf7f09 | ||
|
|
a07a84a8b1 | ||
|
|
0f36f34a4f | ||
|
|
cf2cebd0fc | ||
|
|
90dd59abe6 | ||
|
|
5332ed6bef | ||
|
|
d45d4ded99 | ||
|
|
25b5590d2c | ||
|
|
d2b2ced5bf | ||
|
|
c551953283 | ||
|
|
5ae1f38ea8 | ||
|
|
372f12de95 | ||
|
|
9cfa88dd8c | ||
|
|
7b355ebc49 | ||
|
|
43a56c8ec8 | ||
|
|
3da90bfa97 | ||
|
|
dd572a7456 | ||
|
|
8cd818b80e | ||
|
|
530bbc07c5 | ||
|
|
a992f7a0e8 | ||
|
|
ca3718594d | ||
|
|
17a66853d5 | ||
|
|
8c317c1396 | ||
|
|
20f888b991 | ||
|
|
cd0d3862c7 | ||
|
|
443ffb7906 | ||
|
|
5967c83b03 | ||
|
|
66498a03ce | ||
|
|
9ec772f723 | ||
|
|
20eb56db2d | ||
|
|
e4d064aaa3 | ||
|
|
5602dfa09a | ||
|
|
0138b412b4 | ||
|
|
20a934c9ab | ||
|
|
09d2b397dc | ||
|
|
482a99cf15 | ||
|
|
81ac45a1d4 | ||
|
|
0b8078f3bf | ||
|
|
8b6ace902c | ||
|
|
1dead9fca5 | ||
|
|
524c4b71d7 | ||
|
|
e51318768d | ||
|
|
c821bb20d0 | ||
|
|
c6b693b4ac | ||
|
|
2c5caabab0 | ||
|
|
b7479e8342 | ||
|
|
ffca124762 | ||
|
|
9bedbf6fb8 | ||
|
|
853150e458 | ||
|
|
1ddd48e008 | ||
|
|
17d24d642e | ||
|
|
87a46684cd | ||
|
|
e4c5193945 | ||
|
|
2e969bf6dd | ||
|
|
6db26d9f7b | ||
|
|
4f02c9ec36 | ||
|
|
2fdd39fba5 | ||
|
|
fe1ddc3a94 | ||
|
|
cff2d805db | ||
|
|
34425d3e47 | ||
|
|
4ac4f00620 | ||
|
|
e30a4623c5 | ||
|
|
e2f4dcaaee | ||
|
|
ebbf64ddae | ||
|
|
8b105ad22b | ||
|
|
f3416c5b1d | ||
|
|
e194fc5ee4 | ||
|
|
e465c81bf1 | ||
|
|
9dce6a99fd | ||
|
|
6f2c53d14e | ||
|
|
9044ae4ade | ||
|
|
0a422174bc | ||
|
|
c2020fe77c | ||
|
|
8beca6fcc7 | ||
|
|
18769df2ca | ||
|
|
2806fd6cdb | ||
|
|
b607940c8c | ||
|
|
dff599df4c | ||
|
|
b3cd38e019 | ||
|
|
61975e1992 | ||
|
|
1a8ad69f23 | ||
|
|
f21e40151b | ||
|
|
4e58769648 | ||
|
|
f9d6001025 | ||
|
|
ae5f5f90ff | ||
|
|
89db21d734 | ||
|
|
0e4b072650 | ||
|
|
13b0e00b27 | ||
|
|
bf5d8ca19e | ||
|
|
4a3339c09c | ||
|
|
b28c2b28b2 | ||
|
|
fcb3e648d2 | ||
|
|
5d6f08e38f | ||
|
|
b932af1c97 | ||
|
|
8874eb2b1c | ||
|
|
dcf9816a72 | ||
|
|
c8722c9f62 | ||
|
|
a82a2ccffe | ||
|
|
0bf70e443e | ||
|
|
3d6d690583 | ||
|
|
783f76d55d | ||
|
|
1dd46fb777 | ||
|
|
22bbd3a4ca | ||
|
|
3cd4b2251f | ||
|
|
9e4b003608 | ||
|
|
e66d0bc6b8 | ||
|
|
9a4abd78ed | ||
|
|
826bee8b1e | ||
|
|
143020dc27 | ||
|
|
be23065988 | ||
|
|
2cb62eae96 | ||
|
|
76c411d1e2 | ||
|
|
16fa91c2a1 | ||
|
|
b77f8717fe | ||
|
|
59ecf43c5f | ||
|
|
1fd8546354 | ||
|
|
7cb95cb3f6 | ||
|
|
160d19d6c1 | ||
|
|
a0550ca141 | ||
|
|
ac68fc016d | ||
|
|
5170307f72 | ||
|
|
3af0e51bd2 | ||
|
|
f34d1fe081 | ||
|
|
83c72ebba5 | ||
|
|
6ca8e6b3ea | ||
|
|
ba24fc2fab | ||
|
|
7f28b22599 | ||
|
|
4345db0d2e | ||
|
|
eddc10c3e2 | ||
|
|
46eaada0c4 | ||
|
|
f8b2dea270 | ||
|
|
90ebf0e0f8 | ||
|
|
1a819c807e | ||
|
|
3220809c04 | ||
|
|
72d5255c9e | ||
|
|
5995563a80 | ||
|
|
347743d93a | ||
|
|
3c067ce723 | ||
|
|
8dd2170dfa | ||
|
|
5f3a3dd5cf | ||
|
|
8ad3563963 | ||
|
|
492128ef86 | ||
|
|
b4936274f2 | ||
|
|
64ccd5ba53 | ||
|
|
7ae88f7560 | ||
|
|
ae70af26d1 | ||
|
|
483bdc237c | ||
|
|
2c564b47f4 | ||
|
|
fc80d0b5f3 | ||
|
|
d094c189d3 | ||
|
|
40d8819e77 | ||
|
|
4a3d019f06 | ||
|
|
5366a4a6ed | ||
|
|
e8230d8e5b | ||
|
|
ebdbb9011f | ||
|
|
e43793354e | ||
|
|
438c54599e | ||
|
|
da42183177 | ||
|
|
295fa3f122 | ||
|
|
cb87f1cc36 | ||
|
|
19eb5074f2 | ||
|
|
828a69bbf7 | ||
|
|
c01d471b5f | ||
|
|
8751c0d296 | ||
|
|
334d173dc9 | ||
|
|
1c3ff927a5 | ||
|
|
b12c25fdf4 | ||
|
|
e9f4ac7ca7 | ||
|
|
f1cdab80a9 | ||
|
|
ae6f0bc470 | ||
|
|
3d4b69848c | ||
|
|
c4c4a27dcb | ||
|
|
87ed11d3bf | ||
|
|
23e2fb8d5a | ||
|
|
af5950748d | ||
|
|
df9dc1e002 | ||
|
|
5c7e0f292f | ||
|
|
a5f610b7b7 | ||
|
|
df8e84dd09 | ||
|
|
ee5038106a | ||
|
|
6617c08b89 | ||
|
|
42773790e4 | ||
|
|
1fd6f75ee1 | ||
|
|
8a2ccef8ec | ||
|
|
d30f389db7 | ||
|
|
a2906a534b | ||
|
|
30294842f2 | ||
|
|
eb478d2948 | ||
|
|
e4ee55cd3d | ||
|
|
9c4fe38c97 | ||
|
|
81e64382c9 | ||
|
|
b55db1a79a | ||
|
|
dcdcd23361 | ||
|
|
3ccc057547 | ||
|
|
d04884fe8b | ||
|
|
7ae490ca25 | ||
|
|
cd74534e4c | ||
|
|
e4f9ccec3f | ||
|
|
f106329892 | ||
|
|
14f0194372 | ||
|
|
632ad5085f | ||
|
|
fafaafd578 | ||
|
|
e01f28f2bf | ||
|
|
fd4476c0b3 | ||
|
|
c96c81cf1d | ||
|
|
3e4293f9b0 | ||
|
|
a80af327d7 | ||
|
|
2116bd7a78 | ||
|
|
11571751b9 | ||
|
|
f9de63bc88 | ||
|
|
378bf4dfd6 | ||
|
|
5444d6fd9e | ||
|
|
15d7e2f972 | ||
|
|
357d9447f2 | ||
|
|
6c9d6095c9 | ||
|
|
bba029e901 | ||
|
|
4eafbb8f18 | ||
|
|
e147402250 | ||
|
|
e0602f8de2 | ||
|
|
11f769cf09 | ||
|
|
4ab88ea218 | ||
|
|
20cbe636df | ||
|
|
601b035285 | ||
|
|
0027ae9bb8 | ||
|
|
ffccc435ca | ||
|
|
8d6b9e37d5 | ||
|
|
70c9a3bd83 | ||
|
|
b7c6deddde | ||
|
|
feab4fe744 | ||
|
|
b5ec9e7586 | ||
|
|
a901f59f0f | ||
|
|
ea826f1be0 | ||
|
|
f7afb9c446 | ||
|
|
42198a8773 | ||
|
|
368034ad5a | ||
|
|
0d1a3e5646 | ||
|
|
76be75474b | ||
|
|
cca32e59c2 | ||
|
|
85bb0e62dc | ||
|
|
0ce9c515af | ||
|
|
03854376ef | ||
|
|
88b7603ed1 | ||
|
|
c07ef3c699 | ||
|
|
f3e5fb59f3 | ||
|
|
ef0f6839e5 | ||
|
|
6a488316d8 | ||
|
|
8dc434f45e | ||
|
|
7b65286181 | ||
|
|
05fce566f4 | ||
|
|
cc3ce3f0e0 | ||
|
|
20ba02be8d | ||
|
|
e3d9bf11a4 | ||
|
|
31511851c9 | ||
|
|
4f71c55034 | ||
|
|
46fb63e960 | ||
|
|
034a5255a5 | ||
|
|
1be2764d7b | ||
|
|
34355379ca | ||
|
|
fd8e3c1e5c | ||
|
|
7407bc7d82 | ||
|
|
d636022576 | ||
|
|
ae9ef2bf3b | ||
|
|
624ff9f5c6 | ||
|
|
9bcced524b | ||
|
|
3bb2ddfaac | ||
|
|
50e26aba72 | ||
|
|
267ab6a7c1 | ||
|
|
65ea7987c7 | ||
|
|
a6839666ce | ||
|
|
e5843de4fc | ||
|
|
a330378c16 | ||
|
|
ddb7888e61 | ||
|
|
73aef4f0b2 | ||
|
|
67c3b73e10 | ||
|
|
9a3d381cf0 |
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -22,6 +22,7 @@ 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.
|
||||
|
||||
4
.github/actions/build-plugin/action.yml
vendored
@@ -31,6 +31,10 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Setup cmake
|
||||
uses: jwlawson/actions-setup-cmake@v1.13
|
||||
with:
|
||||
cmake-version: '3.24.x'
|
||||
- name: Run macOS Build
|
||||
if: ${{ runner.os == 'macOS' }}
|
||||
shell: zsh {0}
|
||||
|
||||
2
.github/actions/package-plugin/action.yml
vendored
@@ -65,6 +65,8 @@ runs:
|
||||
if [[ '${{ inputs.notarize }}' == 'true' ]] package_args+=(-n)
|
||||
if (( ${+CI} && ${+RUNNER_DEBUG} )) build_args+=(--debug)
|
||||
|
||||
package_args+=(-z)
|
||||
|
||||
${{ inputs.workingDirectory }}/.github/scripts/package-macos.zsh ${package_args}
|
||||
|
||||
- name: Run Linux packaging
|
||||
|
||||
37
.github/actions/run-tests/action.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: 'Package plugin'
|
||||
description: 'Packages the plugin for specified architecture and build config.'
|
||||
inputs:
|
||||
target:
|
||||
description: 'Build target'
|
||||
required: true
|
||||
workingDirectory:
|
||||
description: 'Working directory'
|
||||
required: false
|
||||
default: ${{ github.workspace }}
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Run macOS tests
|
||||
if: ${{ runner.os == 'macOS' }}
|
||||
shell: zsh {0}
|
||||
run: |
|
||||
if [[ '${{ inputs.target }}' != 'x86_64' ]]; then
|
||||
echo tests skipped!
|
||||
exit 0
|
||||
fi
|
||||
${{ inputs.workingDirectory }}/build_x86_64/tests/advanced-scene-switcher-tests
|
||||
|
||||
- name: Run Linux packaging
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ '${{ inputs.target }}' != 'x86_64' ]]; then
|
||||
exit 0
|
||||
fi
|
||||
${{ inputs.workingDirectory }}/build_x86_64/tests/advanced-scene-switcher-tests
|
||||
|
||||
- name: Run Windows packaging
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
${{ inputs.workingDirectory }}/build_x64/tests/RelWithDebInfo/advanced-scene-switcher-tests.exe
|
||||
87
.github/scripts/.build.zsh
vendored
@@ -170,26 +170,38 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
|
||||
local product_name
|
||||
local product_version
|
||||
local git_tag="$(git describe --tags)"
|
||||
|
||||
read -r product_name product_version <<< \
|
||||
"$(jq -r '. | {name, version} | join(" ")' ${buildspec_file})"
|
||||
|
||||
local opencv_dir="${project_root}/deps/opencv"
|
||||
local opencv_build_dir="${opencv_dir}/build_${target##*-}"
|
||||
if [[ "${git_tag}" =~ '^([0-9]+\.){0,2}(\*|[0-9]+)$' ]] {
|
||||
log_info "Using git tag as version identifier '${git_tag}'"
|
||||
product_version="${git_tag}"
|
||||
} else {
|
||||
log_info "Using buildspec.json version identifier '${product_version}'"
|
||||
}
|
||||
|
||||
case ${host_os} {
|
||||
macos)
|
||||
sed -i '' \
|
||||
"s/project(\(.*\) VERSION \(.*\))/project(${product_name} VERSION ${product_version})/" \
|
||||
"${project_root}/CMakeLists.txt"
|
||||
|
||||
# Note: IPP must be set to OFF to avoid build failures on ARM architectures
|
||||
local opencv_dir="${project_root}/deps/opencv"
|
||||
local opencv_build_dir="${opencv_dir}/build_${target##*-}"
|
||||
|
||||
local -a opencv_cmake_args=(
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DBUILD_LIST=core,imgproc,objdetect
|
||||
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
|
||||
-DWITH_IPP=OFF
|
||||
)
|
||||
|
||||
if [ "${target}" != "macos-x86_64" ]; then
|
||||
opencv_cmake_args+=(-DWITH_IPP=OFF)
|
||||
fi
|
||||
|
||||
pushd ${opencv_dir}
|
||||
log_info "Configure OpenCV ..."
|
||||
cmake -S . -B ${opencv_build_dir} -G ${generator} ${opencv_cmake_args}
|
||||
@@ -198,9 +210,73 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
cmake --build ${opencv_build_dir} --config Release
|
||||
|
||||
log_info "Installing OpenCV..."
|
||||
cmake --install ${opencv_build_dir} --config Release --prefix ${opencv_build_dir}
|
||||
cmake --install ${opencv_build_dir} --config Release || true
|
||||
popd
|
||||
|
||||
local leptonica_dir="${project_root}/deps/leptonica"
|
||||
local leptonica_build_dir="${leptonica_dir}/build_${target##*-}"
|
||||
|
||||
local -a leptonica_cmake_args=(
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
|
||||
-DSW_BUILD=OFF
|
||||
-DOPENJPEG_SUPPORT=OFF
|
||||
-DLIBWEBP_SUPPORT=OFF
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_GIF=TRUE
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_JPEG=TRUE
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_TIFF=TRUE
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_PNG=TRUE
|
||||
)
|
||||
|
||||
pushd ${leptonica_dir}
|
||||
log_info "Configure Leptonica ..."
|
||||
cmake -S . -B ${leptonica_build_dir} -G ${generator} ${leptonica_cmake_args}
|
||||
|
||||
log_info "Building Leptonica ..."
|
||||
cmake --build ${leptonica_build_dir} --config Release
|
||||
|
||||
log_info "Installing Leptonica..."
|
||||
# Workaround for "unknown file attribute: H" errors when running install
|
||||
cmake --install ${leptonica_build_dir} --config Release || :
|
||||
popd
|
||||
|
||||
local tesseract_dir="${project_root}/deps/tesseract"
|
||||
local tesseract_build_dir="${tesseract_dir}/build_${target##*-}"
|
||||
|
||||
local -a tesseract_cmake_args=(
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
|
||||
-DSW_BUILD=OFF
|
||||
-DBUILD_TRAINING_TOOLS=OFF
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
pushd ${tesseract_dir}
|
||||
log_info "Configure Tesseract ..."
|
||||
cmake -S . -B ${tesseract_build_dir} -G ${generator} ${tesseract_cmake_args}
|
||||
|
||||
log_info "Building Tesseract ..."
|
||||
#cmake --build ${tesseract_build_dir} --config Release --target libtesseract
|
||||
cmake --build ${tesseract_build_dir} --config Release
|
||||
|
||||
log_info "Installing Tesseract..."
|
||||
#cmake --install ${tesseract_build_dir} --config Release --component libtesseract
|
||||
cmake --install ${tesseract_build_dir} --config Release
|
||||
popd
|
||||
;;
|
||||
linux)
|
||||
sed -i'' \
|
||||
@@ -242,7 +318,6 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
|
||||
-DOBS_CODESIGN_LINKER=ON
|
||||
-DOBS_BUNDLE_CODESIGN_IDENTITY="${CODESIGN_IDENT:--}"
|
||||
-DOpenCV_DIR="${opencv_build_dir}"
|
||||
)
|
||||
|
||||
;;
|
||||
|
||||
15
.github/scripts/.package.zsh
vendored
@@ -128,9 +128,18 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
|
||||
local product_name
|
||||
local product_version
|
||||
local git_tag="$(git describe --tags)"
|
||||
|
||||
read -r product_name product_version <<< \
|
||||
"$(jq -r '. | {name, version} | join(" ")' ${project_root}/buildspec.json)"
|
||||
|
||||
if [[ "${git_tag}" =~ '^([0-9]+\.){0,2}(\*|[0-9]+)$' ]] {
|
||||
log_info "Using git tag as version identifier '${git_tag}'"
|
||||
product_version="${git_tag}"
|
||||
} else {
|
||||
log_info "Using buildspec.json version identifier '${product_version}'"
|
||||
}
|
||||
|
||||
if [[ ${host_os} == 'macos' ]] {
|
||||
autoload -Uz check_packages read_codesign read_codesign_installer read_codesign_pass
|
||||
|
||||
@@ -180,6 +189,12 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
--keychain-profile "OBS-Codesign-Password" --wait
|
||||
xcrun stapler staple "${project_root}/release/${output_name}"
|
||||
}
|
||||
if (( ${+ZIP} )) {
|
||||
local output_name="${product_name}-${host_os}-${target##*-}.zip"
|
||||
pushd ${project_root}/release
|
||||
zip -r "${output_name}" *~*.pkg
|
||||
popd
|
||||
}
|
||||
popd
|
||||
} elif [[ ${host_os} == 'linux' ]] {
|
||||
if (( ${+ZIP} )) {
|
||||
|
||||
101
.github/scripts/Build-Windows.ps1
vendored
@@ -37,7 +37,7 @@ function Build {
|
||||
|
||||
$UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse
|
||||
|
||||
foreach($Utility in $UtilityFunctions) {
|
||||
foreach ($Utility in $UtilityFunctions) {
|
||||
Write-Debug "Loading $($Utility.FullName)"
|
||||
. $Utility.FullName
|
||||
}
|
||||
@@ -57,18 +57,22 @@ function Build {
|
||||
$CmakeGenerator = $script:VisualStudioVersion
|
||||
}
|
||||
|
||||
$DepsPath = "plugin-deps-${script:DepsVersion}-qt${script:QtVersion}-${script:Target}"
|
||||
$DepInstallPath = "$(Resolve-Path -Path ${ProjectRoot}/../obs-build-dependencies/${DepsPath})"
|
||||
|
||||
$OpenCVPath = "${ProjectRoot}/deps/opencv"
|
||||
$OpenCVBuildPath = "${OpenCVPath}/build"
|
||||
|
||||
Push-Location -Stack BuildOpenCVTemp
|
||||
if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) {
|
||||
Ensure-Location $ProjectRoot
|
||||
|
||||
$OpenCVPath = "${ProjectRoot}/deps/opencv"
|
||||
$OpenCVBuildPath = "${OpenCVPath}/build"
|
||||
$OpenCVCmakeArgs = @(
|
||||
'-G', $CmakeGenerator
|
||||
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
|
||||
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
|
||||
"-DCMAKE_BUILD_TYPE=Release"
|
||||
"-DCMAKE_PREFIX_PATH:PATH=$(Resolve-Path -Path ${ProjectRoot}/../obs-build-dependencies/${DepsPath})"
|
||||
"-DCMAKE_PREFIX_PATH:PATH=${DepInstallPath}"
|
||||
"-DBUILD_LIST=core,imgproc,objdetect"
|
||||
)
|
||||
|
||||
@@ -80,18 +84,92 @@ function Build {
|
||||
)
|
||||
|
||||
if ( $VerbosePreference -eq 'Continue' ) {
|
||||
$OpenCVCmakeArgs+=('--verbose')
|
||||
$OpenCVCmakeArgs += ('--verbose')
|
||||
}
|
||||
|
||||
Log-Information "Building OpenCV..."
|
||||
Invoke-External cmake --build "${OpenCVBuildPath}" @OpenCVCmakeArgs
|
||||
}
|
||||
Log-Information "Install OpenCV}..."
|
||||
Invoke-External cmake --install "${OpenCVBuildPath}" --prefix "${OpenCVBuildPath}" @OpenCVCmakeArgs
|
||||
Invoke-External cmake --install "${OpenCVBuildPath}" --prefix "${DepInstallPath}" @OpenCVCmakeArgs
|
||||
|
||||
$LeptonicaPath = "${ProjectRoot}/deps/leptonica"
|
||||
$LeptonicaBuildPath = "${LeptonicaPath}/build"
|
||||
|
||||
Push-Location -Stack BuildLeptonicaTemp
|
||||
if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) {
|
||||
Ensure-Location $ProjectRoot
|
||||
|
||||
$LeptonicaCmakeArgs = @(
|
||||
'-G', $CmakeGenerator
|
||||
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
|
||||
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
|
||||
"-DCMAKE_BUILD_TYPE=${Configuration}"
|
||||
"-DCMAKE_PREFIX_PATH:PATH=${DepInstallPath}"
|
||||
"-DCMAKE_INSTALL_PREFIX:PATH=${DepInstallPath}"
|
||||
"-DSW_BUILD=OFF"
|
||||
"-DLIBWEBP_SUPPORT=OFF"
|
||||
)
|
||||
|
||||
Log-Information "Configuring leptonica..."
|
||||
Invoke-External cmake -S ${LeptonicaPath} -B ${LeptonicaBuildPath} @LeptonicaCmakeArgs
|
||||
|
||||
$LeptonicaCmakeArgs = @(
|
||||
'--config', "${Configuration}"
|
||||
)
|
||||
|
||||
if ( $VerbosePreference -eq 'Continue' ) {
|
||||
$LeptonicaCmakeArgs += ('--verbose')
|
||||
}
|
||||
|
||||
Log-Information "Building leptonica..."
|
||||
Invoke-External cmake --build "${LeptonicaBuildPath}" @LeptonicaCmakeArgs
|
||||
}
|
||||
Log-Information "Install leptonica..."
|
||||
Invoke-External cmake --install "${LeptonicaBuildPath}" --prefix "${DepInstallPath}" @LeptonicaCmakeArgs
|
||||
|
||||
Push-Location -Stack BuildTesseractTemp
|
||||
if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) {
|
||||
Ensure-Location $ProjectRoot
|
||||
|
||||
$TesseractPath = "${ProjectRoot}/deps/tesseract"
|
||||
$TesseractBuildPath = "${TesseractPath}/build"
|
||||
|
||||
# Explicitly disable PkgConfig and tiff as it will lead build errors
|
||||
$TesseractCmakeArgs = @(
|
||||
'-G', $CmakeGenerator
|
||||
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
|
||||
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
|
||||
"-DCMAKE_BUILD_TYPE=${Configuration}"
|
||||
"-DCMAKE_PREFIX_PATH:PATH=${DepInstallPath}"
|
||||
"-DCMAKE_INSTALL_PREFIX:PATH=${DepInstallPath}"
|
||||
"-DSW_BUILD=OFF"
|
||||
"-DDISABLE_CURL=ON"
|
||||
"-DBUILD_TRAINING_TOOLS=OFF"
|
||||
"-DCMAKE_DISABLE_FIND_PACKAGE_TIFF=TRUE"
|
||||
"-DCMAKE_DISABLE_FIND_PACKAGE_PkgConfig=TRUE"
|
||||
)
|
||||
|
||||
Log-Information "Configuring tesseract..."
|
||||
Invoke-External cmake -S ${TesseractPath} -B ${TesseractBuildPath} @TesseractCmakeArgs
|
||||
|
||||
$TesseractCmakeArgs = @(
|
||||
'--config', "${Configuration}"
|
||||
)
|
||||
|
||||
if ( $VerbosePreference -eq 'Continue' ) {
|
||||
$TesseractCmakeArgs += ('--verbose')
|
||||
}
|
||||
|
||||
Log-Information "Building tesseract..."
|
||||
Invoke-External cmake --build "${TesseractBuildPath}" @TesseractCmakeArgs
|
||||
}
|
||||
Log-Information "Install tesseract..."
|
||||
Invoke-External cmake --install "${TesseractBuildPath}" --prefix "${DepInstallPath}" @TesseractCmakeArgs
|
||||
|
||||
(Get-Content -Path ${ProjectRoot}/CMakeLists.txt -Raw) `
|
||||
-replace "project\((.*) VERSION (.*)\)", "project(${ProductName} VERSION ${ProductVersion})" `
|
||||
| Out-File -Path ${ProjectRoot}/CMakeLists.txt
|
||||
| Out-File -Path ${ProjectRoot}/CMakeLists.txt
|
||||
|
||||
Setup-Obs
|
||||
|
||||
@@ -99,18 +177,13 @@ function Build {
|
||||
if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) {
|
||||
Ensure-Location $ProjectRoot
|
||||
|
||||
$OpenCVPath = "${ProjectRoot}/deps/opencv"
|
||||
$OpenCVBuildPath = "${OpenCVPath}/build"
|
||||
|
||||
$DepsPath = "plugin-deps-${script:DepsVersion}-qt${script:QtVersion}-${script:Target}"
|
||||
$CmakeArgs = @(
|
||||
'-G', $CmakeGenerator
|
||||
"-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}"
|
||||
"-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })"
|
||||
"-DCMAKE_BUILD_TYPE=${Configuration}"
|
||||
"-DCMAKE_PREFIX_PATH:PATH=$(Resolve-Path -Path "${ProjectRoot}/../obs-build-dependencies/${DepsPath}")"
|
||||
"-DCMAKE_PREFIX_PATH:PATH=${DepInstallPath}"
|
||||
"-DQT_VERSION=${script:QtVersion}"
|
||||
"-DOpenCV_DIR=${OpenCVBuildPath}"
|
||||
)
|
||||
|
||||
Log-Debug "Attempting to configure OBS with CMake arguments: $($CmakeArgs | Out-String)"
|
||||
@@ -122,7 +195,7 @@ function Build {
|
||||
)
|
||||
|
||||
if ( $VerbosePreference -eq 'Continue' ) {
|
||||
$CmakeArgs+=('--verbose')
|
||||
$CmakeArgs += ('--verbose')
|
||||
}
|
||||
|
||||
Log-Information "Building ${ProductName}..."
|
||||
|
||||
1
.github/scripts/utils.zsh/setup_linux
vendored
@@ -38,6 +38,7 @@ if (( ! (${skips[(Ie)all]} + ${skips[(Ie)deps]}) )) {
|
||||
libxtst-dev \
|
||||
libxss-dev \
|
||||
libopencv-dev \
|
||||
libtesseract-dev \
|
||||
libprocps-dev
|
||||
|
||||
local -a _qt_packages=()
|
||||
|
||||
15
.github/workflows/build-debian.yml
vendored
@@ -27,23 +27,10 @@ jobs:
|
||||
- name: check_libobs_revision
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libobs-dev
|
||||
sudo apt install devscripts libobs-dev
|
||||
mkdir source
|
||||
cd source
|
||||
dpkg -l libobs-dev | tr -s " "| grep libobs | cut -d" " -f3 > libobs.rev
|
||||
- name: install_frontend_header
|
||||
run: |
|
||||
[ -e /usr/include/obs/obs-frontend-api.h ] && { echo "ERROR: obs-frontend-api.h already in system. Maybe the package libobs-dev is installing it."; exit 1; }
|
||||
cd source
|
||||
LIBOBSREV=$(cat libobs.rev)
|
||||
sudo apt update
|
||||
sudo apt install devscripts
|
||||
dget -u http://archive.ubuntu.com/ubuntu/pool/universe/o/obs-studio/obs-studio_$LIBOBSREV.dsc
|
||||
cd ..
|
||||
sudo find -name obs-frontend-api.h -exec cp {} /usr/include/obs/ \;
|
||||
egrep '#include <obs.h>' /usr/include/obs/obs-frontend-api.h || { echo "ERROR: check if the sed commands are needed from now."; exit 1; }
|
||||
sudo sed -i 's/#include <obs.h>/#include <obs\/obs.h>/' /usr/include/obs/obs-frontend-api.h
|
||||
sudo sed -i 's/#include <util\/darray.h>/#include <obs\/util\/darray.h>/' /usr/include/obs/obs-frontend-api.h
|
||||
- name: create_tarball
|
||||
run: |
|
||||
cd ..
|
||||
|
||||
84
.github/workflows/main.yml
vendored
@@ -92,28 +92,32 @@ jobs:
|
||||
if [[ '${{ secrets.MACOS_SIGNING_APPLICATION_IDENTITY }}' != '' && \
|
||||
'${{ secrets.MACOS_SIGNING_INSTALLER_IDENTITY }}' != '' && \
|
||||
'${{ secrets.MACOS_SIGNING_CERT }}' != '' ]] {
|
||||
print '::set-output name=haveCodesignIdent::true'
|
||||
print 'haveCodesignIdent=true' >> $GITHUB_OUTPUT
|
||||
} else {
|
||||
print '::set-output name=haveCodesignIdent::false'
|
||||
print 'haveCodesignIdent=false' >> $GITHUB_OUTPUT
|
||||
}
|
||||
|
||||
if [[ '${{ secrets.MACOS_NOTARIZATION_USERNAME }}' != '' && \
|
||||
'${{ secrets.MACOS_NOTARIZATION_PASSWORD }}' != '' ]] {
|
||||
print '::set-output name=haveNotarizationUser::true'
|
||||
print 'haveNotarizationUser=true' >> $GITHUB_OUTPUT
|
||||
} else {
|
||||
print '::set-output name=haveNotarizationUser::false'
|
||||
print 'haveNotarizationUser=false' >> $GITHUB_OUTPUT
|
||||
}
|
||||
print '::endgroup::'
|
||||
|
||||
print "::set-output name=ccacheDate::$(date +"%Y-%m-%d")"
|
||||
print "::set-output name=commitHash::${"$(git rev-parse HEAD)"[0,9]}"
|
||||
print "ccacheDate=$(date +"%Y-%m-%d")" >> $GITHUB_OUTPUT
|
||||
print "commitHash=${"$(git rev-parse HEAD)"[0,9]}" >> $GITHUB_OUTPUT
|
||||
echo "$PWD/.github/scripts" >> $GITHUB_PATH
|
||||
|
||||
- name: Restore Compilation Cache
|
||||
id: ccache-cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
path: |
|
||||
${{ github.workspace }}/.ccache
|
||||
${{ github.workspace }}/plugin/deps/opencv/build_*
|
||||
${{ github.workspace }}/plugin/deps/leptonica/build_*
|
||||
${{ github.workspace }}/plugin/deps/tesseract/build*
|
||||
key: macos-${{ matrix.arch }}-ccache-plugin-${{ steps.setup.outputs.ccacheDate }}
|
||||
restore-keys: |
|
||||
macos-${{ matrix.arch }}-ccache-plugin-
|
||||
@@ -123,9 +127,9 @@ jobs:
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
if [[ -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')" ]] {
|
||||
print '::set-output name=found::true'
|
||||
print 'found=true' >> $GITHUB_OUTPUT
|
||||
} else {
|
||||
print '::set-output name=found::false'
|
||||
print 'found=false' >> $GITHUB_OUTPUT
|
||||
}
|
||||
|
||||
- name: Install Apple Developer Certificate
|
||||
@@ -151,6 +155,12 @@ jobs:
|
||||
codesign: 'true'
|
||||
codesignIdent: ${{ env.CODESIGN_IDENT }}
|
||||
|
||||
- name: Run tests
|
||||
uses: ./plugin/.github/actions/run-tests
|
||||
with:
|
||||
workingDirectory: ${{ github.workspace }}/plugin
|
||||
target: ${{ matrix.arch }}
|
||||
|
||||
- name: Package Plugin
|
||||
uses: ./plugin/.github/actions/package-plugin
|
||||
with:
|
||||
@@ -164,12 +174,19 @@ jobs:
|
||||
codesignUser: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}
|
||||
codesignPass: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
|
||||
|
||||
- name: Upload Installer Artifact
|
||||
if: ${{ success() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.PLUGIN_NAME }}-macos-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}-installer
|
||||
path: ${{ github.workspace }}/plugin/release/${{ env.LIB_NAME }}*-macos-${{ matrix.arch }}.pkg
|
||||
|
||||
- name: Upload Build Artifact
|
||||
if: ${{ success() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.PLUGIN_NAME }}-macos-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}
|
||||
path: ${{ github.workspace }}/plugin/release/${{ env.LIB_NAME }}*-macos-${{ matrix.arch }}.pkg
|
||||
path: ${{ github.workspace }}/plugin/release/${{ env.LIB_NAME }}-*.zip
|
||||
|
||||
linux_build:
|
||||
name: 02 - Linux
|
||||
@@ -206,15 +223,16 @@ jobs:
|
||||
id: setup
|
||||
run: |
|
||||
## SETUP ENVIRONMENT SCRIPT
|
||||
echo "::set-output name=ccacheDate::$(date +"%Y-%m-%d")"
|
||||
echo "::set-output name=commitHash::$(git rev-parse HEAD | cut -c1-9)"
|
||||
echo "ccacheDate=$(date +"%Y-%m-%d")" >> $GITHUB_OUTPUT
|
||||
echo "commitHash=$(git rev-parse HEAD | cut -c1-9)" >> $GITHUB_OUTPUT
|
||||
echo "$PWD/.github/scripts" >> $GITHUB_PATH
|
||||
|
||||
- name: Restore Compilation Cache
|
||||
id: ccache-cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
path: |
|
||||
${{ github.workspace }}/.ccache
|
||||
key: linux-${{ matrix.arch }}-ccache-plugin-${{ steps.setup.outputs.ccacheDate }}
|
||||
restore-keys: |
|
||||
linux-${{ matrix.arch }}-ccache-plugin-
|
||||
@@ -225,9 +243,9 @@ jobs:
|
||||
run: |
|
||||
## GITHUB LABEL SCRIPT
|
||||
if [[ -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')" ]]; then
|
||||
echo '::set-output name=found::true'
|
||||
echo 'found=true' >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo '::set-output name=found::false'
|
||||
echo 'found=false' >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build Plugin
|
||||
@@ -238,6 +256,12 @@ jobs:
|
||||
config: RelWithDebInfo
|
||||
portable: ${{ matrix.portable }}
|
||||
|
||||
- name: Run tests
|
||||
uses: ./plugin/.github/actions/run-tests
|
||||
with:
|
||||
workingDirectory: ${{ github.workspace }}/plugin
|
||||
target: ${{ matrix.arch }}
|
||||
|
||||
- name: Package Plugin
|
||||
uses: ./plugin/.github/actions/package-plugin
|
||||
with:
|
||||
@@ -294,8 +318,23 @@ jobs:
|
||||
id: setup
|
||||
run: |
|
||||
## SETUP ENVIRONMENT SCRIPT
|
||||
$CcacheDate = (Get-Date -Format "yyyy-M-d")
|
||||
"ccacheDate=${CcacheDate}" >> $env:GITHUB_OUTPUT
|
||||
$CommitHash = (git rev-parse HEAD)[0..8] -join ''
|
||||
Write-Output "::set-output name=commitHash::${CommitHash}"
|
||||
"commitHash=${CommitHash}" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Restore Compilation Cache
|
||||
id: ccache-cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
${{ github.workspace }}/.ccache
|
||||
${{ github.workspace }}/plugin/deps/opencv/build_*
|
||||
${{ github.workspace }}/plugin/deps/leptonica/build_*
|
||||
${{ github.workspace }}/plugin/deps/tesseract/build*
|
||||
key: windows-${{ matrix.arch }}-ccache-plugin-${{ steps.setup.outputs.ccacheDate }}
|
||||
restore-keys: |
|
||||
windows-${{ matrix.arch }}-ccache-plugin-
|
||||
|
||||
- name: Check for GitHub Labels
|
||||
id: seekingTesters
|
||||
@@ -316,7 +355,7 @@ jobs:
|
||||
$false
|
||||
}
|
||||
|
||||
Write-Output "::set-output name=found::$(([string]${LabelFound}).ToLower())"
|
||||
"found=$(([string]${LabelFound}).ToLower())" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Build Plugin
|
||||
uses: ./plugin/.github/actions/build-plugin
|
||||
@@ -326,6 +365,12 @@ jobs:
|
||||
config: RelWithDebInfo
|
||||
visualStudio: 'Visual Studio 17 2022'
|
||||
|
||||
- name: Run tests
|
||||
uses: ./plugin/.github/actions/run-tests
|
||||
with:
|
||||
workingDirectory: ${{ github.workspace }}/plugin
|
||||
target: ${{ matrix.arch }}
|
||||
|
||||
- name: Package Plugin
|
||||
uses: ./plugin/.github/actions/package-plugin
|
||||
with:
|
||||
@@ -367,7 +412,7 @@ jobs:
|
||||
id: metadata
|
||||
run: |
|
||||
## METADATA SCRIPT
|
||||
echo "::set-output name=version::${GITHUB_REF/refs\/tags\//}"
|
||||
echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
@@ -380,10 +425,11 @@ jobs:
|
||||
for file in ${{ github.workspace }}/**/@(*.pkg|*.exe|*.deb|*.zip); do
|
||||
echo " ${file##*/}: $(sha256sum "${file}" | cut -d " " -f 1)" >> ${{ github.workspace }}/CHECKSUMS.txt
|
||||
done
|
||||
cat ${{ github.workspace }}/CHECKSUMS.txt
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@1e07f4398721186383de40550babbdf2b84acfc5
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
prerelease: true
|
||||
|
||||
9
.gitmodules
vendored
@@ -10,3 +10,12 @@
|
||||
[submodule "deps/openvr"]
|
||||
path = deps/openvr
|
||||
url = https://github.com/ValveSoftware/openvr.git
|
||||
[submodule "deps/leptonica"]
|
||||
path = deps/leptonica
|
||||
url = https://github.com/DanBloomberg/leptonica.git
|
||||
[submodule "deps/tesseract"]
|
||||
path = deps/tesseract
|
||||
url = https://github.com/tesseract-ocr/tesseract.git
|
||||
[submodule "deps/libremidi"]
|
||||
path = deps/libremidi
|
||||
url = https://github.com/jcelerier/libremidi.git
|
||||
|
||||
120
BUILDING.md
@@ -15,7 +15,7 @@ cd obs-studio/UI/frontend-plugins/
|
||||
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
|
||||
```
|
||||
|
||||
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt file and add an entry for the scene switcher:
|
||||
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt Example and add an entry for the scene switcher:
|
||||
```
|
||||
add_subdirectory(SceneSwitcher)
|
||||
```
|
||||
@@ -66,11 +66,123 @@ Finally, start the plugin build using your provided generator. (E.g. Ninja on Li
|
||||
|
||||
Contributions to the plugin are always welcome and if you need any assistance do not hesitate to reach out.
|
||||
|
||||
If you would like to expand upon the macro system by adding a new condition or action type have a loot at the examples in `src/macro-core`.
|
||||
In general changes in the `src/legacy` folder should be avoided.
|
||||
|
||||
If you would like to expand upon the macro system by adding a new condition or action type have a loot at the examples in `src/macro-core`.
|
||||
The key functions to add conditions or are the Register() functions.
|
||||
## Macro condition
|
||||
Macro conditions should inherit from the `MacroCondition` class and must implement the following functions:
|
||||
|
||||
```
|
||||
class MacroConditionExample : public MacroCondition {
|
||||
public:
|
||||
MacroConditionExample(Macro *m) : MacroCondition(m) {}
|
||||
// This function should perfrom the condition check
|
||||
bool CheckCondition();
|
||||
// This function should store the required condition data to "obj"
|
||||
// For example called on OBS shutdown
|
||||
bool Save(obs_data_t *obj);
|
||||
// This function should load the condition data from "obj"
|
||||
// For example called on OBS startup
|
||||
bool Load(obs_data_t *obj);
|
||||
// This function should return a unique id for this condition type
|
||||
// The _id is defined below
|
||||
std::string GetId() { return _id; };
|
||||
// Helper function called when new conditions are created
|
||||
// Will be used later when regeistering the new condition type
|
||||
static std::shared_ptr<MacroCondition> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroConditionExample>(m);
|
||||
}
|
||||
|
||||
private:
|
||||
// Used to register new condition type
|
||||
static bool _registered;
|
||||
// Unique id identifying this condition type
|
||||
static const std::string _id;
|
||||
};
|
||||
```
|
||||
When defining the widget used to control the settings of the condition type, it is important to add a static `Create()` method.
|
||||
It will be called whenever a new condition MacroConditionExample is created. (See `MacroConditionFactory::Register()`)
|
||||
```
|
||||
class MacroConditionExampleEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// ...
|
||||
MacroConditionExampleEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionExample> cond = nullptr);
|
||||
|
||||
// Function will be used to create the widget for editing the settings of the condition
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionExampleEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionExample>(cond));
|
||||
}
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
To finally register the new condition type you will have to add the following call to `MacroConditionFactory::Register()`.
|
||||
|
||||
```
|
||||
bool MacroConditionExample::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionExample::id, // Unique string identifying this condition type
|
||||
{
|
||||
MacroConditionExample::Create, // Function called to create the object performing the condition
|
||||
MacroConditionExampleEdit::Create, // Function called to create the widget configure the condition
|
||||
"AdvSceneSwitcher.condition.example", // User facing name of the condition type (will be translated)
|
||||
true // Condition type supports duration modifiers (default true)
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Macro action
|
||||
|
||||
The process of adding a new action type is very similar to adding a new condition.
|
||||
The differences are highlighted in the comments below.
|
||||
|
||||
```
|
||||
class MacroActionExample : public MacroAction {
|
||||
public:
|
||||
MacroActionExample(Macro *m) : MacroAction(m) {}
|
||||
// This function should perfrom the action
|
||||
// If false is returned the macro will aborted
|
||||
bool PerformAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return _id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionExample>(m);
|
||||
}
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string _id;
|
||||
};
|
||||
```
|
||||
```
|
||||
class MacroActionExampleEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// ...
|
||||
MacroActionExampleEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionExample> entryData = nullptr);
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionExampleEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionExample>(action));
|
||||
}
|
||||
// ...
|
||||
};
|
||||
```
|
||||
```
|
||||
MacroActionFactory::Register(
|
||||
MacroActionExample::id, // Unique string identifying this action type
|
||||
@@ -81,6 +193,6 @@ MacroActionFactory::Register(
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## External dependencies
|
||||
If your intention is to add macro functionality which depends on external libraries, which is likely not to exist on all user setups, try to follow the examples under `src/macro-external`.
|
||||
These are basically plugins themselves that get attempted to be loaded on startup of the advanced scene switcher.
|
||||
|
||||
143
CMakeLists.txt
@@ -42,21 +42,20 @@ target_sources(
|
||||
src/general.cpp
|
||||
src/hotkey.cpp
|
||||
src/hotkey.hpp
|
||||
src/hotkey.hpp
|
||||
src/platform-funcs.hpp
|
||||
src/scene-group.cpp
|
||||
src/scene-group.hpp
|
||||
src/status-control.cpp
|
||||
src/status-control.hpp
|
||||
src/switcher-data-structs.cpp
|
||||
src/switcher-data-structs.hpp
|
||||
src/switcher-data.cpp
|
||||
src/switcher-data.hpp
|
||||
src/version.cpp
|
||||
src/version.h)
|
||||
|
||||
# Legacy function sources
|
||||
target_sources(
|
||||
${LIB_NAME}
|
||||
PRIVATE src/legacy/scene-trigger.cpp
|
||||
PRIVATE src/legacy/scene-group.cpp
|
||||
src/legacy/scene-group.hpp
|
||||
src/legacy/scene-trigger.cpp
|
||||
src/legacy/scene-trigger.hpp
|
||||
src/legacy/switch-audio.cpp
|
||||
src/legacy/switch-audio.hpp
|
||||
@@ -74,7 +73,6 @@ target_sources(
|
||||
src/legacy/switch-network.hpp
|
||||
src/legacy/switch-pause.cpp
|
||||
src/legacy/switch-pause.hpp
|
||||
src/legacy/switch-priority.cpp
|
||||
src/legacy/switch-random.cpp
|
||||
src/legacy/switch-random.hpp
|
||||
src/legacy/switch-screen-region.cpp
|
||||
@@ -109,10 +107,14 @@ target_sources(
|
||||
src/macro-core/macro-action-macro.hpp
|
||||
src/macro-core/macro-action-media.cpp
|
||||
src/macro-core/macro-action-media.hpp
|
||||
src/macro-core/macro-action-osc.cpp
|
||||
src/macro-core/macro-action-osc.hpp
|
||||
src/macro-core/macro-action-plugin-state.cpp
|
||||
src/macro-core/macro-action-plugin-state.hpp
|
||||
src/macro-core/macro-action-profile.cpp
|
||||
src/macro-core/macro-action-profile.hpp
|
||||
src/macro-core/macro-action-projector.cpp
|
||||
src/macro-core/macro-action-projector.hpp
|
||||
src/macro-core/macro-action-random.cpp
|
||||
src/macro-core/macro-action-random.hpp
|
||||
src/macro-core/macro-action-recording.cpp
|
||||
@@ -123,6 +125,8 @@ target_sources(
|
||||
src/macro-core/macro-action-run.hpp
|
||||
src/macro-core/macro-action-scene-collection.cpp
|
||||
src/macro-core/macro-action-scene-collection.hpp
|
||||
src/macro-core/macro-action-scene-lock.cpp
|
||||
src/macro-core/macro-action-scene-lock.hpp
|
||||
src/macro-core/macro-action-scene-order.cpp
|
||||
src/macro-core/macro-action-scene-order.hpp
|
||||
src/macro-core/macro-action-scene-switch.cpp
|
||||
@@ -163,6 +167,8 @@ target_sources(
|
||||
src/macro-core/macro-condition-cursor.hpp
|
||||
src/macro-core/macro-condition-date.cpp
|
||||
src/macro-core/macro-condition-date.hpp
|
||||
src/macro-core/macro-condition-display.cpp
|
||||
src/macro-core/macro-condition-display.hpp
|
||||
src/macro-core/macro-condition-edit.cpp
|
||||
src/macro-core/macro-condition-edit.hpp
|
||||
src/macro-core/macro-condition-file.cpp
|
||||
@@ -189,12 +195,16 @@ target_sources(
|
||||
src/macro-core/macro-condition-recording.hpp
|
||||
src/macro-core/macro-condition-replay-buffer.cpp
|
||||
src/macro-core/macro-condition-replay-buffer.hpp
|
||||
src/macro-core/macro-condition-run.cpp
|
||||
src/macro-core/macro-condition-run.hpp
|
||||
src/macro-core/macro-condition-scene-order.cpp
|
||||
src/macro-core/macro-condition-scene-order.hpp
|
||||
src/macro-core/macro-condition-scene-transform.cpp
|
||||
src/macro-core/macro-condition-scene-transform.hpp
|
||||
src/macro-core/macro-condition-scene-visibility.cpp
|
||||
src/macro-core/macro-condition-scene-visibility.hpp
|
||||
src/macro-core/macro-condition-slideshow.cpp
|
||||
src/macro-core/macro-condition-slideshow.hpp
|
||||
src/macro-core/macro-condition-scene.cpp
|
||||
src/macro-core/macro-condition-scene.hpp
|
||||
src/macro-core/macro-condition-source.cpp
|
||||
@@ -217,8 +227,8 @@ target_sources(
|
||||
src/macro-core/macro-condition-window.hpp
|
||||
src/macro-core/macro-condition.cpp
|
||||
src/macro-core/macro-condition.hpp
|
||||
src/macro-core/macro-list-entry-widget.cpp
|
||||
src/macro-core/macro-list-entry-widget.hpp
|
||||
src/macro-core/macro-dock.cpp
|
||||
src/macro-core/macro-dock.hpp
|
||||
src/macro-core/macro-properties.cpp
|
||||
src/macro-core/macro-properties.hpp
|
||||
src/macro-core/macro-ref.cpp
|
||||
@@ -230,6 +240,8 @@ target_sources(
|
||||
src/macro-core/macro-selection.cpp
|
||||
src/macro-core/macro-selection.hpp
|
||||
src/macro-core/macro-tab.cpp
|
||||
src/macro-core/macro-tree.cpp
|
||||
src/macro-core/macro-tree.hpp
|
||||
src/macro-core/macro.cpp
|
||||
src/macro-core/macro.hpp)
|
||||
|
||||
@@ -244,12 +256,36 @@ target_sources(
|
||||
src/utils/duration-control.hpp
|
||||
src/utils/item-selection-helpers.cpp
|
||||
src/utils/item-selection-helpers.hpp
|
||||
src/utils/log-helper.hpp
|
||||
src/utils/file-selection.cpp
|
||||
src/utils/file-selection.hpp
|
||||
src/utils/filter-combo-box.cpp
|
||||
src/utils/filter-combo-box.hpp
|
||||
src/utils/filter-selection.cpp
|
||||
src/utils/filter-selection.hpp
|
||||
src/utils/macro-export-import-dialog.cpp
|
||||
src/utils/macro-export-import-dialog.hpp
|
||||
src/utils/macro-list.cpp
|
||||
src/utils/macro-list.hpp
|
||||
src/utils/macro-segment-selection.cpp
|
||||
src/utils/macro-segment-selection.hpp
|
||||
src/utils/math-helpers.cpp
|
||||
src/utils/math-helpers.hpp
|
||||
src/utils/mouse-wheel-guard.cpp
|
||||
src/utils/mouse-wheel-guard.hpp
|
||||
src/utils/name-dialog.cpp
|
||||
src/utils/name-dialog.hpp
|
||||
src/utils/non-modal-dialog.cpp
|
||||
src/utils/non-modal-dialog.hpp
|
||||
src/utils/obs-dock.hpp
|
||||
src/utils/obs-module-helper.cpp
|
||||
src/utils/obs-module-helper.hpp
|
||||
src/utils/osc-helpers.cpp
|
||||
src/utils/osc-helpers.hpp
|
||||
src/utils/priority-helper.cpp
|
||||
src/utils/priority-helper.hpp
|
||||
src/utils/process-config.cpp
|
||||
src/utils/process-config.hpp
|
||||
src/utils/regex-config.cpp
|
||||
src/utils/regex-config.hpp
|
||||
src/utils/resizing-text-edit.cpp
|
||||
@@ -258,16 +294,40 @@ target_sources(
|
||||
src/utils/scene-item-selection.hpp
|
||||
src/utils/scene-selection.cpp
|
||||
src/utils/scene-selection.hpp
|
||||
src/utils/scene-switch-helpers.cpp
|
||||
src/utils/scene-switch-helpers.hpp
|
||||
src/utils/screenshot-helper.cpp
|
||||
src/utils/screenshot-helper.hpp
|
||||
src/utils/section.cpp
|
||||
src/utils/section.hpp
|
||||
src/utils/slider-spinbox.cpp
|
||||
src/utils/slider-spinbox.hpp
|
||||
src/utils/source-selection.cpp
|
||||
src/utils/source-selection.hpp
|
||||
src/utils/string-list.cpp
|
||||
src/utils/string-list.hpp
|
||||
src/utils/striped-frame.cpp
|
||||
src/utils/striped-frame.hpp
|
||||
src/utils/switch-button.cpp
|
||||
src/utils/switch-button.hpp
|
||||
src/utils/sync-helper.cpp
|
||||
src/utils/sync-helper.hpp
|
||||
src/utils/transition-selection.cpp
|
||||
src/utils/transition-selection.hpp
|
||||
src/utils/utility.cpp
|
||||
src/utils/utility.hpp
|
||||
src/utils/variable.cpp
|
||||
src/utils/variable.hpp
|
||||
src/utils/variable-line-edit.cpp
|
||||
src/utils/variable-line-edit.hpp
|
||||
src/utils/variable-number.tpp
|
||||
src/utils/variable-number.hpp
|
||||
src/utils/variable-spinbox.cpp
|
||||
src/utils/variable-spinbox.hpp
|
||||
src/utils/variable-string.cpp
|
||||
src/utils/variable-string.hpp
|
||||
src/utils/variable-text-edit.cpp
|
||||
src/utils/variable-text-edit.hpp
|
||||
src/utils/volume-control.cpp
|
||||
src/utils/volume-control.hpp
|
||||
src/utils/websocket-helpers.cpp
|
||||
@@ -312,7 +372,8 @@ 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/obs-websocket/lib"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/deps/exprtk")
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
@@ -321,8 +382,10 @@ if(OS_WINDOWS)
|
||||
configure_file(cmake/bundle/windows/installer-Windows.iss.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/installer-Windows.generated.iss)
|
||||
|
||||
target_compile_definitions(${LIB_NAME} PRIVATE UNICODE _UNICODE)
|
||||
if(MSVC)
|
||||
target_compile_options(${LIB_NAME} PUBLIC /MP /d2FH4- /wd4267 /wd4267)
|
||||
target_compile_options(${LIB_NAME} PUBLIC /MP /d2FH4- /wd4267 /wd4267
|
||||
/bigobj)
|
||||
endif()
|
||||
target_sources(${LIB_NAME} PRIVATE src/win/advanced-scene-switcher-win.cpp)
|
||||
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
|
||||
@@ -346,10 +409,8 @@ elseif(OS_MACOS)
|
||||
|
||||
find_library(COCOA Cocoa)
|
||||
target_include_directories(${LIB_NAME} PRIVATE ${COCOA})
|
||||
target_sources(${LIB_NAME} PRIVATE src/osx/advanced-scene-switcher-osx.mm)
|
||||
set_source_files_properties(advanced-scene-switcher-osx.mm
|
||||
PROPERTIES COMPILE_FLAGS "-fobjc-arc")
|
||||
set("${PROJECT_NAME}_PLATFORM_LIBS" ${COCOA})
|
||||
target_link_libraries(${LIB_NAME} PRIVATE ${COCOA})
|
||||
|
||||
find_package(CURL)
|
||||
find_package(Libcurl)
|
||||
if(CURL_FOUND)
|
||||
@@ -359,6 +420,10 @@ elseif(OS_MACOS)
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find CURL or Libcurl - abort")
|
||||
endif()
|
||||
|
||||
target_sources(${LIB_NAME} PRIVATE src/osx/advanced-scene-switcher-osx.mm)
|
||||
set_source_files_properties(advanced-scene-switcher-osx.mm
|
||||
PROPERTIES COMPILE_FLAGS "-fobjc-arc")
|
||||
# --- End of section ---
|
||||
|
||||
# --- Linux-specific build settings and tasks ---
|
||||
@@ -369,23 +434,36 @@ else()
|
||||
set_target_properties(${LIB_NAME} PROPERTIES SOVERSION 1)
|
||||
|
||||
find_package(X11 REQUIRED COMPONENTS Xtst Xss)
|
||||
find_path(PROCPS_INCLUDE_DIR NAMES proc/procps.h)
|
||||
if(NOT PROCPS_INCLUDE_DIR)
|
||||
message(
|
||||
FATAL_ERROR "procps include dir not found - please set PROCPS_INCLUDE_DIR"
|
||||
)
|
||||
endif()
|
||||
find_library(PROCPS_LIBRARY NAMES procps)
|
||||
if(NOT PROCPS_LIBRARY)
|
||||
message(FATAL_ERROR "procps lib not found - please set PROCPS_LIBRARY")
|
||||
endif()
|
||||
target_link_libraries(${LIB_NAME} PRIVATE ${X11_LIBRARIES}
|
||||
${procps_LIBRARIES})
|
||||
target_include_directories(
|
||||
${LIB_NAME} PRIVATE "${X11_INCLUDE_DIR}" "${X11_Xtst_INCLUDE_PATH}"
|
||||
"${X11_Xss_INCLUDE_PATH}" "${PROCPS_INCLUDE_DIR}")
|
||||
target_sources(${LIB_NAME} PRIVATE src/linux/advanced-scene-switcher-nix.cpp)
|
||||
set("${PROJECT_NAME}_PLATFORM_LIBS" Xss ${PROCPS_LIBRARY})
|
||||
"${X11_Xss_INCLUDE_PATH}")
|
||||
target_link_libraries(${LIB_NAME} PRIVATE ${X11_LIBRARIES})
|
||||
|
||||
find_path(PROCPS_INCLUDE_DIR NAMES proc/procps.h)
|
||||
find_path(PROCPS2_INCLUDE_DIR NAMES libproc2/pids.h)
|
||||
find_library(PROCPS_LIBRARY NAMES procps)
|
||||
find_library(PROCPS2_LIBRARY NAMES proc2)
|
||||
if(PROCPS_INCLUDE_DIR AND PROCPS_LIBRARY)
|
||||
message(STATUS "${PROJECT_NAME} using procps")
|
||||
set(PROC_INCLUDE_DIR "${PROCPS_INCLUDE_DIR}")
|
||||
set(PROC_LIBRARY "${PROCPS_LIBRARY}")
|
||||
target_compile_definitions(${LIB_NAME} PRIVATE USE_PROCPS)
|
||||
elseif(PROCPS2_INCLUDE_DIR AND PROCPS2_LIBRARY)
|
||||
message(STATUS "${PROJECT_NAME} using libproc2")
|
||||
set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}")
|
||||
set(PROC_LIBRARY "${PROCPS2_LIBRARY}")
|
||||
else()
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"found neither procps nor libproc2!
|
||||
please either set ...
|
||||
PROCPS_INCLUDE_DIR and PROCPS_LIBRARY
|
||||
... or ...
|
||||
PROCPS2_INCLUDE_DIR and PROCPS2_LIBRARY")
|
||||
endif()
|
||||
target_link_libraries(${LIB_NAME} PRIVATE "${PROC_LIBRARY}")
|
||||
target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}")
|
||||
|
||||
find_package(CURL)
|
||||
find_package(Libcurl)
|
||||
if(CURL_FOUND)
|
||||
@@ -395,11 +473,11 @@ else()
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find CURL or Libcurl - abort")
|
||||
endif()
|
||||
|
||||
target_sources(${LIB_NAME} PRIVATE src/linux/advanced-scene-switcher-nix.cpp)
|
||||
endif()
|
||||
# --- End of section ---
|
||||
|
||||
target_link_libraries(${LIB_NAME} PUBLIC ${${PROJECT_NAME}_PLATFORM_LIBS})
|
||||
|
||||
if(DEB_INSTALL)
|
||||
file(GLOB ASS_TRANSLATION_FILES "data/locale/*.ini")
|
||||
if(NOT LIB_OUT_DIR)
|
||||
@@ -424,3 +502,4 @@ endif()
|
||||
# --- End of section ---
|
||||
|
||||
add_subdirectory(src/macro-external)
|
||||
add_subdirectory(tests)
|
||||
|
||||
@@ -214,7 +214,7 @@ endif()
|
||||
function(setup_obs_lib_dependency target)
|
||||
if(BUILD_OUT_OF_TREE)
|
||||
find_package(libobs)
|
||||
if(libobs_FOUND)
|
||||
if(libobs_FOUND AND NOT LIBOBS_LIB)
|
||||
target_link_libraries(${target} PUBLIC OBS::libobs)
|
||||
else()
|
||||
if(NOT LIBOBS_LIB)
|
||||
@@ -229,7 +229,7 @@ function(setup_obs_lib_dependency target)
|
||||
target_include_directories(${target} PRIVATE ${LIBOBS_INCLUDE_DIR})
|
||||
endif()
|
||||
find_package(obs-frontend-api)
|
||||
if(obs-frontend-api_FOUND)
|
||||
if(obs-frontend-api_FOUND AND NOT LIBOBS_FRONTEND_API_LIB)
|
||||
target_link_libraries(${target} PUBLIC OBS::obs-frontend-api)
|
||||
else()
|
||||
if(NOT LIBOBS_FRONTEND_API_LIB)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#define MyAppName "@CMAKE_PROJECT_NAME@"
|
||||
#define MyAppVersion "@CMAKE_PROJECT_VERSION@"
|
||||
#define MyAppVersion "@GIT_TAG@"
|
||||
#define MyAppPublisher "@PLUGIN_AUTHOR@"
|
||||
#define MyAppURL "http://www.mywebsite.com"
|
||||
|
||||
@@ -25,7 +25,7 @@ DirExistsWarning=no
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Files]
|
||||
Source: "..\release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "..\release\*"; Excludes: "*.zip"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "..\LICENSE"; Flags: dontcopy
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
|
||||
|
||||
@@ -24,11 +24,14 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Kann nur so gen
|
||||
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.cooldown="Nach einem automatisierten Szenenwechsel wechsle nicht mehr für"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Nach Ausführen von Aktionen überspringe das Ausführen von Aktionen für"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="In diesem Zeitraum werden potentielle erfüllte Bedingungen ignoriert!"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ausführliches Logging"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Fensterposition und -größe speichern"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Benachrichtigungen im System-Tray anzeigen"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deaktiviere UI Tipps"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Tabs verstecken, die in Makros repräsentiert sind"
|
||||
AdvSceneSwitcher.generalTab.matchBehavior="Verhalten anpassen"
|
||||
AdvSceneSwitcher.generalTab.priority="Priorität"
|
||||
AdvSceneSwitcher.generalTab.priority.description="Szenenwechselmethoden sortiert nach Priorität"
|
||||
AdvSceneSwitcher.generalTab.priority.threadPriority="Benutze Thread Priorität"
|
||||
@@ -51,13 +54,543 @@ AdvSceneSwitcher.generalTab.priority.media="Medien"
|
||||
AdvSceneSwitcher.generalTab.priority.time="Zeit"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Makro"
|
||||
AdvSceneSwitcher.generalTab.transition="Übergänge"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="Beim Ändern des Szenenübergangs:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="Verwende Übergangsüberschreibungen"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Wechsle den aktiven Szenenübergangtyp"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Mindestens eine Option muss aktiv sein:\n\n - Verwende Übergangsüberschreibungen\n\n - Wechsle den aktiven Szenenübergangtyp"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Makro"
|
||||
AdvSceneSwitcher.macroTab.macros="Makros"
|
||||
AdvSceneSwitcher.macroTab.priorityWarning="Hinweis: Es wird empfohlen, Makros als Funktion mit höchster Priorität zu konfigurieren.\nDiese Einstellung kann im Allgemein-Tab geändert werden."
|
||||
AdvSceneSwitcher.macroTab.help="Mit Makros können eine Reihe von Aktionen in Abhängigkeit von mehreren Bedingungen ausgeführt werden.\n\nKlicke auf das hervorgehobene Plus-Symbol, um ein neues Makro hinzuzufügen."
|
||||
AdvSceneSwitcher.macroTab.editConditionHelp="In diesem Abschnitt können Bedingungen für die Makros definiert werden.\n\nWähle auf der linken Seite ein vorhandenes Makro aus oder füge ein neues hinzu.\nKlicke dann auf die Plus-Schaltfläche unten, um eine neue Bedingung hinzuzufügen."
|
||||
AdvSceneSwitcher.macroTab.editActionHelp="In diesem Abschnitt können Aktionen für die Makros definiert werden.\n\nWähle auf der linken Seite ein vorhandenes Makro aus oder füge ein neues hinzu.\nKlicke dann auf die Plus-Schaltfläche unten, um eine neue Aktion hinzuzufügen."
|
||||
AdvSceneSwitcher.macroTab.edit="Makro bearbeiten"
|
||||
AdvSceneSwitcher.macroTab.edit.logic="Logik-Typ:"
|
||||
AdvSceneSwitcher.macroTab.edit.condition="Bedingungs-Typ:"
|
||||
AdvSceneSwitcher.macroTab.edit.action="Aktions-Typ:"
|
||||
AdvSceneSwitcher.macroTab.add="Neues Makro hinzufügen"
|
||||
AdvSceneSwitcher.macroTab.name="Name:"
|
||||
AdvSceneSwitcher.macroTab.run="Makro ausführen"
|
||||
AdvSceneSwitcher.macroTab.runFail="Ausführen von \"%1\" fehlgeschlagen!\nEntweder ist eine der Aktionen fehlgeschlagen oder das Makro wird bereits ausgeführt."
|
||||
AdvSceneSwitcher.macroTab.runInParallel="Parallel zu anderen Makros ausführen"
|
||||
AdvSceneSwitcher.macroTab.onChange="Nur bei Änderung ausführen"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Makro %1"
|
||||
AdvSceneSwitcher.macroTab.defaultGroupName="Gruppe %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Makro-Name existiert bereits"
|
||||
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?"
|
||||
AdvSceneSwitcher.macroTab.copy="Kopie des aktuellen Makros erstellen"
|
||||
AdvSceneSwitcher.macroTab.group="Ausgewählte Elemente gruppieren"
|
||||
AdvSceneSwitcher.macroTab.ungroup="Gruppierung ausgewählter Gruppen aufheben"
|
||||
AdvSceneSwitcher.macroTab.expandAll="Alle erweitern"
|
||||
AdvSceneSwitcher.macroTab.collapseAll="Alle einklappen"
|
||||
AdvSceneSwitcher.macroTab.maximize="Maximieren"
|
||||
AdvSceneSwitcher.macroTab.minimize="Minimieren"
|
||||
AdvSceneSwitcher.macroTab.highlightSettings="Visuelle Einstellungen"
|
||||
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey-Einstellungen"
|
||||
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Kürzlich ausgeführte Makros hervorheben"
|
||||
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"
|
||||
|
||||
; Macro List
|
||||
AdvSceneSwitcher.macroList.deleted="gelöscht"
|
||||
AdvSceneSwitcher.macroList.duplicate="\"%1\" ist bereits ausgewählt!"
|
||||
|
||||
; Macro Logic
|
||||
AdvSceneSwitcher.logic.none="Eintrag ignorieren"
|
||||
AdvSceneSwitcher.logic.and="Und"
|
||||
AdvSceneSwitcher.logic.or="Oder"
|
||||
AdvSceneSwitcher.logic.andNot="Und nicht"
|
||||
AdvSceneSwitcher.logic.orNot="Oder nicht"
|
||||
AdvSceneSwitcher.logic.rootNone="Wenn"
|
||||
AdvSceneSwitcher.logic.not="Wenn nicht"
|
||||
|
||||
; Macro Conditions
|
||||
AdvSceneSwitcher.condition.audio="Audio"
|
||||
AdvSceneSwitcher.condition.audio.state.below="unter"
|
||||
AdvSceneSwitcher.condition.audio.state.exact="genau"
|
||||
AdvSceneSwitcher.condition.audio.state.above="über"
|
||||
AdvSceneSwitcher.condition.audio.state.mute="stummgeschaltet"
|
||||
AdvSceneSwitcher.condition.audio.state.unmute="nicht stummgeschaltet"
|
||||
AdvSceneSwitcher.condition.audio.type.output="Ausgabe-Lautstärke"
|
||||
AdvSceneSwitcher.condition.audio.type.volume="Konfigurierter Lautstärkepegel"
|
||||
AdvSceneSwitcher.condition.audio.type.syncOffset="Sync-Offset"
|
||||
AdvSceneSwitcher.condition.audio.type.monitor="Audio-Monitoring"
|
||||
AdvSceneSwitcher.condition.audio.type.balance="Audio-Balance"
|
||||
AdvSceneSwitcher.condition.audio.entry="{{checkType}} von {{audioSources}} ist {{condition}}{{volume}}{{syncOffset}}{{monitorTypes}}"
|
||||
AdvSceneSwitcher.condition.cursor="Cursor"
|
||||
AdvSceneSwitcher.condition.cursor.type.region="ist in der Region"
|
||||
AdvSceneSwitcher.condition.cursor.type.moving="bewegt sich"
|
||||
AdvSceneSwitcher.condition.cursor.type.click="geklickt"
|
||||
AdvSceneSwitcher.condition.cursor.button.left="Linke Maustaste"
|
||||
AdvSceneSwitcher.condition.cursor.button.middle="Mittlere Maustaste"
|
||||
AdvSceneSwitcher.condition.cursor.button.right="Rechte Maustaste"
|
||||
AdvSceneSwitcher.condition.cursor.showFrame="Rahmen anzeigen"
|
||||
AdvSceneSwitcher.condition.cursor.hideFrame="Rahmen ausblenden"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="Cursor {{conditions}}{{buttons}}{{minX}}{{minY}}{{maxX}}{{maxY}}{{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line2="Cursor ist aktuell bei {{xPos}} x {{yPos}}"
|
||||
AdvSceneSwitcher.condition.scene="Szene"
|
||||
AdvSceneSwitcher.condition.scene.type.current="Aktuelle Szene ist"
|
||||
AdvSceneSwitcher.condition.scene.type.previous="Vorherige Szene ist"
|
||||
AdvSceneSwitcher.condition.scene.type.changed="Szene geändert"
|
||||
AdvSceneSwitcher.condition.scene.type.notChanged="Szene hat sich nicht verändert"
|
||||
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.window.entry.line1="{{windows}} existiert und ..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... ist {{fullscreen}} Vollbild {{maximized}} Maximiert {{focused}} Fokusiert {{windowFocusChanged}} Vordergrundfenster geändert"
|
||||
AdvSceneSwitcher.condition.window.entry.line3="Aktuelles Vordergrundfenster: {{focusWindow}}"
|
||||
AdvSceneSwitcher.condition.file="Datei"
|
||||
AdvSceneSwitcher.condition.file.type.match="entspricht"
|
||||
AdvSceneSwitcher.condition.file.type.contentChange="Inhalt geändert"
|
||||
AdvSceneSwitcher.condition.file.type.dateChange="Änderungsdatum geändert"
|
||||
AdvSceneSwitcher.condition.file.remote="Entfernte Datei"
|
||||
AdvSceneSwitcher.condition.file.local="Lokale Datei"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="Medien"
|
||||
AdvSceneSwitcher.condition.media.source="Quelle"
|
||||
AdvSceneSwitcher.condition.media.anyOnScene="Beliebige Medienquelle in"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="Alle Medienquellen in"
|
||||
AdvSceneSwitcher.condition.media.matchOnChange="Nur bei Änderung übereinstimmen (Hinweis: Diese Option wird in einer zukünftigen Version entfernt werden - bitte stattdessen Dauer-Modifikatoren verwenden)"
|
||||
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.video="Video"
|
||||
AdvSceneSwitcher.condition.video.condition.match="entspricht genau"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="stimmt nicht überein"
|
||||
AdvSceneSwitcher.condition.video.condition.hasChanged="hat sich geändert"
|
||||
AdvSceneSwitcher.condition.video.condition.hasNotChanged="hat sich nicht verändert"
|
||||
AdvSceneSwitcher.condition.video.condition.noImage="hat keine Ausgabe"
|
||||
AdvSceneSwitcher.condition.video.condition.pattern="entspricht dem Muster"
|
||||
AdvSceneSwitcher.condition.video.condition.object="enthält Objekt"
|
||||
AdvSceneSwitcher.condition.video.condition.brightness="Helligkeit"
|
||||
AdvSceneSwitcher.condition.video.askFileAction="Vorhandene Datei verwenden oder einen Screenshot der aktuell ausgewählten Quelle erstellen?"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.file="Vorhandene Datei verwenden"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Screenshot erstellen"
|
||||
AdvSceneSwitcher.condition.video.reduceLatency="Reduzierung der Abgleichslatenz"
|
||||
AdvSceneSwitcher.condition.video.reduceLatency.tooltip="Durch die Aktivierung wird die Abgleichslatenz verringert, aber die Bedingungsprüfungen aller Makros werden insgesamt langsamer."
|
||||
AdvSceneSwitcher.condition.video.usePatternForChangedCheck="Musterabgleich verwenden"
|
||||
AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="So kann gesteuert werden, wie stark sich das Bild ändern muss, damit die Bedingung erfüllt ist."
|
||||
AdvSceneSwitcher.condition.video.patternThreshold="Schwellenwert: "
|
||||
AdvSceneSwitcher.condition.video.patternThresholdDescription="Ein höherer Schwellenwert bedeutet, dass das Muster stärker an die Videoquelle angepasst werden muss."
|
||||
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Alphakanal als Maske für Muster verwenden."
|
||||
AdvSceneSwitcher.condition.video.brightnessThreshold="Durchschnittliche Helligkeit liegt über:"
|
||||
AdvSceneSwitcher.condition.video.brightnessThresholdDescription="Ein hoher Wert steht für ein helles Bild, ein niedriger Wert für ein dunkles Bild."
|
||||
AdvSceneSwitcher.condition.video.currentBrightness="Aktuelle durchschnittliche Helligkeit: %1"
|
||||
AdvSceneSwitcher.condition.video.objectScaleThreshold="Skalierungsfaktor: "
|
||||
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="Ein niedrigerer Skalierungsfaktor führt zu mehr Übereinstimmungen, aber zu einer höheren CPU-Belastung."
|
||||
AdvSceneSwitcher.condition.video.minNeighborDescription="Ein höherer Wert für die Mindestanzahl von Nachbarn führt zu weniger, aber qualitativ besseren Übereinstimmungen."
|
||||
AdvSceneSwitcher.condition.video.showMatch="Treffer anzeigen"
|
||||
AdvSceneSwitcher.condition.video.showMatch.loading="Treffer werden gesucht"
|
||||
AdvSceneSwitcher.condition.video.screenshotFail="Screenshot der Quelle konnte nicht erstellt werden!"
|
||||
AdvSceneSwitcher.condition.video.screenshotEmpty="Screenshot ist leer - Ist die Quelle sichtbar?"
|
||||
AdvSceneSwitcher.condition.video.patternMatchFail="Das Muster wurde nicht gefunden!"
|
||||
AdvSceneSwitcher.condition.video.patternMatchSuccess="Das Muster ist rot hervorgehoben."
|
||||
AdvSceneSwitcher.condition.video.objectMatchFail="Objekt wurde nicht gefunden!"
|
||||
AdvSceneSwitcher.condition.video.objectMatchSuccess="Objekt ist rot hervorgehoben."
|
||||
AdvSceneSwitcher.condition.video.modelLoadFail="Modelldaten konnten nicht geladen werden!"
|
||||
AdvSceneSwitcher.condition.video.type.main="OBS's Haupt-Ausgabe"
|
||||
AdvSceneSwitcher.condition.video.type.source="Quelle"
|
||||
AdvSceneSwitcher.condition.video.type.scene="Szene"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.modelPath="Modelldaten (Haar-Kaskaden-Klassifikator): {{modelDataPath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimale Anzahl von Nachbarn: {{minNeighbors}}"
|
||||
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduzieren Sie die CPU-Belastung, indem Sie die Prüfung nur alle {{throttleCount}} Millisekunden"
|
||||
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Kontrolle nur im Bereich durchführen"
|
||||
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
|
||||
AdvSceneSwitcher.condition.video.minSize="Minimale Größe:"
|
||||
AdvSceneSwitcher.condition.video.maxSize="Maximale Größe:"
|
||||
AdvSceneSwitcher.condition.video.selectArea="Bereich auswählen"
|
||||
AdvSceneSwitcher.condition.video.selectArea.status="Nur der markierte Bereich wird überprüft"
|
||||
AdvSceneSwitcher.condition.video.width="Breite"
|
||||
AdvSceneSwitcher.condition.video.height="Höhe"
|
||||
AdvSceneSwitcher.condition.stream="Stream"
|
||||
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"
|
||||
AdvSceneSwitcher.condition.record.state.stop="Aufnahme gestoppt"
|
||||
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
|
||||
AdvSceneSwitcher.condition.process="Prozess"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}} läuft {{focused}} und ist fokusiert"
|
||||
AdvSceneSwitcher.condition.process.entry.focus="Aktueller Vordergrundprozess: {{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.idle="Leerlauf"
|
||||
AdvSceneSwitcher.condition.idle.entry="Keine Tastatur- oder Mauseingaben für {{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Plugin-Status"
|
||||
AdvSceneSwitcher.condition.pluginState.state.start="Plugin gestartet"
|
||||
AdvSceneSwitcher.condition.pluginState.state.restart="Plugin neu gestartet"
|
||||
AdvSceneSwitcher.condition.pluginState.state.running="Plugin läuft"
|
||||
AdvSceneSwitcher.condition.pluginState.state.shutdown="OBS wird runtergefahren"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneCollection="Szenensammlung geladen"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Szene durch vorheriges Makro geändert"
|
||||
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
|
||||
AdvSceneSwitcher.condition.timer="Timer"
|
||||
AdvSceneSwitcher.condition.timer.type.fixed="Fixe"
|
||||
AdvSceneSwitcher.condition.timer.type.random="Zufällige"
|
||||
AdvSceneSwitcher.condition.timer.pause="Pausieren"
|
||||
AdvSceneSwitcher.condition.timer.continue="Fortfahren"
|
||||
AdvSceneSwitcher.condition.timer.entry.line1.fixed="{{type}} Dauer von {{duration}} ist abgelaufen"
|
||||
AdvSceneSwitcher.condition.timer.entry.line1.random="{{type}} Dauer von {{duration}} bis {{duration2}} ist abgelaufen"
|
||||
AdvSceneSwitcher.condition.timer.entry.line2="Restliche Zeit: {{remaining}} Sekunden"
|
||||
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} Verbleibende Zeit speichern {{autoReset}} Automatisches Zurücksetzen des Timers nach Erreichen der Dauer"
|
||||
AdvSceneSwitcher.condition.timer.reset="Zurücksetzen"
|
||||
AdvSceneSwitcher.condition.macro="Makro"
|
||||
AdvSceneSwitcher.condition.macro.type.count="Zähler"
|
||||
AdvSceneSwitcher.condition.macro.type.state="Zustand"
|
||||
AdvSceneSwitcher.condition.macro.type.multiState="Mehrere Zustände"
|
||||
AdvSceneSwitcher.condition.macro.type.selection="Bedingungs-Typ: {{types}}"
|
||||
AdvSceneSwitcher.condition.macro.count.type.below="Weniger als"
|
||||
AdvSceneSwitcher.condition.macro.count.type.above="Mehr als"
|
||||
AdvSceneSwitcher.condition.macro.count.type.equal="Exakt"
|
||||
AdvSceneSwitcher.condition.macro.state.type.below="Weniger als"
|
||||
AdvSceneSwitcher.condition.macro.state.type.above="Mehr als"
|
||||
AdvSceneSwitcher.condition.macro.state.type.equal="Exakt"
|
||||
AdvSceneSwitcher.condition.macro.count.reset="Zurücksetzen"
|
||||
AdvSceneSwitcher.condition.macro.pausedWarning="Ausgewähltes Makro ist derzeit pausiert!"
|
||||
AdvSceneSwitcher.condition.macro.state.entry="Bedingungen für {{macros}} sind wahr"
|
||||
AdvSceneSwitcher.condition.macro.multistate.entry="Bedingungen für {{multiStateConditions}}{{multiStateCount}} der folgenden Makros wahr sind:"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} wurde ausgeführt {{conditions}} {{count}} Mal"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line2="Aktuelle Anzahl: {{currentCount}} {{resetCount}}"
|
||||
AdvSceneSwitcher.condition.source="Quelle"
|
||||
AdvSceneSwitcher.condition.source.type.active="Ist aktiv"
|
||||
AdvSceneSwitcher.condition.source.type.showing="Wird angezeigt"
|
||||
AdvSceneSwitcher.condition.source.type.settings="Einstellungen stimmen überein"
|
||||
AdvSceneSwitcher.condition.source.getSettings="Aktuelle Einstellungen abfragen"
|
||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.virtualCamera="Virtuelle Kamera"
|
||||
AdvSceneSwitcher.condition.virtualCamera.state.start="Virtuelle Kamera gestartet"
|
||||
AdvSceneSwitcher.condition.virtualCamera.state.stop="Virtuelle Kamera gestoppt"
|
||||
AdvSceneSwitcher.condition.virtualCamera.entry="{{states}}"
|
||||
AdvSceneSwitcher.condition.filter="Filter"
|
||||
AdvSceneSwitcher.condition.filter.type.active="Ist aktiviert"
|
||||
AdvSceneSwitcher.condition.filter.type.showing="Ist deaktiviert"
|
||||
AdvSceneSwitcher.condition.filter.type.settings="Einstellungen passen"
|
||||
AdvSceneSwitcher.condition.filter.getSettings="Aktuelle Einstellungen abrufen"
|
||||
AdvSceneSwitcher.condition.filter.entry.line1="Auf {{sources}} {{filters}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.sceneOrder="Reihenfolge der Szenenelemente"
|
||||
AdvSceneSwitcher.condition.sceneOrder.type.above="Ist über"
|
||||
AdvSceneSwitcher.condition.sceneOrder.type.below="Ist unter"
|
||||
AdvSceneSwitcher.condition.sceneOrder.type.position="Ist in Position"
|
||||
AdvSceneSwitcher.condition.sceneOrder.positionInfo="Der Positionswert beginnt ganz unten bei 0 und erhöht sich für jedes Szenenelement, auch in Szenengruppen, um eins"
|
||||
AdvSceneSwitcher.condition.sceneOrder.entry="Auf{{scenes}}{{sources}}{{conditions}}{{sources2}}{{position}}"
|
||||
AdvSceneSwitcher.condition.hotkey="Hotkey"
|
||||
AdvSceneSwitcher.condition.hotkey.name="Makro-Trigger-Hotkey"
|
||||
AdvSceneSwitcher.condition.hotkey.tip="Hinweis: Die Tastenkombinationen für diesen Hotkey können in den OBS-Einstellungen konfiguriert werden"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line1="Hotkey ist gedrückt"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line2="Name: {{name}}"
|
||||
AdvSceneSwitcher.condition.replay="Replay Buffer"
|
||||
AdvSceneSwitcher.condition.replay.state.stopped="Replay Buffer gestoppt"
|
||||
AdvSceneSwitcher.condition.replay.state.started="Replay Buffer gestartet"
|
||||
AdvSceneSwitcher.condition.replay.state.saved="Replay Buffer gespeichert"
|
||||
AdvSceneSwitcher.condition.replay.entry="{{state}}"
|
||||
AdvSceneSwitcher.condition.date="Datum"
|
||||
AdvSceneSwitcher.condition.date.anyDay="Beliebiger Tag"
|
||||
AdvSceneSwitcher.condition.date.monday="Montag"
|
||||
AdvSceneSwitcher.condition.date.tuesday="Dienstag"
|
||||
AdvSceneSwitcher.condition.date.wednesday="Mittwoch"
|
||||
AdvSceneSwitcher.condition.date.thursday="Donnerstag"
|
||||
AdvSceneSwitcher.condition.date.friday="Freitag"
|
||||
AdvSceneSwitcher.condition.date.saturday="Samstag"
|
||||
AdvSceneSwitcher.condition.date.sunday="Sonntag"
|
||||
AdvSceneSwitcher.condition.date.state.at="Am"
|
||||
AdvSceneSwitcher.condition.date.state.after="Nach"
|
||||
AdvSceneSwitcher.condition.date.state.before="Vor"
|
||||
AdvSceneSwitcher.condition.date.state.between="Zwischen"
|
||||
AdvSceneSwitcher.condition.date.state.pattern="Muster"
|
||||
AdvSceneSwitcher.condition.date.separator="und"
|
||||
AdvSceneSwitcher.condition.date.ignoreDate="Wenn diese Option nicht aktiviert ist, wird die Datumskomponente ignoriert"
|
||||
AdvSceneSwitcher.condition.date.ignoreTime="Wenn diese Option nicht aktiviert ist, wird die Zeitkomponente ignoriert"
|
||||
AdvSceneSwitcher.condition.date.showAdvancedSettings="Erweiterte Einstellungen anzeigen"
|
||||
AdvSceneSwitcher.condition.date.showSimpleSettings="Einfache Einstellungen anzeigen"
|
||||
AdvSceneSwitcher.condition.date.entry.simple="Am {{dayOfWeek}} {{weekCondition}} {{ignoreWeekTime}}{{weekTime}}"
|
||||
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
|
||||
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} Wiederholen alle {{duration}} bei Datumsübereinstimmung"
|
||||
AdvSceneSwitcher.condition.date.entry.pattern="Aktuelles Datum \"{{currentDate}}\" entspricht dem Muster {{pattern}}"
|
||||
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}}passt zur Transformation"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.transition="Übergang"
|
||||
AdvSceneSwitcher.condition.transition.type.current="Aktueller Übergangstyp ist"
|
||||
AdvSceneSwitcher.condition.transition.type.duration="Aktuelle Übergangsdauer beträgt"
|
||||
AdvSceneSwitcher.condition.transition.type.started="Übergang gestartet"
|
||||
AdvSceneSwitcher.condition.transition.type.ended="Übergang beendet"
|
||||
AdvSceneSwitcher.condition.transition.type.transitionSource="Übergang von"
|
||||
AdvSceneSwitcher.condition.transition.type.transitionTarget="Übergang zu"
|
||||
AdvSceneSwitcher.condition.transition.durationSuffix="Sekunden"
|
||||
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{scenes}}{{duration}}{{durationSuffix}}"
|
||||
AdvSceneSwitcher.condition.sceneVisibility="Sichtbarkeit von Szenenelementen"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.shown="ist sichtbar"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="ist versteckt"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.changed="Sichtbarkeit verändert"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.entry="Auf{{scenes}}{{sources}}{{conditions}}"
|
||||
AdvSceneSwitcher.condition.studioMode="Studio-Modus"
|
||||
AdvSceneSwitcher.condition.studioMode.state.active="Studio-Modus ist aktiviert"
|
||||
AdvSceneSwitcher.condition.studioMode.state.notActive="Studio-Modus ist nicht aktiviert"
|
||||
AdvSceneSwitcher.condition.studioMode.state.previewScene="Vorschau-Szene ist"
|
||||
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
|
||||
AdvSceneSwitcher.condition.openvr="OpenVR"
|
||||
AdvSceneSwitcher.condition.openvr.errorStatus="OpenVR Fehler: "
|
||||
AdvSceneSwitcher.condition.openvr.entry.line1="HMD ist in ..."
|
||||
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
|
||||
AdvSceneSwitcher.condition.openvr.entry.line3="HMD ist derzeit bei {{xPos}} x {{yPos}} x {{zPos}}"
|
||||
AdvSceneSwitcher.condition.stats="OBS-Statistiken"
|
||||
AdvSceneSwitcher.condition.stats.type.fps="FPS"
|
||||
AdvSceneSwitcher.condition.stats.type.CPUUsage="CPU-Auslastung"
|
||||
AdvSceneSwitcher.condition.stats.type.HDDSpaceAvailable="Verfügbarer Speicherplatz"
|
||||
AdvSceneSwitcher.condition.stats.type.memoryUsage="Speicherverbrauch"
|
||||
AdvSceneSwitcher.condition.stats.type.averageTimeToRender="Durchschnittliche Zeit zum Rendern eines Frames"
|
||||
AdvSceneSwitcher.condition.stats.type.skippedFrames="Übersprungene Frames aufgrund der Kodierungsverzögerung"
|
||||
AdvSceneSwitcher.condition.stats.type.missedFrames="Verpasste Frames durch Rendering-Verzögerung"
|
||||
AdvSceneSwitcher.condition.stats.type.droppedFrames.stream="Verlorene Frames im Stream"
|
||||
AdvSceneSwitcher.condition.stats.type.megabytesSent.stream="Gesamtdatenmenge des Streams"
|
||||
AdvSceneSwitcher.condition.stats.type.bitrate.stream="Stream-Bitrate"
|
||||
AdvSceneSwitcher.condition.stats.type.droppedFrames.recording="Verlorene Frames bei der Aufnahme"
|
||||
AdvSceneSwitcher.condition.stats.type.megabytesSent.recording="Gesamtdatenmenge der Aufnahme"
|
||||
AdvSceneSwitcher.condition.stats.type.bitrate.recording="Aufnahme-Bitrate"
|
||||
AdvSceneSwitcher.condition.stats.condition.above="über"
|
||||
AdvSceneSwitcher.condition.stats.condition.equals="gleich"
|
||||
AdvSceneSwitcher.condition.stats.condition.below="unter"
|
||||
AdvSceneSwitcher.condition.stats.dockHint="Das Dock \"Stats\" öffnen, um den aktuellen Status anzuzeigen"
|
||||
AdvSceneSwitcher.condition.stats.entry="{{stats}} ist {{condition}} {{value}}"
|
||||
AdvSceneSwitcher.condition.profile="Profil"
|
||||
AdvSceneSwitcher.condition.profile.entry="Derzeit aktives Profil ist {{profiles}}"
|
||||
AdvSceneSwitcher.condition.websocket="Websocket"
|
||||
AdvSceneSwitcher.condition.websocket.type.request="Anfrage"
|
||||
AdvSceneSwitcher.condition.websocket.type.event="Ereignis"
|
||||
AdvSceneSwitcher.condition.websocket.useRegex="Reguläre Ausdrücke verwenden"
|
||||
AdvSceneSwitcher.condition.websocket.entry.request="{{type}} wurde empfangen:"
|
||||
AdvSceneSwitcher.condition.websocket.entry.event="{{type}} wurde empfangen von {{connection}}:"
|
||||
AdvSceneSwitcher.condition.variable="Variabel"
|
||||
AdvSceneSwitcher.condition.variable.type.compare="entspricht"
|
||||
AdvSceneSwitcher.condition.variable.type.empty="ist leer"
|
||||
AdvSceneSwitcher.condition.variable.type.number="ist eine Zahl"
|
||||
AdvSceneSwitcher.condition.variable.type.lessThan="ist kleiner als"
|
||||
AdvSceneSwitcher.condition.variable.type.greaterThan="ist größer als"
|
||||
AdvSceneSwitcher.condition.variable.type.valueChanged="Wert verändert"
|
||||
AdvSceneSwitcher.condition.variable.type.equalsVariable="entspricht Variable"
|
||||
AdvSceneSwitcher.condition.variable.type.lessThanVariable="ist kleiner als die Variable"
|
||||
AdvSceneSwitcher.condition.variable.type.greaterThanVariable="ist größer als die Variable"
|
||||
AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}{{variables2}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.switchScene="Szene wechseln"
|
||||
AdvSceneSwitcher.action.scene.entry="Wechsle zu Szene{{scenes}}mittels{{transitions}}mit einer Dauer von{{duration}}Sekunden"
|
||||
AdvSceneSwitcher.action.scene.entry.noDuration="Wechsle zu Szene{{scenes}}mittels{{transitions}}"
|
||||
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Warten, bis der Übergang zur Zielszene abgeschlossen ist"
|
||||
AdvSceneSwitcher.action.wait="Warten"
|
||||
AdvSceneSwitcher.action.wait.type.fixed="fixe"
|
||||
AdvSceneSwitcher.action.wait.type.random="zufällig"
|
||||
AdvSceneSwitcher.action.wait.entry.fixed="Warte für {{waitType}} Dauer von {{duration}}"
|
||||
AdvSceneSwitcher.action.wait.entry.random="Warte für {{waitType}} Dauer von {{duration}} bis {{duration2}}"
|
||||
AdvSceneSwitcher.action.audio="Audio"
|
||||
AdvSceneSwitcher.action.audio.type.mute="Stummgeschaltet"
|
||||
AdvSceneSwitcher.action.audio.type.unmute="Nicht stummgeschaltet"
|
||||
AdvSceneSwitcher.action.audio.type.sourceVolume="Quell-Lautstärke einstellen"
|
||||
AdvSceneSwitcher.action.audio.type.masterVolume="Master-Lautstärke einstellen"
|
||||
AdvSceneSwitcher.action.audio.type.syncOffset="Sync-Offset einstellen"
|
||||
AdvSceneSwitcher.action.audio.type.monitor="Audio-Monitoring einstellen"
|
||||
AdvSceneSwitcher.action.audio.type.balance="Audio-Balance einstellen"
|
||||
AdvSceneSwitcher.action.audio.balance.description="Den Schieberegler in die Richtung des Audiokanals drehen, auf den fokussiert werden soll."
|
||||
AdvSceneSwitcher.action.audio.fade.type.duration="über eine Dauer von"
|
||||
AdvSceneSwitcher.action.audio.fade.type.rate="mit einer Rate von"
|
||||
AdvSceneSwitcher.action.audio.fade.duration="{{fade}}Blende {{fadeTypes}} {{duration}} Sekunden."
|
||||
AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Blende {{fadeTypes}} {{rate}}pro Sekunde."
|
||||
AdvSceneSwitcher.action.audio.fade.wait="Warten, bis die Blende abgeschlossen ist."
|
||||
AdvSceneSwitcher.action.audio.fade.abort="Abbruch einer bereits aktiven Blende."
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}{{monitorTypes}}"
|
||||
AdvSceneSwitcher.action.recording="Aufnahme"
|
||||
AdvSceneSwitcher.action.recording.type.stop="Aufnahme stoppen"
|
||||
AdvSceneSwitcher.action.recording.type.start="Aufnahme starten"
|
||||
AdvSceneSwitcher.action.recording.type.pause="Aufnahme pausieren"
|
||||
AdvSceneSwitcher.action.recording.type.unpause="Aufnahme nicht mehr pausieren"
|
||||
AdvSceneSwitcher.action.recording.type.split="Aufnahme-Datei teilen"
|
||||
AdvSceneSwitcher.action.recording.pause.hint="Bitte beachten, dass je nach Aufnahmeeinstellung die Aufnahme möglicherweise nicht unterbrochen werden kann"
|
||||
AdvSceneSwitcher.action.recording.split.hint="Vergewissern, dass die automatische Dateiaufteilung in den OBS-Einstellungen aktiviert ist!"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{recordFolder}}{{recordFileFormat}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.replay="Replay Buffer"
|
||||
AdvSceneSwitcher.action.replay.saveWarn="Warnung: Ein zu häufiges Speichern kann dazu führen, dass der Replay Buffer nicht gespeichert wird!"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Replay Buffer stoppen"
|
||||
AdvSceneSwitcher.action.replay.type.start="Replay Buffer starten"
|
||||
AdvSceneSwitcher.action.replay.type.save="Replay Buffer speichern"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Stream"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Stream stoppen"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Stream starten"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Ausführen"
|
||||
AdvSceneSwitcher.action.run.arguments="Argumente:"
|
||||
AdvSceneSwitcher.action.run.addArgument="Argument hinzufügen"
|
||||
AdvSceneSwitcher.action.run.addArgumentDescription="Neues Argument hinzufügen:"
|
||||
AdvSceneSwitcher.action.run.entry="Ausführen {{filePath}}"
|
||||
AdvSceneSwitcher.action.run.entry.workingDirectory="Arbeitsverzeichnis:{{workingDirectory}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility="Sichtbarkeit von Szenenelementen"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="Anzeigen"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Verstecken"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Umschalten"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Quelle"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Beliebig"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="Auf{{scenes}}{{actions}}{{sources}}"
|
||||
AdvSceneSwitcher.action.filter="Filter"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Aktivieren"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Deaktivieren"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Einstellungen festlegen"
|
||||
AdvSceneSwitcher.action.filter.entry="Auf {{sources}} {{actions}} {{filters}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Aktuelle Einstellungen abfragen"
|
||||
AdvSceneSwitcher.action.source="Quelle"
|
||||
AdvSceneSwitcher.action.source.type.enable="Aktivieren"
|
||||
AdvSceneSwitcher.action.source.type.disable="Deaktivieren"
|
||||
AdvSceneSwitcher.action.source.type.settings="Einstellungen festlegen"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings="Aktualisieren der Quelleneinstellungen"
|
||||
AdvSceneSwitcher.action.source.type.pressSettingsButton="Drücken der Einstellungstaste"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Kann verwendet werden, um Browser-, Medien- usw. Quellen zu aktualisieren"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="Keine Buttons gefunden!"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
|
||||
AdvSceneSwitcher.action.source.warning="Warnung: Das globale Aktivieren und Deaktivieren von Quellen kann nicht über die OBS-Benutzeroberfläche gesteuert werden"
|
||||
AdvSceneSwitcher.action.source.getSettings="Aktuelle Einstellungen abfragen"
|
||||
AdvSceneSwitcher.action.media="Medien"
|
||||
AdvSceneSwitcher.action.media.type.play="Abspielen"
|
||||
AdvSceneSwitcher.action.media.type.pause="Pausieren"
|
||||
AdvSceneSwitcher.action.media.type.stop="Stoppen"
|
||||
AdvSceneSwitcher.action.media.type.restart="Neu starten"
|
||||
AdvSceneSwitcher.action.media.type.next="Nächste"
|
||||
AdvSceneSwitcher.action.media.type.previous="Vorherige"
|
||||
AdvSceneSwitcher.action.media.type.seek="Suche nach"
|
||||
AdvSceneSwitcher.action.media.entry="{{actions}}{{duration}}{{mediaSources}}"
|
||||
AdvSceneSwitcher.action.macro="Makro"
|
||||
AdvSceneSwitcher.action.macro.type.pause="Pausieren"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Nicht mehr pausieren"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Zähler zurücksetzen"
|
||||
AdvSceneSwitcher.action.macro.type.run="Aktionen ausführen"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Aktionen stoppen"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Plugin-Status"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Erweiterten Automatischen Szenenwechsler stoppen"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Ändern des Nichtübereinstimmungsverhaltens:"
|
||||
AdvSceneSwitcher.action.pluginState.type.import="Einstellungen importieren aus"
|
||||
AdvSceneSwitcher.action.pluginState.importWarning="Hinweis: Die Aktion wird ignoriert, solange das Einstellungsfenster geöffnet ist."
|
||||
AdvSceneSwitcher.action.pluginState.entry="{{actions}}{{values}}{{scenes}}{{settings}}{{settingsWarning}}"
|
||||
AdvSceneSwitcher.action.virtualCamera="Virtuelle Kamera"
|
||||
AdvSceneSwitcher.action.virtualCamera.type.stop="Virtuelle Kamera stoppen"
|
||||
AdvSceneSwitcher.action.virtualCamera.type.start="Virtuelle Kamera starten"
|
||||
AdvSceneSwitcher.action.virtualCamera.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.hotkey="Hotkey"
|
||||
AdvSceneSwitcher.action.hotkey.leftShift="Linke Umschalttaste"
|
||||
AdvSceneSwitcher.action.hotkey.rightShift="Rechte Umschalttaste"
|
||||
AdvSceneSwitcher.action.hotkey.leftCtrl="Linke Strg-Taste"
|
||||
AdvSceneSwitcher.action.hotkey.rightCtrl="Rechte Strg-Taste"
|
||||
AdvSceneSwitcher.action.hotkey.leftAlt="Linke Alt-Taste"
|
||||
AdvSceneSwitcher.action.hotkey.rightAlt="Rechte Alt-Taste"
|
||||
AdvSceneSwitcher.action.hotkey.leftMeta="Linke Meta-Taste"
|
||||
AdvSceneSwitcher.action.hotkey.rightMeta="Rechte Meta-Taste"
|
||||
AdvSceneSwitcher.action.hotkey.onlyOBS="Tastendruck nur an OBS senden"
|
||||
AdvSceneSwitcher.action.hotkey.disabled="Globale Tastendrücke können nicht simuliert werden - die Funktionalität beschränkt sich auf das Senden von Tastendrücken an OBS!"
|
||||
AdvSceneSwitcher.action.hotkey.entry="Drücke {{keys}} für {{duration}}"
|
||||
AdvSceneSwitcher.action.sceneOrder="Reihenfolge der Szenenelemente"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Nach oben verschieben"
|
||||
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.sceneTransform="Szenenelement transformieren"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.file="Datei"
|
||||
AdvSceneSwitcher.action.file.type.write="Schreiben"
|
||||
AdvSceneSwitcher.action.file.type.append="Anhängen"
|
||||
AdvSceneSwitcher.action.file.entry="{{actions}} in {{filePath}}:"
|
||||
AdvSceneSwitcher.action.studioMode="Studio-Modus"
|
||||
AdvSceneSwitcher.action.studioMode.type.swap="Vorschau- und Programm-Szene tauschen"
|
||||
AdvSceneSwitcher.action.studioMode.type.setScene="Vorschau-Szene einstellen auf"
|
||||
AdvSceneSwitcher.action.studioMode.type.enable="Studio-Modus aktivieren"
|
||||
AdvSceneSwitcher.action.studioMode.type.disable="Studio-Modus deaktivieren"
|
||||
AdvSceneSwitcher.action.studioMode.entry="{{actions}}{{scenes}}"
|
||||
AdvSceneSwitcher.action.transition="Übergang"
|
||||
AdvSceneSwitcher.action.transition.type.scene="Szenenübergang"
|
||||
AdvSceneSwitcher.action.transition.type.sceneOverride="Szenenübergang überschreiben"
|
||||
AdvSceneSwitcher.action.transition.type.sourceShow="Übergang der Quelle anzeigen"
|
||||
AdvSceneSwitcher.action.transition.type.sourceHide="Übergang der Quelle verstecken"
|
||||
AdvSceneSwitcher.action.transition.entry.line1="Anpassen {{type}}{{scenes}}{{sources}}"
|
||||
AdvSceneSwitcher.action.transition.entry.line2="{{setTransition}}Übergangstyp festlegen auf {{transitions}}"
|
||||
AdvSceneSwitcher.action.transition.entry.line3="{{setDuration}}Übergangsdauer festlegen auf {{duration}}Sekunden"
|
||||
AdvSceneSwitcher.action.timer="Timer"
|
||||
AdvSceneSwitcher.action.timer.type.pause="Pausieren"
|
||||
AdvSceneSwitcher.action.timer.type.continue="Fortsetzen"
|
||||
AdvSceneSwitcher.action.timer.type.reset="Zurücksetzen"
|
||||
AdvSceneSwitcher.action.timer.type.setTimeRemaining="Verbleibende Zeit einstellen der"
|
||||
AdvSceneSwitcher.action.timer.entry="{{timerAction}} Timer von {{macros}} {{duration}}"
|
||||
AdvSceneSwitcher.action.random="Zufall"
|
||||
AdvSceneSwitcher.action.random.allowRepeat="Aufeinanderfolgende Ausführung desselben Makros zulassen"
|
||||
AdvSceneSwitcher.action.random.entry="Zufällige Ausführung eines der folgenden Makros (pausierte Makros werden ignoriert)"
|
||||
AdvSceneSwitcher.action.systray="Benachrichtigung im System-Tray"
|
||||
AdvSceneSwitcher.action.screenshot="Screenshot"
|
||||
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.profile="Profil"
|
||||
AdvSceneSwitcher.action.profile.entry="Aktives Profil umschalten auf {{profiles}}"
|
||||
AdvSceneSwitcher.action.sceneCollection="Szenensammlung"
|
||||
AdvSceneSwitcher.action.sceneCollection.entry="Aktive Szenensammlung umschalten auf {{sceneCollections}}"
|
||||
AdvSceneSwitcher.action.sceneCollection.warning="Hinweis: Alle danach folgenden Aktionen werden nicht ausgeführt, da durch die Änderung der Szenensammlung auch die Einstellungen des Szenenwechslers neu geladen werden.\nDie Szenensammlungsaktion wird ignoriert, solange das Einstellungsfenster geöffnet ist."
|
||||
AdvSceneSwitcher.action.sequence="Sequenz"
|
||||
AdvSceneSwitcher.action.sequence.entry="Jedes Mal, wenn diese Aktion ausgeführt wird, wird das nächste Makro in der Liste ausgeführt (pausierte Makros werden ignoriert)"
|
||||
AdvSceneSwitcher.action.sequence.status="Zuletzt ausgeführtes Makro: %1 - Nächstes auszuführendes Makro: %2"
|
||||
AdvSceneSwitcher.action.sequence.status.none="keines"
|
||||
AdvSceneSwitcher.action.sequence.restart="Neustart am Anfang, wenn das Ende der Liste erreicht ist"
|
||||
AdvSceneSwitcher.action.sequence.continueFrom="Weiter mit ausgewähltem Element"
|
||||
AdvSceneSwitcher.action.websocket="Websocket"
|
||||
AdvSceneSwitcher.action.websocket.type.request="Anfrage"
|
||||
AdvSceneSwitcher.action.websocket.type.event="Ereignis"
|
||||
AdvSceneSwitcher.action.websocket.entry.request="Szenenwechsler {{type}} via {{connection}} senden"
|
||||
AdvSceneSwitcher.action.websocket.entry.event="Szenenwechsler {{type}} an verbundene Clients senden"
|
||||
AdvSceneSwitcher.action.http="HTTP"
|
||||
AdvSceneSwitcher.action.http.type.get="GET"
|
||||
AdvSceneSwitcher.action.http.type.post="POST"
|
||||
AdvSceneSwitcher.action.http.entry.line1="Sende {{method}} an {{url}}"
|
||||
AdvSceneSwitcher.action.http.entry.line2="Timeout: {{timeout}} Sekunden"
|
||||
AdvSceneSwitcher.action.variable="Variabel"
|
||||
AdvSceneSwitcher.action.variable.type.set="Auf festen Wert einstellen"
|
||||
AdvSceneSwitcher.action.variable.type.append="Anhängen"
|
||||
AdvSceneSwitcher.action.variable.type.appendVar="Variable anhängen"
|
||||
AdvSceneSwitcher.action.variable.type.increment="Erhöhen"
|
||||
AdvSceneSwitcher.action.variable.type.decrement="Verringern"
|
||||
AdvSceneSwitcher.action.variable.type.setActionValue="Auf Aktionswert setzen"
|
||||
AdvSceneSwitcher.action.variable.type.setConditionValue="Auf Bedingungswert setzen"
|
||||
AdvSceneSwitcher.action.variable.invalidSelection="Ungültige Auswahl!"
|
||||
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Aktionen wird nicht unterstützt!"
|
||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Bedingungen wird nicht unterstützt!"
|
||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Aktueller Wert:"
|
||||
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}"
|
||||
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Szenenübergänge"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="Beim Ändern des Szenenübergangs:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Verwende Übergangsüberschreibungen"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Wechsle den aktiven Szenenübergangtyp"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Mindestens eine Option muss aktiv sein:\n\n - Verwende Übergangsüberschreibungen\n\n - Wechsle den aktiven Szenenübergangtyp"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Szenenübergänge für automatisierte Szenenwechsel von Szene A zu Szene B"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Diese Einstellungen beeinflussen <span style=\"font-style:italic;\">nur</span> vom Szenenwechsler ausgelöste Szenenübergänge - Siehe <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> um auch manuelle Szenenübergänge zu konfigurieren.<br/>Einstellungen auf diesem Tab haben Vorrang, vor denen welche auf den übrigen Tabs konfiguriert wurden.<br/><br/>Klicke auf das Plus Symbol, um einen neuen Eintrag hinzuzufügen.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Ändere den Szenenübergang wenn eine Szene aktiv ist"
|
||||
@@ -113,10 +646,11 @@ AdvSceneSwitcher.mediaTab.states.none="Keine Auswahl"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="Abspielen"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="Öffnen"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="Buffern"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="Pausiert"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="Pausiert"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="Gestoppt"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Beendet"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Fehler"
|
||||
AdvSceneSwitcher.mediaTab.states.playlistEnd="Ended(Playlist)"
|
||||
AdvSceneSwitcher.mediaTab.states.any="Beliebig"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.none="Keine Auswahl"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Dauer kürzer"
|
||||
@@ -190,10 +724,10 @@ AdvSceneSwitcher.sceneSequenceTab.loadFail="Laden der Einstellungen ist fehlgesc
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Laden der Einstellungen war erfolgreich!"
|
||||
AdvSceneSwitcher.sceneSequenceTab.fileType="Text Dateien (*.txt)"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="kann unterbrochen werden"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} mit {{transitions}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Szenenfolge erweitern"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Nach {{delay}} wechsle zu {{scenes}} mit {{transitions}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} mit {{transitions}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.help="Dieser Tab ermöglicht es automatisiert zu einer anderen Szene zu wechseln, nachdem eine Szene für die konfigurierte Zeit aktiv war.\nZum Beispiel kann so automatisch zwischen zwei Szenen hin und her gewechselt werden.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||
|
||||
; Audio Tab
|
||||
@@ -221,6 +755,7 @@ AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Dieser Tab ermöglicht es
|
||||
|
||||
; 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"
|
||||
@@ -287,35 +822,111 @@ AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="Virtuelle
|
||||
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 Erweiteren Szenenwechsler"
|
||||
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stoppe den Erweiteren Szenenwechsler"
|
||||
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Starte/Stoppe den Erweiteren Szenenwechsler"
|
||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Starte den Erweiterten Szenenwechsler"
|
||||
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stoppe den Erweiterten Szenenwechsler"
|
||||
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Starte/Stoppe den Erweiterten Szenenwechsler"
|
||||
AdvSceneSwitcher.hotkey.macro.pause="Pausiere Makro %1"
|
||||
AdvSceneSwitcher.hotkey.macro.unpause="Makro %1 nicht mehr pausieren"
|
||||
AdvSceneSwitcher.hotkey.macro.togglePause="Pause von Makro %1 togglen"
|
||||
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Makro-Segmentauswahl nach oben verschieben"
|
||||
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Makro-Segmentauswahl nach unten verschieben"
|
||||
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Ausgewähltes Makro-Segment entfernen"
|
||||
|
||||
AdvSceneSwitcher.askBackup="Neue Version des Erweiterten Automatischen Szenenwechslers wurde erkannt.\nSoll ein Backup der alten Einstellungen angelegt werden?"
|
||||
AdvSceneSwitcher.askForMacro="Makro auswählen {{macroSelection}}"
|
||||
|
||||
AdvSceneSwitcher.close="Schließen"
|
||||
AdvSceneSwitcher.browse="Öffnen"
|
||||
|
||||
AdvSceneSwitcher.item.select="--Element auswählen--"
|
||||
AdvSceneSwitcher.item.add="Neues Element hinzufügen"
|
||||
AdvSceneSwitcher.item.rename="Umbenennen"
|
||||
AdvSceneSwitcher.item.remove="Entfernen"
|
||||
AdvSceneSwitcher.item.properties="Einstellungen"
|
||||
AdvSceneSwitcher.item.newName="Neuer Name:"
|
||||
AdvSceneSwitcher.item.emptyName="Leerer Name nicht zulässig!"
|
||||
AdvSceneSwitcher.item.nameNotAvailable="Name nicht verfügbar!"
|
||||
AdvSceneSwitcher.item.nameReserved="Name reserviert!"
|
||||
|
||||
AdvSceneSwitcher.variable.select="--Variable auswählen--"
|
||||
AdvSceneSwitcher.variable.add="Neue Variable hinzufügen"
|
||||
AdvSceneSwitcher.variable.name="Name:"
|
||||
AdvSceneSwitcher.variable.value="Aktueller Wert:"
|
||||
AdvSceneSwitcher.variable.save="Verhalten speichern / laden"
|
||||
AdvSceneSwitcher.variable.save.dontSave="Variablenwert nicht speichern"
|
||||
AdvSceneSwitcher.variable.save.save="Variablenwert speichern"
|
||||
AdvSceneSwitcher.variable.save.default="Auf Wert setzen"
|
||||
|
||||
AdvSceneSwitcher.connection.select="--Verbindung auswählen--"
|
||||
AdvSceneSwitcher.connection.add="Neue Verbindung hinzufügen"
|
||||
AdvSceneSwitcher.connection.name="Name:"
|
||||
AdvSceneSwitcher.connection.address="Adresse:"
|
||||
AdvSceneSwitcher.connection.port="Port:"
|
||||
AdvSceneSwitcher.connection.password="Passwort:"
|
||||
AdvSceneSwitcher.connection.reconnect="Automatisch neu verbinden:"
|
||||
AdvSceneSwitcher.connection.reconnectDelay="Automatisch neu verbinden nach:"
|
||||
AdvSceneSwitcher.connection.connectOnStart="Beim Start verbinden:"
|
||||
AdvSceneSwitcher.connection.test="Verbindung testen"
|
||||
AdvSceneSwitcher.connection.status.disconnected="Verbindung getrennt"
|
||||
AdvSceneSwitcher.connection.status.connecting="Verbindung wird aufgebaut"
|
||||
AdvSceneSwitcher.connection.status.connected="Verbunden, aber nicht authentifiziert"
|
||||
AdvSceneSwitcher.connection.status.authenticated="Verbunden und authentifiziert"
|
||||
|
||||
AdvSceneSwitcher.regex.enable="Reguläre Ausdrücke aktivieren"
|
||||
AdvSceneSwitcher.regex.partialMatch="Teilweise Übereinstimmung zulassen"
|
||||
AdvSceneSwitcher.regex.caseInsensitive="Groß- und Kleinschreibung wird nicht berücksichtigt"
|
||||
AdvSceneSwitcher.regex.dotMatchNewline=". erkennt Zeilenumbrüche"
|
||||
AdvSceneSwitcher.regex.multiLine="^ und $ erkennen Start/Ende einer Zeile"
|
||||
AdvSceneSwitcher.regex.extendedPattern="Qt's ExtendedPatternSyntax aktivieren"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--Szene auswählen--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Vorhergehende Szene"
|
||||
AdvSceneSwitcher.currentTransition="Aktueller Szenenübergang"
|
||||
AdvSceneSwitcher.selectTransition="--Szenenübergang auswählen--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Vorherige Szene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Aktuelle Szene"
|
||||
AdvSceneSwitcher.selectPreviewScene="Vorschau-Szene"
|
||||
AdvSceneSwitcher.selectAnyScene="Beliebige Szene"
|
||||
AdvSceneSwitcher.currentTransition="Aktueller Übergang"
|
||||
AdvSceneSwitcher.anyTransition="Beliebiger Übergang"
|
||||
AdvSceneSwitcher.selectTransition="--Übergang auswählen--"
|
||||
AdvSceneSwitcher.selectWindow="--Fenster auswählen--"
|
||||
AdvSceneSwitcher.selectAudioSource="--Audio Quelle auswählen--"
|
||||
AdvSceneSwitcher.selectVideoSource="--Video Quelle auswählen--"
|
||||
AdvSceneSwitcher.selectMediaSource="--Medien Quelle auswählen--"
|
||||
AdvSceneSwitcher.selectSource="--Quelle auswählen--"
|
||||
AdvSceneSwitcher.selectAudioSource="--Audio-Quelle auswählen--"
|
||||
AdvSceneSwitcher.selectVideoSource="--Video-Quelle auswählen--"
|
||||
AdvSceneSwitcher.OBSVideoOutput="OBS's Haupt-Ausgabe"
|
||||
AdvSceneSwitcher.selectMediaSource="--Medien-Quelle auswählen--"
|
||||
AdvSceneSwitcher.selectProcess="--Prozess auswählen--"
|
||||
AdvSceneSwitcher.selectFilter="--Filter auswählen--"
|
||||
AdvSceneSwitcher.selectMacro="--Makro auswählen--"
|
||||
AdvSceneSwitcher.selectItem="--Element auswählen--"
|
||||
AdvSceneSwitcher.selectProfile="--Profil auswählen--"
|
||||
AdvSceneSwitcher.selectSceneCollection="--Szenensammlung auswählen--"
|
||||
AdvSceneSwitcher.enterPath="--Pfad eingeben--"
|
||||
AdvSceneSwitcher.enterText="--Text eingeben--"
|
||||
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="Ungültige Einträge werden nicht gespeichert"
|
||||
AdvSceneSwitcher.selectWindowTip="Benutze \"OBS\" um das OBS-Studio Fenster auszuwählen\nBenutze\"Task Switching\"um die ALT + TAB Tastenkombination auszuwählen"
|
||||
AdvSceneSwitcher.enterURL="--URL eingebenL--"
|
||||
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="ungültige Einträge werden nicht gespeichert"
|
||||
AdvSceneSwitcher.selectWindowTip="Verwende \"OBS\", um das OBS-Fenster zu definieren.\n Verwende \"Task Switching\", um ALT + TAB festzulegen."
|
||||
AdvSceneSwitcher.sceneItemSelection.all="Alle"
|
||||
AdvSceneSwitcher.sceneItemSelection.any="Beliebige"
|
||||
|
||||
AdvSceneSwitcher.status.active="Aktiv"
|
||||
AdvSceneSwitcher.status.inactive="Inaktiv"
|
||||
AdvSceneSwitcher.running="Plugin läuft"
|
||||
AdvSceneSwitcher.stopped="Plugin gestoppt"
|
||||
|
||||
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Dies scheint das erste Mal zu sein, dass der Erweiterte Szenenwechsler gestartet wurde.<br>Bitte schaue ins <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> für eine Liste von Anleitungen und Beispielen.<br>Nicht zögern und Fragen im <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">Thread</span></a> des Plugins im OBS-Forum stellen!!</p></body></html>"
|
||||
|
||||
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.minutes="Minuten"
|
||||
AdvSceneSwitcher.unit.hours="Stunden"
|
||||
AdvSceneSwitcher.duration.condition.none="Kein Dauer-Modifikator"
|
||||
AdvSceneSwitcher.duration.condition.more="Mindestens für"
|
||||
AdvSceneSwitcher.duration.condition.equal="Für genau"
|
||||
AdvSceneSwitcher.duration.condition.less="Höchstens für"
|
||||
AdvSceneSwitcher.duration.condition.within="In den letzten"
|
||||
|
||||
AdvSceneSwitcher.audio.monitor.none="Monitoring aus"
|
||||
AdvSceneSwitcher.audio.monitor.monitorOnly="Nur Monitoring (Ausgabe stummschalten)"
|
||||
AdvSceneSwitcher.audio.monitor.both="Monitoring und Ausgabe"
|
||||
|
||||
@@ -17,19 +17,21 @@ AdvSceneSwitcher.generalTab.status.autoStart.never="Never"
|
||||
AdvSceneSwitcher.generalTab.status.autoStart.recording="Recording"
|
||||
AdvSceneSwitcher.generalTab.status.autoStart.streaming="Streaming"
|
||||
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Recording or Streaming"
|
||||
AdvSceneSwitcher.generalTab.status.checkInterval="Check switch conditions every"
|
||||
AdvSceneSwitcher.generalTab.status.checkInterval="Check conditions every"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior="General behavior"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="If no actions are performed for"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Will only ever be as accurate as the configured check interval."
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Don't switch"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Switch to any scene in Random tab"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Switch to:"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After a match don't perform actions for"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After performing actions skip performing actions for"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose logging"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="Display warning if plugins cannot be loaded"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>Loading of the following plugin libraries was unsuccessful, which could result in some Advanced Scene Switcher functions not being available:%1Check the OBS logs for details.<br>This message can be disabled on the General tab.</body></html>"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
|
||||
AdvSceneSwitcher.generalTab.matchBehavior="Match behavior"
|
||||
AdvSceneSwitcher.generalTab.priority="Priority"
|
||||
@@ -55,6 +57,11 @@ AdvSceneSwitcher.generalTab.priority.time="Time"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Macro"
|
||||
AdvSceneSwitcher.generalTab.transition="Transitions"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="When changing transitions:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="Set transition overrides"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Change active transition type"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Macro"
|
||||
@@ -74,17 +81,53 @@ AdvSceneSwitcher.macroTab.runFail="Running \"%1\" failed!\nEither one of the act
|
||||
AdvSceneSwitcher.macroTab.runInParallel="Run macro in parallel to other macros"
|
||||
AdvSceneSwitcher.macroTab.onChange="Perform actions only on condition change"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
|
||||
AdvSceneSwitcher.macroTab.defaultGroupName="Group %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Macro name exists already"
|
||||
AdvSceneSwitcher.macroTab.copy="Create copy"
|
||||
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Are you sure you want to delete \"%1\" and all its elements?"
|
||||
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="Are you sure you want to delete %1 macros?"
|
||||
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="Are you sure you want to delete \"%1\"?"
|
||||
AdvSceneSwitcher.macroTab.contextMenuAdd="Add"
|
||||
AdvSceneSwitcher.macroTab.copy="Duplicate Macro"
|
||||
AdvSceneSwitcher.macroTab.group="Group Selected Macros"
|
||||
AdvSceneSwitcher.macroTab.ungroup="Ungroup Selected Groups"
|
||||
AdvSceneSwitcher.macroTab.rename="Rename"
|
||||
AdvSceneSwitcher.macroTab.remove="Remove"
|
||||
AdvSceneSwitcher.macroTab.export="Export"
|
||||
AdvSceneSwitcher.macroTab.export.info="Paste the string below into the import dialog to import the selected macros:"
|
||||
AdvSceneSwitcher.macroTab.import="Import"
|
||||
AdvSceneSwitcher.macroTab.import.info="Paste the export string into the below text box to import macros:"
|
||||
AdvSceneSwitcher.macroTab.import.invalid="Invalid import data provided!"
|
||||
AdvSceneSwitcher.macroTab.import.nameConflict="Cannot continue importing macro \"%1\" as a macro with the same name already exists!\nDo you want to continue with the import of \"%2\" and choose a new name? (Will be skipped otherwise)"
|
||||
AdvSceneSwitcher.macroTab.expandAll="Expand all"
|
||||
AdvSceneSwitcher.macroTab.collapseAll="Collapse all"
|
||||
AdvSceneSwitcher.macroTab.maximize="Maximize"
|
||||
AdvSceneSwitcher.macroTab.minimize="Minimize"
|
||||
AdvSceneSwitcher.macroTab.highlightSettings="Visual settings"
|
||||
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings"
|
||||
AdvSceneSwitcher.macroTab.generalSettings="General settings"
|
||||
AdvSceneSwitcher.macroTab.dockSettings="Dock settings"
|
||||
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros"
|
||||
AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently"
|
||||
AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro"
|
||||
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control pause state of new macros"
|
||||
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control pause state of selected macro"
|
||||
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros"
|
||||
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control the pause state of selected macro"
|
||||
AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="Skip execution of actions of current macro on startup"
|
||||
AdvSceneSwitcher.macroTab.currentRegisterDock="Register dock widget to control the pause state of selected macro or run it manually"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddRunButton="Add button to run the macro"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddPauseButton="Add button to pause or unpause the macro"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddStatusLabel="Add status label"
|
||||
AdvSceneSwitcher.macroTab.currentDockButtonText.run="Run button text:"
|
||||
AdvSceneSwitcher.macroTab.currentDockButtonText.pause="Pause button text:"
|
||||
AdvSceneSwitcher.macroTab.currentDockButtonText.unpause="Unpause button text:"
|
||||
AdvSceneSwitcher.macroTab.currentDockStatusText.true="Conditions true text:"
|
||||
AdvSceneSwitcher.macroTab.currentDockStatusText.false="Conditions false text:"
|
||||
AdvSceneSwitcher.macroTab.currentDockHighlightIfExecuted="Highlight dock if macro actions were recently executed"
|
||||
|
||||
AdvSceneSwitcher.macroDock.pause="Pause"
|
||||
AdvSceneSwitcher.macroDock.unpause="Unpause"
|
||||
AdvSceneSwitcher.macroDock.run="Run"
|
||||
AdvSceneSwitcher.macroDock.statusLabel.true="Conditions are true."
|
||||
AdvSceneSwitcher.macroDock.statusLabel.false="Conditions are false."
|
||||
|
||||
; Macro List
|
||||
AdvSceneSwitcher.macroList.deleted="deleted"
|
||||
@@ -108,39 +151,57 @@ AdvSceneSwitcher.condition.audio.state.mute="muted"
|
||||
AdvSceneSwitcher.condition.audio.state.unmute="unmuted"
|
||||
AdvSceneSwitcher.condition.audio.type.output="Output volume"
|
||||
AdvSceneSwitcher.condition.audio.type.volume="Configured volume level"
|
||||
AdvSceneSwitcher.condition.audio.entry="{{checkType}} of {{audioSources}} is {{condition}} {{volume}}"
|
||||
AdvSceneSwitcher.condition.audio.type.syncOffset="Sync offset"
|
||||
AdvSceneSwitcher.condition.audio.type.monitor="Audio monitoring"
|
||||
AdvSceneSwitcher.condition.audio.type.balance="Audio balance"
|
||||
AdvSceneSwitcher.condition.audio.entry="{{checkType}} of {{audioSources}} is {{condition}}{{volume}}{{syncOffset}}{{monitorTypes}}"
|
||||
AdvSceneSwitcher.condition.cursor="Cursor"
|
||||
AdvSceneSwitcher.condition.cursor.type.region="is in region"
|
||||
AdvSceneSwitcher.condition.cursor.type.moving="is moving"
|
||||
AdvSceneSwitcher.condition.cursor.type.click="clicked"
|
||||
AdvSceneSwitcher.condition.cursor.button.left="Left mouse button"
|
||||
AdvSceneSwitcher.condition.cursor.button.middle="Middle mouse button"
|
||||
AdvSceneSwitcher.condition.cursor.button.right="Right mouse button"
|
||||
AdvSceneSwitcher.condition.cursor.showFrame="Show frame"
|
||||
AdvSceneSwitcher.condition.cursor.hideFrame="Hide frame"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="Cursor is {{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="Cursor {{conditions}}{{buttons}}{{minX}}{{minY}}{{maxX}}{{maxY}}{{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line2="Cursor is currently at {{xPos}} x {{yPos}}"
|
||||
AdvSceneSwitcher.condition.scene="Scene"
|
||||
AdvSceneSwitcher.condition.scene.type.current="Current scene is"
|
||||
AdvSceneSwitcher.condition.scene.type.previous="Previous scene is"
|
||||
AdvSceneSwitcher.condition.scene.type.changed="Scene changed"
|
||||
AdvSceneSwitcher.condition.scene.type.notChanged="Scene has not changed"
|
||||
AdvSceneSwitcher.condition.scene.type.currentPattern="Current scene matches"
|
||||
AdvSceneSwitcher.condition.scene.type.previousPattern="Previous scene matches"
|
||||
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="During transition check for transition target scene"
|
||||
AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="During transition check for transition source scene"
|
||||
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
|
||||
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
|
||||
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
|
||||
AdvSceneSwitcher.condition.window="Window"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
|
||||
AdvSceneSwitcher.condition.window.entry.window="{{checkTitle}}Window title matches{{windowRegex}}{{windows}}"
|
||||
AdvSceneSwitcher.condition.window.entry.fullscreen="{{fullscreen}}Window is fullscreen"
|
||||
AdvSceneSwitcher.condition.window.entry.maximized="{{maximized}}Window is maximized"
|
||||
AdvSceneSwitcher.condition.window.entry.focused="{{focused}}Window is focused"
|
||||
AdvSceneSwitcher.condition.window.entry.focusedChange="{{windowFocusChanged}}Focus window changed"
|
||||
AdvSceneSwitcher.condition.window.entry.text="{{checkText}}Window contains text{{textRegex}}{{windowText}}"
|
||||
AdvSceneSwitcher.condition.window.entry.text.note="This option might not work on every text being displayed in a window.\nIf that is the case, you can consider using the Video conditions OCR check instead."
|
||||
AdvSceneSwitcher.condition.window.entry.currentFocus="Current focus window:{{focusWindow}}"
|
||||
AdvSceneSwitcher.condition.file="File"
|
||||
AdvSceneSwitcher.condition.file.type.match="matches"
|
||||
AdvSceneSwitcher.condition.file.type.contentChange="content changed"
|
||||
AdvSceneSwitcher.condition.file.type.dateChange="modification date changed"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="Content of{{fileType}}{{filePath}}{{conditions}}"
|
||||
AdvSceneSwitcher.condition.file.remote="Remote file"
|
||||
AdvSceneSwitcher.condition.file.local="Local file"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="Media"
|
||||
AdvSceneSwitcher.condition.media.source="Source"
|
||||
AdvSceneSwitcher.condition.media.anyOnScene="Any media source on"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="All media sources on"
|
||||
AdvSceneSwitcher.condition.media.matchOnChange="Only match on change (Note: This option will be removed in a future version - please use duration modifiers instead)"
|
||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!"
|
||||
AdvSceneSwitcher.condition.media.entry="{{mediaSources}}{{scenes}} state is {{states}} and {{timeRestrictions}} {{time}}"
|
||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}state is{{states}}and{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.video="Video"
|
||||
AdvSceneSwitcher.condition.video.condition.match="exactly matches"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="does not match"
|
||||
@@ -149,6 +210,9 @@ AdvSceneSwitcher.condition.video.condition.hasNotChanged="has not changed"
|
||||
AdvSceneSwitcher.condition.video.condition.noImage="has no output"
|
||||
AdvSceneSwitcher.condition.video.condition.pattern="matches pattern"
|
||||
AdvSceneSwitcher.condition.video.condition.object="contains object"
|
||||
AdvSceneSwitcher.condition.video.condition.brightness="brightness"
|
||||
AdvSceneSwitcher.condition.video.condition.ocr="contains text"
|
||||
AdvSceneSwitcher.condition.video.condition.color="matches color"
|
||||
AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
|
||||
@@ -159,6 +223,14 @@ AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="This will al
|
||||
AdvSceneSwitcher.condition.video.patternThreshold="Threshold: "
|
||||
AdvSceneSwitcher.condition.video.patternThresholdDescription="A higher threshold value means that the pattern needs to match the video source more closely."
|
||||
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Use alpha channel as mask for pattern."
|
||||
AdvSceneSwitcher.condition.video.patternMatchMode="Use pattern matching mode {{patternMatchingModes}}"
|
||||
AdvSceneSwitcher.condition.video.patternMatchMode.crossCorrelation="Cross correlation"
|
||||
AdvSceneSwitcher.condition.video.patternMatchMode.correlationCoefficient="Correlation coefficient"
|
||||
AdvSceneSwitcher.condition.video.patternMatchMode.squaredDifference="Squared difference"
|
||||
AdvSceneSwitcher.condition.video.patternMatchMode.tip="The best method to use depends on the specific problem and the characteristics of the images involved.\nIn practice, it's often necessary to experiment with different methods and parameters to find the best match for a particular use case."
|
||||
AdvSceneSwitcher.condition.video.brightnessThreshold="Average brightness is above:"
|
||||
AdvSceneSwitcher.condition.video.brightnessThresholdDescription="A high value is indicating a bright image and a low one a darker one."
|
||||
AdvSceneSwitcher.condition.video.currentBrightness="Current average brightness: %1"
|
||||
AdvSceneSwitcher.condition.video.objectScaleThreshold="Scale factor: "
|
||||
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load."
|
||||
AdvSceneSwitcher.condition.video.minNeighborDescription="A higher minimum neighbors value will result in fewer but higher quality matches."
|
||||
@@ -168,15 +240,39 @@ AdvSceneSwitcher.condition.video.screenshotFail="Failed to get screenshot of sou
|
||||
AdvSceneSwitcher.condition.video.screenshotEmpty="Screenshot is empty - Is the source visible?"
|
||||
AdvSceneSwitcher.condition.video.patternMatchFail="Pattern was not found!"
|
||||
AdvSceneSwitcher.condition.video.patternMatchSuccess="Pattern is highlighted in red"
|
||||
AdvSceneSwitcher.condition.video.patternMatchSuccessFullImage="Full image matches the pattern"
|
||||
AdvSceneSwitcher.condition.video.objectMatchFail="Object was not found!"
|
||||
AdvSceneSwitcher.condition.video.objectMatchSuccess="Object is highlighted in red"
|
||||
AdvSceneSwitcher.condition.video.ocrMatchSuccess="Detected text:\n\n%1"
|
||||
AdvSceneSwitcher.condition.video.modelLoadFail="Model data could not be loaded!"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
|
||||
AdvSceneSwitcher.condition.video.selectColor="Select Color"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleColumn="Single column of text of variable sizes"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleBlockVertText="Single uniform block of vertically aligned text"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleBlock="Single uniform block of text"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleLine="Single text line"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleWord="Single word"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.circleWord="Single word in a circle"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleChar="Single character"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.sparseText="Find text in no particular order"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.sparseTextOSD="Find text in no particular order (OSD)"
|
||||
AdvSceneSwitcher.condition.video.ocrLanguageNotFound="<html><head/><body><p>Language model for \"%1\" was not found in:<br>\"%2\"<br><br>You can find additional language models <a href=\"https://github.com/tesseract-ocr/tessdata\"><span style=\" text-decoration: underline; color:#268bd2;\">here</span></a>."
|
||||
AdvSceneSwitcher.condition.video.colorMatchThreshold="Match threshold:"
|
||||
AdvSceneSwitcher.condition.video.colorMatchThresholdDescription="How much of the image has to match the given color?\nA value of 1 requires every pixel of the input image to match the given color."
|
||||
AdvSceneSwitcher.condition.video.colorDeviationThreshold="Color deviation:"
|
||||
AdvSceneSwitcher.condition.video.colorDeviationThresholdDescription="How similar can the clolor be to the provided input color for it to still count as a match?\nA value of 0 requires a perfect color match, while a higher value includes similar colors also."
|
||||
AdvSceneSwitcher.condition.video.type.main="OBS's main output"
|
||||
AdvSceneSwitcher.condition.video.type.source="Source"
|
||||
AdvSceneSwitcher.condition.video.type.scene="Scene"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.modelPath="Model data (haar cascade classifier): {{modelDataPath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum neighbors: {{minNeighbors}}"
|
||||
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduce CPU load by performing check only every {{throttleCount}} milliseconds"
|
||||
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Perform check only in area"
|
||||
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
|
||||
AdvSceneSwitcher.condition.video.entry.orcColorPick="Check for text color:{{textColor}}{{selectColor}}"
|
||||
AdvSceneSwitcher.condition.video.entry.orcTextType="Check for text type:{{textType}}"
|
||||
AdvSceneSwitcher.condition.video.entry.orcLanguage="Check for language:{{languageCode}}"
|
||||
AdvSceneSwitcher.condition.video.entry.color="Check for color:{{color}}{{selectColor}}"
|
||||
AdvSceneSwitcher.condition.video.minSize="Minimum size:"
|
||||
AdvSceneSwitcher.condition.video.maxSize="Maximum size:"
|
||||
AdvSceneSwitcher.condition.video.selectArea="Select area"
|
||||
@@ -188,7 +284,8 @@ AdvSceneSwitcher.condition.stream.state.start="Stream running"
|
||||
AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
|
||||
AdvSceneSwitcher.condition.stream.state.starting="Stream starting"
|
||||
AdvSceneSwitcher.condition.stream.state.stopping="Stream stopping"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}"
|
||||
AdvSceneSwitcher.condition.stream.state.keyFrameInterval="Keyframe interval equals"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
|
||||
AdvSceneSwitcher.condition.record="Recording"
|
||||
AdvSceneSwitcher.condition.record.state.start="Recording running"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Recording paused"
|
||||
@@ -196,12 +293,17 @@ AdvSceneSwitcher.condition.record.state.stop="Recording stopped"
|
||||
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
|
||||
AdvSceneSwitcher.condition.process="Process"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused"
|
||||
AdvSceneSwitcher.condition.process.entry.focus="Current foreground process: {{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.idle="Idle"
|
||||
AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Plugin state"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval"
|
||||
AdvSceneSwitcher.condition.pluginState.state.running="Advanced scene switcher is running"
|
||||
AdvSceneSwitcher.condition.pluginState.state.start="Plugin started"
|
||||
AdvSceneSwitcher.condition.pluginState.state.restart="Plugin restarted"
|
||||
AdvSceneSwitcher.condition.pluginState.state.running="Plugin is running"
|
||||
AdvSceneSwitcher.condition.pluginState.state.shutdown="OBS is shutting down"
|
||||
AdvSceneSwitcher.condition.pluginState.state.shutdown.limitation="Note that while actions performing OBS specific changes will still be executed on shutdown those changes will not be saved as OBS is already shutting down!"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneCollection="Scene collection loaded"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Scene changed by previous macro"
|
||||
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
|
||||
AdvSceneSwitcher.condition.timer="Timer"
|
||||
AdvSceneSwitcher.condition.timer.type.fixed="Fixed"
|
||||
@@ -214,10 +316,12 @@ AdvSceneSwitcher.condition.timer.entry.line2="Time remaining: {{remaining}} seco
|
||||
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} Save time remaining {{autoReset}} Automatically reset timer after duration was reached"
|
||||
AdvSceneSwitcher.condition.timer.reset="Reset"
|
||||
AdvSceneSwitcher.condition.macro="Macro"
|
||||
AdvSceneSwitcher.condition.macro.type.count="Count"
|
||||
AdvSceneSwitcher.condition.macro.type.state="State"
|
||||
AdvSceneSwitcher.condition.macro.type.multiState="Multiple states"
|
||||
AdvSceneSwitcher.condition.macro.type.selection="Condition type: {{types}}"
|
||||
AdvSceneSwitcher.condition.macro.type.count="Macro run count"
|
||||
AdvSceneSwitcher.condition.macro.type.state="Macro condition state"
|
||||
AdvSceneSwitcher.condition.macro.type.multiState="Multiple macro condition state"
|
||||
AdvSceneSwitcher.condition.macro.type.actionDisabled="Macro action disabled"
|
||||
AdvSceneSwitcher.condition.macro.type.actionEnabled="Macro action enabled"
|
||||
AdvSceneSwitcher.condition.macro.type.selection="{{types}}"
|
||||
AdvSceneSwitcher.condition.macro.count.type.below="Less than"
|
||||
AdvSceneSwitcher.condition.macro.count.type.above="More than"
|
||||
AdvSceneSwitcher.condition.macro.count.type.equal="Exactly"
|
||||
@@ -230,14 +334,17 @@ AdvSceneSwitcher.condition.macro.state.entry="Conditions of {{macros}} are true"
|
||||
AdvSceneSwitcher.condition.macro.multistate.entry="Conditions of {{multiStateConditions}}{{multiStateCount}} of the following macros are true:"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} was executed {{conditions}} {{count}} times"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line2="Current count: {{currentCount}} {{resetCount}}"
|
||||
AdvSceneSwitcher.condition.macro.actionState.disabled.entry="Action{{actionIndex}}of{{macros}}is disabled"
|
||||
AdvSceneSwitcher.condition.macro.actionState.enabled.entry="Action{{actionIndex}}of{{macros}}is enabled"
|
||||
AdvSceneSwitcher.condition.source="Source"
|
||||
AdvSceneSwitcher.condition.source.type.active="Is active"
|
||||
AdvSceneSwitcher.condition.source.type.showing="Is showing"
|
||||
AdvSceneSwitcher.condition.source.type.settings="Settings match"
|
||||
AdvSceneSwitcher.condition.source.sceneVisibilityHint="Scene specific visibility can be checked using the \"Scene item visibility\" condition"
|
||||
AdvSceneSwitcher.condition.source.getSettings="Get current settings"
|
||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}}{{conditions}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line3="{{regex}}{{getSettings}}"
|
||||
AdvSceneSwitcher.condition.virtualCamera="Virtual camera"
|
||||
AdvSceneSwitcher.condition.virtualCamera.state.start="Virtual camera started"
|
||||
AdvSceneSwitcher.condition.virtualCamera.state.stop="Virtual camera stopped"
|
||||
@@ -249,7 +356,7 @@ AdvSceneSwitcher.condition.filter.type.settings="Settings match"
|
||||
AdvSceneSwitcher.condition.filter.getSettings="Get current settings"
|
||||
AdvSceneSwitcher.condition.filter.entry.line1="On {{sources}} {{filters}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line3="{{regex}}{{getSettings}}"
|
||||
AdvSceneSwitcher.condition.sceneOrder="Scene item order"
|
||||
AdvSceneSwitcher.condition.sceneOrder.type.above="Is above"
|
||||
AdvSceneSwitcher.condition.sceneOrder.type.below="Is below"
|
||||
@@ -279,6 +386,7 @@ AdvSceneSwitcher.condition.date.state.at="At"
|
||||
AdvSceneSwitcher.condition.date.state.after="After"
|
||||
AdvSceneSwitcher.condition.date.state.before="Before"
|
||||
AdvSceneSwitcher.condition.date.state.between="Between"
|
||||
AdvSceneSwitcher.condition.date.state.pattern="Pattern"
|
||||
AdvSceneSwitcher.condition.date.separator="and"
|
||||
AdvSceneSwitcher.condition.date.ignoreDate="If unchecked the date component will be ignored"
|
||||
AdvSceneSwitcher.condition.date.ignoreTime="If unchecked the time component will be ignored"
|
||||
@@ -287,6 +395,7 @@ AdvSceneSwitcher.condition.date.showSimpleSettings="Show simple settings"
|
||||
AdvSceneSwitcher.condition.date.entry.simple="On {{dayOfWeek}} {{weekCondition}} {{ignoreWeekTime}}{{weekTime}}"
|
||||
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
|
||||
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} Repeat every {{duration}} on date match"
|
||||
AdvSceneSwitcher.condition.date.entry.pattern="Current date \"{{currentDate}}\" matches pattern {{pattern}}"
|
||||
AdvSceneSwitcher.condition.date.entry.nextMatchDate="Next match at: %1"
|
||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} On repeat update selected date to repeat date"
|
||||
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
||||
@@ -304,9 +413,10 @@ AdvSceneSwitcher.condition.transition.type.transitionTarget="Transitioning to"
|
||||
AdvSceneSwitcher.condition.transition.durationSuffix="seconds"
|
||||
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{scenes}}{{duration}}{{durationSuffix}}"
|
||||
AdvSceneSwitcher.condition.sceneVisibility="Scene item visibility"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.shown="Shown"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="Hidden"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.entry="On{{scenes}}{{sources}}is{{conditions}}"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.shown="is visible"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="is hidden"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.changed="visibility changed"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.entry="On{{scenes}}{{sources}}{{conditions}}"
|
||||
AdvSceneSwitcher.condition.studioMode="Studio mode"
|
||||
AdvSceneSwitcher.condition.studioMode.state.active="Studio mode is active"
|
||||
AdvSceneSwitcher.condition.studioMode.state.notActive="Studio mode is not active"
|
||||
@@ -339,8 +449,8 @@ AdvSceneSwitcher.condition.stats.entry="{{stats}} is {{condition}} {{value}}"
|
||||
AdvSceneSwitcher.condition.profile="Profile"
|
||||
AdvSceneSwitcher.condition.profile.entry="Current active profile is {{profiles}}"
|
||||
AdvSceneSwitcher.condition.websocket="Websocket"
|
||||
AdvSceneSwitcher.condition.websocket.type.request="Request"
|
||||
AdvSceneSwitcher.condition.websocket.type.event="Event"
|
||||
AdvSceneSwitcher.condition.websocket.type.request="Scene Switcher Request"
|
||||
AdvSceneSwitcher.condition.websocket.type.event="Scene Switcher Event"
|
||||
AdvSceneSwitcher.condition.websocket.useRegex="Use regular expressions"
|
||||
AdvSceneSwitcher.condition.websocket.entry.request="{{type}} was received:"
|
||||
AdvSceneSwitcher.condition.websocket.entry.event="{{type}} was received from {{connection}}:"
|
||||
@@ -351,8 +461,26 @@ AdvSceneSwitcher.condition.variable.type.number="is a number"
|
||||
AdvSceneSwitcher.condition.variable.type.lessThan="is less than"
|
||||
AdvSceneSwitcher.condition.variable.type.greaterThan="is greater than"
|
||||
AdvSceneSwitcher.condition.variable.type.valueChanged="value changed"
|
||||
AdvSceneSwitcher.condition.variable.regex="Use regular expressions"
|
||||
AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}"
|
||||
AdvSceneSwitcher.condition.variable.type.equalsVariable="equals variable"
|
||||
AdvSceneSwitcher.condition.variable.type.lessThanVariable="is less than variable"
|
||||
AdvSceneSwitcher.condition.variable.type.greaterThanVariable="is greater than variable"
|
||||
AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}{{variables2}}"
|
||||
AdvSceneSwitcher.condition.run="Run"
|
||||
AdvSceneSwitcher.condition.run.entry="Process exits before timeout of{{timeout}} seconds"
|
||||
AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}Check for exit code{{exitCode}}"
|
||||
AdvSceneSwitcher.condition.midi="MIDI"
|
||||
AdvSceneSwitcher.condition.midi.entry="Mesasge was received from {{device}} which matches:"
|
||||
AdvSceneSwitcher.condition.midi.entry.listen="Set MIDI message selection to messages incoming on selected device: {{listenButton}}"
|
||||
AdvSceneSwitcher.condition.display="Display"
|
||||
AdvSceneSwitcher.condition.display.type.displayName="Name of connected displays matches"
|
||||
AdvSceneSwitcher.condition.display.type.displayCount="Number of connected displays is"
|
||||
AdvSceneSwitcher.condition.display.entry="{{conditions}}{{displays}}{{regex}}{{displayCount}}"
|
||||
AdvSceneSwitcher.condition.slideshow="Slide Show"
|
||||
AdvSceneSwitcher.condition.slideshow.condition.slideChanged="Slide changed"
|
||||
AdvSceneSwitcher.condition.slideshow.condition.slideIndex="Current slide number is"
|
||||
AdvSceneSwitcher.condition.slideshow.condition.slidePath="Current slide path is"
|
||||
AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip="Information about the slide show status will only be updated based on the configured time between slides"
|
||||
AdvSceneSwitcher.condition.slideshow.entry="{{sources}}{{conditions}}{{index}}{{path}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.switchScene="Switch scene"
|
||||
@@ -369,22 +497,28 @@ AdvSceneSwitcher.action.audio.type.mute="Mute"
|
||||
AdvSceneSwitcher.action.audio.type.unmute="Unmute"
|
||||
AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume"
|
||||
AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume"
|
||||
AdvSceneSwitcher.action.audio.type.syncOffset="Set sync offset"
|
||||
AdvSceneSwitcher.action.audio.type.monitor="Set audio monitoring"
|
||||
AdvSceneSwitcher.action.audio.type.balance="Set balance"
|
||||
AdvSceneSwitcher.action.audio.balance.description="Move the slider in the direction of the audio channel you want to focus on."
|
||||
AdvSceneSwitcher.action.audio.fade.type.duration="over a duration of"
|
||||
AdvSceneSwitcher.action.audio.fade.type.rate="at a rate of"
|
||||
AdvSceneSwitcher.action.audio.fade.duration="{{fade}}Fade {{fadeTypes}} {{duration}} seconds."
|
||||
AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade {{fadeTypes}} {{rate}}per second."
|
||||
AdvSceneSwitcher.action.audio.fade.wait="Wait for fade to complete."
|
||||
AdvSceneSwitcher.action.audio.fade.abort="Abort already active fade."
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}{{monitorTypes}}"
|
||||
AdvSceneSwitcher.action.recording="Recording"
|
||||
AdvSceneSwitcher.action.recording.type.stop="Stop recording"
|
||||
AdvSceneSwitcher.action.recording.type.start="Start recording"
|
||||
AdvSceneSwitcher.action.recording.type.pause="Pause recording"
|
||||
AdvSceneSwitcher.action.recording.type.unpause="Unpause recording"
|
||||
AdvSceneSwitcher.action.recording.type.split="Split recording file"
|
||||
AdvSceneSwitcher.action.recording.type.changeOutputFolder="Change output folder"
|
||||
AdvSceneSwitcher.action.recording.type.changeOutputFileFormat="Change filename formatting"
|
||||
AdvSceneSwitcher.action.recording.pause.hint="Note that depending on your recording settings you might not be able to pause recording"
|
||||
AdvSceneSwitcher.action.recording.split.hint="Make sure to enable automatic file splitting in the OBS settings first!"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{recordFolder}}{{recordFileFormat}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.replay="Replay buffer"
|
||||
AdvSceneSwitcher.action.replay.saveWarn="Warning: Saving too frequently might result in the replay buffer not actually being saved!"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Stop replay buffer"
|
||||
@@ -394,21 +528,24 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Streaming"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Stop streaming"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Start streaming"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming.type.keyFrameInterval="Set keyframe interval"
|
||||
AdvSceneSwitcher.action.streaming.type.server="Set server URL"
|
||||
AdvSceneSwitcher.action.streaming.type.streamKey="Set stream key"
|
||||
AdvSceneSwitcher.action.streaming.type.username="Set username"
|
||||
AdvSceneSwitcher.action.streaming.type.password="Set password"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Run"
|
||||
AdvSceneSwitcher.action.run.arguments="Arguments:"
|
||||
AdvSceneSwitcher.action.run.addArgument="Add argument"
|
||||
AdvSceneSwitcher.action.run.addArgumentDescription="Add new argument:"
|
||||
AdvSceneSwitcher.action.run.entry="Run {{filePath}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Toggle"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="On{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="On{{scenes}}{{actions}}{{sources}}"
|
||||
AdvSceneSwitcher.action.filter="Filter"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Enable"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Disable"
|
||||
AdvSceneSwitcher.action.filter.type.toggle="Toggle"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Set settings"
|
||||
AdvSceneSwitcher.action.filter.entry="On {{sources}} {{actions}} {{filters}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Get current settings"
|
||||
@@ -417,10 +554,25 @@ AdvSceneSwitcher.action.source.type.enable="Enable"
|
||||
AdvSceneSwitcher.action.source.type.disable="Disable"
|
||||
AdvSceneSwitcher.action.source.type.settings="Set settings"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings="Refresh source settings"
|
||||
AdvSceneSwitcher.action.source.type.pressSettingsButton="Press settings button"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Can be used to refresh browser, media, etc. sources"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
|
||||
AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI"
|
||||
AdvSceneSwitcher.action.source.type.deinterlaceMode="Set deinterlace mode"
|
||||
AdvSceneSwitcher.action.source.type.deinterlaceOrder="Set deinterlace field order"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="No buttons found!"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
|
||||
AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI\nYou might be looking for \"Scene item visibility\""
|
||||
AdvSceneSwitcher.action.source.getSettings="Get current settings"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.disable="Disable"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.discard="Discard"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.retro="Retro"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.blend="Blend"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.blend2x="Blend 2x"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.linear="Linear"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.linear2x="Linear 2x"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.yadif="Yadif"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.yadif2x="Yadif 2x"
|
||||
AdvSceneSwitcher.action.source.deinterlaceOrder.topFieldFirst="Top Field First"
|
||||
AdvSceneSwitcher.action.source.deinterlaceOrder.bottomFieldFirst="Bottom Field First"
|
||||
AdvSceneSwitcher.action.media="Media"
|
||||
AdvSceneSwitcher.action.media.type.play="Play"
|
||||
AdvSceneSwitcher.action.media.type.pause="Pause"
|
||||
@@ -434,20 +586,32 @@ AdvSceneSwitcher.action.macro="Macro"
|
||||
AdvSceneSwitcher.action.macro.type.pause="Pause"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Unpause"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
|
||||
AdvSceneSwitcher.action.macro.type.run="Run"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Stop"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
|
||||
AdvSceneSwitcher.action.macro.type.run="Run actions"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Stop actions"
|
||||
AdvSceneSwitcher.action.macro.type.disableAction="Disable action"
|
||||
AdvSceneSwitcher.action.macro.type.enableAction="Enable action"
|
||||
AdvSceneSwitcher.action.macro.type.toggleAction="Toggle action"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Plugin state"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Change the no-match behaviour:"
|
||||
AdvSceneSwitcher.action.pluginState.type.import="Import settings from"
|
||||
AdvSceneSwitcher.action.pluginState.importWarning="Note: Action will be ignored while settings window is opened."
|
||||
AdvSceneSwitcher.action.pluginState.type.terminate="Shut down OBS"
|
||||
AdvSceneSwitcher.action.pluginState.terminateConfirm="OBS will be shut down automatically in 10 seconds!\nDo you want to continue shutting down OBS?"
|
||||
AdvSceneSwitcher.action.pluginState.entry="{{actions}}{{values}}{{scenes}}{{settings}}{{settingsWarning}}"
|
||||
AdvSceneSwitcher.action.virtualCamera="Virtual camera"
|
||||
AdvSceneSwitcher.action.virtualCamera.type.stop="Stop virtual camera"
|
||||
AdvSceneSwitcher.action.virtualCamera.type.start="Start virtual camera"
|
||||
AdvSceneSwitcher.action.virtualCamera.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.hotkey="Hotkey"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs="OBS hotkey"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs.type.frontend="Frontend"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs.type.soruce="Source"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs.type.output="Output"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs.type.encoder="Encoder"
|
||||
AdvSceneSwitcher.action.hotkey.type.obs.type.service="Service"
|
||||
AdvSceneSwitcher.action.hotkey.type.custom="Custom hotkey"
|
||||
AdvSceneSwitcher.action.hotkey.leftShift="Left Shift"
|
||||
AdvSceneSwitcher.action.hotkey.rightShift="Right Shift"
|
||||
AdvSceneSwitcher.action.hotkey.leftCtrl="Left Ctrl"
|
||||
@@ -457,8 +621,9 @@ AdvSceneSwitcher.action.hotkey.rightAlt="Right Alt"
|
||||
AdvSceneSwitcher.action.hotkey.leftMeta="Left Meta"
|
||||
AdvSceneSwitcher.action.hotkey.rightMeta="Right Meta"
|
||||
AdvSceneSwitcher.action.hotkey.onlyOBS="Send keypress only to OBS"
|
||||
AdvSceneSwitcher.action.hotkey.disabled="Cannot simulate key presses - functionality disabled!"
|
||||
AdvSceneSwitcher.action.hotkey.entry="Press {{keys}} for {{duration}} milliseconds"
|
||||
AdvSceneSwitcher.action.hotkey.disabled="Cannot simulate global key presses - functionality limited to sending key press to OBS!"
|
||||
AdvSceneSwitcher.action.hotkey.entry.custom="Press{{actionType}}{{keys}}for{{duration}}"
|
||||
AdvSceneSwitcher.action.hotkey.entry.obs="Press{{actionType}}{{hotkeyType}}{{obsHotkeys}}"
|
||||
AdvSceneSwitcher.action.sceneOrder="Scene item order"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Move up"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Move down"
|
||||
@@ -467,8 +632,18 @@ AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Move to bottom"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Move to position"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Scene item transform"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.manual="Transform"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.reset="Reset transform"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.rotate="Rotate by"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal="Flip horizontally"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.flipVertical="Flip vertically"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.fitToScreen="Fit to screen"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.stretchToScreen="Stretch to screen"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.centerToScreen="Center to screen"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Center vertically"
|
||||
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Center horizontally"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Get transform"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}transform{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.file="File"
|
||||
AdvSceneSwitcher.action.file.type.write="Write"
|
||||
AdvSceneSwitcher.action.file.type.append="Append"
|
||||
@@ -494,9 +669,13 @@ AdvSceneSwitcher.action.timer.type.reset="Reset"
|
||||
AdvSceneSwitcher.action.timer.type.setTimeRemaining="Set time remaining of"
|
||||
AdvSceneSwitcher.action.timer.entry="{{timerAction}} timers on {{macros}} {{duration}}"
|
||||
AdvSceneSwitcher.action.random="Random"
|
||||
AdvSceneSwitcher.action.random.allowRepeat="Allow consecutive execution of the same macro"
|
||||
AdvSceneSwitcher.action.random.entry="Randomly run any of the following macros (paused macros are ignored)"
|
||||
AdvSceneSwitcher.action.systray="System tray notification"
|
||||
AdvSceneSwitcher.action.systray.entry="Show notification: {{message}}"
|
||||
AdvSceneSwitcher.action.systray.title="Title:"
|
||||
AdvSceneSwitcher.action.systray.message="Message:"
|
||||
AdvSceneSwitcher.action.systray.icon="Icon:"
|
||||
AdvSceneSwitcher.action.systray.iconHint="Leave blank to use the default icon"
|
||||
AdvSceneSwitcher.action.screenshot="Screenshot"
|
||||
AdvSceneSwitcher.action.screenshot.save.default="Default"
|
||||
AdvSceneSwitcher.action.screenshot.save.custom="Custom"
|
||||
@@ -517,30 +696,76 @@ AdvSceneSwitcher.action.sequence.status.none="none"
|
||||
AdvSceneSwitcher.action.sequence.restart="Restart from beginning once end of list is reached"
|
||||
AdvSceneSwitcher.action.sequence.continueFrom="Continue with selected item"
|
||||
AdvSceneSwitcher.action.websocket="Websocket"
|
||||
AdvSceneSwitcher.action.websocket.api.sceneSwitcher="Scene Switcher message"
|
||||
AdvSceneSwitcher.action.websocket.api.obsWebsocket="OBS websocket message"
|
||||
AdvSceneSwitcher.action.websocket.api.genericWebsocket="Generic websocket message"
|
||||
AdvSceneSwitcher.action.websocket.type.request="Request"
|
||||
AdvSceneSwitcher.action.websocket.type.event="Event"
|
||||
AdvSceneSwitcher.action.websocket.entry.request="Send scene switcher {{type}} via {{connection}}"
|
||||
AdvSceneSwitcher.action.websocket.entry.event="Send scene switcher {{type}} to connected clients"
|
||||
AdvSceneSwitcher.action.http="Http"
|
||||
AdvSceneSwitcher.action.websocket.settingsConflictGeneric="Possible settings conflict: Generic websocket message selection with connection which follows the OBS protocol!"
|
||||
AdvSceneSwitcher.action.websocket.settingsConflictOBS="Possible settings conflict: Connection selected which only supports generic websockte messages while message type requires OBS protocol!"
|
||||
AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.request="Send{{api}}of type{{type}}via{{connection}}"
|
||||
AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.event="Send{{api}}of type{{type}}to connected clients"
|
||||
AdvSceneSwitcher.action.websocket.entry.generic="Send{{api}}via{{connection}}"
|
||||
AdvSceneSwitcher.action.http="HTTP"
|
||||
AdvSceneSwitcher.action.http.setHeaders="Set headers"
|
||||
AdvSceneSwitcher.action.http.headers="Headers:"
|
||||
AdvSceneSwitcher.action.http.addHeader="Add header"
|
||||
AdvSceneSwitcher.action.http.type.get="GET"
|
||||
AdvSceneSwitcher.action.http.type.post="POST"
|
||||
AdvSceneSwitcher.action.http.entry.line1="Send {{method}} to {{url}}"
|
||||
AdvSceneSwitcher.action.http.entry.line2="Timeout: {{timeout}} seconds"
|
||||
AdvSceneSwitcher.action.variable="Variable"
|
||||
AdvSceneSwitcher.action.variable.type.set="Set"
|
||||
AdvSceneSwitcher.action.variable.type.set="Set to fixed value"
|
||||
AdvSceneSwitcher.action.variable.type.append="Append"
|
||||
AdvSceneSwitcher.action.variable.type.appendVar="Append variable"
|
||||
AdvSceneSwitcher.action.variable.type.increment="Increment"
|
||||
AdvSceneSwitcher.action.variable.type.decrement="Decrement"
|
||||
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}"
|
||||
|
||||
AdvSceneSwitcher.action.variable.type.setActionValue="Set to action value"
|
||||
AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value"
|
||||
AdvSceneSwitcher.action.variable.type.roundToInt="Round to nearest integer"
|
||||
AdvSceneSwitcher.action.variable.type.subString="Set to substring of current value"
|
||||
AdvSceneSwitcher.action.variable.type.findAndReplace="Find and replace in current value"
|
||||
AdvSceneSwitcher.action.variable.type.mathExpression="Mathematical expression"
|
||||
AdvSceneSwitcher.action.variable.type.askForValue="Get user input"
|
||||
AdvSceneSwitcher.action.variable.askForValuePromptDefault="Assign value to variable \"%1\":"
|
||||
AdvSceneSwitcher.action.variable.askForValuePrompt="Assign value to variable:"
|
||||
AdvSceneSwitcher.action.variable.mathExpression.example="( 1 + 2 * 3 ) / 4"
|
||||
AdvSceneSwitcher.action.variable.findAndReplace.find="Text to find"
|
||||
AdvSceneSwitcher.action.variable.findAndReplace.replace="Text to replace with"
|
||||
AdvSceneSwitcher.action.variable.subString.begin="Beginning"
|
||||
AdvSceneSwitcher.action.variable.subString.all="All"
|
||||
AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
|
||||
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
|
||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
|
||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
|
||||
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}"
|
||||
AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}} Substring size:{{subStringSize}}"
|
||||
AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
|
||||
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{replaceStr}}"
|
||||
AdvSceneSwitcher.action.variable.entry.userInput="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
|
||||
AdvSceneSwitcher.action.projector="Projector"
|
||||
AdvSceneSwitcher.action.projector.type.source="Source"
|
||||
AdvSceneSwitcher.action.projector.type.scene="Scene"
|
||||
AdvSceneSwitcher.action.projector.type.preview="Preview"
|
||||
AdvSceneSwitcher.action.projector.type.program="Program"
|
||||
AdvSceneSwitcher.action.projector.type.multiview="Multiview"
|
||||
AdvSceneSwitcher.action.projector.display="Display"
|
||||
AdvSceneSwitcher.action.projector.windowed="Windowed"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="Fullscreen"
|
||||
AdvSceneSwitcher.action.projector.entry="Open{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="on{{monitors}}"
|
||||
AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="Send message to {{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="Set MIDI message selection to messages incoming on {{listenDevices}}: {{listenButton}}"
|
||||
AdvSceneSwitcher.action.osc="Open Sound Control"
|
||||
AdvSceneSwitcher.action.sceneLock="Scene item lock"
|
||||
AdvSceneSwitcher.action.sceneLock.type.lock="lock"
|
||||
AdvSceneSwitcher.action.sceneLock.type.unlock="unlock"
|
||||
AdvSceneSwitcher.action.sceneLock.type.toggle="toggle lock of"
|
||||
AdvSceneSwitcher.action.sceneLock.entry="On{{scenes}}{{actions}}{{sources}}"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="When changing transitions:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Set transition overrides"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Change active transition type"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Change transition if scene is active"
|
||||
@@ -596,7 +821,7 @@ AdvSceneSwitcher.mediaTab.states.none="None"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="Playing"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="Opening"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="Buffering"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="Paused"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="Paused"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="Stopped"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Ended"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Error"
|
||||
@@ -799,8 +1024,11 @@ AdvSceneSwitcher.item.emptyName="Empty name not allowed!"
|
||||
AdvSceneSwitcher.item.nameNotAvailable="Name not available!"
|
||||
AdvSceneSwitcher.item.nameReserved="Name reserved!"
|
||||
|
||||
AdvSceneSwitcher.macroSegmentSelection.invalid="Invalid selection!"
|
||||
|
||||
AdvSceneSwitcher.variable.select="--select variable--"
|
||||
AdvSceneSwitcher.variable.add="Add new variable"
|
||||
AdvSceneSwitcher.variable.configure="Configure variable settings"
|
||||
AdvSceneSwitcher.variable.name="Name:"
|
||||
AdvSceneSwitcher.variable.value="Current value:"
|
||||
AdvSceneSwitcher.variable.save="Save / load behavior"
|
||||
@@ -810,13 +1038,17 @@ AdvSceneSwitcher.variable.save.default="Set to value"
|
||||
|
||||
AdvSceneSwitcher.connection.select="--select connection--"
|
||||
AdvSceneSwitcher.connection.add="Add new connection"
|
||||
AdvSceneSwitcher.connection.configure="Configure connection settings"
|
||||
AdvSceneSwitcher.connection.name="Name:"
|
||||
AdvSceneSwitcher.connection.useCustomURI="Use custom URI"
|
||||
AdvSceneSwitcher.connection.customURI="Address:"
|
||||
AdvSceneSwitcher.connection.address="Address:"
|
||||
AdvSceneSwitcher.connection.port="Port:"
|
||||
AdvSceneSwitcher.connection.password="Password:"
|
||||
AdvSceneSwitcher.connection.reconnect="Reconnect automatically:"
|
||||
AdvSceneSwitcher.connection.reconnectDelay="Automatically reconnect after:"
|
||||
AdvSceneSwitcher.connection.connectOnStart="Connect on startup:"
|
||||
AdvSceneSwitcher.connection.useOBSWebsocketProtocol="<html><head/><body><p>Use the obs-websocket <a href=\"https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md\"><span style=\" text-decoration: underline; color:#268bd2;\">protocol</span></a></p></body></html>"
|
||||
AdvSceneSwitcher.connection.test="Test connection"
|
||||
AdvSceneSwitcher.connection.status.disconnected="Disconnected"
|
||||
AdvSceneSwitcher.connection.status.connecting="Connecting"
|
||||
@@ -824,12 +1056,75 @@ AdvSceneSwitcher.connection.status.connected="Connected, but not authenticated"
|
||||
AdvSceneSwitcher.connection.status.authenticated="Connected and authenticated"
|
||||
|
||||
AdvSceneSwitcher.regex.enable="Enable regular expressions"
|
||||
AdvSceneSwitcher.regex.configure="Configure regular expression settings"
|
||||
AdvSceneSwitcher.regex.partialMatch="Allow partial match"
|
||||
AdvSceneSwitcher.regex.caseInsensitive="Match case insensitive"
|
||||
AdvSceneSwitcher.regex.dotMatchNewline=". matches newlines"
|
||||
AdvSceneSwitcher.regex.multiLine="^ and $ match start/end of line"
|
||||
AdvSceneSwitcher.regex.extendedPattern="Enable Qt's ExtendedPatternSyntax"
|
||||
|
||||
AdvSceneSwitcher.process.showAdvanced="Show advanced settings"
|
||||
AdvSceneSwitcher.process.arguments="Arguments:"
|
||||
AdvSceneSwitcher.process.addArgument="Add argument"
|
||||
AdvSceneSwitcher.process.addArgumentDescription="Add new argument:"
|
||||
AdvSceneSwitcher.process.entry="Run{{filePath}}{{advancedSettings}}"
|
||||
AdvSceneSwitcher.process.entry.workingDirectory="Working directory:{{workingDirectory}}"
|
||||
|
||||
AdvSceneSwitcher.math.expressionFail="Failed evaluate expression"
|
||||
|
||||
AdvSceneSwitcher.midi.deviceNamePattern="[Port #%1] %2"
|
||||
AdvSceneSwitcher.midi.message.type="Type:"
|
||||
AdvSceneSwitcher.midi.message.type.optional="Any"
|
||||
AdvSceneSwitcher.midi.message.type.invalid="Invalid"
|
||||
AdvSceneSwitcher.midi.message.type.noteOff="Note Off"
|
||||
AdvSceneSwitcher.midi.message.type.noteOn="Note On"
|
||||
AdvSceneSwitcher.midi.message.type.polyphonicPressure="Polyphonic Pressure"
|
||||
AdvSceneSwitcher.midi.message.type.controlChange="Control Change"
|
||||
AdvSceneSwitcher.midi.message.type.programChange="Program Change"
|
||||
AdvSceneSwitcher.midi.message.type.channelAftertouch="Channel Aftertouch"
|
||||
AdvSceneSwitcher.midi.message.type.pitchBend="Pitch Bend"
|
||||
AdvSceneSwitcher.midi.message.type.systemExclusive="System Exclusive"
|
||||
AdvSceneSwitcher.midi.message.type.timeCode="Time Code"
|
||||
AdvSceneSwitcher.midi.message.type.songPositionPointer="Song Position Pointer"
|
||||
AdvSceneSwitcher.midi.message.type.songSelect="Song Select"
|
||||
AdvSceneSwitcher.midi.message.type.reserved1="Reserved (1)"
|
||||
AdvSceneSwitcher.midi.message.type.reserved2="Reserved (2)"
|
||||
AdvSceneSwitcher.midi.message.type.tuneRequest="Tune Request"
|
||||
AdvSceneSwitcher.midi.message.type.endOfSystemExclusive="End of System Exclusive"
|
||||
AdvSceneSwitcher.midi.message.type.timeClock="Time Clock"
|
||||
AdvSceneSwitcher.midi.message.type.reserved3="Reserved (3)"
|
||||
AdvSceneSwitcher.midi.message.type.startFile="Start File"
|
||||
AdvSceneSwitcher.midi.message.type.continueFile="Continue File"
|
||||
AdvSceneSwitcher.midi.message.type.stopFile="Stop File"
|
||||
AdvSceneSwitcher.midi.message.type.reserved4="Reserved (4)"
|
||||
AdvSceneSwitcher.midi.message.type.activeSensing="Active Sensing"
|
||||
AdvSceneSwitcher.midi.message.type.systemReset="System Reset"
|
||||
AdvSceneSwitcher.midi.message.type.unknown="Unknown"
|
||||
AdvSceneSwitcher.midi.message.channel="Channel:"
|
||||
AdvSceneSwitcher.midi.message.note="Note / Value(1):"
|
||||
AdvSceneSwitcher.midi.message.value="Value(2):"
|
||||
AdvSceneSwitcher.midi.message.placeholder="Any"
|
||||
AdvSceneSwitcher.midi.resetDevices="Reconnect all MIDI devices"
|
||||
AdvSceneSwitcher.midi.startListen="Start listening"
|
||||
AdvSceneSwitcher.midi.stopListen="Stop listening"
|
||||
AdvSceneSwitcher.midi.startListenFail="Device is busy!\nSomething else is already listening!"
|
||||
AdvSceneSwitcher.midi.deviceOpenFail="Failed to initialize MIDI device!"
|
||||
|
||||
AdvSceneSwitcher.osc.network="Network"
|
||||
AdvSceneSwitcher.osc.network.protocol="Protocol:"
|
||||
AdvSceneSwitcher.osc.network.address="Address:"
|
||||
AdvSceneSwitcher.osc.network.port="Port:"
|
||||
AdvSceneSwitcher.osc.message="Message"
|
||||
AdvSceneSwitcher.osc.message.type.none="None"
|
||||
AdvSceneSwitcher.osc.message.type.float="Float"
|
||||
AdvSceneSwitcher.osc.message.type.int="Integer"
|
||||
AdvSceneSwitcher.osc.message.type.string="String"
|
||||
AdvSceneSwitcher.osc.message.type.binaryBlob="Binary blob"
|
||||
AdvSceneSwitcher.osc.message.type.true="True"
|
||||
AdvSceneSwitcher.osc.message.type.false="False"
|
||||
AdvSceneSwitcher.osc.message.type.infinity="Infinitum"
|
||||
AdvSceneSwitcher.osc.message.type.null="Nil"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
@@ -853,8 +1148,25 @@ AdvSceneSwitcher.selectSceneCollection="--select scene collection--"
|
||||
AdvSceneSwitcher.enterPath="--enter path--"
|
||||
AdvSceneSwitcher.enterText="--enter text--"
|
||||
AdvSceneSwitcher.enterURL="--enter URL--"
|
||||
AdvSceneSwitcher.selectHotkey="--select hotkey--"
|
||||
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved"
|
||||
AdvSceneSwitcher.selectWindowTip="Use \"OBS\" to specify OBS window\nUse \"Task Switching\"to specify ALT + TAB"
|
||||
|
||||
AdvSceneSwitcher.sceneItemSelection.configure="Configure scene item selection type"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceName="Source name"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry="{{nameConflictIndex}}{{sourceName}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceVariable="Variable name"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.entry="{{nameConflictIndex}}{{variable}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern="Source name matches pattern"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry="{{nameConflictIndex}}matching{{pattern}}{{regex}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup="Sources of type"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.entry="{{nameConflictIndex}}Source type{{sourceGroups}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.index="Source with index"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.index.entry="{{index}}source"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.indexRange="Sources in index range"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.indexRange.entry="Sources from{{index}}to{{indexEnd}}"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.all="All sources"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.all.entry="All sources"
|
||||
AdvSceneSwitcher.sceneItemSelection.all="All"
|
||||
AdvSceneSwitcher.sceneItemSelection.any="Any"
|
||||
|
||||
@@ -876,3 +1188,7 @@ AdvSceneSwitcher.duration.condition.more="For at least"
|
||||
AdvSceneSwitcher.duration.condition.equal="For exactly"
|
||||
AdvSceneSwitcher.duration.condition.less="For at most"
|
||||
AdvSceneSwitcher.duration.condition.within="Within the last"
|
||||
|
||||
AdvSceneSwitcher.audio.monitor.none="Monitor Off"
|
||||
AdvSceneSwitcher.audio.monitor.monitorOnly="Monitor Only (mute output)"
|
||||
AdvSceneSwitcher.audio.monitor.both="Monitor and Output"
|
||||
|
||||
@@ -24,7 +24,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Solo será tan
|
||||
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.cooldown="Después de una coincidencia, no cambiar de escena durante"
|
||||
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"
|
||||
@@ -54,6 +53,10 @@ AdvSceneSwitcher.generalTab.priority.time="Tiempo"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Macro"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="Al cambiar las transiciones:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="Establecer anulaciones de transición"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Cambiar tipo de transición activa"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Al menos una opción debe estar habilitada:\n\n - Usar anulaciones de transición\n\n - Cambiar el tipo de transición activo"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Macro"
|
||||
@@ -82,7 +85,6 @@ AdvSceneSwitcher.macroTab.minimize="Minimizar"
|
||||
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Resaltar macros ejecutadas recientemente"
|
||||
AdvSceneSwitcher.macroTab.highlightTrueConditions="Resaltar condiciones de la macro seleccionada, que se evaluaron como verdaderas recientemente"
|
||||
AdvSceneSwitcher.macroTab.highlightPerformedActions="Resaltar acciones realizadas recientemente de la macro seleccionada actualmente"
|
||||
AdvSceneSwitcher.macroTab.disableHotkeys="Registre teclas de acceso rápido para controlar el estado de pausa de la macro seleccionada"
|
||||
|
||||
; Lógica de macros
|
||||
AdvSceneSwitcher.logic.none="Omitir entrada"
|
||||
@@ -102,13 +104,13 @@ AdvSceneSwitcher.condition.audio.state.mute="silenciado"
|
||||
AdvSceneSwitcher.condition.audio.state.unmute="no silenciado"
|
||||
AdvSceneSwitcher.condition.audio.type.output="Volumen de salida"
|
||||
AdvSceneSwitcher.condition.audio.type.volume="Nivel de volumen configurado"
|
||||
AdvSceneSwitcher.condition.audio.entry="{{checkType}} de {{audioSources}} es {{condition}} {{volume}}"
|
||||
AdvSceneSwitcher.condition.audio.entry="{{checkType}} de {{audioSources}} es {{condition}}{{volume}}{{syncOffset}}{{monitorTypes}}"
|
||||
AdvSceneSwitcher.condition.cursor="Cursor"
|
||||
AdvSceneSwitcher.condition.cursor.type.region="está en la región"
|
||||
AdvSceneSwitcher.condition.cursor.type.moving="se está moviendo"
|
||||
AdvSceneSwitcher.condition.cursor.showFrame="Mostrar cuadro"
|
||||
AdvSceneSwitcher.condition.cursor.hideFrame="Ocultar cuadro"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="El cursor es {{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="El cursor es {{conditions}}{{buttons}}{{minX}}{{minY}}{{maxX}}{{maxY}}{{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line2="El cursor se encuentra actualmente en {{xPos}} x {{yPos}}"
|
||||
AdvSceneSwitcher.condition.scene="Escena"
|
||||
AdvSceneSwitcher.condition.scene.type.current="La escena actual es"
|
||||
@@ -117,7 +119,7 @@ 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}}"
|
||||
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
|
||||
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
|
||||
AdvSceneSwitcher.condition.window="Ventana"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} existen y ..."
|
||||
@@ -131,7 +133,7 @@ AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activad
|
||||
AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas"
|
||||
AdvSceneSwitcher.condition.media.matchOnChange="Solo coincidir con el cambio (Nota: esta opción se eliminará en una versión futura; use modificadores de duración en su lugar)"
|
||||
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 {{mediaSources}}{{scenes}} es {{states}} y {{timeRestrictions}} {{time}}"
|
||||
AdvSceneSwitcher.condition.media.entry="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}es{{states}}y{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.video="Video"
|
||||
AdvSceneSwitcher.condition.video.condition.match="coincide exactamente"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="no coincide"
|
||||
@@ -160,7 +162,7 @@ AdvSceneSwitcher.condition.video.patternMatchSuccess="El patrón está resaltado
|
||||
AdvSceneSwitcher.condition.video.objectMatchFail="¡No se encontró el objeto!"
|
||||
AdvSceneSwitcher.condition.video.objectMatchSuccess="El objeto está resaltado en rojo"
|
||||
AdvSceneSwitcher.condition.video.modelLoadFail="¡No se pudieron cargar los datos del modelo!"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.modelPath="Datos del modelo (haar cascade classifier): {{modelDataPath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.minNeighbor="Mínimo de vecinos: {{minNeighbors}}"
|
||||
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduzca la carga de la CPU realizando una comprobación solo cada {{throttleCount}} milisegundos"
|
||||
@@ -177,7 +179,7 @@ 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}}"
|
||||
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"
|
||||
@@ -218,7 +220,6 @@ AdvSceneSwitcher.condition.source="Fuente"
|
||||
AdvSceneSwitcher.condition.source.type.active="Está activo"
|
||||
AdvSceneSwitcher.condition.source.type.showing="Se muestra"
|
||||
AdvSceneSwitcher.condition.source.type.settings="Coincidencia de configuración"
|
||||
AdvSceneSwitcher.condition.source.regex="Usar expresiones regulares"
|
||||
AdvSceneSwitcher.condition.source.getSettings="Obtener la configuración actual"
|
||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
|
||||
@@ -231,7 +232,6 @@ AdvSceneSwitcher.condition.filter="Filtro"
|
||||
AdvSceneSwitcher.condition.filter.type.active="Está habilitado"
|
||||
AdvSceneSwitcher.condition.filter.type.showing="Está deshabilitado"
|
||||
AdvSceneSwitcher.condition.filter.type.settings="Coincidencia de configuración"
|
||||
AdvSceneSwitcher.condition.filter.regex="Usar expresiones regulares"
|
||||
AdvSceneSwitcher.condition.filter.getSettings="Obtener la configuración actual"
|
||||
AdvSceneSwitcher.condition.filter.entry.line1="En {{sources}} {{filters}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
|
||||
@@ -277,7 +277,6 @@ 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.regex="Usar expresiones regulares"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="En{{scenes}}{{sources}}coincide con la transformación"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||
@@ -300,7 +299,7 @@ AdvSceneSwitcher.condition.studioMode.state.notActive="El modo de estudio no est
|
||||
AdvSceneSwitcher.condition.studioMode.state.previewScene="La vista previa de la escena es"
|
||||
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
|
||||
AdvSceneSwitcher.condition.openvr="OpenVR"
|
||||
AdvSceneSwitcher.condition.errorStatus="Error de OpenVR: "
|
||||
AdvSceneSwitcher.condition.openvr.errorStatus="Error de OpenVR: "
|
||||
AdvSceneSwitcher.condition.openvr.entry.line1="HMD está en..."
|
||||
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
|
||||
AdvSceneSwitcher.condition.openvr.entry.line3="HMD se encuentra actualmente en {{xPos}} x {{yPos}} x {{zPos}}"
|
||||
@@ -344,14 +343,14 @@ AdvSceneSwitcher.action.audio.fade.duration="{{fade}}Fade {{fadeTypes}} {{durati
|
||||
AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade {{fadeTypes}} {{rate}}por segundo."
|
||||
AdvSceneSwitcher.action.audio.fade.wait="Espere a que se complete el desvanecimiento".
|
||||
AdvSceneSwitcher.action.audio.fade.abort="Cancelar atenuación ya activa."
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}{{monitorTypes}}"
|
||||
AdvSceneSwitcher.action.recording="Grabando"
|
||||
AdvSceneSwitcher.action.recording.type.stop="Detener grabación"
|
||||
AdvSceneSwitcher.action.recording.type.start="Iniciar grabación"
|
||||
AdvSceneSwitcher.action.recording.type.pause="Pausar grabación"
|
||||
AdvSceneSwitcher.action.recording.type.unpause="Reanudar grabación"
|
||||
AdvSceneSwitcher.action.recording.pause.hint="Tenga en cuenta que, dependiendo de la configuración de grabación, es posible que no pueda pausar la grabación"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{recordFolder}}{{recordFileFormat}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.replay="Búfer de reproducción"
|
||||
AdvSceneSwitcher.action.replay.saveWarn="Advertencia: ¡Guardar con demasiada frecuencia puede hacer que el búfer de reproducción no se guarde realmente!"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Detener el búfer de reproducción"
|
||||
@@ -361,7 +360,7 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Transmisión"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Detener transmisión"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Iniciar transmisión"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Ejecutar"
|
||||
AdvSceneSwitcher.action.run.arguments="Argumentos:"
|
||||
AdvSceneSwitcher.action.run.addArgument="Agregar argumento"
|
||||
@@ -372,7 +371,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.show="Mostrar"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Ocultar"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Fuente"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Cualquiera"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="En{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="En{{scenes}}{{actions}}{{sources}}"
|
||||
AdvSceneSwitcher.action.filter="Filtro"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Habilitar"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Deshabilitar"
|
||||
@@ -383,7 +382,7 @@ AdvSceneSwitcher.action.source="Fuente"
|
||||
AdvSceneSwitcher.action.source.type.enable="Habilitar"
|
||||
AdvSceneSwitcher.action.source.type.disable="Deshabilitar"
|
||||
AdvSceneSwitcher.action.source.type.settings="Establecer configuración"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
|
||||
AdvSceneSwitcher.action.source.warning="Advertencia: la IU de OBS no puede controlar la habilitación y deshabilitación global de fuentes"
|
||||
AdvSceneSwitcher.action.source.getSettings="Obtener la configuración actual"
|
||||
AdvSceneSwitcher.action.media="Medios"
|
||||
@@ -401,7 +400,7 @@ AdvSceneSwitcher.action.macro.type.unpause="Reanudar"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Reiniciar contador"
|
||||
AdvSceneSwitcher.action.macro.type.run="Ejecutar"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Detener"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Estado del complemento"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Detener el complemento Advanced Scene Switcher"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Cambiar el comportamiento de no coincidencia:"
|
||||
@@ -423,7 +422,7 @@ AdvSceneSwitcher.action.hotkey.leftMeta="Meta izquierda"
|
||||
AdvSceneSwitcher.action.hotkey.rightMeta="Meta derecho"
|
||||
AdvSceneSwitcher.action.hotkey.onlyOBS="Enviar pulsación de tecla solo a OBS"
|
||||
AdvSceneSwitcher.action.hotkey.disabled="No se puede simular la pulsación de teclas: ¡funcionalidad desactivada!"
|
||||
AdvSceneSwitcher.action.hotkey.entry="Presione {{keys}} durante {{duration}} milisegundos"
|
||||
AdvSceneSwitcher.action.hotkey.entry="Presione {{keys}} durante {{duration}}"
|
||||
AdvSceneSwitcher.action.sceneOrder="Orden de elementos de escena"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Mover hacia arriba"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover hacia abajo"
|
||||
@@ -433,7 +432,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}transformar{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.file="Archivo"
|
||||
AdvSceneSwitcher.action.file.type.write="Escribir"
|
||||
AdvSceneSwitcher.action.file.type.append="Agregar"
|
||||
@@ -461,7 +460,6 @@ AdvSceneSwitcher.action.timer.entry="{{timerAction}} temporizadores en {{macros}
|
||||
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.systray.entry="Mostrar notificación: {{message}}"
|
||||
AdvSceneSwitcher.action.screenshot="Captura de pantalla"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="Salida principal de OBS"
|
||||
AdvSceneSwitcher.action.profile="Perfil"
|
||||
@@ -478,10 +476,6 @@ AdvSceneSwitcher.action.sequence.continueFrom="Continuar con el elemento selecci
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transición"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="Al cambiar las transiciones:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Establecer anulaciones de transición"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Cambiar tipo de transición activa"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Al menos una opción debe estar habilitada:\n\n - Usar anulaciones de transición\n\n - Cambiar el tipo de transición activo"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Utiliza la transición para el cambio de escena automatizado de la escena A a la escena B"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body> <p> Estos ajustes <span style =\"font-style: italic; \"> solo </span> surgió a las transiciones causadas por el selector de escenas - Echa un vistazo a <a href =\"https://obsproject.com/forum/resources/transition-table.1174/\"> <span style=\" texto-decoración: subrayado; color: # 268bd2; \"> Tabla de transición </span> </a> si desea configurar esto para cambios de escena manuales. <br/> Los definiciones definidos aquí tienen prioridad sobre los ajustes de transición configurados en cualquier otro lugar del selector de escenas. < br/> <br/> Haz clic en el símbolo más a continuación para agregar una nueva entrada. </p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Cambiar transición si la escena está activa"
|
||||
@@ -537,7 +531,7 @@ AdvSceneSwitcher.mediaTab.states.none="Ninguno"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="Reproduciéndose"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="Abriendo"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="Almacenando en búfer"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="Pausado"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="Pausado"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="Detenido"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Finalizado"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Error"
|
||||
|
||||
@@ -24,7 +24,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Будет то
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Не переключаться"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Переключиться на любую сцену на вкладке Random"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Переключиться на:"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="После совпадения не переключаться между сценами в течение"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="В течение этого времени потенциальные совпадения будут игнорироваться!"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Включить ведение подробного журнала"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Сохранять положение и размер окна"
|
||||
@@ -52,6 +51,10 @@ AdvSceneSwitcher.generalTab.priority.time="Время"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Аудио"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Видео"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Макрос"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="При изменении переходов:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="Установить переопределение переходов"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Изменить тип активного перехода"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Должна быть включена хотя бы одна опция:\n\n - Использовать переопределения переходов\n\n - Изменить тип активного перехода"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Макрос"
|
||||
@@ -125,14 +128,14 @@ AdvSceneSwitcher.action.audio.type.mute="Замьютить"
|
||||
AdvSceneSwitcher.action.audio.type.unmute="Размьютить"
|
||||
AdvSceneSwitcher.action.audio.type.sourceVolume="Установить громкость источника"
|
||||
AdvSceneSwitcher.action.audio.type.masterVolume="Установить главную громкость"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}{{monitorTypes}}"
|
||||
AdvSceneSwitcher.action.recording="Запись"
|
||||
AdvSceneSwitcher.action.recording.type.stop="Остановить запись"
|
||||
AdvSceneSwitcher.action.recording.type.start="Начать запись"
|
||||
AdvSceneSwitcher.action.recording.type.pause="Пауза записи"
|
||||
AdvSceneSwitcher.action.recording.type.unpause="Снять запись с паузы"
|
||||
AdvSceneSwitcher.action.recording.pause.hint="Обратите внимание, что в зависимости от настроек записи вы можете не иметь возможности приостановить запись"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{recordFolder}}{{recordFileFormat}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.replay="Буфер воспроизведения"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Остановить буфер воспроизведения"
|
||||
AdvSceneSwitcher.action.replay.type.start="Начать воспроизведение буфера"
|
||||
@@ -141,16 +144,12 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Остановить потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Запустить"
|
||||
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Переход"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="При изменении переходов:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Установить переопределение переходов"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Изменить тип активного перехода"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Должна быть включена хотя бы одна опция:\n\n - Использовать переопределения переходов\n\n - Изменить тип активного перехода"
|
||||
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>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Изменить переход, если сцена активна"
|
||||
@@ -206,7 +205,7 @@ AdvSceneSwitcher.mediaTab.states.none="Нет"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="Играет"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="Открытие"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="Буферизация"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="Приостановлено"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="Приостановлено"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="Остановлено"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Закончилось"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Ошибка"
|
||||
|
||||
@@ -53,6 +53,10 @@ AdvSceneSwitcher.generalTab.priority.time="Zaman"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Ses"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Makro"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="Geçişleri değiştirirken:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="Geçiş geçersiz kılmaları ayarla"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Etkin geçiş türünü değiştir"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="En az bir seçenek etkinleştirilmelidir: \n\n - Geçiş geçersiz kılmalarını kullan \n\n - Etkin geçiş türünü değiştir"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Makro"
|
||||
@@ -97,7 +101,7 @@ AdvSceneSwitcher.condition.cursor.type.region="alanında"
|
||||
AdvSceneSwitcher.condition.cursor.type.moving="hareketlidir"
|
||||
AdvSceneSwitcher.condition.cursor.showFrame="Çerçeveyi Göster"
|
||||
AdvSceneSwitcher.condition.cursor.hideFrame="Çerçeveyi Gizle"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="İmleç {{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="İmleç {{conditions}}{{buttons}}{{minX}}{{minY}}{{maxX}}{{maxY}}{{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line2="İmleç şuanda {{xPos}} x {{yPos}}"
|
||||
AdvSceneSwitcher.condition.scene="Sahne"
|
||||
AdvSceneSwitcher.condition.scene.type.current="Mevcut Sahne"
|
||||
@@ -105,7 +109,7 @@ AdvSceneSwitcher.condition.scene.type.previous="Önceki Sahne"
|
||||
AdvSceneSwitcher.condition.scene.type.changed="Sahne değişti"
|
||||
AdvSceneSwitcher.condition.scene.type.notChanged="Sahne değişmedi"
|
||||
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Geçiş hedefi sahnesi için geçiş kontrolü sırasında"
|
||||
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
|
||||
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
|
||||
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
|
||||
AdvSceneSwitcher.condition.window="Pencere"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} Varolan ve ..."
|
||||
@@ -118,7 +122,7 @@ AdvSceneSwitcher.condition.media="Medya"
|
||||
AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları "
|
||||
AdvSceneSwitcher.condition.media.matchOnChange="Yalnızca değişiklikle eşleştirin (Not: Bu seçenek gelecekteki bir sürümde kaldırılacaktır - lütfen bunun yerine zaman kısıtlamalarını kullanın)"
|
||||
AdvSceneSwitcher.condition.media.entry="{{mediaSources}}{{scenes}} durumu {{states}} ve {{timeRestrictions}} {{time}}"
|
||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}durumu{{states}}ve{{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.video="Video"
|
||||
AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="Eşleştirme"
|
||||
@@ -143,7 +147,7 @@ AdvSceneSwitcher.condition.video.screenshotFail="Kaynağın ekran görüntüsü
|
||||
AdvSceneSwitcher.condition.video.patternMatchFail="Desen bulunamadı!"
|
||||
AdvSceneSwitcher.condition.video.objectMatchFail="Nesne bulunamadı!"
|
||||
AdvSceneSwitcher.condition.video.modelLoadFail="Model verileri yüklenemedi!"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.modelPath="Model verileri (haar kademeli sınıflandırıcı):{{modelDataPath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum komşular: {{minNeighbors}}"
|
||||
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}} Yalnızca her seferinde kontrol gerçekleştirerek CPU yükünü azaltın {{throttleCount}} millisaniyeler"
|
||||
@@ -152,7 +156,7 @@ AdvSceneSwitcher.condition.stream.state.start="Yayın çalışıyor"
|
||||
AdvSceneSwitcher.condition.stream.state.stop="Yayın durdu"
|
||||
AdvSceneSwitcher.condition.stream.state.starting="Yayın başlıyor"
|
||||
AdvSceneSwitcher.condition.stream.state.stopping="Yayın duruyor"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}"
|
||||
AdvSceneSwitcher.condition.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ı"
|
||||
@@ -192,7 +196,6 @@ AdvSceneSwitcher.condition.source="Kaynak"
|
||||
AdvSceneSwitcher.condition.source.type.active="Aktiftir"
|
||||
AdvSceneSwitcher.condition.source.type.showing="gösteriyor"
|
||||
AdvSceneSwitcher.condition.source.type.settings="Ayarlar eşleştirildi"
|
||||
AdvSceneSwitcher.condition.source.regex="Normal ifadeler kullanın"
|
||||
AdvSceneSwitcher.condition.source.getSettings="Mevcut ayarları al"
|
||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
|
||||
@@ -205,7 +208,6 @@ AdvSceneSwitcher.condition.filter="Filtrele"
|
||||
AdvSceneSwitcher.condition.filter.type.active="Etkinleştirildi"
|
||||
AdvSceneSwitcher.condition.filter.type.showing="Etkisizleştirildi"
|
||||
AdvSceneSwitcher.condition.filter.type.settings="Ayarlar eşleşti"
|
||||
AdvSceneSwitcher.condition.filter.regex="Normal ifadeler kullanın"
|
||||
AdvSceneSwitcher.condition.filter.getSettings="Mevcut ayarları al"
|
||||
AdvSceneSwitcher.condition.filter.entry.line1="Açık {{sources}} {{filters}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
|
||||
@@ -231,7 +233,6 @@ AdvSceneSwitcher.condition.date.state.before="Önce"
|
||||
AdvSceneSwitcher.condition.date.state.between="Arasında"
|
||||
AdvSceneSwitcher.condition.sceneTransform="Sahne öğesi dönüşümü"
|
||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Dönüşümü al"
|
||||
AdvSceneSwitcher.condition.sceneTransform.regex="Normal ifadeler kullanın"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Açık{{scenes}}{{sources}} dönüşümle eşleşir"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||
@@ -254,7 +255,7 @@ AdvSceneSwitcher.condition.studioMode.state.notActive="Studyo modu aktif değil"
|
||||
AdvSceneSwitcher.condition.studioMode.state.previewScene="Önizleme sahnesi"
|
||||
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
|
||||
AdvSceneSwitcher.condition.openvr="Aç VR"
|
||||
AdvSceneSwitcher.condition.errorStatus="Aç VR hatası: "
|
||||
AdvSceneSwitcher.condition.openvr.errorStatus="Aç VR hatası: "
|
||||
AdvSceneSwitcher.condition.openvr.entry.line1="HMD içinde ..."
|
||||
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
|
||||
AdvSceneSwitcher.condition.openvr.entry.line3="HMD mevcut {{xPos}} x {{yPos}} x {{zPos}}"
|
||||
@@ -273,14 +274,14 @@ AdvSceneSwitcher.action.audio.type.mute="Sessiz"
|
||||
AdvSceneSwitcher.action.audio.type.unmute="Ses açmak"
|
||||
AdvSceneSwitcher.action.audio.type.sourceVolume="Kaynak ses seviyesini ayarla"
|
||||
AdvSceneSwitcher.action.audio.type.masterVolume="Ana ses seviyesini ayarla"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}{{monitorTypes}}"
|
||||
AdvSceneSwitcher.action.recording="Kayıt"
|
||||
AdvSceneSwitcher.action.recording.type.stop="Kayıt Durdur"
|
||||
AdvSceneSwitcher.action.recording.type.start="Kayıt Başlat"
|
||||
AdvSceneSwitcher.action.recording.type.pause="Kayıt Duraklat"
|
||||
AdvSceneSwitcher.action.recording.type.unpause="Kayıt Duraklatma"
|
||||
AdvSceneSwitcher.action.recording.pause.hint="Kayıt ayarlarınıza bağlı olarak kaydı duraklatamayabileceğinizi unutmayın."
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{recordFolder}}{{recordFileFormat}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.replay="Tekrar arabelleği"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Tekrar arabelleğini durdur"
|
||||
AdvSceneSwitcher.action.replay.type.start="Tekrar arabelleğini başlat"
|
||||
@@ -289,7 +290,7 @@ AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Yayın"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Yayın durdur"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Yayın başlat"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Çalıştır"
|
||||
AdvSceneSwitcher.action.run.arguments="Argümanlar:"
|
||||
AdvSceneSwitcher.action.run.addArgument="Argüman ekle"
|
||||
@@ -300,7 +301,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.show="Göster"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Gizle"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Kayıt"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Herhangi"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="Açık{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="Açık{{scenes}}{{actions}}{{sources}}"
|
||||
AdvSceneSwitcher.action.filter="Filtrele"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Etkin"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
|
||||
@@ -311,7 +312,7 @@ AdvSceneSwitcher.action.source="Kaynak"
|
||||
AdvSceneSwitcher.action.source.type.enable="Etkin"
|
||||
AdvSceneSwitcher.action.source.type.disable="Etkisiz"
|
||||
AdvSceneSwitcher.action.source.type.settings="Ayarları yap"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
|
||||
AdvSceneSwitcher.action.source.warning="Uyarı: Kaynakların global olarak etkinleştirilmesi ve devre dışı bırakılması, OBS UI tarafından kontrol edilemez"
|
||||
AdvSceneSwitcher.action.source.getSettings="Mevcut Ayarları yap"
|
||||
AdvSceneSwitcher.action.media="Medya"
|
||||
@@ -327,7 +328,7 @@ AdvSceneSwitcher.action.macro.type.pause="Duraklat"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Duraklatma"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Sayacı sıfırla"
|
||||
AdvSceneSwitcher.action.macro.type.run="Çalıştır"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Eklenti durumu"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcher eklentisini durdurun"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Eşleşmeme davranışını değiştirin:"
|
||||
@@ -349,7 +350,7 @@ AdvSceneSwitcher.action.hotkey.leftMeta="Sol Meta"
|
||||
AdvSceneSwitcher.action.hotkey.rightMeta="Sağ Meta"
|
||||
AdvSceneSwitcher.action.hotkey.onlyOBS="Tuşa basımı yalnızca OBS'ye gönder"
|
||||
AdvSceneSwitcher.action.hotkey.disabled="Tuşa basma simülasyonu yapılamıyor - işlevsellik devre dışı!"
|
||||
AdvSceneSwitcher.action.hotkey.entry="Bas {{keys}} uygun {{duration}} millisaniye"
|
||||
AdvSceneSwitcher.action.hotkey.entry="Bas {{keys}} uygun {{duration}}"
|
||||
AdvSceneSwitcher.action.sceneOrder="Sahne öğesi sırası"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Yukarı taşı"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Aşağı indir"
|
||||
@@ -359,15 +360,11 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}transform{{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.file="Dosya"
|
||||
AdvSceneSwitcher.action.file.type.write="Yaz"
|
||||
AdvSceneSwitcher.action.file.type.append="Ekle"
|
||||
AdvSceneSwitcher.action.file.entry="{{actions}} {{filePath}}:"
|
||||
AdvSceneSwitcher.action.previewScene="Önizleme sahnesini değiştir"
|
||||
AdvSceneSwitcher.action.previewScene.entry="Önizleme sahnesini şu şekilde değiştir: {{scenes}}"
|
||||
AdvSceneSwitcher.action.SceneSwap="Sahneyi değiştir (Studyo modu)"
|
||||
AdvSceneSwitcher.action.SceneSwap.entry="Stüdyo modunda önizleme ve program sahnesini değiştir"
|
||||
AdvSceneSwitcher.action.transition="Geçiş"
|
||||
AdvSceneSwitcher.action.transition.entry.line2="{{setTransition}}Geçiş türünü ayarla {{transitions}}"
|
||||
AdvSceneSwitcher.action.transition.entry.line3="{{setDuration}}Geçiş süresini şuna ayarla: {{duration}}saniyeler"
|
||||
@@ -380,7 +377,6 @@ AdvSceneSwitcher.action.timer.entry="{{timerAction}} zamanlayıcılar açık {{m
|
||||
AdvSceneSwitcher.action.random="Rastgele"
|
||||
AdvSceneSwitcher.action.random.entry="Aşağıdaki makrolardan herhangi birini rastgele çalıştırın (duraklatılmış makrolar yoksayılır)"
|
||||
AdvSceneSwitcher.action.systray="Sistem tepsisi bildirimi"
|
||||
AdvSceneSwitcher.action.systray.entry="Bildirimleri göster: {{message}}"
|
||||
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
|
||||
AdvSceneSwitcher.action.profile="Profil"
|
||||
@@ -391,10 +387,6 @@ AdvSceneSwitcher.action.sceneCollection.warning="Not: Değişen sahne koleksiyon
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Geçiş"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="Geçişleri değiştirirken:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Geçiş geçersiz kılmaları ayarla"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Etkin geçiş türünü değiştir"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="En az bir seçenek etkinleştirilmelidir: \n\n - Geçiş geçersiz kılmalarını kullan \n\n - Etkin geçiş türünü değiştir"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Sahne A'dan sahne B'ye otomatik sahne geçişi için geçişi kullanın"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Bu ayarlar <span style=\"font-style:italic;\">yalnızca</span>, sahne değiştiricinin neden olduğu geçişleri etkiler - <a href=\ Göz atın "https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Geçiş Tablosu</span></ a> bunu manuel sahne değişiklikleri için yapılandırmak istiyorsanız.<br/>Burada tanımlanan ayarlar, sahne değiştiricide başka bir yerde yapılandırılmış geçiş ayarlarına göre önceliklidir.<br/><br/>Yeni bir giriş eklemek için aşağıdaki artı simgesini tıklayın. .</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Sahne aktifse geçişi değiştir"
|
||||
@@ -450,7 +442,7 @@ AdvSceneSwitcher.mediaTab.states.none="Yok"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="Oynamak"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="Açmak"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="Arabelleğe alma"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="Duraklatıldı"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="Duraklatıldı"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="Durduruldu"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Bitti"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Hata"
|
||||
|
||||
@@ -4,7 +4,7 @@ AdvSceneSwitcher.windowTitle="高级场景切换器"
|
||||
; General Tab
|
||||
AdvSceneSwitcher.generalTab.title="通用"
|
||||
AdvSceneSwitcher.generalTab.status="状态"
|
||||
AdvSceneSwitcher.generalTab.status.hotkeytips="快捷键可在OBS设置中设置"
|
||||
AdvSceneSwitcher.generalTab.status.hotkeytips="快捷键可在OBS设置中设置<br><br>汉化作者:[ 从不咕咕的阿坤 ] - 全平台统一名 - 如对你有所帮助,劳烦点个关注!"
|
||||
AdvSceneSwitcher.generalTab.status.currentStatus="当前状态:"
|
||||
AdvSceneSwitcher.generalTab.status.onStartup="在OBS启动时"
|
||||
AdvSceneSwitcher.generalTab.status.onStartup.asLastRun="按照最后运行时的状态"
|
||||
@@ -24,13 +24,16 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="时间精度最
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="不切换"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="切换到随机场景列表中任意随机场景"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="切换到"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="比赛结束多少秒后切换场景"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="执行完操作后,跳过执行操作,为"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="在此期间,可能的匹配项将被忽略!"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="详细日志输出"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="保存窗口位置和大小"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="显示系统托盘通知"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="禁用UI提示"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="如果无法加载插件显示警告"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>加载以下插件库失败,这可能导致某些高级场景切换器功能不可用:%1 查看 OBS 日志了解详细信息.<br>可以在“常规”选项卡上禁用此消息.</body></html>"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="隐藏可通过宏操作的分组栏"
|
||||
AdvSceneSwitcher.generalTab.matchBehavior="场景匹配设置"
|
||||
AdvSceneSwitcher.generalTab.priority="优先级"
|
||||
AdvSceneSwitcher.generalTab.priority.description="切换场景优先级 (最上方的项优先级最高)"
|
||||
AdvSceneSwitcher.generalTab.priority.threadPriority="使用线程优先级"
|
||||
@@ -54,6 +57,11 @@ AdvSceneSwitcher.generalTab.priority.time="时间"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="音频"
|
||||
AdvSceneSwitcher.generalTab.priority.video="视频"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="宏"
|
||||
AdvSceneSwitcher.generalTab.transition="转场特效"
|
||||
AdvSceneSwitcher.generalTab.setTransitionBy="更改转场特效时:"
|
||||
AdvSceneSwitcher.generalTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
|
||||
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="更改激活转场特效类型"
|
||||
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="必须至少启用一个选项:\n\n - 使用转场特效替代\n\n - 改变激活的转场特效类型"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="宏"
|
||||
@@ -70,15 +78,46 @@ AdvSceneSwitcher.macroTab.add="添加新宏"
|
||||
AdvSceneSwitcher.macroTab.name="名称:"
|
||||
AdvSceneSwitcher.macroTab.run="运行宏"
|
||||
AdvSceneSwitcher.macroTab.runFail="运行 \"%1\" 失败!\n其中一个操作失败,或者宏已在运行."
|
||||
AdvSceneSwitcher.macroTab.runInParallel="与其他宏并行运行宏"
|
||||
AdvSceneSwitcher.macroTab.runInParallel="与其他宏同时运行宏"
|
||||
AdvSceneSwitcher.macroTab.onChange="仅在条件更改时执行操作"
|
||||
AdvSceneSwitcher.macroTab.defaultname="宏 %1"
|
||||
AdvSceneSwitcher.macroTab.defaultGroupName="组 %1"
|
||||
AdvSceneSwitcher.macroTab.exists="宏名称已存在"
|
||||
AdvSceneSwitcher.macroTab.groupDeleteConfirm="您确定要删除 \"%1\"及其所有元素吗?"
|
||||
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="您确定要删除 %1 宏吗?"
|
||||
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="你确定你要删除 \"%1\"?"
|
||||
AdvSceneSwitcher.macroTab.contextMenuAdd="增加"
|
||||
AdvSceneSwitcher.macroTab.copy="创建副本"
|
||||
AdvSceneSwitcher.macroTab.group="对选定的宏进行分组"
|
||||
AdvSceneSwitcher.macroTab.ungroup="解除选定的组的分组"
|
||||
AdvSceneSwitcher.macroTab.rename="重命名"
|
||||
AdvSceneSwitcher.macroTab.remove="删除"
|
||||
AdvSceneSwitcher.macroTab.expandAll="全部展开"
|
||||
AdvSceneSwitcher.macroTab.collapseAll="全部收回"
|
||||
AdvSceneSwitcher.macroTab.maximize="最大化"
|
||||
AdvSceneSwitcher.macroTab.minimize="最小化"
|
||||
AdvSceneSwitcher.macroTab.highlightSettings="显示 设置"
|
||||
AdvSceneSwitcher.macroTab.hotkeySettings="热键 设置"
|
||||
AdvSceneSwitcher.macroTab.dockSettings="停靠 设置"
|
||||
AdvSceneSwitcher.macroTab.highlightExecutedMacros="突出显示最近执行的宏"
|
||||
AdvSceneSwitcher.macroTab.highlightTrueConditions="突出显示最近运算为 真 的当前选定宏的条件"
|
||||
AdvSceneSwitcher.macroTab.highlightPerformedActions="突出显示当前选定宏的最近执行的操作"
|
||||
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="使用快捷键控制新增宏的暂停状态"
|
||||
AdvSceneSwitcher.macroTab.currentDisableHotkeys="使用快捷键控制所选宏的暂停状态"
|
||||
AdvSceneSwitcher.macroTab.currentRegisterDock="注册停靠小部件以控制所选宏的暂停状态或手动运行它"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddRunButton="添加按钮以运行宏"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddPauseButton="添加按钮以暂停或取消暂停宏"
|
||||
AdvSceneSwitcher.macroTab.currentDockButtonText.run="运行按钮文本:"
|
||||
AdvSceneSwitcher.macroTab.currentDockButtonText.pause="暂停按钮文本:"
|
||||
AdvSceneSwitcher.macroTab.currentDockButtonText.unpause="取消暂停按钮文本:"
|
||||
|
||||
AdvSceneSwitcher.macroDock.pause="暂停"
|
||||
AdvSceneSwitcher.macroDock.unpause="取消暂停"
|
||||
AdvSceneSwitcher.macroDock.run="运行"
|
||||
|
||||
; Macro List
|
||||
AdvSceneSwitcher.macroList.deleted="删除"
|
||||
AdvSceneSwitcher.macroList.duplicate="\"%1\" 已选择!"
|
||||
|
||||
; Macro Logic
|
||||
AdvSceneSwitcher.logic.none="忽略条目"
|
||||
@@ -98,107 +137,181 @@ AdvSceneSwitcher.condition.audio.state.mute="静音"
|
||||
AdvSceneSwitcher.condition.audio.state.unmute="取消静音"
|
||||
AdvSceneSwitcher.condition.audio.type.output="输出音频"
|
||||
AdvSceneSwitcher.condition.audio.type.volume="配置音量级别"
|
||||
AdvSceneSwitcher.condition.audio.type.syncOffset="同步偏移"
|
||||
AdvSceneSwitcher.condition.audio.type.monitor="音频监控"
|
||||
AdvSceneSwitcher.condition.audio.type.balance="音频平衡"
|
||||
AdvSceneSwitcher.condition.audio.entry="{{checkType}} 的 {{audioSources}} 是 {{condition}}{{volume}}{{syncOffset}}{{monitorTypes}}"
|
||||
AdvSceneSwitcher.condition.cursor="屏幕区域"
|
||||
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="鼠标右键"
|
||||
AdvSceneSwitcher.condition.cursor.showFrame="显示帧率"
|
||||
AdvSceneSwitcher.condition.cursor.hideFrame="隐藏帧率"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="鼠标 处于{{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="鼠标 处于{{conditions}}{{buttons}}{{minX}}{{minY}}{{maxX}}{{maxY}}{{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line2="鼠标当前位于 {{xPos}} x {{yPos}}"
|
||||
AdvSceneSwitcher.condition.scene="场景"
|
||||
AdvSceneSwitcher.condition.scene.type.current="当前场景是"
|
||||
AdvSceneSwitcher.condition.scene.type.previous="上一个场景是"
|
||||
AdvSceneSwitcher.condition.scene.type.changed="场景改变了"
|
||||
AdvSceneSwitcher.condition.scene.type.notChanged="场景没有改变"
|
||||
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
|
||||
AdvSceneSwitcher.condition.scene.type.currentPattern="当前场景匹配"
|
||||
AdvSceneSwitcher.condition.scene.type.previousPattern="上一个场景匹配"
|
||||
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="在转场过程中检查转场源场景"
|
||||
AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="在转场过程中检查转场源场景"
|
||||
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
|
||||
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
|
||||
AdvSceneSwitcher.condition.window="窗口"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} 存在..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... 并且是 {{fullscreen}} 全屏 {{maximized}} 最大化 {{focused}} 获得焦点 {{windowFocusChanged}} 焦点窗口已更改"
|
||||
AdvSceneSwitcher.condition.window.entry.window="{{checkTitle}}窗口标题匹配{{windowRegex}}{{windows}}"
|
||||
AdvSceneSwitcher.condition.window.entry.fullscreen="{{fullscreen}}窗口为全屏"
|
||||
AdvSceneSwitcher.condition.window.entry.maximized="{{maximized}}窗口为最大化"
|
||||
AdvSceneSwitcher.condition.window.entry.focused="{{focused}}窗口为焦点"
|
||||
AdvSceneSwitcher.condition.window.entry.focusedChange="{{windowFocusChanged}}焦点窗口被改变"
|
||||
AdvSceneSwitcher.condition.window.entry.text="{{checkText}}窗口包含文本{{textRegex}}{{windowText}}"
|
||||
AdvSceneSwitcher.condition.window.entry.text.note="此选项可能不会对窗口中显示的每个文本起作用。\n如果是这种情况,您可以考虑使用视频条件OCR检查来代替。"
|
||||
AdvSceneSwitcher.condition.window.entry.currentFocus="当前焦点窗口:{{focusWindow}}"
|
||||
AdvSceneSwitcher.condition.file="文件"
|
||||
AdvSceneSwitcher.condition.file.type.match="内容完全匹配"
|
||||
AdvSceneSwitcher.condition.file.type.contentChange="内容已更改"
|
||||
AdvSceneSwitcher.condition.file.type.dateChange="修改日期已更改"
|
||||
AdvSceneSwitcher.condition.file.remote="远程文件"
|
||||
AdvSceneSwitcher.condition.file.local="本地文件"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="媒体"
|
||||
AdvSceneSwitcher.condition.media.anyOnScene="任何媒体来源"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="所有媒体来源"
|
||||
AdvSceneSwitcher.condition.media.source="源"
|
||||
AdvSceneSwitcher.condition.media.anyOnScene="任何媒体源"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="所有媒体源"
|
||||
AdvSceneSwitcher.condition.media.matchOnChange="仅在更改时匹配(注意:此选项将在未来版本中删除-请改用时间限制)"
|
||||
AdvSceneSwitcher.condition.media.inconsistencyInfo="不幸的是,并非所有媒体源类型的行为都相同(例如,媒体源与VLC视频源“停止”状态)。\n所以,请尝试适合您的设置!"
|
||||
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}} 的状态是 {{states}} 和 {{timeRestrictions}}{{time}}"
|
||||
AdvSceneSwitcher.condition.video="视频"
|
||||
AdvSceneSwitcher.condition.video.condition.match="完全匹配"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="不匹配"
|
||||
AdvSceneSwitcher.condition.video.condition.hasChanged="改变了"
|
||||
AdvSceneSwitcher.condition.video.condition.hasNotChanged="没有改变"
|
||||
AdvSceneSwitcher.condition.video.condition.hasChanged="已改变"
|
||||
AdvSceneSwitcher.condition.video.condition.hasNotChanged="未改变"
|
||||
AdvSceneSwitcher.condition.video.condition.noImage="没有输出"
|
||||
AdvSceneSwitcher.condition.video.condition.pattern="匹配模式"
|
||||
AdvSceneSwitcher.condition.video.condition.object="包含对象"
|
||||
AdvSceneSwitcher.condition.video.condition.brightness="亮度"
|
||||
AdvSceneSwitcher.condition.video.condition.ocr="包含文本"
|
||||
AdvSceneSwitcher.condition.video.askFileAction="是否要使用现有文件或创建当前选定源的屏幕截图?"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.file="使用现有文件"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="创建屏幕截图"
|
||||
AdvSceneSwitcher.condition.video.reduceLatency="减少匹配延迟"
|
||||
AdvSceneSwitcher.condition.video.reduceLatency.tooltip="启用将减少匹配延迟,但总体上会减慢所有宏的总体条件检查."
|
||||
AdvSceneSwitcher.condition.video.usePatternForChangedCheck="使用模式匹配"
|
||||
AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="这将允许您控制图像必须更改多少才能使条件为真."
|
||||
AdvSceneSwitcher.condition.video.patternThreshold="阈值: "
|
||||
AdvSceneSwitcher.condition.video.patternThresholdDescription="较高的阈值意味着模式需要更紧密地匹配视频源."
|
||||
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="使用alpha通道作为图案的遮罩."
|
||||
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="使用alpha通道作为模式的遮罩."
|
||||
AdvSceneSwitcher.condition.video.patternMatchMode="使用模式匹配方式 {{patternMatchingModes}}"
|
||||
AdvSceneSwitcher.condition.video.patternMatchMode.crossCorrelation="交叉相关"
|
||||
AdvSceneSwitcher.condition.video.patternMatchMode.correlationCoefficient="相关系数"
|
||||
AdvSceneSwitcher.condition.video.patternMatchMode.squaredDifference="平方差"
|
||||
AdvSceneSwitcher.condition.video.patternMatchMode.tip="使用的最佳方法取决于具体问题和所涉及图像的特性。\n在实践中,通常需要使用不同的方法和参数进行实验,以找到对特定用例的最佳匹配。"
|
||||
AdvSceneSwitcher.condition.video.brightnessThreshold="平均亮度:"
|
||||
AdvSceneSwitcher.condition.video.brightnessThresholdDescription="高的值表示明亮的图像,低的值表示较暗的图像。"
|
||||
AdvSceneSwitcher.condition.video.currentBrightness="当前平均亮度: %1"
|
||||
AdvSceneSwitcher.condition.video.objectScaleThreshold="模型比例: "
|
||||
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="较低的模比例将导致更多的匹配,但会导致更高的CPU负载."
|
||||
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="较低的模型比例会产生更多的匹配,但将导致CPU负载更高."
|
||||
AdvSceneSwitcher.condition.video.minNeighborDescription="较高的“最小区域”值将导致较少但质量较高的匹配."
|
||||
AdvSceneSwitcher.condition.video.showMatch="显示模式"
|
||||
AdvSceneSwitcher.condition.video.showMatch.loading="Checking for match"
|
||||
AdvSceneSwitcher.condition.video.showMatch.loading="检查是否匹配"
|
||||
AdvSceneSwitcher.condition.video.screenshotFail="无法获取源的屏幕截图!"
|
||||
AdvSceneSwitcher.condition.video.screenshotEmpty="Screenshot is empty - Is the source visible?"
|
||||
AdvSceneSwitcher.condition.video.screenshotEmpty="屏幕截图为空 - 源是否可见?"
|
||||
AdvSceneSwitcher.condition.video.patternMatchFail="没有找到模式!"
|
||||
AdvSceneSwitcher.condition.video.patternMatchSuccess="Pattern is highlighted in red"
|
||||
AdvSceneSwitcher.condition.video.patternMatchSuccess="模式以红色突出显示"
|
||||
AdvSceneSwitcher.condition.video.objectMatchFail="找不到对象!"
|
||||
AdvSceneSwitcher.condition.video.objectMatchSuccess="Object is highlighted in red"
|
||||
AdvSceneSwitcher.condition.video.objectMatchSuccess="对象以红色突出显示"
|
||||
AdvSceneSwitcher.condition.video.ocrMatchSuccess="检测到的文本:\n\n%1"
|
||||
AdvSceneSwitcher.condition.video.modelLoadFail="无法加载模型数据!"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
|
||||
AdvSceneSwitcher.condition.video.selectColor="选择颜色"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleColumn="单列可变大小的文本"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleBlockVertText="垂直对齐的单个统一文本块"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleBlock="单个统一的文本块"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleLine="单个文本行"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleWord="单词"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.circleWord="圆圈中的单个单词"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.singleChar="单个字符"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.sparseText="不按特定顺序查找文本"
|
||||
AdvSceneSwitcher.condition.video.ocrMode.sparseTextOSD="不按特定顺序查找文本(OSD)"
|
||||
AdvSceneSwitcher.condition.video.type.main="OBS的主输出"
|
||||
AdvSceneSwitcher.condition.video.type.source="源"
|
||||
AdvSceneSwitcher.condition.video.type.scene="场景"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.modelPath="模型数据 (haar级联分类器): {{modelDataPath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.minNeighbor="最小区域: {{minNeighbors}}"
|
||||
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}通过只执行每一次检查来减少CPU负载 {{throttleCount}} 毫秒"
|
||||
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}每隔一段时间检查一次,来减少CPU负载 {{throttleCount}} 毫秒"
|
||||
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="仅在区域内执行检查"
|
||||
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
|
||||
AdvSceneSwitcher.condition.video.entry.orcColorPick="检查文本颜色:{{textColor}}{{selectColor}}"
|
||||
AdvSceneSwitcher.condition.video.entry.orcTextType="检查文本类型:{{textType}}"
|
||||
AdvSceneSwitcher.condition.video.minSize="最小尺寸:"
|
||||
AdvSceneSwitcher.condition.video.maxSize="最大尺寸:"
|
||||
AdvSceneSwitcher.condition.video.selectArea="选择区域"
|
||||
AdvSceneSwitcher.condition.video.selectArea.status="仅检查突出显示的区域"
|
||||
AdvSceneSwitcher.condition.video.width="宽度"
|
||||
AdvSceneSwitcher.condition.video.height="长度"
|
||||
AdvSceneSwitcher.condition.stream="推流"
|
||||
AdvSceneSwitcher.condition.stream.state.start="推流启动"
|
||||
AdvSceneSwitcher.condition.stream.state.stop="推流停止"
|
||||
AdvSceneSwitcher.condition.stream.state.starting="推流启动中"
|
||||
AdvSceneSwitcher.condition.stream.state.stopping="推流停止中"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}"
|
||||
AdvSceneSwitcher.condition.stream.state.keyFrameInterval="关键帧间隔等于"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
|
||||
AdvSceneSwitcher.condition.record="录制"
|
||||
AdvSceneSwitcher.condition.record.state.start="录制运行中"
|
||||
AdvSceneSwitcher.condition.record.state.pause="录制暂停"
|
||||
AdvSceneSwitcher.condition.record.state.stop="录制停止"
|
||||
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
|
||||
AdvSceneSwitcher.condition.process="进程"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}} 正在运行 {{focused}} 而且是获得焦点"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}} 是正在运行 {{focused}} 并且是获得焦点"
|
||||
AdvSceneSwitcher.condition.process.entry.focus="当前焦点进程: {{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.idle="闲置检测"
|
||||
AdvSceneSwitcher.condition.idle.entry="没有键盘或鼠标输入{{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="插件状态"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="在此时间间隔内触发了自动场景更改"
|
||||
AdvSceneSwitcher.condition.pluginState.state.running="高级场景切换程序正在运行"
|
||||
AdvSceneSwitcher.condition.pluginState.state.start="插件已启动"
|
||||
AdvSceneSwitcher.condition.pluginState.state.restart="插件已重新启动"
|
||||
AdvSceneSwitcher.condition.pluginState.state.running="插件正在运行"
|
||||
AdvSceneSwitcher.condition.pluginState.state.shutdown="OBS正在关闭"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneCollection="场景集合已加载"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="场景被上一个宏改变"
|
||||
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
|
||||
AdvSceneSwitcher.condition.timer="计时器"
|
||||
AdvSceneSwitcher.condition.timer.type.fixed="固定"
|
||||
AdvSceneSwitcher.condition.timer.type.random="随机"
|
||||
AdvSceneSwitcher.condition.timer.pause="暂停"
|
||||
AdvSceneSwitcher.condition.timer.continue="继续"
|
||||
AdvSceneSwitcher.condition.timer.entry.line1.fixed="{{type}} 持续时间 {{duration}} 后结束"
|
||||
AdvSceneSwitcher.condition.timer.entry.line1.random="{{type}} 持续时间从 {{duration}} 到 {{duration2}} 后结束"
|
||||
AdvSceneSwitcher.condition.timer.entry.line2="剩余时间: {{remaining}} 秒"
|
||||
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} 保存剩余时间 {{autoReset}} 达到时间后自动重置计时器"
|
||||
AdvSceneSwitcher.condition.timer.reset="重置"
|
||||
AdvSceneSwitcher.condition.macro="宏"
|
||||
AdvSceneSwitcher.condition.macro.type.count="计数"
|
||||
AdvSceneSwitcher.condition.macro.type.state="状态"
|
||||
AdvSceneSwitcher.condition.macro.type.multiState="多线宏状态"
|
||||
AdvSceneSwitcher.condition.macro.type.selection="条件类型: {{types}}"
|
||||
AdvSceneSwitcher.condition.macro.count.type.below="少于"
|
||||
AdvSceneSwitcher.condition.macro.count.type.above="大于"
|
||||
AdvSceneSwitcher.condition.macro.count.type.equal="等于"
|
||||
AdvSceneSwitcher.condition.macro.state.type.below="少于"
|
||||
AdvSceneSwitcher.condition.macro.state.type.above="大于"
|
||||
AdvSceneSwitcher.condition.macro.state.type.equal="等于"
|
||||
AdvSceneSwitcher.condition.macro.count.reset="重置"
|
||||
AdvSceneSwitcher.condition.macro.pausedWarning="所选宏当前已暂停!"
|
||||
AdvSceneSwitcher.condition.macro.state.entry="条件 {{macros}} 是真的"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} 运行 {{conditions}} {{count}} 次(相对于下面计数匹配)"
|
||||
AdvSceneSwitcher.condition.macro.state.entry="{{macros}} 的条件为真"
|
||||
AdvSceneSwitcher.condition.macro.multistate.entry=" 以下宏的 {{multiStateConditions}}{{multiStateCount}} 的条件为真:"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} 执行 {{conditions}} {{count}} 次(相对于下面计数匹配)"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line2="当前计数: {{currentCount}} {{resetCount}}"
|
||||
AdvSceneSwitcher.condition.source="源"
|
||||
AdvSceneSwitcher.condition.source.type.active="是激活状态"
|
||||
AdvSceneSwitcher.condition.source.type.showing="是显示状态"
|
||||
AdvSceneSwitcher.condition.source.type.settings="设置完全匹配"
|
||||
AdvSceneSwitcher.condition.source.regex="使用正则表达式"
|
||||
AdvSceneSwitcher.condition.source.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
|
||||
@@ -211,9 +324,8 @@ AdvSceneSwitcher.condition.filter="滤镜"
|
||||
AdvSceneSwitcher.condition.filter.type.active="已启用"
|
||||
AdvSceneSwitcher.condition.filter.type.showing="已关闭"
|
||||
AdvSceneSwitcher.condition.filter.type.settings="设置完全匹配"
|
||||
AdvSceneSwitcher.condition.filter.regex="使用正则表达式"
|
||||
AdvSceneSwitcher.condition.filter.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.condition.filter.entry.line1="在 {{sources}} {{filters}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line1="在 {{sources}} 的 {{filters}} 是 {{conditions}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.sceneOrder="场景项目顺序"
|
||||
@@ -245,16 +357,20 @@ AdvSceneSwitcher.condition.date.state.at="现在"
|
||||
AdvSceneSwitcher.condition.date.state.after="之后"
|
||||
AdvSceneSwitcher.condition.date.state.before="之前"
|
||||
AdvSceneSwitcher.condition.date.state.between="之间"
|
||||
AdvSceneSwitcher.condition.date.separator="和"
|
||||
AdvSceneSwitcher.condition.date.state.pattern="模式"
|
||||
AdvSceneSwitcher.condition.date.separator="到"
|
||||
AdvSceneSwitcher.condition.date.ignoreDate="如果未选中,日期组件将被忽略"
|
||||
AdvSceneSwitcher.condition.date.ignoreTime="如果未选中,时间组件将被忽略"
|
||||
AdvSceneSwitcher.condition.date.showAdvancedSettings="显示高级设置"
|
||||
AdvSceneSwitcher.condition.date.showSimpleSettings="显示简单设置"
|
||||
AdvSceneSwitcher.condition.date.entry.simple="在 {{dayOfWeek}} {{weekCondition}} {{ignoreWeekTime}}{{weekTime}}"
|
||||
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
|
||||
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} 在匹配到日期时间后,每隔 {{duration}} 重复一次"
|
||||
AdvSceneSwitcher.condition.date.entry.pattern="当前日期 \"{{currentDate}}\" 匹配模式 {{pattern}}"
|
||||
AdvSceneSwitcher.condition.date.entry.nextMatchDate="下一次匹配 在: %1"
|
||||
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} 重复时,将选定的日期更新为重复日期"
|
||||
AdvSceneSwitcher.condition.sceneTransform="场景项目被改变"
|
||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="内容或者设置被改变了"
|
||||
AdvSceneSwitcher.condition.sceneTransform.regex="使用正则表达式"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="在 {{scenes}} {{sources}} 匹配变换"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||
@@ -270,6 +386,7 @@ AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{scen
|
||||
AdvSceneSwitcher.condition.sceneVisibility="场景项目可见"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.shown="显示的"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="隐藏的"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.changed="可见性已更改"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.entry="在 {{scenes}} {{sources}} 是 {{conditions}} "
|
||||
AdvSceneSwitcher.condition.studioMode="工作室模式"
|
||||
AdvSceneSwitcher.condition.studioMode.state.active="工作室模式处于活动状态"
|
||||
@@ -277,7 +394,7 @@ AdvSceneSwitcher.condition.studioMode.state.notActive="工作室模式未处于
|
||||
AdvSceneSwitcher.condition.studioMode.state.previewScene="预览场景是"
|
||||
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
|
||||
AdvSceneSwitcher.condition.openvr="OpenVR"
|
||||
AdvSceneSwitcher.condition.errorStatus="OpenVR 错误: "
|
||||
AdvSceneSwitcher.condition.openvr.errorStatus="OpenVR 错误: "
|
||||
AdvSceneSwitcher.condition.openvr.entry.line1="HMD在 ..."
|
||||
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
|
||||
AdvSceneSwitcher.condition.openvr.entry.line3="HMD目前处于 {{xPos}} x {{yPos}} x {{zPos}}"
|
||||
@@ -299,11 +416,41 @@ AdvSceneSwitcher.condition.stats.condition.above="大于"
|
||||
AdvSceneSwitcher.condition.stats.condition.equals="等于"
|
||||
AdvSceneSwitcher.condition.stats.condition.below="少于"
|
||||
AdvSceneSwitcher.condition.stats.dockHint="你可以打开“统计”窗口查看当前状态"
|
||||
AdvSceneSwitcher.condition.stats.entry="{{stats}} is {{condition}} {{value}}"
|
||||
AdvSceneSwitcher.condition.stats.entry="{{stats}} 是 {{condition}} {{value}}"
|
||||
AdvSceneSwitcher.condition.profile="配置文件"
|
||||
AdvSceneSwitcher.condition.profile.entry="当前活动的配置文件为 {{profiles}}"
|
||||
AdvSceneSwitcher.condition.websocket="websocket"
|
||||
AdvSceneSwitcher.condition.websocket.type.request="request"
|
||||
AdvSceneSwitcher.condition.websocket.type.event="event"
|
||||
AdvSceneSwitcher.condition.websocket.useRegex="使用正则表达式"
|
||||
AdvSceneSwitcher.condition.websocket.entry.request="{{type}} 收到内容:"
|
||||
AdvSceneSwitcher.condition.websocket.entry.event="{{type}} 收到自 {{connection}}:"
|
||||
AdvSceneSwitcher.condition.variable="变量"
|
||||
AdvSceneSwitcher.condition.variable.type.compare="等于"
|
||||
AdvSceneSwitcher.condition.variable.type.empty="为空"
|
||||
AdvSceneSwitcher.condition.variable.type.number="是一个数字"
|
||||
AdvSceneSwitcher.condition.variable.type.lessThan="小于"
|
||||
AdvSceneSwitcher.condition.variable.type.greaterThan="大于"
|
||||
AdvSceneSwitcher.condition.variable.type.valueChanged="值已更改"
|
||||
AdvSceneSwitcher.condition.variable.type.equalsVariable="等于变量"
|
||||
AdvSceneSwitcher.condition.variable.type.lessThanVariable="小于变量"
|
||||
AdvSceneSwitcher.condition.variable.type.greaterThanVariable="大于变量"
|
||||
AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}{{variables2}}"
|
||||
AdvSceneSwitcher.condition.run="运行"
|
||||
AdvSceneSwitcher.condition.run.entry="进程在超时 {{timeout}} 秒之前退出"
|
||||
AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}检查退出代码{{exitCode}}"
|
||||
AdvSceneSwitcher.condition.midi="MIDI"
|
||||
AdvSceneSwitcher.condition.midi.entry="从{{device}} 收到的信息,符合:(从不咕咕的阿坤:这个好像是声卡工作是录音相关的功能,英文太抽象了,我就硬译过来了,如有相关设备,音乐好的,可以根据英文翻译好发给我!)"
|
||||
AdvSceneSwitcher.condition.midi.entry.listen="将MIDI信息选择设置为从选定设备上接收的信息: {{listenButton}}"
|
||||
AdvSceneSwitcher.condition.display="显示器"
|
||||
AdvSceneSwitcher.condition.display.type.displayName="连接的显示器的名称匹配"
|
||||
AdvSceneSwitcher.condition.display.type.displayCount="连接的显示器的数量为"
|
||||
AdvSceneSwitcher.condition.display.entry="{{conditions}}{{displays}}{{regex}}{{displayCount}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.switchScene="切换场景"
|
||||
AdvSceneSwitcher.action.scene.entry="切换场景 {{scenes}} 使用 {{transitions}} 时长 {{duration}} 秒"
|
||||
AdvSceneSwitcher.action.scene.entry.noDuration="切换到场景{{scenes}}使用{{transitions}}"
|
||||
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="等待目标场景的过渡完成"
|
||||
AdvSceneSwitcher.action.wait="等待"
|
||||
AdvSceneSwitcher.action.wait.type.fixed="固定数值"
|
||||
@@ -315,44 +462,62 @@ AdvSceneSwitcher.action.audio.type.mute="静音"
|
||||
AdvSceneSwitcher.action.audio.type.unmute="取消静音"
|
||||
AdvSceneSwitcher.action.audio.type.sourceVolume="来源音量"
|
||||
AdvSceneSwitcher.action.audio.type.masterVolume="主音量"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
|
||||
AdvSceneSwitcher.action.audio.type.syncOffset="设置同步偏移"
|
||||
AdvSceneSwitcher.action.audio.type.monitor="设置音频监听"
|
||||
AdvSceneSwitcher.action.audio.type.balance="设置平衡"
|
||||
AdvSceneSwitcher.action.audio.balance.description="选择的音频源将滑块方向移动(从不咕咕的阿坤的理解:左/右声道偏侧向)"
|
||||
AdvSceneSwitcher.action.audio.fade.type.duration="持续时间"
|
||||
AdvSceneSwitcher.action.audio.fade.type.rate="百分比"
|
||||
AdvSceneSwitcher.action.audio.fade.duration="{{fade}}淡出 {{fadeTypes}} {{duration}} 秒."
|
||||
AdvSceneSwitcher.action.audio.fade.rate="{{fade}}淡出 {{fadeTypes}} {{rate}}每秒."
|
||||
AdvSceneSwitcher.action.audio.fade.wait="等待淡入淡出完成."
|
||||
AdvSceneSwitcher.action.audio.fade.abort="中止已处于活动状态的淡入淡出."
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}{{monitorTypes}}"
|
||||
AdvSceneSwitcher.action.recording="录制"
|
||||
AdvSceneSwitcher.action.recording.type.stop="停止录制"
|
||||
AdvSceneSwitcher.action.recording.type.start="开始录制"
|
||||
AdvSceneSwitcher.action.recording.type.pause="暂停录制"
|
||||
AdvSceneSwitcher.action.recording.type.unpause="取消录制暂停"
|
||||
AdvSceneSwitcher.action.recording.type.split="分割录制文件"
|
||||
AdvSceneSwitcher.action.recording.type.changeOutputFolder="更改输出文件夹"
|
||||
AdvSceneSwitcher.action.recording.type.changeOutputFileFormat="改变文件名的格式"
|
||||
AdvSceneSwitcher.action.recording.pause.hint="请注意,根据您的录制设置,您可能无法暂停录制"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}{{splitHint}}"
|
||||
AdvSceneSwitcher.action.replay="回放缓冲区"
|
||||
AdvSceneSwitcher.action.replay.type.stop="停止回放缓冲区"
|
||||
AdvSceneSwitcher.action.replay.type.start="启动回放缓冲区"
|
||||
AdvSceneSwitcher.action.replay.type.save="保存回放缓冲区"
|
||||
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.type.stop="停止回放缓存"
|
||||
AdvSceneSwitcher.action.replay.type.start="启动回放缓存"
|
||||
AdvSceneSwitcher.action.replay.type.save="保存回放缓存"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="推流"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="停止推流"
|
||||
AdvSceneSwitcher.action.streaming.type.start="开始推流"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming.type.keyFrameInterval="设置关键帧间隔"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="运行"
|
||||
AdvSceneSwitcher.action.run.arguments="参数:"
|
||||
AdvSceneSwitcher.action.run.addArgument="添加参数"
|
||||
AdvSceneSwitcher.action.run.addArgumentDescription="添加新参数:"
|
||||
AdvSceneSwitcher.action.run.entry="运行 {{filePath}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility="场景项目可见性"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="显示"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="隐藏"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.toggle="切换"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="来源"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="全部"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="置{{scenes}}{{actions}}{{sources}}"
|
||||
AdvSceneSwitcher.action.filter="滤镜"
|
||||
AdvSceneSwitcher.action.filter.type.enable="开启"
|
||||
AdvSceneSwitcher.action.filter.type.disable="关闭"
|
||||
AdvSceneSwitcher.action.filter.type.settings="参数设置"
|
||||
AdvSceneSwitcher.action.filter.entry="在 {{sources}} {{actions}} {{filters}}"
|
||||
AdvSceneSwitcher.action.filter.entry="置 {{sources}} {{actions}} {{filters}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.action.source="来源"
|
||||
AdvSceneSwitcher.action.source="源"
|
||||
AdvSceneSwitcher.action.source.type.enable="启用"
|
||||
AdvSceneSwitcher.action.source.type.disable="停用"
|
||||
AdvSceneSwitcher.action.source.type.settings="配置设置"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings="刷新源设置"
|
||||
AdvSceneSwitcher.action.source.type.pressSettingsButton="按下设置按钮"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="可用于刷新浏览器、媒体、源、etc"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="未找到按钮!"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}"
|
||||
AdvSceneSwitcher.action.source.warning="警告:OBS UI无法控制全局启用和禁用源,注意,通过这个设置禁用,我测试,你必须通过这个在启用才行,不然点击小眼睛你无法显示!"
|
||||
AdvSceneSwitcher.action.source.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.action.media="媒体"
|
||||
@@ -362,19 +527,23 @@ AdvSceneSwitcher.action.media.type.stop="停止"
|
||||
AdvSceneSwitcher.action.media.type.restart="重新启动"
|
||||
AdvSceneSwitcher.action.media.type.next="下一个"
|
||||
AdvSceneSwitcher.action.media.type.previous="上一个"
|
||||
AdvSceneSwitcher.action.media.type.seek="时间轴定位"
|
||||
AdvSceneSwitcher.action.media.entry="{{actions}}{{duration}}{{mediaSources}}"
|
||||
AdvSceneSwitcher.action.macro="宏"
|
||||
AdvSceneSwitcher.action.macro.type.pause="暂停"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="取消暂停"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="复位计数器"
|
||||
AdvSceneSwitcher.action.macro.type.run="运行"
|
||||
AdvSceneSwitcher.action.macro.type.stop="停止"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
|
||||
AdvSceneSwitcher.action.macro.type.run="运行操作"
|
||||
AdvSceneSwitcher.action.macro.type.stop="停止操作"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="插件状态"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项:"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项时:"
|
||||
AdvSceneSwitcher.action.pluginState.type.import="从设置导入"
|
||||
AdvSceneSwitcher.action.pluginState.importWarning="注意:当“设置”窗口打开时,操作将被忽略。"
|
||||
AdvSceneSwitcher.action.pluginState.type.terminate="关闭 OBS"
|
||||
AdvSceneSwitcher.action.pluginState.terminateConfirm="OBS将在10秒后自动关闭!/n你想继续关闭OBS吗?"
|
||||
AdvSceneSwitcher.action.pluginState.entry="{{actions}}{{values}}{{scenes}}{{settings}}{{settingsWarning}}"
|
||||
AdvSceneSwitcher.action.virtualCamera="虚拟摄像机"
|
||||
AdvSceneSwitcher.action.virtualCamera.type.stop="停止虚拟摄像机"
|
||||
AdvSceneSwitcher.action.virtualCamera.type.start="启动虚拟摄像机"
|
||||
@@ -390,7 +559,7 @@ AdvSceneSwitcher.action.hotkey.leftMeta="左 Meta"
|
||||
AdvSceneSwitcher.action.hotkey.rightMeta="右 Meta"
|
||||
AdvSceneSwitcher.action.hotkey.onlyOBS="仅向OBS发送按键"
|
||||
AdvSceneSwitcher.action.hotkey.disabled="无法模拟按键-功能已禁用!"
|
||||
AdvSceneSwitcher.action.hotkey.entry="按下 {{keys}} for {{duration}} 毫秒"
|
||||
AdvSceneSwitcher.action.hotkey.entry="按下 {{keys}} 在 {{duration}} 秒"
|
||||
AdvSceneSwitcher.action.sceneOrder="场景项目顺序"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveUp="上移"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveDown="下移"
|
||||
@@ -398,20 +567,27 @@ 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.sceneTransform="场景项目变换"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="得到变换"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="在 {{scenes}} 变换 {{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="场景项目转换"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="获得转换"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="在 {{scenes}}{{action}}{{rotation}}{{sources}}"
|
||||
AdvSceneSwitcher.action.file="文件"
|
||||
AdvSceneSwitcher.action.file.type.write="写入"
|
||||
AdvSceneSwitcher.action.file.type.append="追加写入"
|
||||
AdvSceneSwitcher.action.file.entry="{{actions}} 到 {{filePath}}:"
|
||||
AdvSceneSwitcher.action.previewScene="切换预览场景"
|
||||
AdvSceneSwitcher.action.previewScene.entry="将预览场景切换到 {{scenes}}"
|
||||
AdvSceneSwitcher.action.SceneSwap="交换场景 (Studio mode)"
|
||||
AdvSceneSwitcher.action.SceneSwap.entry="在studio模式下交换预览和编程场景"
|
||||
AdvSceneSwitcher.action.transition="过场特效"
|
||||
AdvSceneSwitcher.action.studioMode="工作室模式"
|
||||
AdvSceneSwitcher.action.studioMode.type.swap="切换预览和编程场景"
|
||||
AdvSceneSwitcher.action.studioMode.type.setScene="将预览场景设置为"
|
||||
AdvSceneSwitcher.action.studioMode.type.enable="启用工作室模式"
|
||||
AdvSceneSwitcher.action.studioMode.type.disable="停用工作室模式"
|
||||
AdvSceneSwitcher.action.studioMode.entry="{{actions}}{{scenes}}"
|
||||
AdvSceneSwitcher.action.transition="转场特效"
|
||||
AdvSceneSwitcher.action.transition.type.scene="场景过渡"
|
||||
AdvSceneSwitcher.action.transition.type.sceneOverride="场景过渡覆盖"
|
||||
AdvSceneSwitcher.action.transition.type.sourceShow="源显示过渡"
|
||||
AdvSceneSwitcher.action.transition.type.sourceHide="源隐藏过渡"
|
||||
AdvSceneSwitcher.action.transition.entry.line1="修改 {{type}}{{scenes}}{{sources}}"
|
||||
AdvSceneSwitcher.action.transition.entry.line2="{{setTransition}}将过场特效类型设置为 {{transitions}}"
|
||||
AdvSceneSwitcher.action.transition.entry.line3="{{setDuration}}将过场特效持续时间设置为 {{duration}}seconds"
|
||||
AdvSceneSwitcher.action.transition.entry.line3="{{setDuration}}将过场特效持续时间设置为 {{duration}}秒"
|
||||
AdvSceneSwitcher.action.timer="计时器"
|
||||
AdvSceneSwitcher.action.timer.type.pause="暂停"
|
||||
AdvSceneSwitcher.action.timer.type.continue="继续"
|
||||
@@ -419,10 +595,21 @@ AdvSceneSwitcher.action.timer.type.reset="重置"
|
||||
AdvSceneSwitcher.action.timer.type.setTimeRemaining="设置剩余时间"
|
||||
AdvSceneSwitcher.action.timer.entry="{{timerAction}} 定时在 {{macros}} {{duration}}"
|
||||
AdvSceneSwitcher.action.random="随机"
|
||||
AdvSceneSwitcher.action.random.allowRepeat="允许连续执行同一宏"
|
||||
AdvSceneSwitcher.action.random.entry="随机运行以下任何宏(忽略暂停的宏)"
|
||||
AdvSceneSwitcher.action.systray="系统托盘通知"
|
||||
AdvSceneSwitcher.action.systray.entry="显示通知: {{message}}"
|
||||
AdvSceneSwitcher.action.systray.title="标题:"
|
||||
AdvSceneSwitcher.action.systray.message="信息:"
|
||||
AdvSceneSwitcher.action.systray.icon="图标:"
|
||||
AdvSceneSwitcher.action.systray.iconHint="留空以使用默认图标"
|
||||
AdvSceneSwitcher.action.screenshot="截图"
|
||||
AdvSceneSwitcher.action.screenshot.save.default="默认"
|
||||
AdvSceneSwitcher.action.screenshot.save.custom="其他"
|
||||
AdvSceneSwitcher.action.screenshot.type.source="来源"
|
||||
AdvSceneSwitcher.action.screenshot.type.scene="场景"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS 预览画面"
|
||||
AdvSceneSwitcher.action.screenshot.blackscreenNote="源和场景并不总是呈现,可能会导致屏幕截图的某些部分保持空白。"
|
||||
AdvSceneSwitcher.action.screenshot.entry="截图{{targetType}}{{sources}}{{scenes}}保存在{{saveType}}位置"
|
||||
AdvSceneSwitcher.action.profile="配置文件"
|
||||
AdvSceneSwitcher.action.profile.entry="将活动配置文件切换到 {{profiles}}"
|
||||
AdvSceneSwitcher.action.sceneCollection="场景集合"
|
||||
@@ -434,15 +621,66 @@ AdvSceneSwitcher.action.sequence.status="上次执行的宏:%1 - 要执行的
|
||||
AdvSceneSwitcher.action.sequence.status.none="无"
|
||||
AdvSceneSwitcher.action.sequence.restart="到达列表末尾后,从开头重新启动"
|
||||
AdvSceneSwitcher.action.sequence.continueFrom="继续所选项目"
|
||||
AdvSceneSwitcher.action.websocket="websocket"
|
||||
AdvSceneSwitcher.action.websocket.type.request="request"
|
||||
AdvSceneSwitcher.action.websocket.type.event="event"
|
||||
AdvSceneSwitcher.action.websocket.entry.request="通过{{connection}} 发送场景切换器 {{type}}"
|
||||
AdvSceneSwitcher.action.websocket.entry.event="发送场景切换器 {{type}} 到连接的客户端"
|
||||
AdvSceneSwitcher.action.http="Http"
|
||||
AdvSceneSwitcher.action.http.setHeaders="设置头信息(SET headers)"
|
||||
AdvSceneSwitcher.action.http.headers="头信息(headers):"
|
||||
AdvSceneSwitcher.action.http.addHeader="增加头信息(ADD headers)"
|
||||
AdvSceneSwitcher.action.http.type.get="获取(GET)"
|
||||
AdvSceneSwitcher.action.http.type.post="发送(POST)"
|
||||
AdvSceneSwitcher.action.http.entry.line1="发送 {{method}} 到 {{url}}"
|
||||
AdvSceneSwitcher.action.http.entry.line2="超时: {{timeout}} 秒"
|
||||
AdvSceneSwitcher.action.variable="变量"
|
||||
AdvSceneSwitcher.action.variable.type.set="设置为固定值"
|
||||
AdvSceneSwitcher.action.variable.type.append="追加"
|
||||
AdvSceneSwitcher.action.variable.type.appendVar="追加变量"
|
||||
AdvSceneSwitcher.action.variable.type.increment="增加"
|
||||
AdvSceneSwitcher.action.variable.type.decrement="减少"
|
||||
AdvSceneSwitcher.action.variable.type.setActionValue="设置为操作值"
|
||||
AdvSceneSwitcher.action.variable.type.setConditionValue="设置为条件值"
|
||||
AdvSceneSwitcher.action.variable.type.roundToInt="取整到最近的整数"
|
||||
AdvSceneSwitcher.action.variable.type.subString="设置为当前值的子字符串"
|
||||
AdvSceneSwitcher.action.variable.type.findAndReplace="在当前值中查找并替换"
|
||||
AdvSceneSwitcher.action.variable.type.mathExpression="数学表达式"
|
||||
AdvSceneSwitcher.action.variable.mathExpression.example="( 1 + 2 * 3 ) / 4"
|
||||
AdvSceneSwitcher.action.variable.findAndReplace.find="要查找的文本"
|
||||
AdvSceneSwitcher.action.variable.findAndReplace.replace="要替换为的文本"
|
||||
AdvSceneSwitcher.action.variable.subString.begin="开始"
|
||||
AdvSceneSwitcher.action.variable.subString.all="全部"
|
||||
AdvSceneSwitcher.action.variable.invalidSelection="无效选择!"
|
||||
AdvSceneSwitcher.action.variable.actionNoVariableSupport="不支持从 %1 个操作获取变量值!"
|
||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="不支持从 %1 条件中获取变量值!"
|
||||
AdvSceneSwitcher.action.variable.currentSegmentValue="当前值:"
|
||||
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}"
|
||||
AdvSceneSwitcher.action.variable.entry.substringIndex="子字符串开始:{{subStringStart}} 子字符串大小:{{subStringSize}}"
|
||||
AdvSceneSwitcher.action.variable.entry.substringRegex="使用正则表达式为 {{regexMatchIdx}} 匹配的值:"
|
||||
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{replaceStr}}"
|
||||
AdvSceneSwitcher.action.projector="投影仪"
|
||||
AdvSceneSwitcher.action.projector.type.source="源"
|
||||
AdvSceneSwitcher.action.projector.type.scene="场景"
|
||||
AdvSceneSwitcher.action.projector.type.preview="预览"
|
||||
AdvSceneSwitcher.action.projector.type.program="程序"
|
||||
AdvSceneSwitcher.action.projector.type.multiview="多视图"
|
||||
AdvSceneSwitcher.action.projector.display="显示"
|
||||
AdvSceneSwitcher.action.projector.windowed="窗口"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="全屏"
|
||||
AdvSceneSwitcher.action.projector.entry="打开为{{windowTypes}} 的 {{types}}{{scenes}}{{sources}}投影"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="在 {{monitors}} 上 (从不咕咕的阿坤说:谨慎开启!)"
|
||||
AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="发送消息到 {{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="将MIDI消息选择设置为 {{listenDevices}} 上传入的消息: {{listenButton}}"
|
||||
AdvSceneSwitcher.action.osc="打开声音控制"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="转场特效"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="更改转场特效时:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="更改激活转场特效类型"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>这里的设定<span style=\"font-style:bold;\">只影响由场景切换器引起的转场有效</span>,不影响你手动的引起的转场。<br/>在这里设定的转场特效优先级高于场景切换器其他地方设置的<br/><br/>单击加号添加项目.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="当切换到这个场景时修改默认转场特效"
|
||||
AdvSceneSwitcher.transitionTab.entry="切换自 {{scenes}} 到 {{scenes2}} 使用 {{transitions}} 持续时间为 {{duration}}"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="当场景 {{scenes}} 被激活时更改默认转场特效为 {{transitions}}"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="单击加号添加项目."
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition.delay="场景更改 {{defTransitionDelay}} 后切换转换特效."
|
||||
@@ -494,10 +732,11 @@ AdvSceneSwitcher.mediaTab.states.none="无"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="播放中"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="打开中"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="缓冲中"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="暂停"
|
||||
AdvSceneSwitcher.mediaTab.states.paused="暂停"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="停止"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="结束"
|
||||
AdvSceneSwitcher.mediaTab.states.error="错误"
|
||||
AdvSceneSwitcher.mediaTab.states.playlistEnd="已结束(播放列表)"
|
||||
AdvSceneSwitcher.mediaTab.states.any="任意"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.none="无"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="播放时间小于"
|
||||
@@ -582,6 +821,7 @@ AdvSceneSwitcher.audioTab.title="音频"
|
||||
AdvSceneSwitcher.audioTab.condition.above="高于"
|
||||
AdvSceneSwitcher.audioTab.condition.below="低于"
|
||||
AdvSceneSwitcher.audioTab.ignoreInactiveSource="除非源处于非活动状态"
|
||||
AdvSceneSwitcher.audioTab.entry="当音量 {{audioSources}} 在 {{condition}} {{volumeWidget}} 时 {{duration}} 秒后 切换至 {{scenes}} 使用 {{transitions}} {{ignoreInactiveSource}}"
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="如果多个条目匹配 ..."
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... {{duration}} 秒切换到 {{scenes}} 使用 {{transitions}}"
|
||||
AdvSceneSwitcher.audioTab.help="此选项卡允许您根据源音量切换场景。\n例如,如果麦克风音量达到某个阈值,您可以自动切换到其他场景。\n\n单击高亮显示的加号继续."
|
||||
@@ -675,6 +915,8 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="切换(启动/停止)高
|
||||
AdvSceneSwitcher.hotkey.macro.pause="暂停宏 %1"
|
||||
AdvSceneSwitcher.hotkey.macro.unpause="取消暂停宏 %1"
|
||||
AdvSceneSwitcher.hotkey.macro.togglePause="切换暂停宏 %1"
|
||||
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="上移选择宏"
|
||||
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="下移选择宏"
|
||||
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="删除选定的宏"
|
||||
|
||||
AdvSceneSwitcher.askBackup="检测到新版本的高级场景切换程序。\n是否要创建旧设置的备份?"
|
||||
@@ -683,9 +925,114 @@ AdvSceneSwitcher.askForMacro="选择宏 {{macroSelection}}"
|
||||
AdvSceneSwitcher.close="关闭"
|
||||
AdvSceneSwitcher.browse="浏览文件"
|
||||
|
||||
AdvSceneSwitcher.item.select="--选择项目--"
|
||||
AdvSceneSwitcher.item.add="添加新项目"
|
||||
AdvSceneSwitcher.item.rename="重命名"
|
||||
AdvSceneSwitcher.item.remove="删除"
|
||||
AdvSceneSwitcher.item.properties="属性"
|
||||
AdvSceneSwitcher.item.newName="新名称:"
|
||||
AdvSceneSwitcher.item.emptyName="不允许使用空名称!"
|
||||
AdvSceneSwitcher.item.nameNotAvailable="名称不可用!"
|
||||
AdvSceneSwitcher.item.nameReserved="保留名称!"
|
||||
|
||||
AdvSceneSwitcher.variable.select="--选择变量--"
|
||||
AdvSceneSwitcher.variable.add="增加新变量"
|
||||
AdvSceneSwitcher.variable.name="名称:"
|
||||
AdvSceneSwitcher.variable.value="当前值:"
|
||||
AdvSceneSwitcher.variable.save="保存 / 载入 行为"
|
||||
AdvSceneSwitcher.variable.save.dontSave="不保存变量值"
|
||||
AdvSceneSwitcher.variable.save.save="保存变量值"
|
||||
AdvSceneSwitcher.variable.save.default="设置变量值"
|
||||
|
||||
AdvSceneSwitcher.connection.select="--选择连接--"
|
||||
AdvSceneSwitcher.connection.add="增加新连接"
|
||||
AdvSceneSwitcher.connection.name="名称:"
|
||||
AdvSceneSwitcher.connection.address="地址:"
|
||||
AdvSceneSwitcher.connection.port="端口:"
|
||||
AdvSceneSwitcher.connection.password="密码:"
|
||||
AdvSceneSwitcher.connection.reconnect="自动重新连接:"
|
||||
AdvSceneSwitcher.connection.reconnectDelay="自动重新连接延迟:"
|
||||
AdvSceneSwitcher.connection.connectOnStart="启动时自动连接:"
|
||||
AdvSceneSwitcher.connection.useOBSWebsocketProtocol="<html><head/><body><p>如何使用 obs-websocket <a href=\"https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md\"><span style=\" text-decoration: underline; color:#268bd2;\">protocol</span></a></p></body></html>"
|
||||
AdvSceneSwitcher.connection.test="测试连接"
|
||||
AdvSceneSwitcher.connection.status.disconnected="断开"
|
||||
AdvSceneSwitcher.connection.status.connecting="连接"
|
||||
AdvSceneSwitcher.connection.status.connected="已连接,但未经过身份验证"
|
||||
AdvSceneSwitcher.connection.status.authenticated="已连接,并经过身份验证"
|
||||
|
||||
AdvSceneSwitcher.regex.enable="启用正则表达式"
|
||||
AdvSceneSwitcher.regex.partialMatch="允许部分匹配"
|
||||
AdvSceneSwitcher.regex.caseInsensitive="匹配不区分大小写"
|
||||
AdvSceneSwitcher.regex.dotMatchNewline=". 匹配换行符"
|
||||
AdvSceneSwitcher.regex.multiLine="^ 和 $ 是匹配行的开始/结束"
|
||||
AdvSceneSwitcher.regex.extendedPattern="启用Qt的扩展模式语法"
|
||||
|
||||
AdvSceneSwitcher.process.showAdvanced="显示高级设置"
|
||||
AdvSceneSwitcher.process.arguments="参数:"
|
||||
AdvSceneSwitcher.process.addArgument="增加参数"
|
||||
AdvSceneSwitcher.process.addArgumentDescription="添加新参数:"
|
||||
AdvSceneSwitcher.process.entry="运行{{filePath}}{{advancedSettings}}"
|
||||
AdvSceneSwitcher.process.entry.workingDirectory="工作目录:{{workingDirectory}}"
|
||||
|
||||
AdvSceneSwitcher.math.expressionFail="计算表达式失败"
|
||||
|
||||
AdvSceneSwitcher.midi.deviceNamePattern="[端口 #%1] %2"
|
||||
AdvSceneSwitcher.midi.message.type="类型:"
|
||||
AdvSceneSwitcher.midi.message.type.optional="任意"
|
||||
AdvSceneSwitcher.midi.message.type.invalid="无效"
|
||||
AdvSceneSwitcher.midi.message.type.noteOff="关闭音符"
|
||||
AdvSceneSwitcher.midi.message.type.noteOn="打开音符"
|
||||
AdvSceneSwitcher.midi.message.type.polyphonicPressure="合成音压力"
|
||||
AdvSceneSwitcher.midi.message.type.controlChange="控制更改"
|
||||
AdvSceneSwitcher.midi.message.type.programChange="程序更改"
|
||||
AdvSceneSwitcher.midi.message.type.channelAftertouch="频道余音"
|
||||
AdvSceneSwitcher.midi.message.type.pitchBend="音高弯曲"
|
||||
AdvSceneSwitcher.midi.message.type.systemExclusive="系统独占"
|
||||
AdvSceneSwitcher.midi.message.type.timeCode="时间码"
|
||||
AdvSceneSwitcher.midi.message.type.songPositionPointer="歌曲位置指针"
|
||||
AdvSceneSwitcher.midi.message.type.songSelect="歌曲选择"
|
||||
AdvSceneSwitcher.midi.message.type.reserved1="保留 (1)"
|
||||
AdvSceneSwitcher.midi.message.type.reserved2="保留 (2)"
|
||||
AdvSceneSwitcher.midi.message.type.tuneRequest="调音请求"
|
||||
AdvSceneSwitcher.midi.message.type.endOfSystemExclusive="系统独占结束"
|
||||
AdvSceneSwitcher.midi.message.type.timeClock="时间时钟"
|
||||
AdvSceneSwitcher.midi.message.type.reserved3="保留 (3)"
|
||||
AdvSceneSwitcher.midi.message.type.startFile="开始文件"
|
||||
AdvSceneSwitcher.midi.message.type.continueFile="继续文件"
|
||||
AdvSceneSwitcher.midi.message.type.stopFile="停止文件"
|
||||
AdvSceneSwitcher.midi.message.type.reserved4="保留 (4)"
|
||||
AdvSceneSwitcher.midi.message.type.activeSensing="主动感应"
|
||||
AdvSceneSwitcher.midi.message.type.systemReset="系统充值"
|
||||
AdvSceneSwitcher.midi.message.type.unknown="未知"
|
||||
AdvSceneSwitcher.midi.message.channel="频道 :"
|
||||
AdvSceneSwitcher.midi.message.note="音符 / 值(1):"
|
||||
AdvSceneSwitcher.midi.message.value="值(2):"
|
||||
AdvSceneSwitcher.midi.message.placeholder="任意"
|
||||
AdvSceneSwitcher.midi.resetDevices="重新连接所有 MIDI 设备"
|
||||
AdvSceneSwitcher.midi.startListen="开始监听"
|
||||
AdvSceneSwitcher.midi.stopListen="停止监听"
|
||||
AdvSceneSwitcher.midi.startListenFail="设备正忙!\n其他设备已经在监听!"
|
||||
AdvSceneSwitcher.midi.deviceOpenFail="初始化 MIDI 设备失败!"
|
||||
|
||||
AdvSceneSwitcher.osc.network="网络"
|
||||
AdvSceneSwitcher.osc.network.protocol="协议:"
|
||||
AdvSceneSwitcher.osc.network.address="地址:"
|
||||
AdvSceneSwitcher.osc.network.port="端口:"
|
||||
AdvSceneSwitcher.osc.message="信息"
|
||||
AdvSceneSwitcher.osc.message.type.none="无"
|
||||
AdvSceneSwitcher.osc.message.type.float="浮点数哦"
|
||||
AdvSceneSwitcher.osc.message.type.int="整数"
|
||||
AdvSceneSwitcher.osc.message.type.string="字符串"
|
||||
AdvSceneSwitcher.osc.message.type.binaryBlob="二进制"
|
||||
AdvSceneSwitcher.osc.message.type.true="真"
|
||||
AdvSceneSwitcher.osc.message.type.false="假"
|
||||
AdvSceneSwitcher.osc.message.type.infinity="无限"
|
||||
AdvSceneSwitcher.osc.message.type.null="无"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--选择场景--"
|
||||
AdvSceneSwitcher.selectPreviousScene="上个场景"
|
||||
AdvSceneSwitcher.selectCurrentScene="当前场景"
|
||||
AdvSceneSwitcher.selectPreviewScene="预览场景"
|
||||
AdvSceneSwitcher.selectAnyScene="任何场景"
|
||||
AdvSceneSwitcher.currentTransition="当前转场特效"
|
||||
AdvSceneSwitcher.anyTransition="任何转场特效"
|
||||
@@ -694,6 +1041,7 @@ AdvSceneSwitcher.selectWindow="--选择窗口--"
|
||||
AdvSceneSwitcher.selectSource="--选择源--"
|
||||
AdvSceneSwitcher.selectAudioSource="--选择音频源--"
|
||||
AdvSceneSwitcher.selectVideoSource="--选择视频源--"
|
||||
AdvSceneSwitcher.OBSVideoOutput="OBS的主要输出"
|
||||
AdvSceneSwitcher.selectMediaSource="--选择媒体源--"
|
||||
AdvSceneSwitcher.selectProcess="--选择进程--"
|
||||
AdvSceneSwitcher.selectFilter="--选择滤镜--"
|
||||
@@ -703,6 +1051,7 @@ AdvSceneSwitcher.selectProfile="--选择配置文件--"
|
||||
AdvSceneSwitcher.selectSceneCollection="--选择场景集合--"
|
||||
AdvSceneSwitcher.enterPath="--输入路径--"
|
||||
AdvSceneSwitcher.enterText="--输入文本--"
|
||||
AdvSceneSwitcher.enterURL="--输入网址--"
|
||||
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="无效项将不会被保存"
|
||||
AdvSceneSwitcher.selectWindowTip="使用 \"OBS\" 来指定OBS窗口\n使用 \"任务切换\" 来指定 Alt + Tab"
|
||||
AdvSceneSwitcher.sceneItemSelection.all="全部"
|
||||
@@ -710,8 +1059,8 @@ AdvSceneSwitcher.sceneItemSelection.any="任何"
|
||||
|
||||
AdvSceneSwitcher.status.active="运行中"
|
||||
AdvSceneSwitcher.status.inactive="已停止"
|
||||
AdvSceneSwitcher.running="插件运行"
|
||||
AdvSceneSwitcher.stopped="插件停止"
|
||||
AdvSceneSwitcher.running="插件正在运行"
|
||||
AdvSceneSwitcher.stopped="插件已停止"
|
||||
|
||||
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>这似乎是第一次启动高级场景切换程序.<br>请看一下 <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> 查看指南和示例列表.<br>如果有问题,在在OBS论坛插件帖子内提问 <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">thread</span></a></p></body></html>"
|
||||
|
||||
@@ -725,3 +1074,8 @@ AdvSceneSwitcher.duration.condition.none="没有时间限制"
|
||||
AdvSceneSwitcher.duration.condition.more="时间少于"
|
||||
AdvSceneSwitcher.duration.condition.equal="时间等于"
|
||||
AdvSceneSwitcher.duration.condition.less="时间大于"
|
||||
AdvSceneSwitcher.duration.condition.within="在最后"
|
||||
|
||||
AdvSceneSwitcher.audio.monitor.none="关闭监听"
|
||||
AdvSceneSwitcher.audio.monitor.monitorOnly="仅监听(输出静音)"
|
||||
AdvSceneSwitcher.audio.monitor.both="监听并输出"
|
||||
|
||||
4
data/res/images/DarkAdvanced.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m 13.855469 0 l -1.539063 1.4375 c -0.453125 0.421875 -0.53125 1.148438 -0.269531 1.707031 l -5.886719 5.996094 c -0.011718 0 -0.019531 0 -0.03125 0 c -0.257812 -0.128906 -0.550781 -0.183594 -0.839844 -0.148437 c -0.328124 0.046874 -0.632812 0.199218 -0.867187 0.441406 l -3.945313 3.996094 c -0.3906245 0.375 -0.5468745 0.933593 -0.4062495 1.457031 c 0.1406255 0.523437 0.5546875 0.929687 1.0820315 1.058593 c 0.527344 0.132813 1.082031 -0.03125 1.453125 -0.425781 l 3.945312 -3.996093 c 0.472657 -0.453126 0.59375 -1.15625 0.296875 -1.738282 l 5.890625 -5.964844 c 0.558594 0.25 1.273438 0.148438 1.707031 -0.289062 l 1.414063 -1.5625 z m -10.308594 0.0898438 c -0.398437 0 -0.785156 0.0937502 -1.140625 0.2187502 l 1.882812 1.878906 c 0.390626 0.382812 0.390626 1 0 1.386719 l -0.710937 0.707031 c -0.386719 0.386719 -1 0.386719 -1.390625 0 l -1.882812 -1.878906 c -0.125 0.355468 -0.2187505 0.742187 -0.2187505 1.140625 c 0 1.90625 1.5507815 3.453125 3.4609375 3.453125 c 0.402344 0 0.789063 -0.09375 1.144531 -0.21875 l 1.175782 1.171875 h 0.058593 l 2.070313 -2.0625 l -1.203125 -1.203125 c 0.125 -0.359375 0.214843 -0.742188 0.214843 -1.140625 c 0 -1.90625 -1.546874 -3.4531252 -3.460937 -3.4531252 z m 6.550781 7.8906252 l -2.070312 2.066406 c 0.011718 0.027344 0.023437 0.058594 0.03125 0.089844 l 1.144531 1.140625 c -0.125 0.355468 -0.21875 0.742187 -0.21875 1.140625 c 0 1.902343 1.550781 3.449219 3.460937 3.449219 c 0.433594 0 0.855469 -0.101563 1.238282 -0.246094 l -2.007813 -2 c -0.386719 -0.386719 -0.386719 -1.035156 0 -1.417969 l 0.679688 -0.679687 c 0.195312 -0.191407 0.457031 -0.308594 0.710937 -0.308594 s 0.515625 0.117187 0.710938 0.308594 l 1.945312 1.941406 c 0.105469 -0.328125 0.183594 -0.683594 0.183594 -1.046875 c 0 -1.90625 -1.546875 -3.453125 -3.460938 -3.453125 c -0.398437 0 -0.785156 0.09375 -1.140624 0.21875 z m 0 0" fill="#fefefe"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
9
data/res/images/DarkDoubleDown.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<path d="m1,2c0,-0.27 0.11,-0.52 0.29,-0.71c0.39,-0.39 1.03,-0.39 1.42,0l5.29,5.3l5.29,-5.3c0.39,-0.39 1.03,-0.39 1.42,0c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71l-6,6c-0.39,0.39 -1.03,0.39 -1.42,0l-6,-6c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#fefefe" id="svg_1" transform="matrix(1, 0, 0, 1, 0, 0)"/>
|
||||
<path d="m1,8c0,-0.27 0.09,-0.52 0.28,-0.71c0.38,-0.39 1.03,-0.39 1.41,0l5.31,5.3l5.28,-5.3c0.38,-0.39 1.03,-0.39 1.41,0c0.19,0.19 0.31,0.44 0.31,0.71s-0.13,0.52 -0.31,0.71l-6,6c-0.38,0.39 -1.03,0.39 -1.41,0l-6,-6c-0.19,-0.19 -0.28,-0.44 -0.28,-0.71zm0,0" fill="#fefefe" id="svg_2"/>
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 791 B |
9
data/res/images/DarkDoubleUp.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<path d="m1,8c0,-0.27 0.11,-0.52 0.29,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.29,-5.3l-5.29,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#fefefe" id="svg_1"/>
|
||||
<path d="m1,14c0,-0.27 0.11,-0.52 0.28,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.19,0.19 0.3,0.44 0.3,0.71s-0.11,0.52 -0.3,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.28,-5.3l-5.3,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.17,-0.19 -0.28,-0.44 -0.28,-0.71zm0,0" fill="#fefefe" id="svg_2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 753 B |
7
data/res/images/DarkGroup.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#fefefe">
|
||||
<path d="m 3 1 c -1.644531 0 -3 1.355469 -3 3 v 8 c 0 1.644531 1.355469 3 3 3 h 8.882812 c 0.832032 0 1.578126 -0.402344 2.054688 -0.9375 c 0.472656 -0.53125 0.738281 -1.167969 0.910156 -1.800781 l 0.972656 -2.609375 c 0.390626 -1.449219 -0.09375 -2.652344 -0.820312 -3.167969 c -0.484375 -0.34375 -0.714844 -0.292969 -1 -0.324219 v -1.160156 c 0 -0.855469 -0.558594 -1.589844 -1.09375 -1.828125 c -0.53125 -0.238281 -1.011719 -0.167969 -1.011719 -0.167969 l 0.105469 -0.003906 h -3.585938 l -1.707031 -1.707031 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 z m 0 2 h 2.585938 l 1.707031 1.707031 c 0.1875 0.1875 0.441406 0.292969 0.707031 0.292969 h 4 c 0.035156 0 0.070312 -0.003906 0.105469 -0.007812 c 0 0 0.019531 0.019531 -0.011719 0.003906 c -0.035156 -0.011719 -0.09375 -0.25 -0.09375 0.003906 v 2 c 0 0.550781 0.449219 1 1 1 c 1 0 1.046875 0.703125 0.886719 1.128906 l -0.972657 2.609375 c -0.117187 0.4375 -0.296874 0.800781 -0.472656 0.996094 c -0.175781 0.199219 -0.285156 0.265625 -0.558594 0.265625 h -8.882812 c -0.570312 0 -1 -0.429688 -1 -1 v -8 c 0 -0.570312 0.46875 -0.792969 1 -1 z m 0 0"/>
|
||||
<path d="m 7 6 l 0.042969 0.003906 c -0.914063 -0.042968 -1.75 0.390625 -2.195313 0.96875 c -0.710937 1.222656 -1.15625 2.277344 -1.800781 3.71875 c -0.171875 0.523438 0.117187 1.089844 0.640625 1.261719 c 0.527344 0.171875 1.09375 -0.117187 1.261719 -0.640625 c 0.488281 -1.011719 0.921875 -1.816406 1.339843 -2.808594 c 0.210938 -0.503906 0.703126 -0.492187 0.898438 -0.503906 h 5.8125 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 0 0"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
24
data/res/images/DarkNote.svg
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="640" height="640" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" version="1.1">
|
||||
<defs>
|
||||
<path d="m599.62,325.49c0,151.77 -123.21,274.98 -274.98,274.98c-151.76,0 -274.97,-123.21 -274.97,-274.98c0,-151.76 123.21,-274.97 274.97,-274.97c151.77,0 274.98,123.21 274.98,274.97z" id="c3Wyq0kfYS"/>
|
||||
<mask height="689.95" id="maskanTyyngos" maskUnits="userSpaceOnUse" width="689.95" x="-20.33" y="-19.48">
|
||||
<rect fill="white" height="689.95" id="svg_1" width="689.95" x="-20.33" y="-19.48"/>
|
||||
<use fill="black" id="svg_2" xlink:href="#c3Wyq0kfYS"/>
|
||||
</mask>
|
||||
<path d="m334.97,96.07c4.54,0 8.23,3.68 8.23,8.23c0,46.94 0,179.54 0,226.48c0,4.54 -3.69,8.22 -8.23,8.22c-5.77,0 -14.88,0 -20.66,0c-4.54,0 -8.22,-3.68 -8.22,-8.22c0,-46.94 0,-179.54 0,-226.48c0,-4.55 3.68,-8.23 8.22,-8.23c5.78,0 14.89,0 20.66,0z" id="fxMeu932c"/>
|
||||
<path d="m471.96,338.21c0.01,3.1 -2.5,5.62 -5.59,5.63c-32.02,0.09 -122.45,0.37 -154.46,0.47c-3.1,0.01 -5.61,-2.49 -5.62,-5.59c-0.02,-6.3 -0.06,-19.59 -0.08,-25.89c-0.01,-3.1 2.49,-5.62 5.59,-5.63c32.01,-0.09 122.44,-0.37 154.45,-0.47c3.1,-0.01 5.62,2.5 5.63,5.59c0.02,6.3 0.06,19.59 0.08,25.89z" id="h65nGmP2BL"/>
|
||||
</defs>
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<path fill="none" fill-opacity="null" id="svg_11" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02"/>
|
||||
<ellipse cx="103.84" cy="562.04" fill="#fefefe" id="svg_3" rx="80" ry="45" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" transform="rotate(-18.699, 103.843, 562.038)"/>
|
||||
<ellipse cx="506" cy="554" fill="#fefefe" id="svg_4" rx="77" ry="45" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" transform="rotate(-18.699, 506, 554)"/>
|
||||
<rect fill="#fefefe" height="359" id="svg_7" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" stroke-width="0" width="58" x="122" y="185"/>
|
||||
<rect fill="#fefefe" height="453" id="svg_8" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" width="58" x="522" y="84.5"/>
|
||||
<rect fill="#fefefe" height="37" id="svg_9" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" transform="rotate(-14.1985, 351.556, 146.245)" width="463.07" x="120.02" y="127.74"/>
|
||||
<rect fill="none" fill-opacity="null" height="0" id="svg_10" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" width="0" x="538.25" y="107.12"/>
|
||||
<path d="m537.42,112.06l0,-67.88l17.58,67.88z" fill="#fefefe" id="svg_16" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" transform="rotate(-90, 546.203, 78.1248)"/>
|
||||
<rect fill="#fefefe" height="0" id="svg_18" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" width="0" x="82.12" y="287.56"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
10
data/res/images/DarkRegex.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" version="1.1">
|
||||
<!-- Created with SVG-edit - https://github.com/SVG-Edit/svgedit-->
|
||||
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<text fill="#fefefe" font-family="Serif" font-size="24" id="svg_1" stroke="#fefefe" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0" text-anchor="middle" transform="matrix(1.0974, 0, 0, 1.0974, -4.98544, 4.97639)" x="14" xml:space="preserve" y="13">*</text>
|
||||
<rect fill="#fefefe" height="3.3" id="svg_2" stroke="#fefefe" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" transform="matrix(1, 0, 0, 1, 0, 0)" width="5" x="1" y="12"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 815 B |
@@ -1,3 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 640" width="640" height="640"><defs><path d="M599.62 325.49C599.62 477.26 476.41 600.47 324.64 600.47C172.88 600.47 49.67 477.26 49.67 325.49C49.67 173.73 172.88 50.52 324.64 50.52C476.41 50.52 599.62 173.73 599.62 325.49Z" id="c3Wyq0kfYS"></path><mask id="maskanTyyngos" x="-20.33" y="-19.48" width="689.95" height="689.95" maskUnits="userSpaceOnUse"><rect x="-20.33" y="-19.48" width="689.95" height="689.95" fill="white"></rect><use xlink:href="#c3Wyq0kfYS" opacity="1" fill="black"></use></mask><path d="M334.97 96.07C339.51 96.07 343.2 99.75 343.2 104.3C343.2 151.24 343.2 283.84 343.2 330.78C343.2 335.32 339.51 339 334.97 339C329.2 339 320.09 339 314.31 339C309.77 339 306.09 335.32 306.09 330.78C306.09 283.84 306.09 151.24 306.09 104.3C306.09 99.75 309.77 96.07 314.31 96.07C320.09 96.07 329.2 96.07 334.97 96.07Z" id="fxMeu932c"></path><path d="M471.96 338.21C471.97 341.31 469.46 343.83 466.37 343.84C434.35 343.93 343.92 344.21 311.91 344.31C308.81 344.32 306.3 341.82 306.29 338.72C306.27 332.42 306.23 319.13 306.21 312.83C306.2 309.73 308.7 307.21 311.8 307.2C343.81 307.11 434.24 306.83 466.25 306.73C469.35 306.72 471.87 309.23 471.88 312.32C471.9 318.62 471.94 331.91 471.96 338.21Z" id="h65nGmP2BL"></path></defs><g><g><g><g mask="url(#maskanTyyngos)"><use xlink:href="#c3Wyq0kfYS" opacity="1" fill-opacity="0" stroke="#d2d2d2" stroke-width="70" stroke-opacity="1"></use></g></g><g><use xlink:href="#fxMeu932c" opacity="1" fill="#d2d2d2" fill-opacity="1"></use></g><g><use xlink:href="#h65nGmP2BL" opacity="1" fill="#d2d2d2" fill-opacity="1"></use></g></g></g></svg>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 640" width="640" height="640"><defs><path d="M599.62 325.49C599.62 477.26 476.41 600.47 324.64 600.47C172.88 600.47 49.67 477.26 49.67 325.49C49.67 173.73 172.88 50.52 324.64 50.52C476.41 50.52 599.62 173.73 599.62 325.49Z" id="c3Wyq0kfYS"></path><mask id="maskanTyyngos" x="-20.33" y="-19.48" width="689.95" height="689.95" maskUnits="userSpaceOnUse"><rect x="-20.33" y="-19.48" width="689.95" height="689.95" fill="white"></rect><use xlink:href="#c3Wyq0kfYS" opacity="1" fill="black"></use></mask><path d="M334.97 96.07C339.51 96.07 343.2 99.75 343.2 104.3C343.2 151.24 343.2 283.84 343.2 330.78C343.2 335.32 339.51 339 334.97 339C329.2 339 320.09 339 314.31 339C309.77 339 306.09 335.32 306.09 330.78C306.09 283.84 306.09 151.24 306.09 104.3C306.09 99.75 309.77 96.07 314.31 96.07C320.09 96.07 329.2 96.07 334.97 96.07Z" id="fxMeu932c"></path><path d="M471.96 338.21C471.97 341.31 469.46 343.83 466.37 343.84C434.35 343.93 343.92 344.21 311.91 344.31C308.81 344.32 306.3 341.82 306.29 338.72C306.27 332.42 306.23 319.13 306.21 312.83C306.2 309.73 308.7 307.21 311.8 307.2C343.81 307.11 434.24 306.83 466.25 306.73C469.35 306.72 471.87 309.23 471.88 312.32C471.9 318.62 471.94 331.91 471.96 338.21Z" id="h65nGmP2BL"></path></defs><g><g><g><g mask="url(#maskanTyyngos)"><use xlink:href="#c3Wyq0kfYS" opacity="1" fill-opacity="0" stroke="#fefefe" stroke-width="70" stroke-opacity="1"></use></g></g><g><use xlink:href="#fxMeu932c" opacity="1" fill="#fefefe" fill-opacity="1"></use></g><g><use xlink:href="#h65nGmP2BL" opacity="1" fill="#fefefe" fill-opacity="1"></use></g></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
4
data/res/images/LightAdvanced.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m 13.855469 0 l -1.539063 1.4375 c -0.453125 0.421875 -0.53125 1.148438 -0.269531 1.707031 l -5.886719 5.996094 c -0.011718 0 -0.019531 0 -0.03125 0 c -0.257812 -0.128906 -0.550781 -0.183594 -0.839844 -0.148437 c -0.328124 0.046874 -0.632812 0.199218 -0.867187 0.441406 l -3.945313 3.996094 c -0.3906245 0.375 -0.5468745 0.933593 -0.4062495 1.457031 c 0.1406255 0.523437 0.5546875 0.929687 1.0820315 1.058593 c 0.527344 0.132813 1.082031 -0.03125 1.453125 -0.425781 l 3.945312 -3.996093 c 0.472657 -0.453126 0.59375 -1.15625 0.296875 -1.738282 l 5.890625 -5.964844 c 0.558594 0.25 1.273438 0.148438 1.707031 -0.289062 l 1.414063 -1.5625 z m -10.308594 0.0898438 c -0.398437 0 -0.785156 0.0937502 -1.140625 0.2187502 l 1.882812 1.878906 c 0.390626 0.382812 0.390626 1 0 1.386719 l -0.710937 0.707031 c -0.386719 0.386719 -1 0.386719 -1.390625 0 l -1.882812 -1.878906 c -0.125 0.355468 -0.2187505 0.742187 -0.2187505 1.140625 c 0 1.90625 1.5507815 3.453125 3.4609375 3.453125 c 0.402344 0 0.789063 -0.09375 1.144531 -0.21875 l 1.175782 1.171875 h 0.058593 l 2.070313 -2.0625 l -1.203125 -1.203125 c 0.125 -0.359375 0.214843 -0.742188 0.214843 -1.140625 c 0 -1.90625 -1.546874 -3.4531252 -3.460937 -3.4531252 z m 6.550781 7.8906252 l -2.070312 2.066406 c 0.011718 0.027344 0.023437 0.058594 0.03125 0.089844 l 1.144531 1.140625 c -0.125 0.355468 -0.21875 0.742187 -0.21875 1.140625 c 0 1.902343 1.550781 3.449219 3.460937 3.449219 c 0.433594 0 0.855469 -0.101563 1.238282 -0.246094 l -2.007813 -2 c -0.386719 -0.386719 -0.386719 -1.035156 0 -1.417969 l 0.679688 -0.679687 c 0.195312 -0.191407 0.457031 -0.308594 0.710937 -0.308594 s 0.515625 0.117187 0.710938 0.308594 l 1.945312 1.941406 c 0.105469 -0.328125 0.183594 -0.683594 0.183594 -1.046875 c 0 -1.90625 -1.546875 -3.453125 -3.460938 -3.453125 c -0.398437 0 -0.785156 0.09375 -1.140624 0.21875 z m 0 0" fill="#202020"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
9
data/res/images/LightDoubleDown.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<path d="m1,2c0,-0.27 0.11,-0.52 0.29,-0.71c0.39,-0.39 1.03,-0.39 1.42,0l5.29,5.3l5.29,-5.3c0.39,-0.39 1.03,-0.39 1.42,0c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71l-6,6c-0.39,0.39 -1.03,0.39 -1.42,0l-6,-6c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_1"/>
|
||||
<path d="m1,8c0,-0.27 0.11,-0.52 0.29,-0.71c0.39,-0.39 1.03,-0.39 1.42,0l5.29,5.3l5.29,-5.3c0.39,-0.39 1.03,-0.39 1.42,0c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71l-6,6c-0.39,0.39 -1.03,0.39 -1.42,0l-6,-6c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_4" transform="matrix(1, 0, 0, 1, 0, 0)"/>
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 791 B |
9
data/res/images/LightDoubleUp.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<path d="m1,8c0,-0.27 0.11,-0.52 0.29,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.29,-5.3l-5.29,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_1"/>
|
||||
<path d="m1,14c0,-0.27 0.11,-0.52 0.29,-0.71l6,-6c0.39,-0.39 1.03,-0.39 1.42,0l6,6c0.18,0.19 0.29,0.44 0.29,0.71s-0.11,0.52 -0.29,0.71c-0.39,0.39 -1.03,0.39 -1.42,0l-5.29,-5.3l-5.29,5.3c-0.39,0.39 -1.03,0.39 -1.42,0c-0.18,-0.19 -0.29,-0.44 -0.29,-0.71zm0,0" fill="#202020" id="svg_2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 757 B |
7
data/res/images/LightGroup.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#202020">
|
||||
<path d="m 3 1 c -1.644531 0 -3 1.355469 -3 3 v 8 c 0 1.644531 1.355469 3 3 3 h 8.882812 c 0.832032 0 1.578126 -0.402344 2.054688 -0.9375 c 0.472656 -0.53125 0.738281 -1.167969 0.910156 -1.800781 l 0.972656 -2.609375 c 0.390626 -1.449219 -0.09375 -2.652344 -0.820312 -3.167969 c -0.484375 -0.34375 -0.714844 -0.292969 -1 -0.324219 v -1.160156 c 0 -0.855469 -0.558594 -1.589844 -1.09375 -1.828125 c -0.53125 -0.238281 -1.011719 -0.167969 -1.011719 -0.167969 l 0.105469 -0.003906 h -3.585938 l -1.707031 -1.707031 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 z m 0 2 h 2.585938 l 1.707031 1.707031 c 0.1875 0.1875 0.441406 0.292969 0.707031 0.292969 h 4 c 0.035156 0 0.070312 -0.003906 0.105469 -0.007812 c 0 0 0.019531 0.019531 -0.011719 0.003906 c -0.035156 -0.011719 -0.09375 -0.25 -0.09375 0.003906 v 2 c 0 0.550781 0.449219 1 1 1 c 1 0 1.046875 0.703125 0.886719 1.128906 l -0.972657 2.609375 c -0.117187 0.4375 -0.296874 0.800781 -0.472656 0.996094 c -0.175781 0.199219 -0.285156 0.265625 -0.558594 0.265625 h -8.882812 c -0.570312 0 -1 -0.429688 -1 -1 v -8 c 0 -0.570312 0.46875 -0.792969 1 -1 z m 0 0"/>
|
||||
<path d="m 7 6 l 0.042969 0.003906 c -0.914063 -0.042968 -1.75 0.390625 -2.195313 0.96875 c -0.710937 1.222656 -1.15625 2.277344 -1.800781 3.71875 c -0.171875 0.523438 0.117187 1.089844 0.640625 1.261719 c 0.527344 0.171875 1.09375 -0.117187 1.261719 -0.640625 c 0.488281 -1.011719 0.921875 -1.816406 1.339843 -2.808594 c 0.210938 -0.503906 0.703126 -0.492187 0.898438 -0.503906 h 5.8125 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 0 0"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
24
data/res/images/LightNote.svg
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="640" height="640" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" version="1.1">
|
||||
<defs>
|
||||
<path d="m599.62,325.49c0,151.77 -123.21,274.98 -274.98,274.98c-151.76,0 -274.97,-123.21 -274.97,-274.98c0,-151.76 123.21,-274.97 274.97,-274.97c151.77,0 274.98,123.21 274.98,274.97z" id="c3Wyq0kfYS"/>
|
||||
<mask height="689.95" id="maskanTyyngos" maskUnits="userSpaceOnUse" width="689.95" x="-20.33" y="-19.48">
|
||||
<rect fill="white" height="689.95" id="svg_1" width="689.95" x="-20.33" y="-19.48"/>
|
||||
<use fill="black" id="svg_2" xlink:href="#c3Wyq0kfYS"/>
|
||||
</mask>
|
||||
<path d="m334.97,96.07c4.54,0 8.23,3.68 8.23,8.23c0,46.94 0,179.54 0,226.48c0,4.54 -3.69,8.22 -8.23,8.22c-5.77,0 -14.88,0 -20.66,0c-4.54,0 -8.22,-3.68 -8.22,-8.22c0,-46.94 0,-179.54 0,-226.48c0,-4.55 3.68,-8.23 8.22,-8.23c5.78,0 14.89,0 20.66,0z" id="fxMeu932c"/>
|
||||
<path d="m471.96,338.21c0.01,3.1 -2.5,5.62 -5.59,5.63c-32.02,0.09 -122.45,0.37 -154.46,0.47c-3.1,0.01 -5.61,-2.49 -5.62,-5.59c-0.02,-6.3 -0.06,-19.59 -0.08,-25.89c-0.01,-3.1 2.49,-5.62 5.59,-5.63c32.01,-0.09 122.44,-0.37 154.45,-0.47c3.1,-0.01 5.62,2.5 5.63,5.59c0.02,6.3 0.06,19.59 0.08,25.89z" id="h65nGmP2BL"/>
|
||||
</defs>
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<path fill="none" fill-opacity="null" id="svg_11" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02"/>
|
||||
<ellipse cx="103.84" cy="562.04" fill="#202020" id="svg_3" rx="80" ry="45" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" transform="rotate(-18.699, 103.843, 562.038)"/>
|
||||
<ellipse cx="506" cy="554" fill="#202020" id="svg_4" rx="77" ry="45" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" transform="rotate(-18.699, 506, 554)"/>
|
||||
<rect fill="#202020" height="359" id="svg_7" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" stroke-width="0" width="58" x="122" y="185"/>
|
||||
<rect fill="#202020" height="453" id="svg_8" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" width="58" x="522" y="84.5"/>
|
||||
<rect fill="#202020" height="37" id="svg_9" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" transform="rotate(-14.1985, 351.556, 146.245)" width="463.07" x="120.02" y="127.74"/>
|
||||
<rect fill="none" fill-opacity="null" height="0" id="svg_10" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" width="0" x="538.25" y="107.12"/>
|
||||
<path d="m537.42,112.06l0,-67.88l17.58,67.88z" fill="#202020" id="svg_16" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" transform="rotate(-90, 546.203, 78.1248)"/>
|
||||
<rect fill="#202020" height="0" id="svg_18" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="0.02" width="0" x="82.12" y="287.56"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
10
data/res/images/LightRegex.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" version="1.1">
|
||||
<!-- Created with SVG-edit - https://github.com/SVG-Edit/svgedit-->
|
||||
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<text fill="#202020" font-family="Serif" font-size="24" id="svg_1" stroke="#000000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0" text-anchor="middle" transform="matrix(1.0974, 0, 0, 1.0974, -4.98544, 4.97639)" x="14" xml:space="preserve" y="13">*</text>
|
||||
<rect fill="#202020" height="3.3" id="svg_2" stroke="#202020" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" transform="matrix(1, 0, 0, 1, 0, 0)" width="5" x="1" y="12"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 815 B |
2
data/res/images/LightTime.svg
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 640" width="640" height="640"><defs><path d="M599.62 325.49C599.62 477.26 476.41 600.47 324.64 600.47C172.88 600.47 49.67 477.26 49.67 325.49C49.67 173.73 172.88 50.52 324.64 50.52C476.41 50.52 599.62 173.73 599.62 325.49Z" id="c3Wyq0kfYS"></path><mask id="maskanTyyngos" x="-20.33" y="-19.48" width="689.95" height="689.95" maskUnits="userSpaceOnUse"><rect x="-20.33" y="-19.48" width="689.95" height="689.95" fill="white"></rect><use xlink:href="#c3Wyq0kfYS" opacity="1" fill="black"></use></mask><path d="M334.97 96.07C339.51 96.07 343.2 99.75 343.2 104.3C343.2 151.24 343.2 283.84 343.2 330.78C343.2 335.32 339.51 339 334.97 339C329.2 339 320.09 339 314.31 339C309.77 339 306.09 335.32 306.09 330.78C306.09 283.84 306.09 151.24 306.09 104.3C306.09 99.75 309.77 96.07 314.31 96.07C320.09 96.07 329.2 96.07 334.97 96.07Z" id="fxMeu932c"></path><path d="M471.96 338.21C471.97 341.31 469.46 343.83 466.37 343.84C434.35 343.93 343.92 344.21 311.91 344.31C308.81 344.32 306.3 341.82 306.29 338.72C306.27 332.42 306.23 319.13 306.21 312.83C306.2 309.73 308.7 307.21 311.8 307.2C343.81 307.11 434.24 306.83 466.25 306.73C469.35 306.72 471.87 309.23 471.88 312.32C471.9 318.62 471.94 331.91 471.96 338.21Z" id="h65nGmP2BL"></path></defs><g><g><g><g mask="url(#maskanTyyngos)"><use xlink:href="#c3Wyq0kfYS" opacity="1" fill-opacity="0" stroke="#202020" stroke-width="70" stroke-opacity="1"></use></g></g><g><use xlink:href="#fxMeu932c" opacity="1" fill="#202020" fill-opacity="1"></use></g><g><use xlink:href="#h65nGmP2BL" opacity="1" fill="#202020" fill-opacity="1"></use></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
BIN
data/res/ocr/eng.traineddata
Normal file
41032
deps/exprtk/exprtk.hpp
vendored
Normal file
1
deps/leptonica
vendored
Submodule
1
deps/libremidi
vendored
Submodule
1
deps/tesseract
vendored
Submodule
@@ -68,7 +68,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>957</width>
|
||||
<height>817</height>
|
||||
<height>905</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_19">
|
||||
@@ -256,6 +256,30 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_26">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="warnPluginLoadFailure">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_19">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_24">
|
||||
<item>
|
||||
@@ -419,6 +443,49 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="transitionBox">
|
||||
<property name="title">
|
||||
<string>AdvSceneSwitcher.generalTab.transition</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="transitionLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="adjustActiveTransitionType">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.generalTab.adjustActiveTransitionType</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.generalTab.setTransitionBy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="transitionOverridecheckBox">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.generalTab.transitionOverride</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="priorityBox">
|
||||
<property name="title">
|
||||
@@ -591,49 +658,26 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_26">
|
||||
<item row="0" column="0">
|
||||
<widget class="QListWidget" name="macros">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::MoveAction</enum>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="macroHelp">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.help</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="advss::MacroTree" name="macros">
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::TargetMoveAction</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_61">
|
||||
@@ -763,7 +807,7 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_33">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0,0,0,0,0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0,0,0,0,0,0,0">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="text">
|
||||
@@ -783,16 +827,36 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="runMacroInParallel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.runInParallel</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="runMacroOnChange">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.onChange</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -924,6 +988,19 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionTop">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionUp">
|
||||
<property name="maximumSize">
|
||||
@@ -959,6 +1036,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionBottom">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_15">
|
||||
<property name="orientation">
|
||||
@@ -1066,6 +1156,19 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionTop">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionUp">
|
||||
<property name="maximumSize">
|
||||
@@ -1098,6 +1201,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionBottom">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_14">
|
||||
<property name="orientation">
|
||||
@@ -1710,44 +1826,6 @@
|
||||
<string>AdvSceneSwitcher.transitionTab.title</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_15">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="adjustActiveTransitionType">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.transitionTab.adjustActiveTransitionType</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.transitionTab.setTransitionBy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="transitionOverridecheckBox">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.transitionTab.transitionOverride</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="transitionsGroup">
|
||||
<property name="title">
|
||||
@@ -4535,6 +4613,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>advss::MacroTree</class>
|
||||
<extends>QListView</extends>
|
||||
<header>macro-tree.hpp</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -9,26 +9,15 @@
|
||||
#include <obs-frontend-api.h>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "status-control.hpp"
|
||||
#include "scene-switch-helpers.hpp"
|
||||
#include "curl-helper.hpp"
|
||||
#include "platform-funcs.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "version.h"
|
||||
|
||||
const char *obs_module_text(const char *text)
|
||||
{
|
||||
return switcher->translate(text);
|
||||
}
|
||||
|
||||
obs_module_t *obs_current_module()
|
||||
{
|
||||
return switcher->modulePtr;
|
||||
}
|
||||
|
||||
SwitcherData *switcher = nullptr;
|
||||
SwitcherData *GetSwitcher()
|
||||
{
|
||||
return switcher;
|
||||
}
|
||||
namespace advss {
|
||||
|
||||
AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr;
|
||||
|
||||
@@ -40,11 +29,9 @@ AdvSceneSwitcher::AdvSceneSwitcher(QWidget *parent)
|
||||
{
|
||||
switcher->settingsWindowOpened = true;
|
||||
ui->setupUi(this);
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
|
||||
switcher->Prune();
|
||||
loadUI();
|
||||
LoadUI();
|
||||
}
|
||||
|
||||
AdvSceneSwitcher::~AdvSceneSwitcher()
|
||||
@@ -55,51 +42,76 @@ AdvSceneSwitcher::~AdvSceneSwitcher()
|
||||
}
|
||||
}
|
||||
|
||||
bool translationAvailable()
|
||||
static bool translationAvailable()
|
||||
{
|
||||
return !!strcmp(obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||
"AdvSceneSwitcher.pluginName");
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::loadUI()
|
||||
static void DisplayMissingDependencyWarning()
|
||||
{
|
||||
if (!translationAvailable()) {
|
||||
QString msg = "Failed to find plug-in's 'data' directory.\n"
|
||||
"Please check installation instructions!\n\n"
|
||||
"Data most likely expected at:\n\n";
|
||||
#ifdef _WIN32
|
||||
msg += QString::fromStdString(
|
||||
(std::filesystem::current_path().string()));
|
||||
msg += "/";
|
||||
#endif
|
||||
msg += obs_get_module_data_path(obs_current_module());
|
||||
(void)DisplayMessage(msg);
|
||||
if (!switcher->warnPluginLoadFailure ||
|
||||
switcher->loadFailureLibs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setupGeneralTab();
|
||||
setupTitleTab();
|
||||
setupExecutableTab();
|
||||
setupRegionTab();
|
||||
setupPauseTab();
|
||||
setupSequenceTab();
|
||||
setupTransitionsTab();
|
||||
setupIdleTab();
|
||||
setupRandomTab();
|
||||
setupMediaTab();
|
||||
setupFileTab();
|
||||
setupTimeTab();
|
||||
setupAudioTab();
|
||||
setupVideoTab();
|
||||
setupNetworkTab();
|
||||
setupSceneGroupTab();
|
||||
setupTriggerTab();
|
||||
setupMacroTab();
|
||||
QString failedLibsString = "<ul>";
|
||||
for (const auto &lib : switcher->loadFailureLibs) {
|
||||
failedLibsString += "<li>" + lib + "</li>";
|
||||
}
|
||||
failedLibsString += "</ul>";
|
||||
QString warning(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage"));
|
||||
DisplayMessage(warning.arg(failedLibsString));
|
||||
}
|
||||
|
||||
setDeprecationWarnings();
|
||||
setTabOrder();
|
||||
setCurrentTab();
|
||||
restoreWindowGeo();
|
||||
checkFirstTimeSetup();
|
||||
static void DisplayMissingDataDirWarning()
|
||||
{
|
||||
if (translationAvailable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString msg = "Failed to find plug-in's 'data' directory.\n"
|
||||
"Please check installation instructions!\n\n"
|
||||
"Data most likely expected at:\n\n";
|
||||
#ifdef _WIN32
|
||||
msg += QString::fromStdString(
|
||||
(std::filesystem::current_path().string()));
|
||||
msg += "/";
|
||||
#endif
|
||||
msg += obs_get_module_data_path(obs_current_module());
|
||||
DisplayMessage(msg);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::LoadUI()
|
||||
{
|
||||
DisplayMissingDataDirWarning();
|
||||
DisplayMissingDependencyWarning();
|
||||
|
||||
SetupGeneralTab();
|
||||
SetupTitleTab();
|
||||
SetupExecutableTab();
|
||||
SetupRegionTab();
|
||||
SetupPauseTab();
|
||||
SetupSequenceTab();
|
||||
SetupTransitionsTab();
|
||||
SetupIdleTab();
|
||||
SetupRandomTab();
|
||||
SetupMediaTab();
|
||||
SetupFileTab();
|
||||
SetupTimeTab();
|
||||
SetupAudioTab();
|
||||
SetupVideoTab();
|
||||
SetupNetworkTab();
|
||||
SetupSceneGroupTab();
|
||||
SetupTriggerTab();
|
||||
SetupMacroTab();
|
||||
|
||||
SetDeprecationWarnings();
|
||||
SetTabOrder();
|
||||
SetCurrentTab();
|
||||
RestoreWindowGeo();
|
||||
CheckFirstTimeSetup();
|
||||
|
||||
loading = false;
|
||||
}
|
||||
@@ -107,13 +119,19 @@ void AdvSceneSwitcher::loadUI()
|
||||
/******************************************************************************
|
||||
* Saving and loading
|
||||
******************************************************************************/
|
||||
static void AskForBackup(obs_data_t *obj);
|
||||
|
||||
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
{
|
||||
if (!switcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (saving) {
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->Prune();
|
||||
obs_data_t *obj = obs_data_create();
|
||||
switcher->saveSettings(obj);
|
||||
switcher->SaveSettings(obj);
|
||||
obs_data_set_obj(save_data, "advanced-scene-switcher", obj);
|
||||
obs_data_release(obj);
|
||||
} else {
|
||||
@@ -127,10 +145,10 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
if (!obj) {
|
||||
obj = obs_data_create();
|
||||
}
|
||||
if (switcher->versionChanged(obj, g_GIT_SHA1)) {
|
||||
if (switcher->VersionChanged(obj, g_GIT_SHA1)) {
|
||||
AskForBackup(obj);
|
||||
}
|
||||
switcher->loadSettings(obj);
|
||||
switcher->LoadSettings(obj);
|
||||
obs_data_release(obj);
|
||||
switcher->m.unlock();
|
||||
|
||||
@@ -140,6 +158,34 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
}
|
||||
}
|
||||
|
||||
static void AskForBackup(obs_data_t *obj)
|
||||
{
|
||||
bool backupSettings = DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.askBackup"), true);
|
||||
|
||||
if (!backupSettings) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getSaveFileName(
|
||||
nullptr,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle"),
|
||||
GetDefaultSettingsSaveLocation(),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType"));
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
obs_data_save_json(obj, file.fileName().toUtf8().constData());
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Main switcher thread
|
||||
******************************************************************************/
|
||||
@@ -151,9 +197,11 @@ void SwitcherData::Thread()
|
||||
std::chrono::milliseconds duration;
|
||||
auto startTime = std::chrono::high_resolution_clock::now();
|
||||
auto endTime = std::chrono::high_resolution_clock::now();
|
||||
switcher->firstIntervalAfterStop = true;
|
||||
|
||||
while (true) {
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
mainLoopLock = &lock;
|
||||
|
||||
bool match = false;
|
||||
OBSWeakSource scene;
|
||||
@@ -181,7 +229,7 @@ void SwitcherData::Thread()
|
||||
}
|
||||
|
||||
vblog(LOG_INFO, "try to sleep for %ld", duration.count());
|
||||
setWaitScene();
|
||||
SetWaitScene();
|
||||
cv.wait_for(lock, duration);
|
||||
|
||||
startTime = std::chrono::high_resolution_clock::now();
|
||||
@@ -195,13 +243,13 @@ void SwitcherData::Thread()
|
||||
if (checkPause()) {
|
||||
continue;
|
||||
}
|
||||
setPreconditions();
|
||||
match = checkForMatch(scene, transition, linger,
|
||||
SetPreconditions();
|
||||
match = CheckForMatch(scene, transition, linger,
|
||||
setPrevSceneAfterLinger, macroMatch);
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
checkNoMatchSwitch(match, scene, transition, sleep);
|
||||
CheckNoMatchSwitch(match, scene, transition, sleep);
|
||||
checkSwitchCooldown(match);
|
||||
|
||||
if (linger) {
|
||||
@@ -209,14 +257,14 @@ void SwitcherData::Thread()
|
||||
vblog(LOG_INFO, "sleep for %ld before switching scene",
|
||||
duration.count());
|
||||
|
||||
setWaitScene();
|
||||
SetWaitScene();
|
||||
cv.wait_for(lock, duration);
|
||||
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (sceneChangedDuringWait()) {
|
||||
if (SceneChangedDuringWait()) {
|
||||
vblog(LOG_INFO,
|
||||
"scene was changed manually - ignoring match");
|
||||
|
||||
@@ -227,36 +275,26 @@ void SwitcherData::Thread()
|
||||
}
|
||||
}
|
||||
|
||||
ClearWebsocketMessages();
|
||||
|
||||
// After this point we will call frontend functions like
|
||||
// obs_frontend_set_current_scene() and
|
||||
// obs_frontend_set_current_transition()
|
||||
//
|
||||
// During this time SaveSceneSwitcher() could be called
|
||||
// leading to a deadlock with the frontend function being stuck
|
||||
// in QMetaObject::invokeMethod() holding the mutex and
|
||||
// OBS being stuck in SaveSceneSwitcher().
|
||||
//
|
||||
// So we have to unlock() risking race conditions as these are
|
||||
// less frequent than the above described deadlock
|
||||
lock.unlock();
|
||||
ResetForNextInterval();
|
||||
|
||||
if (match) {
|
||||
if (macroMatch) {
|
||||
runMacros();
|
||||
RunMacros();
|
||||
} else {
|
||||
switchScene({scene, transition, 0});
|
||||
SwitchScene({scene, transition, 0});
|
||||
}
|
||||
}
|
||||
|
||||
writeSceneInfoToFile();
|
||||
switcher->firstInterval = false;
|
||||
switcher->firstIntervalAfterStop = false;
|
||||
}
|
||||
|
||||
mainLoopLock = nullptr;
|
||||
blog(LOG_INFO, "stopped");
|
||||
}
|
||||
|
||||
void SwitcherData::setPreconditions()
|
||||
void SwitcherData::SetPreconditions()
|
||||
{
|
||||
// Window title
|
||||
lastTitle = currentTitle;
|
||||
@@ -280,14 +318,27 @@ void SwitcherData::setPreconditions()
|
||||
}
|
||||
currentTitle = title;
|
||||
|
||||
// Process name
|
||||
GetForegroundProcessName(currentForegroundProcess);
|
||||
|
||||
// Cursor
|
||||
std::pair<int, int> cursorPos = getCursorPos();
|
||||
std::pair<int, int> cursorPos = GetCursorPos();
|
||||
cursorPosChanged = cursorPos.first != switcher->lastCursorPos.first ||
|
||||
cursorPos.second != switcher->lastCursorPos.second;
|
||||
lastCursorPos = getCursorPos();
|
||||
lastCursorPos = GetCursorPos();
|
||||
}
|
||||
|
||||
bool SwitcherData::checkForMatch(OBSWeakSource &scene,
|
||||
void SwitcherData::ResetForNextInterval()
|
||||
{
|
||||
// Core reset functions
|
||||
ClearWebsocketMessages();
|
||||
// Plugin reset functions
|
||||
for (const auto &func : resetForNextIntervalFuncs) {
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger,
|
||||
bool &setPrevSceneAfterLinger,
|
||||
bool ¯oMatch)
|
||||
@@ -337,7 +388,7 @@ bool SwitcherData::checkForMatch(OBSWeakSource &scene,
|
||||
match = checkVideoSwitch(scene, transition);
|
||||
break;
|
||||
case macro_func:
|
||||
if (checkMacros()) {
|
||||
if (CheckMacros()) {
|
||||
match = true;
|
||||
macroMatch = true;
|
||||
}
|
||||
@@ -354,48 +405,20 @@ bool SwitcherData::checkForMatch(OBSWeakSource &scene,
|
||||
return match;
|
||||
}
|
||||
|
||||
void switchScene(const sceneSwitchInfo &sceneSwitch, bool force)
|
||||
static void ResetMacros()
|
||||
{
|
||||
if (!sceneSwitch.scene && switcher->verbose) {
|
||||
blog(LOG_INFO, "nothing to switch to");
|
||||
return;
|
||||
for (auto &m : switcher->macros) {
|
||||
m->ResetRunCount();
|
||||
m->ResetTimers();
|
||||
}
|
||||
|
||||
obs_source_t *source = obs_weak_source_get_source(sceneSwitch.scene);
|
||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||
|
||||
if (source && (source != currentSource || force)) {
|
||||
transitionData currentTransitionData;
|
||||
setNextTransition(sceneSwitch, currentSource,
|
||||
currentTransitionData);
|
||||
obs_frontend_set_current_scene(source);
|
||||
if (switcher->transitionOverrideOverride) {
|
||||
restoreTransitionOverride(source,
|
||||
currentTransitionData);
|
||||
}
|
||||
|
||||
if (switcher->verbose) {
|
||||
blog(LOG_INFO, "switched scene");
|
||||
}
|
||||
|
||||
if (switcher->networkConfig.ShouldSendSceneChange()) {
|
||||
switcher->server.sendMessage(sceneSwitch);
|
||||
}
|
||||
}
|
||||
obs_source_release(currentSource);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
void switchPreviewScene(const OBSWeakSource &ws)
|
||||
{
|
||||
auto source = obs_weak_source_get_source(ws);
|
||||
obs_frontend_set_current_preview_scene(source);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
void SwitcherData::Start()
|
||||
{
|
||||
if (!(th && th->isRunning())) {
|
||||
ResetForNextInterval();
|
||||
ResetMacros();
|
||||
|
||||
stop = false;
|
||||
th = new SwitcherThread();
|
||||
th->start((QThread::Priority)threadPriority);
|
||||
@@ -420,12 +443,7 @@ void SwitcherData::Start()
|
||||
}
|
||||
}
|
||||
|
||||
void ResetMacroCounters()
|
||||
{
|
||||
for (auto &m : switcher->macros) {
|
||||
m->ResetCount();
|
||||
}
|
||||
}
|
||||
bool CloseAllInputDialogs();
|
||||
|
||||
void SwitcherData::Stop()
|
||||
{
|
||||
@@ -435,12 +453,24 @@ void SwitcherData::Stop()
|
||||
abortMacroWait = true;
|
||||
macroWaitCv.notify_all();
|
||||
macroTransitionCv.notify_all();
|
||||
th->wait();
|
||||
delete th;
|
||||
th = nullptr;
|
||||
|
||||
// Not waiting if a dialog was closed is a workaround to avoid
|
||||
// deadlocks when a variable input dialog is opened while Stop()
|
||||
// is called.
|
||||
//
|
||||
// The QEventLoop handling the dialog would not exit until Stop()
|
||||
// would return, which itself would wait for the switcher thread
|
||||
// to complete, which itself is blocked by the variable input
|
||||
// dialog.
|
||||
//
|
||||
// Must be reworked in the future, as this will not go well when
|
||||
// OBS is shutting down and a dialog is still opened.
|
||||
if (!CloseAllInputDialogs()) {
|
||||
th->wait();
|
||||
delete th;
|
||||
th = nullptr;
|
||||
}
|
||||
writeToStatusFile("Advanced Scene Switcher stopped");
|
||||
ResetMacroCounters();
|
||||
}
|
||||
|
||||
server.stop();
|
||||
@@ -453,13 +483,13 @@ void SwitcherData::Stop()
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::setWaitScene()
|
||||
void SwitcherData::SetWaitScene()
|
||||
{
|
||||
waitScene = obs_frontend_get_current_scene();
|
||||
obs_source_release(waitScene);
|
||||
}
|
||||
|
||||
bool SwitcherData::sceneChangedDuringWait()
|
||||
bool SwitcherData::SceneChangedDuringWait()
|
||||
{
|
||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||
if (!currentSource) {
|
||||
@@ -472,7 +502,7 @@ bool SwitcherData::sceneChangedDuringWait()
|
||||
// Relies on the fact that switcher->currentScene will only be updated on event
|
||||
// OBS_FRONTEND_EVENT_SCENE_CHANGED but obs_frontend_get_current_scene() will
|
||||
// already return the scene to be transitioned to.
|
||||
bool SwitcherData::anySceneTransitionStarted()
|
||||
bool SwitcherData::AnySceneTransitionStarted()
|
||||
{
|
||||
auto currentSceneSrouce = obs_frontend_get_current_scene();
|
||||
auto currentScene = obs_source_get_weak_source(currentSceneSrouce);
|
||||
@@ -493,13 +523,13 @@ extern "C" void FreeSceneSwitcher()
|
||||
switcher = nullptr;
|
||||
}
|
||||
|
||||
void handleSceneChange()
|
||||
static void handleSceneChange()
|
||||
{
|
||||
switcher->lastSceneChangeTime =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
|
||||
// Stop waiting if scene was changed
|
||||
if (switcher->sceneChangedDuringWait()) {
|
||||
if (switcher->SceneChangedDuringWait()) {
|
||||
switcher->cv.notify_one();
|
||||
}
|
||||
|
||||
@@ -527,31 +557,33 @@ void handleSceneChange()
|
||||
}
|
||||
}
|
||||
|
||||
void setLiveTime()
|
||||
static void setLiveTime()
|
||||
{
|
||||
switcher->liveTime = QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
void resetLiveTime()
|
||||
static void resetLiveTime()
|
||||
{
|
||||
switcher->liveTime = QDateTime();
|
||||
}
|
||||
|
||||
void checkAutoStartRecording()
|
||||
static void checkAutoStartRecording()
|
||||
{
|
||||
if (switcher->autoStartEvent == AutoStartEvent::RECORDING ||
|
||||
switcher->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
|
||||
if (switcher->autoStartEvent == SwitcherData::AutoStart::RECORDING ||
|
||||
switcher->autoStartEvent ==
|
||||
SwitcherData::AutoStart::RECORINDG_OR_STREAMING)
|
||||
switcher->Start();
|
||||
}
|
||||
|
||||
void checkAutoStartStreaming()
|
||||
static void checkAutoStartStreaming()
|
||||
{
|
||||
if (switcher->autoStartEvent == AutoStartEvent::STREAMING ||
|
||||
switcher->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
|
||||
if (switcher->autoStartEvent == SwitcherData::AutoStart::STREAMING ||
|
||||
switcher->autoStartEvent ==
|
||||
SwitcherData::AutoStart::RECORINDG_OR_STREAMING)
|
||||
switcher->Start();
|
||||
}
|
||||
|
||||
void handlePeviewSceneChange()
|
||||
static void handlePeviewSceneChange()
|
||||
{
|
||||
if (switcher->networkConfig.ShouldSendPrviewSceneChange()) {
|
||||
auto source = obs_frontend_get_current_preview_scene();
|
||||
@@ -562,40 +594,59 @@ void handlePeviewSceneChange()
|
||||
}
|
||||
}
|
||||
|
||||
void setReplayBufferSaved()
|
||||
static void setReplayBufferSaved()
|
||||
{
|
||||
switcher->replayBufferSaved = true;
|
||||
}
|
||||
|
||||
void setTranstionEnd()
|
||||
static void setTranstionEnd()
|
||||
{
|
||||
switcher->lastTransitionEndTime =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
switcher->macroTransitionCv.notify_all();
|
||||
}
|
||||
|
||||
void setStreamStarting()
|
||||
static void setStreamStarting()
|
||||
{
|
||||
switcher->lastStreamStartingTime =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void setStreamStopping()
|
||||
static void setStreamStopping()
|
||||
{
|
||||
switcher->lastStreamStoppingTime =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void handleExit()
|
||||
static void handleShutdown()
|
||||
{
|
||||
switcher->obsIsShuttingDown = true;
|
||||
if (switcher && switcher->obsIsShuttingDown &&
|
||||
switcher->shutdownConditionCount) {
|
||||
switcher->Stop();
|
||||
switcher->checkMacros();
|
||||
switcher->runMacros();
|
||||
if (!switcher) {
|
||||
return;
|
||||
}
|
||||
switcher->obsIsShuttingDown = true;
|
||||
if (switcher->shutdownConditionCount) {
|
||||
switcher->Stop();
|
||||
switcher->CheckMacros();
|
||||
switcher->RunMacros();
|
||||
|
||||
// Unfortunately this will not work as OBS will now allow saving
|
||||
// the scene collection data at this point, So any OBS specific
|
||||
// changes done during shutdown will be lost
|
||||
//
|
||||
// TODO: Look for a way to possibly resolve this
|
||||
obs_frontend_save();
|
||||
}
|
||||
}
|
||||
|
||||
static void handleSceneCollectionChanging()
|
||||
{
|
||||
if (switcher->settingsWindowOpened) {
|
||||
AdvSceneSwitcher::window->close();
|
||||
}
|
||||
if (!switcher->stop) {
|
||||
switcher->sceneColletionStop = true;
|
||||
switcher->Stop();
|
||||
}
|
||||
FreeSceneSwitcher();
|
||||
}
|
||||
|
||||
// Note to future self:
|
||||
@@ -608,8 +659,12 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case OBS_FRONTEND_EVENT_EXIT:
|
||||
handleExit();
|
||||
case OBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN:
|
||||
// Note: We are intentionally not listening for
|
||||
// OBS_FRONTEND_EVENT_EXIT here as at that point all scene
|
||||
// collection data will already have been cleared and thus all
|
||||
// macros will have already been cleared
|
||||
handleShutdown();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
||||
handleSceneChange();
|
||||
@@ -643,12 +698,20 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||
case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
|
||||
setStreamStopping();
|
||||
break;
|
||||
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 2, 0)
|
||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:
|
||||
handleSceneCollectionChanging();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP:
|
||||
SaveSceneSwitcher(nullptr, false, nullptr);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadPlugins()
|
||||
static void LoadPlugins()
|
||||
{
|
||||
QFileInfo libPath(
|
||||
QString(obs_get_module_binary_path(obs_current_module())));
|
||||
@@ -672,42 +735,43 @@ void LoadPlugins()
|
||||
blog(LOG_WARNING, "failed to load \"%s\": %s",
|
||||
file.toStdString().c_str(),
|
||||
lib->errorString().toStdString().c_str());
|
||||
switcher->loadFailureLibs << file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void InitSceneSwitcher(obs_module_t *m, translateFunc t)
|
||||
void OpenSettingsWindow()
|
||||
{
|
||||
if (switcher->settingsWindowOpened) {
|
||||
AdvSceneSwitcher::window->show();
|
||||
AdvSceneSwitcher::window->raise();
|
||||
AdvSceneSwitcher::window->activateWindow();
|
||||
} else {
|
||||
AdvSceneSwitcher::window =
|
||||
new AdvSceneSwitcher(static_cast<QMainWindow *>(
|
||||
obs_frontend_get_main_window()));
|
||||
AdvSceneSwitcher::window->setAttribute(Qt::WA_DeleteOnClose);
|
||||
AdvSceneSwitcher::window->show();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void InitSceneSwitcher(obs_module_t *module, translateFunc translate)
|
||||
{
|
||||
blog(LOG_INFO, "version: %s", g_GIT_TAG);
|
||||
blog(LOG_INFO, "version: %s", g_GIT_SHA1);
|
||||
|
||||
switcher = new SwitcherData;
|
||||
switcher->modulePtr = m;
|
||||
switcher->translate = t;
|
||||
switcher = new SwitcherData(module, translate);
|
||||
|
||||
PlatformInit();
|
||||
LoadPlugins();
|
||||
SetupDock();
|
||||
|
||||
auto cb = []() {
|
||||
if (switcher->settingsWindowOpened) {
|
||||
AdvSceneSwitcher::window->show();
|
||||
AdvSceneSwitcher::window->raise();
|
||||
AdvSceneSwitcher::window->activateWindow();
|
||||
} else {
|
||||
AdvSceneSwitcher::window =
|
||||
new AdvSceneSwitcher(static_cast<QMainWindow *>(
|
||||
obs_frontend_get_main_window()));
|
||||
AdvSceneSwitcher::window->setAttribute(
|
||||
Qt::WA_DeleteOnClose);
|
||||
AdvSceneSwitcher::window->show();
|
||||
}
|
||||
};
|
||||
|
||||
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
|
||||
obs_frontend_add_event_callback(OBSEvent, switcher);
|
||||
|
||||
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
|
||||
obs_module_text("AdvSceneSwitcher.pluginName"));
|
||||
action->connect(action, &QAction::triggered, cb);
|
||||
action->connect(action, &QAction::triggered, OpenSettingsWindow);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
#pragma once
|
||||
#include "switcher-data-structs.hpp"
|
||||
#include "platform-funcs.hpp"
|
||||
#include "macro-segment-list.hpp"
|
||||
#include "log-helper.hpp"
|
||||
|
||||
#include <macro-segment-list.hpp>
|
||||
#include <ui_advanced-scene-switcher.h>
|
||||
|
||||
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
|
||||
#define vblog(level, msg, ...) \
|
||||
if (switcher->verbose) { \
|
||||
blog(level, msg, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
class QCloseEvent;
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroActionEdit;
|
||||
class MacroConditionEdit;
|
||||
class Duration;
|
||||
class SequenceWidget;
|
||||
struct SceneGroup;
|
||||
|
||||
/*******************************************************************************
|
||||
* Advanced Scene Switcher window
|
||||
@@ -33,23 +32,58 @@ public:
|
||||
void reject() override;
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
void SetShowFrames();
|
||||
void SetHideFrames();
|
||||
void clearFrames(QListWidget *list);
|
||||
void LoadUI();
|
||||
|
||||
int IgnoreWindowsFindByData(const QString &window);
|
||||
int IgnoreIdleWindowsFindByData(const QString &window);
|
||||
void SetTabOrder();
|
||||
void SetCurrentTab();
|
||||
void RestoreWindowGeo();
|
||||
void CheckFirstTimeSetup();
|
||||
|
||||
/* --- Begin of general tab section --- */
|
||||
public:
|
||||
void SetupGeneralTab();
|
||||
void UpdateNonMatchingScene(const QString &name);
|
||||
void OpenSequenceExtendEdit(SequenceWidget *sw);
|
||||
void SetEditSceneGroup(SceneGroup &sg);
|
||||
void SetDeprecationWarnings();
|
||||
|
||||
bool addNewMacro(std::string &name, std::string format = "");
|
||||
Macro *getSelectedMacro();
|
||||
public slots:
|
||||
void on_noMatchDontSwitch_clicked();
|
||||
void on_noMatchSwitch_clicked();
|
||||
void on_noMatchRandomSwitch_clicked();
|
||||
void NoMatchDelayDurationChanged(const Duration &);
|
||||
void CooldownDurationChanged(const Duration &);
|
||||
void on_startupBehavior_currentIndexChanged(int index);
|
||||
void on_autoStartEvent_currentIndexChanged(int index);
|
||||
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
|
||||
void on_checkInterval_valueChanged(int value);
|
||||
void on_tabMoved(int from, int to);
|
||||
void on_tabWidget_currentChanged(int index);
|
||||
void on_exportSettings_clicked();
|
||||
void on_importSettings_clicked();
|
||||
void on_verboseLogging_stateChanged(int state);
|
||||
void on_saveWindowGeo_stateChanged(int state);
|
||||
void on_showTrayNotifications_stateChanged(int state);
|
||||
void on_uiHintsDisable_stateChanged(int state);
|
||||
void on_warnPluginLoadFailure_stateChanged(int state);
|
||||
void on_hideLegacyTabs_stateChanged(int state);
|
||||
void on_priorityUp_clicked();
|
||||
void on_priorityDown_clicked();
|
||||
void on_threadPriority_currentTextChanged(const QString &text);
|
||||
|
||||
/* --- End of legacy tab section --- */
|
||||
|
||||
/* --- Begin of macro tab section --- */
|
||||
public:
|
||||
void SetupMacroTab();
|
||||
bool AddNewMacro(std::shared_ptr<Macro> &res, std::string &name,
|
||||
std::string format = "");
|
||||
void RemoveMacro(std::shared_ptr<Macro> &);
|
||||
void RenameMacro(std::shared_ptr<Macro> &, const QString &name);
|
||||
std::shared_ptr<Macro> GetSelectedMacro();
|
||||
std::vector<std::shared_ptr<Macro>> GetSelectedMacros();
|
||||
void SetEditMacro(Macro &m);
|
||||
void SetMacroEditAreaDisabled(bool);
|
||||
void HighlightAction(int idx);
|
||||
void HighlightCondition(int idx);
|
||||
void HighlightAction(int idx, QColor color = QColor(Qt::green));
|
||||
void HighlightCondition(int idx, QColor color = QColor(Qt::green));
|
||||
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
|
||||
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
|
||||
void SetActionData(Macro &m);
|
||||
@@ -57,67 +91,7 @@ public:
|
||||
void SwapActions(Macro *m, int pos1, int pos2);
|
||||
void SwapConditions(Macro *m, int pos1, int pos2);
|
||||
|
||||
void loadUI();
|
||||
void setupGeneralTab();
|
||||
void setupTitleTab();
|
||||
void setupExecutableTab();
|
||||
void setupRegionTab();
|
||||
void setupPauseTab();
|
||||
void setupSequenceTab();
|
||||
void setupTransitionsTab();
|
||||
void setupIdleTab();
|
||||
void setupRandomTab();
|
||||
void setupMediaTab();
|
||||
void setupFileTab();
|
||||
void setupTimeTab();
|
||||
void setupAudioTab();
|
||||
void setupSceneGroupTab();
|
||||
void setupTriggerTab();
|
||||
void setupVideoTab();
|
||||
void setupNetworkTab();
|
||||
void setupMacroTab();
|
||||
void setDeprecationWarnings();
|
||||
void setTabOrder();
|
||||
void setCurrentTab();
|
||||
void restoreWindowGeo();
|
||||
void checkFirstTimeSetup();
|
||||
|
||||
signals:
|
||||
void MacroAdded(const QString &name);
|
||||
void MacroRemoved(const QString &name);
|
||||
void MacroRenamed(const QString &oldName, const QString newName);
|
||||
void HighlightMacrosChanged(bool value);
|
||||
void HighlightActionsChanged(bool value);
|
||||
void HighlightConditionsChanged(bool value);
|
||||
void SceneGroupAdded(const QString &name);
|
||||
void SceneGroupRemoved(const QString &name);
|
||||
void SceneGroupRenamed(const QString &oldName, const QString newName);
|
||||
void ConnectionAdded(const QString &);
|
||||
void ConnectionRenamed(const QString &oldName, const QString &newName);
|
||||
void ConnectionRemoved(const QString &);
|
||||
void VariableAdded(const QString &);
|
||||
void VariableRenamed(const QString &oldName, const QString &newName);
|
||||
void VariableRemoved(const QString &);
|
||||
|
||||
public slots:
|
||||
void on_windowUp_clicked();
|
||||
void on_windowDown_clicked();
|
||||
void on_windowAdd_clicked();
|
||||
void on_windowRemove_clicked();
|
||||
void on_noMatchDontSwitch_clicked();
|
||||
void on_noMatchSwitch_clicked();
|
||||
void on_noMatchRandomSwitch_clicked();
|
||||
void NoMatchDelayDurationChanged(double);
|
||||
void NoMatchDelayUnitChanged(DurationUnit);
|
||||
void CooldownDurationChanged(double);
|
||||
void CooldownUnitChanged(DurationUnit);
|
||||
void on_startupBehavior_currentIndexChanged(int index);
|
||||
void on_autoStartEvent_currentIndexChanged(int index);
|
||||
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
|
||||
void on_checkInterval_valueChanged(int value);
|
||||
void on_tabMoved(int from, int to);
|
||||
void on_tabWidget_currentChanged(int index);
|
||||
|
||||
void on_macroAdd_clicked();
|
||||
void on_macroRemove_clicked();
|
||||
void on_macroUp_clicked();
|
||||
@@ -126,15 +100,19 @@ public slots:
|
||||
void on_runMacro_clicked();
|
||||
void on_runMacroInParallel_stateChanged(int value);
|
||||
void on_runMacroOnChange_stateChanged(int value);
|
||||
void on_macros_currentRowChanged(int idx);
|
||||
void on_conditionAdd_clicked();
|
||||
void on_conditionRemove_clicked();
|
||||
void on_conditionTop_clicked();
|
||||
void on_conditionUp_clicked();
|
||||
void on_conditionDown_clicked();
|
||||
void on_conditionBottom_clicked();
|
||||
void on_actionAdd_clicked();
|
||||
void on_actionRemove_clicked();
|
||||
void on_actionTop_clicked();
|
||||
void on_actionUp_clicked();
|
||||
void on_actionDown_clicked();
|
||||
void on_actionBottom_clicked();
|
||||
void MacroSelectionChanged();
|
||||
void UpMacroSegementHotkey();
|
||||
void DownMacroSegementHotkey();
|
||||
void DeleteMacroSegementHotkey();
|
||||
@@ -142,6 +120,9 @@ public slots:
|
||||
void ShowMacroActionsContextMenu(const QPoint &);
|
||||
void ShowMacroConditionsContextMenu(const QPoint &);
|
||||
void CopyMacro();
|
||||
void RenameCurrentMacro();
|
||||
void ExportMacros();
|
||||
void ImportMacros();
|
||||
void ExpandAllActions();
|
||||
void ExpandAllConditions();
|
||||
void CollapseAllActions();
|
||||
@@ -165,141 +146,27 @@ public slots:
|
||||
void ResetOpacityActionControls();
|
||||
void ResetOpacityConditionControls();
|
||||
void HighlightControls();
|
||||
void MacroDragDropReorder(QModelIndex, int, int, QModelIndex, int);
|
||||
void HighlightOnChange();
|
||||
void on_macroProperties_clicked();
|
||||
|
||||
void on_screenRegionSwitches_currentRowChanged(int idx);
|
||||
void on_showFrame_clicked();
|
||||
void on_screenRegionAdd_clicked();
|
||||
void on_screenRegionRemove_clicked();
|
||||
void on_screenRegionUp_clicked();
|
||||
void on_screenRegionDown_clicked();
|
||||
signals:
|
||||
void MacroAdded(const QString &name);
|
||||
void MacroRemoved(const QString &name);
|
||||
void MacroRenamed(const QString &oldName, const QString &newName);
|
||||
void MacroSegmentOrderChanged();
|
||||
void HighlightMacrosChanged(bool value);
|
||||
void HighlightActionsChanged(bool value);
|
||||
void HighlightConditionsChanged(bool value);
|
||||
|
||||
void on_pauseUp_clicked();
|
||||
void on_pauseDown_clicked();
|
||||
void on_pauseAdd_clicked();
|
||||
void on_pauseRemove_clicked();
|
||||
|
||||
void on_ignoreWindows_currentRowChanged(int idx);
|
||||
void on_ignoreWindowsAdd_clicked();
|
||||
void on_ignoreWindowsRemove_clicked();
|
||||
|
||||
void on_sceneSequenceAdd_clicked();
|
||||
void on_sceneSequenceRemove_clicked();
|
||||
void on_sceneSequenceUp_clicked();
|
||||
void on_sceneSequenceDown_clicked();
|
||||
void on_sceneSequenceSave_clicked();
|
||||
void on_sceneSequenceLoad_clicked();
|
||||
void on_sequenceEdit_clicked();
|
||||
void on_sceneSequenceSwitches_itemDoubleClicked(QListWidgetItem *item);
|
||||
|
||||
void on_verboseLogging_stateChanged(int state);
|
||||
void on_saveWindowGeo_stateChanged(int state);
|
||||
void on_showTrayNotifications_stateChanged(int state);
|
||||
void on_uiHintsDisable_stateChanged(int state);
|
||||
void on_hideLegacyTabs_stateChanged(int state);
|
||||
|
||||
void on_exportSettings_clicked();
|
||||
void on_importSettings_clicked();
|
||||
|
||||
void on_transitionsAdd_clicked();
|
||||
void on_transitionsRemove_clicked();
|
||||
void on_transitionsUp_clicked();
|
||||
void on_transitionsDown_clicked();
|
||||
void on_defaultTransitionsAdd_clicked();
|
||||
void on_defaultTransitionsRemove_clicked();
|
||||
void on_defaultTransitionsUp_clicked();
|
||||
void on_defaultTransitionsDown_clicked();
|
||||
void on_transitionOverridecheckBox_stateChanged(int state);
|
||||
void on_adjustActiveTransitionType_stateChanged(int state);
|
||||
void defTransitionDelayValueChanged(int value);
|
||||
|
||||
void on_browseButton_clicked();
|
||||
void on_readFileCheckBox_stateChanged(int state);
|
||||
void on_readPathLineEdit_textChanged(const QString &text);
|
||||
void on_writePathLineEdit_textChanged(const QString &text);
|
||||
void on_browseButton_2_clicked();
|
||||
|
||||
void on_executableUp_clicked();
|
||||
void on_executableDown_clicked();
|
||||
void on_executableAdd_clicked();
|
||||
void on_executableRemove_clicked();
|
||||
|
||||
void on_idleCheckBox_stateChanged(int state);
|
||||
void on_ignoreIdleWindows_currentRowChanged(int idx);
|
||||
void on_ignoreIdleAdd_clicked();
|
||||
void on_ignoreIdleRemove_clicked();
|
||||
|
||||
void on_randomAdd_clicked();
|
||||
void on_randomRemove_clicked();
|
||||
|
||||
void on_fileAdd_clicked();
|
||||
void on_fileRemove_clicked();
|
||||
void on_fileSwitches_currentRowChanged(int idx);
|
||||
void on_fileUp_clicked();
|
||||
void on_fileDown_clicked();
|
||||
|
||||
void on_mediaAdd_clicked();
|
||||
void on_mediaRemove_clicked();
|
||||
void on_mediaUp_clicked();
|
||||
void on_mediaDown_clicked();
|
||||
|
||||
void on_timeAdd_clicked();
|
||||
void on_timeRemove_clicked();
|
||||
void on_timeUp_clicked();
|
||||
void on_timeDown_clicked();
|
||||
|
||||
void on_audioAdd_clicked();
|
||||
void on_audioRemove_clicked();
|
||||
void on_audioUp_clicked();
|
||||
void on_audioDown_clicked();
|
||||
void on_audioFallback_toggled(bool on);
|
||||
|
||||
void on_videoAdd_clicked();
|
||||
void on_videoRemove_clicked();
|
||||
void on_videoUp_clicked();
|
||||
void on_videoDown_clicked();
|
||||
void on_getScreenshot_clicked();
|
||||
|
||||
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();
|
||||
|
||||
void on_sceneGroupAdd_clicked();
|
||||
void on_sceneGroupRemove_clicked();
|
||||
void on_sceneGroupUp_clicked();
|
||||
void on_sceneGroupDown_clicked();
|
||||
void on_sceneGroupName_editingFinished();
|
||||
void on_sceneGroups_currentRowChanged(int idx);
|
||||
|
||||
void on_sceneGroupSceneAdd_clicked();
|
||||
void on_sceneGroupSceneRemove_clicked();
|
||||
void on_sceneGroupSceneUp_clicked();
|
||||
void on_sceneGroupSceneDown_clicked();
|
||||
|
||||
void on_triggerAdd_clicked();
|
||||
void on_triggerRemove_clicked();
|
||||
void on_triggerUp_clicked();
|
||||
void on_triggerDown_clicked();
|
||||
|
||||
void on_priorityUp_clicked();
|
||||
void on_priorityDown_clicked();
|
||||
void on_threadPriority_currentTextChanged(const QString &text);
|
||||
|
||||
void updateScreenRegionCursorPos();
|
||||
void ConnectionAdded(const QString &);
|
||||
void ConnectionRenamed(const QString &oldName, const QString &newName);
|
||||
void ConnectionRemoved(const QString &);
|
||||
void VariableAdded(const QString &);
|
||||
void VariableRenamed(const QString &oldName, const QString &newName);
|
||||
void VariableRemoved(const QString &);
|
||||
|
||||
private:
|
||||
bool ResolveMacroImportNameConflict(std::shared_ptr<Macro> &);
|
||||
bool MacroTabIsInFocus();
|
||||
|
||||
MacroSegmentList *conditionsList = nullptr;
|
||||
@@ -312,28 +179,209 @@ private:
|
||||
MacroSection lastInteracted = MacroSection::CONDITIONS;
|
||||
int currentConditionIdx = -1;
|
||||
int currentActionIdx = -1;
|
||||
|
||||
/* --- End of macro tab section --- */
|
||||
|
||||
/* --- Begin of legacy tab section --- */
|
||||
public:
|
||||
void ClearFrames(QListWidget *list);
|
||||
int IgnoreWindowsFindByData(const QString &window);
|
||||
int IgnoreIdleWindowsFindByData(const QString &window);
|
||||
void OpenSequenceExtendEdit(SequenceWidget *sw);
|
||||
|
||||
// Window tab
|
||||
public:
|
||||
void SetupTitleTab();
|
||||
public slots:
|
||||
void on_windowUp_clicked();
|
||||
void on_windowDown_clicked();
|
||||
void on_windowAdd_clicked();
|
||||
void on_windowRemove_clicked();
|
||||
void on_ignoreWindows_currentRowChanged(int idx);
|
||||
void on_ignoreWindowsAdd_clicked();
|
||||
void on_ignoreWindowsRemove_clicked();
|
||||
|
||||
// Screen region tab
|
||||
public:
|
||||
void SetupRegionTab();
|
||||
public slots:
|
||||
void SetShowFrames();
|
||||
void SetHideFrames();
|
||||
void on_screenRegionSwitches_currentRowChanged(int idx);
|
||||
void on_showFrame_clicked();
|
||||
void on_screenRegionAdd_clicked();
|
||||
void on_screenRegionRemove_clicked();
|
||||
void on_screenRegionUp_clicked();
|
||||
void on_screenRegionDown_clicked();
|
||||
void updateScreenRegionCursorPos();
|
||||
|
||||
// Pause tab
|
||||
public:
|
||||
void SetupPauseTab();
|
||||
public slots:
|
||||
void on_pauseUp_clicked();
|
||||
void on_pauseDown_clicked();
|
||||
void on_pauseAdd_clicked();
|
||||
void on_pauseRemove_clicked();
|
||||
|
||||
// Sequence tab
|
||||
public:
|
||||
void SetupSequenceTab();
|
||||
public slots:
|
||||
void on_sceneSequenceAdd_clicked();
|
||||
void on_sceneSequenceRemove_clicked();
|
||||
void on_sceneSequenceUp_clicked();
|
||||
void on_sceneSequenceDown_clicked();
|
||||
void on_sceneSequenceSave_clicked();
|
||||
void on_sceneSequenceLoad_clicked();
|
||||
void on_sequenceEdit_clicked();
|
||||
void on_sceneSequenceSwitches_itemDoubleClicked(QListWidgetItem *item);
|
||||
|
||||
// Transition tab
|
||||
public:
|
||||
void SetupTransitionsTab();
|
||||
public slots:
|
||||
void on_transitionsAdd_clicked();
|
||||
void on_transitionsRemove_clicked();
|
||||
void on_transitionsUp_clicked();
|
||||
void on_transitionsDown_clicked();
|
||||
void on_defaultTransitionsAdd_clicked();
|
||||
void on_defaultTransitionsRemove_clicked();
|
||||
void on_defaultTransitionsUp_clicked();
|
||||
void on_defaultTransitionsDown_clicked();
|
||||
void on_transitionOverridecheckBox_stateChanged(int state);
|
||||
void on_adjustActiveTransitionType_stateChanged(int state);
|
||||
void DefTransitionDelayValueChanged(int value);
|
||||
|
||||
// Executable tab
|
||||
public:
|
||||
void SetupExecutableTab();
|
||||
public slots:
|
||||
void on_executableUp_clicked();
|
||||
void on_executableDown_clicked();
|
||||
void on_executableAdd_clicked();
|
||||
void on_executableRemove_clicked();
|
||||
|
||||
// Idle tab
|
||||
public:
|
||||
void SetupIdleTab();
|
||||
public slots:
|
||||
void on_idleCheckBox_stateChanged(int state);
|
||||
void on_ignoreIdleWindows_currentRowChanged(int idx);
|
||||
void on_ignoreIdleAdd_clicked();
|
||||
void on_ignoreIdleRemove_clicked();
|
||||
|
||||
// Random tab
|
||||
public:
|
||||
void SetupRandomTab();
|
||||
public slots:
|
||||
void on_randomAdd_clicked();
|
||||
void on_randomRemove_clicked();
|
||||
|
||||
// File tab
|
||||
public:
|
||||
void SetupFileTab();
|
||||
public slots:
|
||||
void on_fileAdd_clicked();
|
||||
void on_fileRemove_clicked();
|
||||
void on_fileSwitches_currentRowChanged(int idx);
|
||||
void on_fileUp_clicked();
|
||||
void on_fileDown_clicked();
|
||||
void on_browseButton_clicked();
|
||||
void on_readFileCheckBox_stateChanged(int state);
|
||||
void on_readPathLineEdit_textChanged(const QString &text);
|
||||
void on_writePathLineEdit_textChanged(const QString &text);
|
||||
void on_browseButton_2_clicked();
|
||||
|
||||
// Media tab
|
||||
public:
|
||||
void SetupMediaTab();
|
||||
public slots:
|
||||
void on_mediaAdd_clicked();
|
||||
void on_mediaRemove_clicked();
|
||||
void on_mediaUp_clicked();
|
||||
void on_mediaDown_clicked();
|
||||
|
||||
// Time tab
|
||||
public:
|
||||
void SetupTimeTab();
|
||||
public slots:
|
||||
void on_timeAdd_clicked();
|
||||
void on_timeRemove_clicked();
|
||||
void on_timeUp_clicked();
|
||||
void on_timeDown_clicked();
|
||||
|
||||
// Video tab
|
||||
public:
|
||||
void SetupAudioTab();
|
||||
public slots:
|
||||
void on_audioAdd_clicked();
|
||||
void on_audioRemove_clicked();
|
||||
void on_audioUp_clicked();
|
||||
void on_audioDown_clicked();
|
||||
void on_audioFallback_toggled(bool on);
|
||||
|
||||
// Video tab
|
||||
public:
|
||||
void SetupVideoTab();
|
||||
public slots:
|
||||
void on_videoAdd_clicked();
|
||||
void on_videoRemove_clicked();
|
||||
void on_videoUp_clicked();
|
||||
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();
|
||||
void SetEditSceneGroup(SceneGroup &sg);
|
||||
public slots:
|
||||
void on_sceneGroupAdd_clicked();
|
||||
void on_sceneGroupRemove_clicked();
|
||||
void on_sceneGroupUp_clicked();
|
||||
void on_sceneGroupDown_clicked();
|
||||
void on_sceneGroupName_editingFinished();
|
||||
void on_sceneGroups_currentRowChanged(int idx);
|
||||
void on_sceneGroupSceneAdd_clicked();
|
||||
void on_sceneGroupSceneRemove_clicked();
|
||||
void on_sceneGroupSceneUp_clicked();
|
||||
void on_sceneGroupSceneDown_clicked();
|
||||
|
||||
signals:
|
||||
void SceneGroupAdded(const QString &name);
|
||||
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 --- */
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* Sceneswitch helper
|
||||
******************************************************************************/
|
||||
void OpenSettingsWindow();
|
||||
|
||||
void setNextTransition(const sceneSwitchInfo &ssi, obs_source_t *currentSource,
|
||||
transitionData &td);
|
||||
void overwriteTransitionOverride(const sceneSwitchInfo &ssi,
|
||||
transitionData &td);
|
||||
void restoreTransitionOverride(obs_source_t *scene, const transitionData &td);
|
||||
void switchScene(const sceneSwitchInfo &ssi, bool force = false);
|
||||
void switchPreviewScene(const OBSWeakSource &ws);
|
||||
|
||||
/******************************************************************************
|
||||
* Settings helper
|
||||
******************************************************************************/
|
||||
void AskForBackup(obs_data_t *obj);
|
||||
|
||||
/******************************************************************************
|
||||
* Main SwitcherData
|
||||
******************************************************************************/
|
||||
struct SwitcherData;
|
||||
extern SwitcherData *switcher;
|
||||
SwitcherData *GetSwitcher();
|
||||
} // namespace advss
|
||||
|
||||
491
src/general.cpp
@@ -1,10 +1,13 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "status-control.hpp"
|
||||
#include "file-selection.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "version.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QStandardPaths>
|
||||
|
||||
namespace advss {
|
||||
|
||||
void AdvSceneSwitcher::reject()
|
||||
{
|
||||
@@ -29,7 +32,7 @@ void AdvSceneSwitcher::on_noMatchDontSwitch_clicked()
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->switchIfNotMatching = NO_SWITCH;
|
||||
switcher->switchIfNotMatching = SwitcherData::NoMatch::NO_SWITCH;
|
||||
ui->noMatchSwitchScene->setEnabled(false);
|
||||
ui->randomDisabledWarning->setVisible(true);
|
||||
}
|
||||
@@ -41,7 +44,7 @@ void AdvSceneSwitcher::on_noMatchSwitch_clicked()
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->switchIfNotMatching = SWITCH;
|
||||
switcher->switchIfNotMatching = SwitcherData::NoMatch::SWITCH;
|
||||
ui->noMatchSwitchScene->setEnabled(true);
|
||||
UpdateNonMatchingScene(ui->noMatchSwitchScene->currentText());
|
||||
ui->randomDisabledWarning->setVisible(true);
|
||||
@@ -54,49 +57,29 @@ void AdvSceneSwitcher::on_noMatchRandomSwitch_clicked()
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->switchIfNotMatching = RANDOM_SWITCH;
|
||||
switcher->switchIfNotMatching = SwitcherData::NoMatch::RANDOM_SWITCH;
|
||||
ui->noMatchSwitchScene->setEnabled(false);
|
||||
ui->randomDisabledWarning->setVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::NoMatchDelayDurationChanged(double sec)
|
||||
void AdvSceneSwitcher::NoMatchDelayDurationChanged(const Duration &dur)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->noMatchDelay.seconds = sec;
|
||||
switcher->noMatchDelay = dur;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::NoMatchDelayUnitChanged(DurationUnit unit)
|
||||
void AdvSceneSwitcher::CooldownDurationChanged(const Duration &dur)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->noMatchDelay.displayUnit = unit;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::CooldownDurationChanged(double sec)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->cooldown.seconds = sec;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::CooldownUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->cooldown.displayUnit = unit;
|
||||
switcher->cooldown = dur;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
||||
@@ -106,7 +89,8 @@ void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->startupBehavior = (StartupBehavior)index;
|
||||
switcher->startupBehavior =
|
||||
static_cast<SwitcherData::StartupBehavior>(index);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_autoStartEvent_currentIndexChanged(int index)
|
||||
@@ -116,7 +100,7 @@ void AdvSceneSwitcher::on_autoStartEvent_currentIndexChanged(int index)
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->autoStartEvent = static_cast<AutoStartEvent>(index);
|
||||
switcher->autoStartEvent = static_cast<SwitcherData::AutoStart>(index);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_noMatchSwitchScene_currentTextChanged(
|
||||
@@ -151,6 +135,7 @@ void AdvSceneSwitcher::closeEvent(QCloseEvent *)
|
||||
ui->macroActionConditionSplitter->sizes();
|
||||
switcher->macroListMacroEditSplitterPosition =
|
||||
ui->macroListMacroEditSplitter->sizes();
|
||||
|
||||
obs_frontend_save();
|
||||
}
|
||||
|
||||
@@ -190,7 +175,16 @@ void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
|
||||
switcher->disableHints = state;
|
||||
}
|
||||
|
||||
bool isLegacyTab(const QString &name)
|
||||
void AdvSceneSwitcher::on_warnPluginLoadFailure_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
switcher->warnPluginLoadFailure = state;
|
||||
}
|
||||
|
||||
static bool isLegacyTab(const QString &name)
|
||||
{
|
||||
return name == obs_module_text(
|
||||
"AdvSceneSwitcher.sceneGroupTab.title") ||
|
||||
@@ -235,7 +229,7 @@ void AdvSceneSwitcher::on_hideLegacyTabs_stateChanged(int state)
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setDeprecationWarnings()
|
||||
void AdvSceneSwitcher::SetDeprecationWarnings()
|
||||
{
|
||||
QString toolTip =
|
||||
switcher->disableHints
|
||||
@@ -249,53 +243,13 @@ void AdvSceneSwitcher::setDeprecationWarnings()
|
||||
}
|
||||
}
|
||||
|
||||
QString getDefaultSaveLocation()
|
||||
{
|
||||
QString desktopPath = QStandardPaths::writableLocation(
|
||||
QStandardPaths::DesktopLocation);
|
||||
auto scName = obs_frontend_get_current_scene_collection();
|
||||
QString sceneCollectionName(scName);
|
||||
bfree(scName);
|
||||
auto timestamp = QDateTime::currentDateTime();
|
||||
return desktopPath + "/adv-ss-" + sceneCollectionName + "-" +
|
||||
timestamp.toString("yyyy.MM.dd.hh.mm.ss");
|
||||
}
|
||||
|
||||
void AskForBackup(obs_data_t *obj)
|
||||
{
|
||||
bool backupSettings = DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.askBackup"), true);
|
||||
|
||||
if (!backupSettings) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getSaveFileName(
|
||||
nullptr,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle"),
|
||||
getDefaultSaveLocation(),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType"));
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
obs_data_save_json(obj, file.fileName().toUtf8().constData());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_exportSettings_clicked()
|
||||
{
|
||||
QString directory = QFileDialog::getSaveFileName(
|
||||
this,
|
||||
tr(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle")),
|
||||
getDefaultSaveLocation(),
|
||||
GetDefaultSettingsSaveLocation(),
|
||||
tr(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType")));
|
||||
if (directory.isEmpty()) {
|
||||
@@ -308,11 +262,8 @@ void AdvSceneSwitcher::on_exportSettings_clicked()
|
||||
}
|
||||
|
||||
obs_data_t *obj = obs_data_create();
|
||||
|
||||
switcher->saveSettings(obj);
|
||||
|
||||
switcher->SaveSettings(obj);
|
||||
obs_data_save_json(obj, file.fileName().toUtf8().constData());
|
||||
|
||||
obs_data_release(obj);
|
||||
}
|
||||
|
||||
@@ -323,20 +274,20 @@ void AdvSceneSwitcher::on_importSettings_clicked()
|
||||
bool start = !switcher->stop;
|
||||
switcher->Stop();
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
|
||||
QString directory = QFileDialog::getOpenFileName(
|
||||
auto basePath = FileSelection::ValidPathOrDesktop(
|
||||
QString::fromStdString(switcher->lastImportPath));
|
||||
QString path = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
tr(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle")),
|
||||
QDir::currentPath(),
|
||||
basePath,
|
||||
tr(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType")));
|
||||
if (directory.isEmpty()) {
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(directory);
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return;
|
||||
}
|
||||
@@ -350,12 +301,15 @@ void AdvSceneSwitcher::on_importSettings_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
switcher->loadSettings(obj);
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->LoadSettings(obj);
|
||||
obs_data_release(obj);
|
||||
switcher->lastImportPath = path.toStdString();
|
||||
|
||||
(void)DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess"));
|
||||
// Just close the UI and let the user reopen it to not have to
|
||||
// implement updating each setting
|
||||
close();
|
||||
|
||||
// Restart scene switcher if it was active
|
||||
@@ -364,7 +318,7 @@ void AdvSceneSwitcher::on_importSettings_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
int findTabIndex(QTabWidget *tabWidget, int pos)
|
||||
static int findTabIndex(QTabWidget *tabWidget, int pos)
|
||||
{
|
||||
int at = -1;
|
||||
|
||||
@@ -438,10 +392,10 @@ int findTabIndex(QTabWidget *tabWidget, int pos)
|
||||
return at;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setTabOrder()
|
||||
void AdvSceneSwitcher::SetTabOrder()
|
||||
{
|
||||
if (!switcher->tabOrderValid()) {
|
||||
switcher->resetTabOrder();
|
||||
if (!switcher->TabOrderValid()) {
|
||||
switcher->ResetTabOrder();
|
||||
}
|
||||
|
||||
QTabBar *bar = ui->tabWidget->tabBar();
|
||||
@@ -456,22 +410,22 @@ void AdvSceneSwitcher::setTabOrder()
|
||||
connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setCurrentTab()
|
||||
void AdvSceneSwitcher::SetCurrentTab()
|
||||
{
|
||||
if (switcher->lastOpenedTab >= 0) {
|
||||
ui->tabWidget->setCurrentIndex(switcher->lastOpenedTab);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::restoreWindowGeo()
|
||||
void AdvSceneSwitcher::RestoreWindowGeo()
|
||||
{
|
||||
if (switcher->saveWindowGeo && windowPosValid(switcher->windowPos)) {
|
||||
if (switcher->saveWindowGeo && WindowPosValid(switcher->windowPos)) {
|
||||
this->resize(switcher->windowSize);
|
||||
this->move(switcher->windowPos);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::checkFirstTimeSetup()
|
||||
void AdvSceneSwitcher::CheckFirstTimeSetup()
|
||||
{
|
||||
if (switcher->firstBoot && !switcher->disableHints) {
|
||||
switcher->firstBoot = false;
|
||||
@@ -492,11 +446,44 @@ void AdvSceneSwitcher::on_tabMoved(int from, int to)
|
||||
void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
|
||||
{
|
||||
switcher->showFrame = false;
|
||||
clearFrames(ui->screenRegionSwitches);
|
||||
ClearFrames(ui->screenRegionSwitches);
|
||||
SetShowFrames();
|
||||
}
|
||||
|
||||
void SwitcherData::loadSettings(obs_data_t *obj)
|
||||
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state && !switcher->adjustActiveTransitionType) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError"));
|
||||
ui->adjustActiveTransitionType->setChecked(true);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->transitionOverrideOverride = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_adjustActiveTransitionType_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This option only makes sense if we are allowed to use transition overrides
|
||||
if (!state && !switcher->transitionOverrideOverride) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError"));
|
||||
ui->transitionOverridecheckBox->setChecked(true);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->adjustActiveTransitionType = state;
|
||||
}
|
||||
|
||||
void SwitcherData::LoadSettings(obs_data_t *obj)
|
||||
{
|
||||
if (!obj) {
|
||||
return;
|
||||
@@ -505,9 +492,9 @@ void SwitcherData::loadSettings(obs_data_t *obj)
|
||||
// Needs to be loaded before any entries which might rely on scene group
|
||||
// selections to be available.
|
||||
loadSceneGroups(obj);
|
||||
loadVariables(obj);
|
||||
loadMacros(obj);
|
||||
loadConnections(obj);
|
||||
LoadVariables(obj);
|
||||
LoadConnections(obj);
|
||||
LoadMacros(obj);
|
||||
loadWindowTitleSwitches(obj);
|
||||
loadScreenRegionSwitches(obj);
|
||||
loadPauseSwitches(obj);
|
||||
@@ -523,24 +510,25 @@ void SwitcherData::loadSettings(obs_data_t *obj)
|
||||
loadVideoSwitches(obj);
|
||||
loadNetworkSettings(obj);
|
||||
loadSceneTriggers(obj);
|
||||
loadGeneralSettings(obj);
|
||||
loadHotkeys(obj);
|
||||
loadUISettings(obj);
|
||||
LoadGeneralSettings(obj);
|
||||
LoadHotkeys(obj);
|
||||
LoadUISettings(obj);
|
||||
|
||||
// Reset on startup and scene collection change
|
||||
switcher->lastOpenedTab = -1;
|
||||
startupLoadDone = true;
|
||||
}
|
||||
|
||||
void SwitcherData::saveSettings(obs_data_t *obj)
|
||||
void SwitcherData::SaveSettings(obs_data_t *obj)
|
||||
{
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
saveSceneGroups(obj);
|
||||
saveMacros(obj);
|
||||
saveConnections(obj);
|
||||
saveVariables(obj);
|
||||
SaveMacros(obj);
|
||||
SaveConnections(obj);
|
||||
SaveVariables(obj);
|
||||
saveWindowTitleSwitches(obj);
|
||||
saveScreenRegionSwitches(obj);
|
||||
savePauseSwitches(obj);
|
||||
@@ -556,26 +544,29 @@ void SwitcherData::saveSettings(obs_data_t *obj)
|
||||
saveVideoSwitches(obj);
|
||||
saveNetworkSwitches(obj);
|
||||
saveSceneTriggers(obj);
|
||||
saveGeneralSettings(obj);
|
||||
saveHotkeys(obj);
|
||||
saveUISettings(obj);
|
||||
saveVersion(obj, g_GIT_SHA1);
|
||||
SaveGeneralSettings(obj);
|
||||
SaveHotkeys(obj);
|
||||
SaveUISettings(obj);
|
||||
SaveVersion(obj, g_GIT_SHA1);
|
||||
}
|
||||
|
||||
void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
||||
void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_int(obj, "interval", interval);
|
||||
|
||||
std::string nonMatchingSceneName = GetWeakSourceName(nonMatchingScene);
|
||||
obs_data_set_string(obj, "non_matching_scene",
|
||||
nonMatchingSceneName.c_str());
|
||||
obs_data_set_int(obj, "switch_if_not_matching", switchIfNotMatching);
|
||||
noMatchDelay.Save(obj, "noMatchDelay", "noMatchDelayUnit");
|
||||
obs_data_set_int(obj, "switch_if_not_matching",
|
||||
static_cast<int>(switchIfNotMatching));
|
||||
noMatchDelay.Save(obj, "noMatchDelay");
|
||||
|
||||
cooldown.Save(obj, "cooldown", "cooldownUnit");
|
||||
cooldown.Save(obj, "cooldown");
|
||||
|
||||
obs_data_set_bool(obj, "active", !stop);
|
||||
obs_data_set_int(obj, "startup_behavior", startupBehavior);
|
||||
obs_data_set_bool(obj, "active", sceneColletionStop ? true : !stop);
|
||||
sceneColletionStop = false;
|
||||
obs_data_set_int(obj, "startup_behavior",
|
||||
static_cast<int>(startupBehavior));
|
||||
|
||||
obs_data_set_int(obj, "autoStartEvent",
|
||||
static_cast<int>(autoStartEvent));
|
||||
@@ -584,99 +575,85 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
||||
obs_data_set_bool(obj, "showSystemTrayNotifications",
|
||||
showSystemTrayNotifications);
|
||||
obs_data_set_bool(obj, "disableHints", disableHints);
|
||||
obs_data_set_bool(obj, "warnPluginLoadFailure", warnPluginLoadFailure);
|
||||
obs_data_set_bool(obj, "hideLegacyTabs", hideLegacyTabs);
|
||||
|
||||
obs_data_set_int(obj, "priority0", functionNamesByPriority[0]);
|
||||
obs_data_set_int(obj, "priority1", functionNamesByPriority[1]);
|
||||
obs_data_set_int(obj, "priority2", functionNamesByPriority[2]);
|
||||
obs_data_set_int(obj, "priority3", functionNamesByPriority[3]);
|
||||
obs_data_set_int(obj, "priority4", functionNamesByPriority[4]);
|
||||
obs_data_set_int(obj, "priority5", functionNamesByPriority[5]);
|
||||
obs_data_set_int(obj, "priority6", functionNamesByPriority[6]);
|
||||
obs_data_set_int(obj, "priority7", functionNamesByPriority[7]);
|
||||
obs_data_set_int(obj, "priority8", functionNamesByPriority[8]);
|
||||
obs_data_set_int(obj, "priority9", functionNamesByPriority[9]);
|
||||
obs_data_set_int(obj, "priority10", functionNamesByPriority[10]);
|
||||
SaveFunctionPriorities(obj, functionNamesByPriority);
|
||||
|
||||
obs_data_set_int(obj, "threadPriority", threadPriority);
|
||||
|
||||
obs_data_set_bool(obj, "transitionOverrideOverride",
|
||||
transitionOverrideOverride);
|
||||
obs_data_set_bool(obj, "adjustActiveTransitionType",
|
||||
adjustActiveTransitionType);
|
||||
|
||||
obs_data_set_string(obj, "lastImportPath", lastImportPath.c_str());
|
||||
}
|
||||
|
||||
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||
void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_default_int(obj, "interval", default_interval);
|
||||
interval = obs_data_get_int(obj, "interval");
|
||||
|
||||
obs_data_set_default_int(obj, "switch_if_not_matching", NO_SWITCH);
|
||||
obs_data_set_default_int(obj, "switch_if_not_matching",
|
||||
static_cast<int>(NoMatch::NO_SWITCH));
|
||||
switchIfNotMatching =
|
||||
(NoMatch)obs_data_get_int(obj, "switch_if_not_matching");
|
||||
std::string nonMatchingSceneName =
|
||||
obs_data_get_string(obj, "non_matching_scene");
|
||||
nonMatchingScene = GetWeakSourceByName(nonMatchingSceneName.c_str());
|
||||
noMatchDelay.Load(obj, "noMatchDelay", "noMatchDelayUnit");
|
||||
noMatchDelay.Load(obj, "noMatchDelay");
|
||||
|
||||
cooldown.Load(obj, "cooldown", "cooldownUnit");
|
||||
cooldown.Load(obj, "cooldown");
|
||||
|
||||
stop = !obs_data_get_bool(obj, "active");
|
||||
startupBehavior =
|
||||
(StartupBehavior)obs_data_get_int(obj, "startup_behavior");
|
||||
if (startupBehavior == START) {
|
||||
if (startupBehavior == StartupBehavior::START) {
|
||||
stop = false;
|
||||
}
|
||||
if (startupBehavior == STOP) {
|
||||
if (startupBehavior == StartupBehavior::STOP) {
|
||||
stop = true;
|
||||
}
|
||||
|
||||
autoStartEvent = static_cast<AutoStartEvent>(
|
||||
obs_data_get_int(obj, "autoStartEvent"));
|
||||
autoStartEvent =
|
||||
static_cast<AutoStart>(obs_data_get_int(obj, "autoStartEvent"));
|
||||
|
||||
verbose = obs_data_get_bool(obj, "verbose");
|
||||
showSystemTrayNotifications =
|
||||
obs_data_get_bool(obj, "showSystemTrayNotifications");
|
||||
disableHints = obs_data_get_bool(obj, "disableHints");
|
||||
obs_data_set_default_bool(obj, "warnPluginLoadFailure", true);
|
||||
warnPluginLoadFailure = obs_data_get_bool(obj, "warnPluginLoadFailure");
|
||||
obs_data_set_default_bool(obj, "hideLegacyTabs", true);
|
||||
hideLegacyTabs = obs_data_get_bool(obj, "hideLegacyTabs");
|
||||
|
||||
obs_data_set_default_int(obj, "priority0", default_priority_0);
|
||||
obs_data_set_default_int(obj, "priority1", default_priority_1);
|
||||
obs_data_set_default_int(obj, "priority2", default_priority_2);
|
||||
obs_data_set_default_int(obj, "priority3", default_priority_3);
|
||||
obs_data_set_default_int(obj, "priority4", default_priority_4);
|
||||
obs_data_set_default_int(obj, "priority5", default_priority_5);
|
||||
obs_data_set_default_int(obj, "priority6", default_priority_6);
|
||||
obs_data_set_default_int(obj, "priority7", default_priority_7);
|
||||
obs_data_set_default_int(obj, "priority8", default_priority_8);
|
||||
|
||||
functionNamesByPriority[0] = (obs_data_get_int(obj, "priority0"));
|
||||
functionNamesByPriority[1] = (obs_data_get_int(obj, "priority1"));
|
||||
functionNamesByPriority[2] = (obs_data_get_int(obj, "priority2"));
|
||||
functionNamesByPriority[3] = (obs_data_get_int(obj, "priority3"));
|
||||
functionNamesByPriority[4] = (obs_data_get_int(obj, "priority4"));
|
||||
functionNamesByPriority[5] = (obs_data_get_int(obj, "priority5"));
|
||||
functionNamesByPriority[6] = (obs_data_get_int(obj, "priority6"));
|
||||
functionNamesByPriority[7] = (obs_data_get_int(obj, "priority7"));
|
||||
functionNamesByPriority[8] = (obs_data_get_int(obj, "priority8"));
|
||||
functionNamesByPriority[9] = (obs_data_get_int(obj, "priority9"));
|
||||
functionNamesByPriority[10] = (obs_data_get_int(obj, "priority10"));
|
||||
if (!prioFuncsValid()) {
|
||||
functionNamesByPriority[0] = (default_priority_0);
|
||||
functionNamesByPriority[1] = (default_priority_1);
|
||||
functionNamesByPriority[2] = (default_priority_2);
|
||||
functionNamesByPriority[3] = (default_priority_3);
|
||||
functionNamesByPriority[4] = (default_priority_4);
|
||||
functionNamesByPriority[5] = (default_priority_5);
|
||||
functionNamesByPriority[6] = (default_priority_6);
|
||||
functionNamesByPriority[7] = (default_priority_7);
|
||||
functionNamesByPriority[8] = (default_priority_8);
|
||||
functionNamesByPriority[9] = (default_priority_9);
|
||||
functionNamesByPriority[10] = (default_priority_10);
|
||||
SetDefaultFunctionPriorities(obj);
|
||||
LoadFunctionPriorities(obj, functionNamesByPriority);
|
||||
if (!PrioFuncsValid()) {
|
||||
functionNamesByPriority = GetDefaultFunctionPriorityList();
|
||||
}
|
||||
|
||||
obs_data_set_default_int(obj, "threadPriority",
|
||||
QThread::NormalPriority);
|
||||
threadPriority = obs_data_get_int(obj, "threadPriority");
|
||||
|
||||
transitionOverrideOverride =
|
||||
obs_data_get_bool(obj, "transitionOverrideOverride");
|
||||
adjustActiveTransitionType =
|
||||
obs_data_get_bool(obj, "adjustActiveTransitionType");
|
||||
|
||||
if (!transitionOverrideOverride && !adjustActiveTransitionType) {
|
||||
blog(LOG_INFO,
|
||||
"reset transition behaviour to adjust active transition type");
|
||||
adjustActiveTransitionType = true;
|
||||
}
|
||||
|
||||
lastImportPath = obs_data_get_string(obj, "lastImportPath");
|
||||
}
|
||||
|
||||
void saveSplitterPos(QList<int> &sizes, obs_data_t *obj, const std::string name)
|
||||
static void saveSplitterPos(QList<int> &sizes, obs_data_t *obj,
|
||||
const std::string name)
|
||||
{
|
||||
auto array = obs_data_array_create();
|
||||
for (int i = 0; i < sizes.count(); ++i) {
|
||||
@@ -689,7 +666,8 @@ void saveSplitterPos(QList<int> &sizes, obs_data_t *obj, const std::string name)
|
||||
obs_data_array_release(array);
|
||||
}
|
||||
|
||||
void loadSplitterPos(QList<int> &sizes, obs_data_t *obj, const std::string name)
|
||||
static void loadSplitterPos(QList<int> &sizes, obs_data_t *obj,
|
||||
const std::string name)
|
||||
{
|
||||
sizes.clear();
|
||||
obs_data_array_t *array = obs_data_get_array(obj, name.c_str());
|
||||
@@ -702,7 +680,7 @@ void loadSplitterPos(QList<int> &sizes, obs_data_t *obj, const std::string name)
|
||||
obs_data_array_release(array);
|
||||
}
|
||||
|
||||
void SwitcherData::saveUISettings(obs_data_t *obj)
|
||||
void SwitcherData::SaveUISettings(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_int(obj, "generalTabPos", tabOrder[0]);
|
||||
obs_data_set_int(obj, "macroTabPos", tabOrder[1]);
|
||||
@@ -735,7 +713,7 @@ void SwitcherData::saveUISettings(obs_data_t *obj)
|
||||
"macroListMacroEditSplitterPosition");
|
||||
}
|
||||
|
||||
void SwitcherData::loadUISettings(obs_data_t *obj)
|
||||
void SwitcherData::LoadUISettings(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_default_int(obj, "generalTabPos", 0);
|
||||
obs_data_set_default_int(obj, "macroTabPos", 1);
|
||||
@@ -776,8 +754,8 @@ void SwitcherData::loadUISettings(obs_data_t *obj)
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "sceneGroupTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "triggerTabPos")));
|
||||
|
||||
if (!tabOrderValid()) {
|
||||
resetTabOrder();
|
||||
if (!TabOrderValid()) {
|
||||
ResetTabOrder();
|
||||
}
|
||||
|
||||
saveWindowGeo = obs_data_get_bool(obj, "saveWindowGeo");
|
||||
@@ -791,7 +769,7 @@ void SwitcherData::loadUISettings(obs_data_t *obj)
|
||||
"macroListMacroEditSplitterPosition");
|
||||
}
|
||||
|
||||
bool SwitcherData::tabOrderValid()
|
||||
bool SwitcherData::TabOrderValid()
|
||||
{
|
||||
auto tmp = std::vector<int>(tab_count);
|
||||
std::iota(tmp.begin(), tmp.end(), 0);
|
||||
@@ -805,13 +783,13 @@ bool SwitcherData::tabOrderValid()
|
||||
return true;
|
||||
}
|
||||
|
||||
void SwitcherData::resetTabOrder()
|
||||
void SwitcherData::ResetTabOrder()
|
||||
{
|
||||
tabOrder = std::vector<int>(tab_count);
|
||||
std::iota(tabOrder.begin(), tabOrder.end(), 0);
|
||||
}
|
||||
|
||||
void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||
void SwitcherData::CheckNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &sleep)
|
||||
{
|
||||
if (match) {
|
||||
@@ -823,12 +801,12 @@ void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||
return;
|
||||
}
|
||||
|
||||
if (switchIfNotMatching == SWITCH && nonMatchingScene) {
|
||||
if (switchIfNotMatching == NoMatch::SWITCH && nonMatchingScene) {
|
||||
match = true;
|
||||
scene = nonMatchingScene;
|
||||
transition = nullptr;
|
||||
}
|
||||
if (switchIfNotMatching == RANDOM_SWITCH) {
|
||||
if (switchIfNotMatching == NoMatch::RANDOM_SWITCH) {
|
||||
match = checkRandom(scene, transition, sleep);
|
||||
}
|
||||
}
|
||||
@@ -847,7 +825,7 @@ void SwitcherData::checkSwitchCooldown(bool &match)
|
||||
}
|
||||
}
|
||||
|
||||
void populateStartupBehavior(QComboBox *cb)
|
||||
static void populateStartupBehavior(QComboBox *cb)
|
||||
{
|
||||
cb->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.status.onStartup.asLastRun"));
|
||||
@@ -857,7 +835,7 @@ void populateStartupBehavior(QComboBox *cb)
|
||||
"AdvSceneSwitcher.generalTab.status.onStartup.doNotStart"));
|
||||
}
|
||||
|
||||
void populateAutoStartEventSelection(QComboBox *cb)
|
||||
static void populateAutoStartEventSelection(QComboBox *cb)
|
||||
{
|
||||
cb->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.status.autoStart.never"));
|
||||
@@ -869,53 +847,8 @@ void populateAutoStartEventSelection(QComboBox *cb)
|
||||
"AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming"));
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupGeneralTab()
|
||||
static void populatePriorityFunctionList(QListWidget *list)
|
||||
{
|
||||
populateSceneSelection(ui->noMatchSwitchScene, false);
|
||||
|
||||
if (switcher->switchIfNotMatching == SWITCH) {
|
||||
ui->noMatchSwitch->setChecked(true);
|
||||
ui->noMatchSwitchScene->setEnabled(true);
|
||||
} else if (switcher->switchIfNotMatching == NO_SWITCH) {
|
||||
ui->noMatchDontSwitch->setChecked(true);
|
||||
ui->noMatchSwitchScene->setEnabled(false);
|
||||
} else {
|
||||
ui->noMatchRandomSwitch->setChecked(true);
|
||||
ui->noMatchSwitchScene->setEnabled(false);
|
||||
}
|
||||
ui->noMatchSwitchScene->setCurrentText(
|
||||
GetWeakSourceName(switcher->nonMatchingScene).c_str());
|
||||
|
||||
DurationSelection *noMatchDelay = new DurationSelection();
|
||||
noMatchDelay->SetDuration(switcher->noMatchDelay);
|
||||
noMatchDelay->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip"));
|
||||
ui->noMatchLayout->addWidget(noMatchDelay);
|
||||
QWidget::connect(noMatchDelay, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(NoMatchDelayDurationChanged(double)));
|
||||
QWidget::connect(noMatchDelay, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(NoMatchDelayUnitChanged(DurationUnit)));
|
||||
|
||||
ui->checkInterval->setValue(switcher->interval);
|
||||
|
||||
DurationSelection *cooldownTime = new DurationSelection();
|
||||
cooldownTime->SetDuration(switcher->cooldown);
|
||||
cooldownTime->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint"));
|
||||
ui->cooldownLayout->addWidget(cooldownTime);
|
||||
ui->cooldownLayout->addStretch();
|
||||
QWidget::connect(cooldownTime, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(CooldownDurationChanged(double)));
|
||||
QWidget::connect(cooldownTime, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(CooldownUnitChanged(DurationUnit)));
|
||||
|
||||
ui->verboseLogging->setChecked(switcher->verbose);
|
||||
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
|
||||
ui->showTrayNotifications->setChecked(
|
||||
switcher->showSystemTrayNotifications);
|
||||
ui->uiHintsDisable->setChecked(switcher->disableHints);
|
||||
ui->hideLegacyTabs->setChecked(switcher->hideLegacyTabs);
|
||||
|
||||
for (int p : switcher->functionNamesByPriority) {
|
||||
std::string s = "";
|
||||
switch (p) {
|
||||
@@ -965,26 +898,114 @@ void AdvSceneSwitcher::setupGeneralTab()
|
||||
break;
|
||||
}
|
||||
QString text(s.c_str());
|
||||
QListWidgetItem *item =
|
||||
new QListWidgetItem(text, ui->priorityList);
|
||||
QListWidgetItem *item = new QListWidgetItem(text, list);
|
||||
item->setData(Qt::UserRole, text);
|
||||
}
|
||||
}
|
||||
|
||||
static void populateThreadPriorityList(QComboBox *list)
|
||||
{
|
||||
for (int i = 0; i < (int)switcher->threadPriorities.size(); ++i) {
|
||||
ui->threadPriority->addItem(
|
||||
switcher->threadPriorities[i].name.c_str());
|
||||
ui->threadPriority->setItemData(
|
||||
list->addItem(switcher->threadPriorities[i].name.c_str());
|
||||
list->setItemData(
|
||||
i, switcher->threadPriorities[i].description.c_str(),
|
||||
Qt::ToolTipRole);
|
||||
if (switcher->threadPriority ==
|
||||
switcher->threadPriorities[i].value) {
|
||||
ui->threadPriority->setCurrentText(
|
||||
list->setCurrentText(
|
||||
switcher->threadPriorities[i].name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool isGeneralTab(const QString &name)
|
||||
{
|
||||
return name == obs_module_text("AdvSceneSwitcher.generalTab.title");
|
||||
}
|
||||
|
||||
static int getGeneralTabIdx(QTabWidget *tabWidget)
|
||||
{
|
||||
for (int idx = 0; idx < tabWidget->count(); idx++) {
|
||||
if (isGeneralTab(tabWidget->tabText(idx))) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void setupGeneralTabInactiveWarning(QTabWidget *tabs)
|
||||
{
|
||||
auto callback = [tabs]() {
|
||||
const bool isRunning = switcher && switcher->th &&
|
||||
switcher->th->isRunning();
|
||||
auto idx = getGeneralTabIdx(tabs);
|
||||
tabs->setTabIcon(
|
||||
idx,
|
||||
isRunning ? QIcon()
|
||||
: QIcon::fromTheme(
|
||||
"obs",
|
||||
QIcon(":/res/images/warning.svg")));
|
||||
};
|
||||
|
||||
auto inactiveTimer = new QTimer(tabs);
|
||||
inactiveTimer->setInterval(1000);
|
||||
QObject::connect(inactiveTimer, &QTimer::timeout, callback);
|
||||
inactiveTimer->start();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetupGeneralTab()
|
||||
{
|
||||
PopulateSceneSelection(ui->noMatchSwitchScene, false);
|
||||
|
||||
if (switcher->switchIfNotMatching == SwitcherData::NoMatch::SWITCH) {
|
||||
ui->noMatchSwitch->setChecked(true);
|
||||
ui->noMatchSwitchScene->setEnabled(true);
|
||||
} else if (switcher->switchIfNotMatching ==
|
||||
SwitcherData::NoMatch::NO_SWITCH) {
|
||||
ui->noMatchDontSwitch->setChecked(true);
|
||||
ui->noMatchSwitchScene->setEnabled(false);
|
||||
} else {
|
||||
ui->noMatchRandomSwitch->setChecked(true);
|
||||
ui->noMatchSwitchScene->setEnabled(false);
|
||||
}
|
||||
ui->noMatchSwitchScene->setCurrentText(
|
||||
GetWeakSourceName(switcher->nonMatchingScene).c_str());
|
||||
|
||||
DurationSelection *noMatchDelay = new DurationSelection();
|
||||
noMatchDelay->SetDuration(switcher->noMatchDelay);
|
||||
noMatchDelay->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip"));
|
||||
ui->noMatchLayout->addWidget(noMatchDelay);
|
||||
QWidget::connect(noMatchDelay,
|
||||
SIGNAL(DurationChanged(const Duration &)), this,
|
||||
SLOT(NoMatchDelayDurationChanged(const Duration &)));
|
||||
|
||||
ui->checkInterval->setValue(switcher->interval);
|
||||
|
||||
DurationSelection *cooldownTime = new DurationSelection();
|
||||
cooldownTime->SetDuration(switcher->cooldown);
|
||||
cooldownTime->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint"));
|
||||
ui->cooldownLayout->addWidget(cooldownTime);
|
||||
ui->cooldownLayout->addStretch();
|
||||
QWidget::connect(cooldownTime,
|
||||
SIGNAL(DurationChanged(const Duration &)), this,
|
||||
SLOT(CooldownDurationChanged(const Duration &)));
|
||||
|
||||
ui->verboseLogging->setChecked(switcher->verbose);
|
||||
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
|
||||
ui->showTrayNotifications->setChecked(
|
||||
switcher->showSystemTrayNotifications);
|
||||
ui->uiHintsDisable->setChecked(switcher->disableHints);
|
||||
ui->warnPluginLoadFailure->setChecked(switcher->warnPluginLoadFailure);
|
||||
ui->hideLegacyTabs->setChecked(switcher->hideLegacyTabs);
|
||||
|
||||
populatePriorityFunctionList(ui->priorityList);
|
||||
populateThreadPriorityList(ui->threadPriority);
|
||||
|
||||
populateStartupBehavior(ui->startupBehavior);
|
||||
ui->startupBehavior->setCurrentIndex(switcher->startupBehavior);
|
||||
ui->startupBehavior->setCurrentIndex(
|
||||
static_cast<int>(switcher->startupBehavior));
|
||||
|
||||
populateAutoStartEventSelection(ui->autoStartEvent);
|
||||
ui->autoStartEvent->setCurrentIndex(
|
||||
@@ -1001,6 +1022,10 @@ void AdvSceneSwitcher::setupGeneralTab()
|
||||
// Hide the now empty invisible shell of the statusControl widget
|
||||
// as otherwise it could block user input
|
||||
statusControl->hide();
|
||||
setupGeneralTabInactiveWarning(ui->tabWidget);
|
||||
|
||||
MinimizeSizeOfColumn(ui->statusLayout, 0);
|
||||
setWindowTitle(windowTitle() + " - " + g_GIT_TAG);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
148
src/hotkey.cpp
@@ -1,10 +1,126 @@
|
||||
#include "hotkey.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include <obs-module.h>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
void startHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
namespace advss {
|
||||
|
||||
std::vector<std::weak_ptr<Hotkey>> Hotkey::_registeredHotkeys = {};
|
||||
uint32_t Hotkey::_hotkeyCounter = 1;
|
||||
|
||||
std::shared_ptr<Hotkey> Hotkey::GetHotkey(const std::string &description,
|
||||
bool ignoreExistingHotkeys)
|
||||
{
|
||||
// Clean up expired hotkeys
|
||||
auto it = _registeredHotkeys.begin();
|
||||
while (it != _registeredHotkeys.end()) {
|
||||
if (it->expired()) {
|
||||
it = _registeredHotkeys.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for existing hotkey with same description
|
||||
for (const auto &h : _registeredHotkeys) {
|
||||
auto hotkey = h.lock();
|
||||
if (!hotkey) {
|
||||
continue;
|
||||
}
|
||||
if (hotkey->_description == description) {
|
||||
hotkey->_ignoreExistingHotkeys = ignoreExistingHotkeys;
|
||||
return hotkey;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new hotkey
|
||||
auto hotkey = std::make_shared<Hotkey>(description);
|
||||
_registeredHotkeys.emplace_back(hotkey);
|
||||
hotkey->_ignoreExistingHotkeys = ignoreExistingHotkeys;
|
||||
return hotkey;
|
||||
}
|
||||
|
||||
Hotkey::Hotkey(const std::string &description) : _description(description)
|
||||
{
|
||||
std::string name =
|
||||
"macro_condition_hotkey_" + std::to_string(_hotkeyCounter);
|
||||
_hotkeyID = obs_hotkey_register_frontend(
|
||||
name.c_str(), _description.c_str(), Callback, this);
|
||||
_hotkeyCounter++;
|
||||
}
|
||||
|
||||
bool Hotkey::Save(obs_data_t *obj) const
|
||||
{
|
||||
obs_data_set_string(obj, "desc", _description.c_str());
|
||||
obs_data_array_t *hotkeyData = obs_hotkey_save(_hotkeyID);
|
||||
obs_data_set_array(obj, "keyBind", hotkeyData);
|
||||
obs_data_array_release(hotkeyData);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Hotkey::Load(obs_data_t *obj)
|
||||
{
|
||||
auto description = obs_data_get_string(obj, "desc");
|
||||
if (!DescriptionAvailable(description)) {
|
||||
return false;
|
||||
}
|
||||
_description = description;
|
||||
obs_data_array_t *hotkeyData = obs_data_get_array(obj, "keyBind");
|
||||
obs_hotkey_load(_hotkeyID, hotkeyData);
|
||||
obs_data_array_release(hotkeyData);
|
||||
obs_hotkey_set_description(_hotkeyID, _description.c_str());
|
||||
_ignoreExistingHotkeys = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Hotkey::~Hotkey()
|
||||
{
|
||||
obs_hotkey_unregister(_hotkeyID);
|
||||
}
|
||||
|
||||
bool Hotkey::UpdateDescription(const std::string &descritpion)
|
||||
{
|
||||
if (!DescriptionAvailable(descritpion)) {
|
||||
return false;
|
||||
}
|
||||
_description = descritpion;
|
||||
obs_hotkey_set_description(_hotkeyID, descritpion.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Hotkey::DescriptionAvailable(const std::string &descritpion)
|
||||
{
|
||||
for (const auto &hotkey : _registeredHotkeys) {
|
||||
auto h = hotkey.lock();
|
||||
if (!h) {
|
||||
continue;
|
||||
}
|
||||
if (!h->_ignoreExistingHotkeys &&
|
||||
h->_description == descritpion) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Hotkey::Callback(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
{
|
||||
auto hotkey = static_cast<Hotkey *>(data);
|
||||
if (pressed) {
|
||||
hotkey->_lastPressed =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
hotkey->_pressed = pressed;
|
||||
}
|
||||
|
||||
void Hotkey::ClearAllHotkeys()
|
||||
{
|
||||
_registeredHotkeys.clear();
|
||||
}
|
||||
|
||||
static void startHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
{
|
||||
if (pressed) {
|
||||
if (!(switcher->th && switcher->th->isRunning())) {
|
||||
@@ -13,7 +129,7 @@ void startHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
}
|
||||
}
|
||||
|
||||
void stopHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
static void stopHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
{
|
||||
if (pressed) {
|
||||
if (switcher->th && switcher->th->isRunning()) {
|
||||
@@ -22,8 +138,8 @@ void stopHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
}
|
||||
}
|
||||
|
||||
void startStopToggleHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed)
|
||||
static void startStopToggleHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed)
|
||||
{
|
||||
if (pressed) {
|
||||
if (switcher->th && switcher->th->isRunning()) {
|
||||
@@ -34,8 +150,8 @@ void startStopToggleHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
}
|
||||
}
|
||||
|
||||
void upMacroSegmentHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed)
|
||||
static void upMacroSegmentHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed)
|
||||
{
|
||||
if (pressed && switcher->settingsWindowOpened &&
|
||||
AdvSceneSwitcher::window) {
|
||||
@@ -45,8 +161,8 @@ void upMacroSegmentHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
}
|
||||
}
|
||||
|
||||
void downMacroSegmentHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed)
|
||||
static void downMacroSegmentHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed)
|
||||
{
|
||||
if (pressed && switcher->settingsWindowOpened &&
|
||||
AdvSceneSwitcher::window) {
|
||||
@@ -56,8 +172,8 @@ void downMacroSegmentHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
}
|
||||
}
|
||||
|
||||
void removeMacroSegmentHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed)
|
||||
static void removeMacroSegmentHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed)
|
||||
{
|
||||
if (pressed && switcher->settingsWindowOpened &&
|
||||
AdvSceneSwitcher::window) {
|
||||
@@ -67,7 +183,7 @@ void removeMacroSegmentHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
}
|
||||
}
|
||||
|
||||
void registerHotkeys()
|
||||
static void registerHotkeys()
|
||||
{
|
||||
switcher->startHotkey = obs_hotkey_register_frontend(
|
||||
"startSwitcherHotkey",
|
||||
@@ -100,14 +216,14 @@ void registerHotkeys()
|
||||
switcher->hotkeysRegistered = true;
|
||||
}
|
||||
|
||||
void saveHotkey(obs_data_t *obj, obs_hotkey_id id, const char *name)
|
||||
static void saveHotkey(obs_data_t *obj, obs_hotkey_id id, const char *name)
|
||||
{
|
||||
obs_data_array_t *a = obs_hotkey_save(id);
|
||||
obs_data_set_array(obj, name, a);
|
||||
obs_data_array_release(a);
|
||||
}
|
||||
|
||||
void SwitcherData::saveHotkeys(obs_data_t *obj)
|
||||
void SwitcherData::SaveHotkeys(obs_data_t *obj)
|
||||
{
|
||||
saveHotkey(obj, startHotkey, "startHotkey");
|
||||
saveHotkey(obj, stopHotkey, "stopHotkey");
|
||||
@@ -117,14 +233,14 @@ void SwitcherData::saveHotkeys(obs_data_t *obj)
|
||||
saveHotkey(obj, removeMacroSegment, "removeMacroSegmentHotkey");
|
||||
}
|
||||
|
||||
void loadHotkey(obs_data_t *obj, obs_hotkey_id id, const char *name)
|
||||
static void loadHotkey(obs_data_t *obj, obs_hotkey_id id, const char *name)
|
||||
{
|
||||
obs_data_array_t *a = obs_data_get_array(obj, name);
|
||||
obs_hotkey_load(id, a);
|
||||
obs_data_array_release(a);
|
||||
}
|
||||
|
||||
void SwitcherData::loadHotkeys(obs_data_t *obj)
|
||||
void SwitcherData::LoadHotkeys(obs_data_t *obj)
|
||||
{
|
||||
if (!hotkeysRegistered) {
|
||||
registerHotkeys();
|
||||
@@ -136,3 +252,5 @@ void SwitcherData::loadHotkeys(obs_data_t *obj)
|
||||
loadHotkey(obj, downMacroSegment, "downMacroSegmentHotkey");
|
||||
loadHotkey(obj, removeMacroSegment, "removeMacroSegmentHotkey");
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,7 +1,48 @@
|
||||
#pragma once
|
||||
#include <obs.hpp>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
namespace advss {
|
||||
|
||||
extern bool canSimulateKeyPresses;
|
||||
|
||||
class Hotkey {
|
||||
public:
|
||||
Hotkey(const std::string &description);
|
||||
~Hotkey();
|
||||
|
||||
static std::shared_ptr<Hotkey>
|
||||
GetHotkey(const std::string &description,
|
||||
bool ignoreExistingHotkeys = false);
|
||||
static void ClearAllHotkeys();
|
||||
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
|
||||
bool GetPressed() const { return _pressed; }
|
||||
auto GetLastPressed() const { return _lastPressed; }
|
||||
std::string GetDescription() const { return _description; }
|
||||
bool UpdateDescription(const std::string &);
|
||||
|
||||
private:
|
||||
static bool DescriptionAvailable(const std::string &);
|
||||
static void Callback(void *data, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed);
|
||||
|
||||
static std::vector<std::weak_ptr<Hotkey>> _registeredHotkeys;
|
||||
static uint32_t _hotkeyCounter;
|
||||
|
||||
std::string _description;
|
||||
obs_hotkey_id _hotkeyID = OBS_INVALID_HOTKEY_ID;
|
||||
bool _pressed = false;
|
||||
std::chrono::high_resolution_clock::time_point _lastPressed{};
|
||||
// When set will not attempt to share settings with existing hotkey
|
||||
bool _ignoreExistingHotkeys = false;
|
||||
};
|
||||
|
||||
enum class HotkeyType {
|
||||
Key_NoKey = 0,
|
||||
|
||||
@@ -122,4 +163,4 @@ enum class HotkeyType {
|
||||
Key_NumpadEnter
|
||||
};
|
||||
|
||||
void registerHotkeys();
|
||||
} // namespace advss
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
#include <random>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "name-dialog.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
static QMetaObject::Connection addPulse;
|
||||
SceneGroupEditWidget *typeEdit = nullptr;
|
||||
|
||||
@@ -487,9 +490,9 @@ void SwitcherData::loadSceneGroups(obs_data_t *obj)
|
||||
obs_data_array_release(sceneGroupArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupSceneGroupTab()
|
||||
void AdvSceneSwitcher::SetupSceneGroupTab()
|
||||
{
|
||||
populateSceneSelection(ui->sceneGroupSceneSelection);
|
||||
PopulateSceneSelection(ui->sceneGroupSceneSelection);
|
||||
|
||||
for (auto &sg : switcher->sceneGroups) {
|
||||
QString text = QString::fromStdString(sg.name);
|
||||
@@ -538,7 +541,7 @@ SceneGroupEditWidget::SceneGroupEditWidget()
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{type}}", type}};
|
||||
placeWidgets(
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.sceneGroupTab.edit.type"),
|
||||
typeLayout, widgetPlaceholders);
|
||||
|
||||
@@ -555,7 +558,7 @@ SceneGroupEditWidget::SceneGroupEditWidget()
|
||||
countLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
widgetPlaceholders = {{"{{count}}", count}};
|
||||
placeWidgets(
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.sceneGroupTab.edit.count"),
|
||||
countLayout, widgetPlaceholders);
|
||||
|
||||
@@ -573,7 +576,7 @@ SceneGroupEditWidget::SceneGroupEditWidget()
|
||||
timeLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
widgetPlaceholders = {{"{{time}}", time}};
|
||||
placeWidgets(
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.sceneGroupTab.edit.time"),
|
||||
timeLayout, widgetPlaceholders);
|
||||
|
||||
@@ -705,3 +708,5 @@ SceneGroup *GetSceneGroupByQString(const QString &name)
|
||||
{
|
||||
return GetSceneGroupByName(name.toUtf8().constData());
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <QCheckBox>
|
||||
#include <obs.hpp>
|
||||
|
||||
namespace advss {
|
||||
|
||||
auto constexpr invalid_scene_group_name = "invalid-scene-group";
|
||||
|
||||
enum class AdvanceCondition {
|
||||
@@ -85,3 +87,5 @@ private:
|
||||
|
||||
SceneGroup *GetSceneGroupByName(const char *name);
|
||||
SceneGroup *GetSceneGroupByQString(const QString &name);
|
||||
|
||||
} // namespace advss
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
namespace advss {
|
||||
|
||||
bool SceneTrigger::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
@@ -156,7 +159,7 @@ void SceneTrigger::logMatch()
|
||||
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);
|
||||
actionName.c_str(), duration.Seconds());
|
||||
}
|
||||
|
||||
void frontEndActionThread(sceneTriggerAction action, double delay)
|
||||
@@ -266,14 +269,14 @@ void SceneTrigger::performAction()
|
||||
|
||||
if (isFrontendAction(triggerAction)) {
|
||||
t = std::thread(frontEndActionThread, triggerAction,
|
||||
duration.seconds);
|
||||
duration.Seconds());
|
||||
} else if (isAudioAction(triggerAction)) {
|
||||
bool mute = triggerAction == sceneTriggerAction::MUTE_SOURCE;
|
||||
t = std::thread(muteThread, audioSource, duration.seconds,
|
||||
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);
|
||||
t = std::thread(statusThread, duration.Seconds(), stop);
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignoring unknown action '%d'",
|
||||
static_cast<int>(triggerAction));
|
||||
@@ -349,7 +352,7 @@ void SwitcherData::loadSceneTriggers(obs_data_t *obj)
|
||||
obs_data_array_release(triggerArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupTriggerTab()
|
||||
void AdvSceneSwitcher::SetupTriggerTab()
|
||||
{
|
||||
for (auto &s : switcher->sceneTriggers) {
|
||||
QListWidgetItem *item;
|
||||
@@ -398,7 +401,7 @@ void SceneTrigger::load(obs_data_t *obj)
|
||||
|
||||
static inline void populateTriggers(QComboBox *list)
|
||||
{
|
||||
addSelectionEntry(
|
||||
AddSelectionEntry(
|
||||
list,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none"));
|
||||
@@ -413,7 +416,7 @@ static inline void populateTriggers(QComboBox *list)
|
||||
|
||||
inline void populateActions(QComboBox *list)
|
||||
{
|
||||
addSelectionEntry(
|
||||
AddSelectionEntry(
|
||||
list,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none"));
|
||||
@@ -460,17 +463,15 @@ SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
|
||||
SLOT(TriggerTypeChanged(int)));
|
||||
QWidget::connect(actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(TriggerActionChanged(int)));
|
||||
QWidget::connect(duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
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);
|
||||
PopulateAudioSelection(audioSources);
|
||||
|
||||
if (s) {
|
||||
triggers->setCurrentIndex(static_cast<int>(s->triggerType));
|
||||
@@ -494,7 +495,7 @@ SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
|
||||
{"{{audioSources}}", audioSources},
|
||||
{"{{duration}}", duration},
|
||||
{"{{scenes}}", scenes}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.sceneTriggerTab.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.sceneTriggerTab.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -552,24 +553,14 @@ void SceneTriggerWidget::TriggerActionChanged(int index)
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTriggerWidget::DurationChanged(double seconds)
|
||||
void SceneTriggerWidget::DurationChanged(const Duration &duration)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void SceneTriggerWidget::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->duration.displayUnit = unit;
|
||||
switchData->duration = duration;
|
||||
}
|
||||
|
||||
void SceneTriggerWidget::AudioSourceChanged(const QString &text)
|
||||
@@ -581,3 +572,5 @@ void SceneTriggerWidget::AudioSourceChanged(const QString &text)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->audioSource = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "switch-generic.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
enum class sceneTriggerType {
|
||||
NONE = 0,
|
||||
SCENE_ACTIVE = 1,
|
||||
@@ -71,8 +73,7 @@ public:
|
||||
private slots:
|
||||
void TriggerTypeChanged(int index);
|
||||
void TriggerActionChanged(int index);
|
||||
void DurationChanged(double seconds);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
void DurationChanged(const Duration &);
|
||||
void AudioSourceChanged(const QString &text);
|
||||
|
||||
private:
|
||||
@@ -83,3 +84,5 @@ private:
|
||||
|
||||
SceneTrigger *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#include <float.h>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "volume-control.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
namespace advss {
|
||||
|
||||
bool AudioSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
|
||||
@@ -212,7 +215,7 @@ void SwitcherData::loadAudioSwitches(obs_data_t *obj)
|
||||
audioFallback.load(obj);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupAudioTab()
|
||||
void AdvSceneSwitcher::SetupAudioTab()
|
||||
{
|
||||
for (auto &s : switcher->audioSwitches) {
|
||||
QListWidgetItem *item;
|
||||
@@ -438,15 +441,15 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
|
||||
SLOT(VolumeThresholdChanged(int)));
|
||||
QWidget::connect(condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(duration, SIGNAL(DurationChanged(const Duration &)),
|
||||
this, SLOT(DurationChanged(const Duration &)));
|
||||
QWidget::connect(audioSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(ignoreInactiveSource, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(IgnoreInactiveChanged(int)));
|
||||
|
||||
populateAudioSelection(audioSources);
|
||||
PopulateAudioSelection(audioSources);
|
||||
populateConditionSelection(condition);
|
||||
|
||||
if (s) {
|
||||
@@ -467,7 +470,7 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
|
||||
{"{{ignoreInactiveSource}}", ignoreInactiveSource},
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.audioTab.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.audioTab.entry"),
|
||||
switchLayout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
@@ -554,14 +557,14 @@ void AudioSwitchWidget::ConditionChanged(int cond)
|
||||
switchData->condition = (audioCondition)cond;
|
||||
}
|
||||
|
||||
void AudioSwitchWidget::DurationChanged(double sec)
|
||||
void AudioSwitchWidget::DurationChanged(const Duration &dur)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->duration.seconds = sec;
|
||||
switchData->duration = dur;
|
||||
}
|
||||
|
||||
void AudioSwitchWidget::IgnoreInactiveChanged(int state)
|
||||
@@ -580,8 +583,8 @@ AudioSwitchFallbackWidget::AudioSwitchFallbackWidget(QWidget *parent,
|
||||
{
|
||||
duration = new DurationSelection(this, false);
|
||||
|
||||
QWidget::connect(duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(duration, SIGNAL(DurationChanged(const Duration &)),
|
||||
this, SLOT(DurationChanged(const Duration &)));
|
||||
|
||||
if (s) {
|
||||
duration->SetDuration(s->duration);
|
||||
@@ -592,7 +595,7 @@ AudioSwitchFallbackWidget::AudioSwitchFallbackWidget(QWidget *parent,
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{duration}}", duration},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.audioTab.multiMatchfallback"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
@@ -602,12 +605,14 @@ AudioSwitchFallbackWidget::AudioSwitchFallbackWidget(QWidget *parent,
|
||||
loading = false;
|
||||
}
|
||||
|
||||
void AudioSwitchFallbackWidget::DurationChanged(double sec)
|
||||
void AudioSwitchFallbackWidget::DurationChanged(const Duration &dur)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->duration.seconds = sec;
|
||||
switchData->duration = dur;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "duration-control.hpp"
|
||||
#include "volume-control.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto audio_func = 8;
|
||||
|
||||
typedef enum {
|
||||
@@ -75,7 +77,7 @@ private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void VolumeThresholdChanged(int vol);
|
||||
void ConditionChanged(int cond);
|
||||
void DurationChanged(double dur);
|
||||
void DurationChanged(const Duration &);
|
||||
void IgnoreInactiveChanged(int state);
|
||||
|
||||
private:
|
||||
@@ -96,10 +98,12 @@ public:
|
||||
AudioSwitchFallbackWidget(QWidget *parent, AudioSwitchFallback *s);
|
||||
|
||||
private slots:
|
||||
void DurationChanged(double dur);
|
||||
void DurationChanged(const Duration &dur);
|
||||
|
||||
private:
|
||||
DurationSelection *duration;
|
||||
|
||||
AudioSwitchFallback *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "platform-funcs.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
bool ExecutableSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
|
||||
@@ -99,7 +103,7 @@ bool SwitcherData::checkExeSwitch(OBSWeakSource &scene,
|
||||
bool equals = runningProcesses.contains(s.exe);
|
||||
bool matches = (runningProcesses.indexOf(
|
||||
QRegularExpression(s.exe)) != -1);
|
||||
bool focus = (!s.inFocus || isInFocus(s.exe));
|
||||
bool focus = (!s.inFocus || IsInFocus(s.exe));
|
||||
|
||||
// True if current window is ignored AND switch equals OR matches last window
|
||||
bool ignore =
|
||||
@@ -156,7 +160,7 @@ void SwitcherData::loadExecutableSwitches(obs_data_t *obj)
|
||||
obs_data_array_release(executableArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupExecutableTab()
|
||||
void AdvSceneSwitcher::SetupExecutableTab()
|
||||
{
|
||||
for (auto &s : switcher->executableSwitches) {
|
||||
QListWidgetItem *item;
|
||||
@@ -208,7 +212,7 @@ ExecutableSwitchWidget::ExecutableSwitchWidget(QWidget *parent,
|
||||
QWidget::connect(requiresFocus, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(FocusChanged(int)));
|
||||
|
||||
populateProcessSelection(processes);
|
||||
PopulateProcessSelection(processes);
|
||||
|
||||
processes->setEditable(true);
|
||||
processes->setMaxVisibleItems(20);
|
||||
@@ -224,7 +228,7 @@ ExecutableSwitchWidget::ExecutableSwitchWidget(QWidget *parent,
|
||||
{"{{requiresFocus}}", requiresFocus},
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.executableTab.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.executableTab.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -272,3 +276,5 @@ void ExecutableSwitchWidget::FocusChanged(int state)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->inFocus = state;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <QCheckBox>
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto exe_func = 3;
|
||||
|
||||
struct ExecutableSwitch : SceneSwitcherEntry {
|
||||
@@ -42,3 +44,5 @@ private:
|
||||
|
||||
ExecutableSwitch *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "curl-helper.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QFileDialog>
|
||||
#include <QTextStream>
|
||||
@@ -6,9 +11,7 @@
|
||||
#include <regex>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "curl-helper.hpp"
|
||||
#include "utility.hpp"
|
||||
namespace advss {
|
||||
|
||||
bool FileSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
@@ -209,7 +212,7 @@ bool matchFileContent(QString &filedata, FileSwitch &s)
|
||||
}
|
||||
|
||||
QString text = QString::fromStdString(s.text);
|
||||
return compareIgnoringLineEnding(text, filedata);
|
||||
return CompareIgnoringLineEnding(text, filedata);
|
||||
}
|
||||
|
||||
bool checkRemoteFileContent(FileSwitch &s)
|
||||
@@ -411,7 +414,7 @@ void SwitcherData::loadFileSwitches(obs_data_t *obj)
|
||||
fileIO.writePath = obs_data_get_string(obj, "writePath");
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupFileTab()
|
||||
void AdvSceneSwitcher::SetupFileTab()
|
||||
{
|
||||
ui->remoteFileWarningLabel->setText(
|
||||
obs_module_text("AdvSceneSwitcher.fileTab.remoteFileWarning1") +
|
||||
@@ -542,11 +545,11 @@ FileSwitchWidget::FileSwitchWidget(QWidget *parent, FileSwitch *s)
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line3Layout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.fileTab.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.fileTab.entry"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.fileTab.entry2"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.fileTab.entry2"),
|
||||
line2Layout, widgetPlaceholders, false);
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.fileTab.entry3"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.fileTab.entry3"),
|
||||
line3Layout, widgetPlaceholders);
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
@@ -665,3 +668,5 @@ void FileSwitchWidget::CheckFileContentChanged(int state)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->onlyMatchIfChanged = state;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
#pragma once
|
||||
#include "switch-generic.hpp"
|
||||
#include <QPlainTextEdit>
|
||||
#include <QDateTime>
|
||||
#include <obs-module.h>
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto read_file_func = 0;
|
||||
|
||||
typedef enum {
|
||||
@@ -77,3 +80,5 @@ static inline QString MakeFileSwitchName(const QString &scene,
|
||||
const QString &fileName,
|
||||
const QString &text, bool useRegex,
|
||||
bool useTime, bool onlyMatchIfChanged);
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
constexpr auto previous_scene_name = "Previous Scene";
|
||||
constexpr auto current_transition_name = "Current Transition";
|
||||
|
||||
namespace advss {
|
||||
|
||||
static inline bool SceneGroupValid(SceneGroup *group)
|
||||
{
|
||||
if (group) {
|
||||
@@ -208,9 +214,9 @@ SwitchWidget::SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
|
||||
SIGNAL(SceneGroupRenamed(const QString &, const QString &)),
|
||||
this, SLOT(SceneGroupRename(const QString &, const QString &)));
|
||||
|
||||
populateSceneSelection(scenes, usePreviousScene, false, false,
|
||||
PopulateSceneSelection(scenes, usePreviousScene, false, false,
|
||||
addSceneGroup, &switcher->sceneGroups);
|
||||
populateTransitionSelection(transitions, addCurrentTransition);
|
||||
PopulateTransitionSelection(transitions, addCurrentTransition);
|
||||
|
||||
switchData = s;
|
||||
showSwitchData();
|
||||
@@ -300,3 +306,5 @@ void SwitchWidget::TransitionChanged(const QString &text)
|
||||
switchData->transition = GetWeakTransitionByQString(text);
|
||||
switchData->useCurrentTransition = switchData->transition == nullptr;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "scene-group.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
enum class SwitchTargetType {
|
||||
Scene,
|
||||
SceneGroup,
|
||||
@@ -95,3 +97,5 @@ protected:
|
||||
|
||||
SceneSwitcherEntry *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "platform-funcs.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
namespace advss {
|
||||
|
||||
bool IdleData::pause = false;
|
||||
IdleWidget *idleWidget = nullptr;
|
||||
@@ -38,7 +42,7 @@ bool SwitcherData::checkIdleSwitch(OBSWeakSource &scene,
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignoreIdle && secondsSinceLastInput() > idleData.time) {
|
||||
if (!ignoreIdle && SecondsSinceLastInput() > idleData.time) {
|
||||
if (idleData.alreadySwitched) {
|
||||
return false;
|
||||
}
|
||||
@@ -205,9 +209,9 @@ void SwitcherData::loadIdleSwitches(obs_data_t *obj)
|
||||
idleData.load(obj);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupIdleTab()
|
||||
void AdvSceneSwitcher::SetupIdleTab()
|
||||
{
|
||||
populateWindowSelection(ui->ignoreIdleWindowsWindows);
|
||||
PopulateWindowSelection(ui->ignoreIdleWindowsWindows);
|
||||
|
||||
for (auto &window : switcher->ignoreIdleWindows) {
|
||||
QString text = QString::fromStdString(window);
|
||||
@@ -267,7 +271,7 @@ IdleWidget::IdleWidget(QWidget *parent, IdleData *s)
|
||||
{"{{duration}}", duration},
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.idleTab.idleswitch"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.idleTab.idleswitch"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -285,3 +289,5 @@ void IdleWidget::DurationChanged(int dur)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->idleData.time = dur;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#pragma once
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto default_idle_time = 60;
|
||||
constexpr auto idle_func = 2;
|
||||
|
||||
@@ -36,3 +38,5 @@ private:
|
||||
|
||||
IdleData *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
bool MediaSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
|
||||
@@ -232,7 +235,7 @@ void SwitcherData::loadMediaSwitches(obs_data_t *obj)
|
||||
obs_data_array_release(mediaArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupMediaTab()
|
||||
void AdvSceneSwitcher::SetupMediaTab()
|
||||
{
|
||||
for (auto &s : switcher->mediaSwitches) {
|
||||
QListWidgetItem *item;
|
||||
@@ -460,7 +463,7 @@ MediaSwitchWidget::MediaSwitchWidget(QWidget *parent, MediaSwitch *s)
|
||||
QWidget::connect(time, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(TimeChanged(int)));
|
||||
|
||||
populateMediaSelection(mediaSources);
|
||||
PopulateMediaSelection(mediaSources);
|
||||
populateMediaStates(states);
|
||||
populateTimeRestrictions(timeRestrictions);
|
||||
|
||||
@@ -483,7 +486,7 @@ MediaSwitchWidget::MediaSwitchWidget(QWidget *parent, MediaSwitch *s)
|
||||
{"{{time}}", time},
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.mediaTab.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.mediaTab.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -560,3 +563,5 @@ void MediaSwitchWidget::TimeChanged(int time)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->time = time;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#pragma once
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto media_func = 6;
|
||||
|
||||
typedef enum {
|
||||
@@ -83,3 +85,5 @@ private:
|
||||
|
||||
MediaSwitch *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -7,8 +7,12 @@ Most of this code is based on https://github.com/Palakis/obs-websocket
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "scene-switch-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
#define PARAM_SERVER_ENABLE "ServerEnabled"
|
||||
#define PARAM_SERVER_PORT "ServerPort"
|
||||
#define PARAM_LOCKTOIPV4 "LockToIPv4"
|
||||
@@ -124,10 +128,9 @@ WSServer::WSServer() : QObject(nullptr), _connections(), _clMutex()
|
||||
_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));
|
||||
_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()
|
||||
@@ -221,7 +224,7 @@ void WSServer::stop()
|
||||
blog(LOG_INFO, "server stopped successfully");
|
||||
}
|
||||
|
||||
void WSServer::sendMessage(sceneSwitchInfo sceneSwitch, bool preview)
|
||||
void WSServer::sendMessage(SceneSwitchInfo sceneSwitch, bool preview)
|
||||
{
|
||||
if (!sceneSwitch.scene) {
|
||||
return;
|
||||
@@ -305,9 +308,9 @@ std::string processMessage(std::string payload)
|
||||
"'";
|
||||
}
|
||||
if (preview) {
|
||||
switchPreviewScene(scene);
|
||||
SwitchPreviewScene(scene);
|
||||
} else {
|
||||
switchScene({scene, transition, duration});
|
||||
SwitchScene({scene, transition, duration});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -361,11 +364,10 @@ WSClient::WSClient() : QObject(nullptr)
|
||||
_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));
|
||||
_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()
|
||||
@@ -501,7 +503,7 @@ void SwitcherData::saveNetworkSwitches(obs_data_t *obj)
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupNetworkTab()
|
||||
void AdvSceneSwitcher::SetupNetworkTab()
|
||||
{
|
||||
ui->serverSettings->setChecked(switcher->networkConfig.ServerEnabled);
|
||||
ui->serverPort->setValue(switcher->networkConfig.ServerPort);
|
||||
@@ -519,9 +521,9 @@ void AdvSceneSwitcher::setupNetworkTab()
|
||||
|
||||
QTimer *statusTimer = new QTimer(this);
|
||||
connect(statusTimer, SIGNAL(timeout()), this,
|
||||
SLOT(updateClientStatus()));
|
||||
SLOT(UpdateClientStatus()));
|
||||
connect(statusTimer, SIGNAL(timeout()), this,
|
||||
SLOT(updateServerStatus()));
|
||||
SLOT(UpdateServerStatus()));
|
||||
statusTimer->start(500);
|
||||
}
|
||||
|
||||
@@ -572,7 +574,7 @@ void AdvSceneSwitcher::on_serverRestart_clicked()
|
||||
switcher->networkConfig.LockToIPv4);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::updateServerStatus()
|
||||
void AdvSceneSwitcher::UpdateServerStatus()
|
||||
{
|
||||
switch (switcher->serverStatus) {
|
||||
case ServerStatus::NOT_RUNNING:
|
||||
@@ -670,7 +672,7 @@ void AdvSceneSwitcher::on_clientReconnect_clicked()
|
||||
switcher->client.connect(switcher->networkConfig.GetClientUri());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::updateClientStatus()
|
||||
void AdvSceneSwitcher::UpdateClientStatus()
|
||||
{
|
||||
switch (switcher->clientStatus) {
|
||||
case ClientStatus::DISCONNECTED:
|
||||
@@ -709,3 +711,5 @@ Compatability::StdFunctionRunnable::StdFunctionRunnable(
|
||||
: cb(std::move(func))
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -19,11 +19,13 @@ Most of this code is based on https://github.com/Palakis/obs-websocket
|
||||
#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;
|
||||
struct SceneSwitchInfo;
|
||||
|
||||
class NetworkConfig {
|
||||
public:
|
||||
@@ -60,7 +62,7 @@ public:
|
||||
virtual ~WSServer();
|
||||
void start(quint16 port, bool lockToIPv4);
|
||||
void stop();
|
||||
void sendMessage(sceneSwitchInfo sceneSwitch, bool preview = false);
|
||||
void sendMessage(SceneSwitchInfo sceneSwitch, bool preview = false);
|
||||
QThreadPool *threadPool() { return &_threadPool; }
|
||||
|
||||
private:
|
||||
@@ -131,3 +133,5 @@ public:
|
||||
|
||||
QRunnable *CreateFunctionRunnable(std::function<void()> func);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
namespace advss {
|
||||
|
||||
static QMetaObject::Connection addPulse;
|
||||
|
||||
@@ -244,7 +247,7 @@ void SwitcherData::loadPauseSwitches(obs_data_t *obj)
|
||||
obs_data_array_release(pauseArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupPauseTab()
|
||||
void AdvSceneSwitcher::SetupPauseTab()
|
||||
{
|
||||
for (auto &s : switcher->pauseEntries) {
|
||||
QListWidgetItem *item;
|
||||
@@ -309,7 +312,7 @@ PauseEntryWidget::PauseEntryWidget(QWidget *parent, PauseEntry *s)
|
||||
|
||||
populatePauseTypes(pauseTypes);
|
||||
populatePauseTargets(pauseTargets);
|
||||
populateWindowSelection(windows);
|
||||
PopulateWindowSelection(windows);
|
||||
|
||||
windows->setEditable(true);
|
||||
windows->setMaxVisibleItems(20);
|
||||
@@ -335,7 +338,7 @@ PauseEntryWidget::PauseEntryWidget(QWidget *parent, PauseEntry *s)
|
||||
{"{{pauseTargets}}", pauseTargets},
|
||||
{"{{windows}}", windows}};
|
||||
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.pauseTab.pauseEntry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.pauseTab.pauseEntry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -410,3 +413,5 @@ bool PauseEntry::valid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#pragma once
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
enum class PauseType {
|
||||
Scene,
|
||||
Window,
|
||||
@@ -70,3 +72,5 @@ private:
|
||||
|
||||
PauseEntry *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
|
||||
void AdvSceneSwitcher::on_threadPriority_currentTextChanged(const QString &text)
|
||||
{
|
||||
if (loading || ui->threadPriority->count() !=
|
||||
(int)switcher->threadPriorities.size())
|
||||
return;
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
|
||||
for (auto p : switcher->threadPriorities) {
|
||||
if (p.name == text.toUtf8().constData()) {
|
||||
switcher->threadPriority = p.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_priorityUp_clicked()
|
||||
{
|
||||
int currentIndex = ui->priorityList->currentRow();
|
||||
if (currentIndex != -1 && currentIndex != 0) {
|
||||
ui->priorityList->insertItem(
|
||||
currentIndex - 1,
|
||||
ui->priorityList->takeItem(currentIndex));
|
||||
ui->priorityList->setCurrentRow(currentIndex - 1);
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
|
||||
iter_swap(switcher->functionNamesByPriority.begin() +
|
||||
currentIndex,
|
||||
switcher->functionNamesByPriority.begin() +
|
||||
currentIndex - 1);
|
||||
}
|
||||
ui->macroPriorityWarning->setVisible(
|
||||
switcher->functionNamesByPriority[0] != macro_func);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_priorityDown_clicked()
|
||||
{
|
||||
int currentIndex = ui->priorityList->currentRow();
|
||||
if (currentIndex != -1 &&
|
||||
currentIndex != ui->priorityList->count() - 1) {
|
||||
ui->priorityList->insertItem(
|
||||
currentIndex + 1,
|
||||
ui->priorityList->takeItem(currentIndex));
|
||||
ui->priorityList->setCurrentRow(currentIndex + 1);
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
|
||||
iter_swap(switcher->functionNamesByPriority.begin() +
|
||||
currentIndex,
|
||||
switcher->functionNamesByPriority.begin() +
|
||||
currentIndex + 1);
|
||||
}
|
||||
ui->macroPriorityWarning->setVisible(
|
||||
switcher->functionNamesByPriority[0] != macro_func);
|
||||
}
|
||||
|
||||
bool SwitcherData::prioFuncsValid()
|
||||
{
|
||||
auto fNBPCopy = functionNamesByPriority;
|
||||
|
||||
std::sort(fNBPCopy.begin(), fNBPCopy.end());
|
||||
auto it = std::unique(fNBPCopy.begin(), fNBPCopy.end());
|
||||
bool wasUnique = (it == fNBPCopy.end());
|
||||
|
||||
if (!wasUnique) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int p : functionNamesByPriority) {
|
||||
if (p < 0 || p > 10) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
namespace advss {
|
||||
|
||||
bool RandomSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
@@ -113,7 +116,7 @@ void SwitcherData::loadRandomSwitches(obs_data_t *obj)
|
||||
obs_data_array_release(randomArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupRandomTab()
|
||||
void AdvSceneSwitcher::SetupRandomTab()
|
||||
{
|
||||
for (auto &s : switcher->randomSwitches) {
|
||||
QListWidgetItem *item;
|
||||
@@ -140,7 +143,8 @@ void AdvSceneSwitcher::setupRandomTab()
|
||||
border-radius: 7px; \
|
||||
border-color: rgb(0,0,0,0) \
|
||||
}");
|
||||
if (switcher->switchIfNotMatching != RANDOM_SWITCH) {
|
||||
if (switcher->switchIfNotMatching !=
|
||||
SwitcherData::NoMatch::RANDOM_SWITCH) {
|
||||
if (!switcher->disableHints) {
|
||||
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
|
||||
QColor(0, 0, 0, 0));
|
||||
@@ -182,7 +186,7 @@ RandomSwitchWidget::RandomSwitchWidget(QWidget *parent, RandomSwitch *s)
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions},
|
||||
{"{{delay}}", delay}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.randomTab.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.randomTab.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -220,3 +224,5 @@ void RandomSwitchWidget::DelayChanged(double d)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->delay = d;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#pragma once
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
struct RandomSwitch : SceneSwitcherEntry {
|
||||
static bool pause;
|
||||
double delay = 0.0;
|
||||
@@ -36,3 +38,5 @@ private:
|
||||
|
||||
RandomSwitch *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
namespace advss {
|
||||
|
||||
bool ScreenRegionSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
|
||||
void AdvSceneSwitcher::clearFrames(QListWidget *list)
|
||||
void AdvSceneSwitcher::ClearFrames(QListWidget *list)
|
||||
{
|
||||
for (int i = 0; i < list->count(); ++i) {
|
||||
ScreenRegionWidget *sw =
|
||||
@@ -48,7 +51,7 @@ void AdvSceneSwitcher::on_showFrame_clicked()
|
||||
showCurrentFrame(ui->screenRegionSwitches);
|
||||
} else {
|
||||
SetShowFrames();
|
||||
clearFrames(ui->screenRegionSwitches);
|
||||
ClearFrames(ui->screenRegionSwitches);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +62,7 @@ void AdvSceneSwitcher::on_screenRegionSwitches_currentRowChanged(int idx)
|
||||
}
|
||||
|
||||
if (switcher->showFrame) {
|
||||
clearFrames(ui->screenRegionSwitches);
|
||||
ClearFrames(ui->screenRegionSwitches);
|
||||
showCurrentFrame(ui->screenRegionSwitches);
|
||||
}
|
||||
}
|
||||
@@ -153,7 +156,7 @@ bool SwitcherData::checkScreenRegionSwitch(OBSWeakSource &scene,
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<int, int> cursorPos = getCursorPos();
|
||||
std::pair<int, int> cursorPos = GetCursorPos();
|
||||
int minRegionSize = 99999;
|
||||
bool match = false;
|
||||
|
||||
@@ -188,7 +191,7 @@ bool SwitcherData::checkScreenRegionSwitch(OBSWeakSource &scene,
|
||||
|
||||
void AdvSceneSwitcher::updateScreenRegionCursorPos()
|
||||
{
|
||||
std::pair<int, int> position = getCursorPos();
|
||||
std::pair<int, int> position = GetCursorPos();
|
||||
ui->cursorXPosition->setText(QString::number(position.first));
|
||||
ui->cursorYPosition->setText(QString::number(position.second));
|
||||
}
|
||||
@@ -228,7 +231,7 @@ void SwitcherData::loadScreenRegionSwitches(obs_data_t *obj)
|
||||
obs_data_array_release(screenRegionArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupRegionTab()
|
||||
void AdvSceneSwitcher::SetupRegionTab()
|
||||
{
|
||||
for (auto &s : switcher->screenRegionSwitches) {
|
||||
QListWidgetItem *item;
|
||||
@@ -318,7 +321,7 @@ ScreenRegionWidget::ScreenRegionWidget(QWidget *parent, ScreenRegionSwitch *s)
|
||||
QWidget::connect(maxY, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MaxYChanged(int)));
|
||||
|
||||
populateSceneSelection(
|
||||
PopulateSceneSelection(
|
||||
excludeScenes, false, false, false, false, nullptr, true,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.screenRegionTab.excludeScenes.None"),
|
||||
@@ -342,7 +345,7 @@ ScreenRegionWidget::ScreenRegionWidget(QWidget *parent, ScreenRegionSwitch *s)
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions},
|
||||
{"{{excludeScenes}}", excludeScenes}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.screenRegionTab.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.screenRegionTab.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -455,3 +458,5 @@ void ScreenRegionWidget::drawFrame()
|
||||
switchData->maxY - switchData->minY);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <QSpinBox>
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto screen_region_func = 4;
|
||||
|
||||
struct ScreenRegionSwitch : SceneSwitcherEntry {
|
||||
@@ -55,3 +57,5 @@ private:
|
||||
|
||||
void drawFrame();
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QTextStream>
|
||||
#include <QScrollArea>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
namespace advss {
|
||||
|
||||
constexpr auto max_extend_text_size = 150;
|
||||
|
||||
@@ -278,7 +281,7 @@ void SwitcherData::loadSceneSequenceSwitches(obs_data_t *obj)
|
||||
obs_data_array_release(sceneSequenceArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupSequenceTab()
|
||||
void AdvSceneSwitcher::SetupSequenceTab()
|
||||
{
|
||||
for (auto &s : switcher->sceneSequenceSwitches) {
|
||||
QListWidgetItem *item;
|
||||
@@ -455,7 +458,7 @@ bool SceneSequenceSwitch::checkDurationMatchInterruptible()
|
||||
|
||||
void SceneSequenceSwitch::prepareUninterruptibleMatch(int &linger)
|
||||
{
|
||||
int dur = delay.seconds * 1000;
|
||||
int dur = delay.Milliseconds();
|
||||
if (dur > 0) {
|
||||
linger = dur;
|
||||
}
|
||||
@@ -588,10 +591,8 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
QWidget::connect(scenes, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SceneChanged(const QString &)));
|
||||
|
||||
QWidget::connect(delay, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DelayChanged(double)));
|
||||
QWidget::connect(delay, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DelayUnitsChanged(DurationUnit)));
|
||||
QWidget::connect(delay, SIGNAL(DurationChanged(const Duration &)), this,
|
||||
SLOT(DelayChanged(const Duration &)));
|
||||
QWidget::connect(startScenes,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(StartSceneChanged(const QString &)));
|
||||
@@ -603,7 +604,7 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
QWidget::connect(reduce, SIGNAL(clicked()), this,
|
||||
SLOT(ReduceClicked()));
|
||||
|
||||
populateSceneSelection(startScenes);
|
||||
PopulateSceneSelection(startScenes);
|
||||
interruptible->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneSequenceTab.interruptibleHint"));
|
||||
|
||||
@@ -615,7 +616,7 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{delay}}", delay},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.sceneSequenceTab.extendEntry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
@@ -628,7 +629,7 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
{"{{delay}}", delay},
|
||||
{"{{transitions}}", transitions},
|
||||
{"{{interruptible}}", interruptible}};
|
||||
placeWidgets(obs_module_text(
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneSequenceTab.entry"),
|
||||
startSequence, widgetPlaceholders);
|
||||
|
||||
@@ -689,24 +690,14 @@ void SequenceWidget::swapSwitchData(SequenceWidget *s1, SequenceWidget *s2)
|
||||
s2->setSwitchData(t);
|
||||
}
|
||||
|
||||
void SequenceWidget::DelayChanged(double sec)
|
||||
void SequenceWidget::DelayChanged(const Duration &delay)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->delay.seconds = sec;
|
||||
}
|
||||
|
||||
void SequenceWidget::DelayUnitsChanged(DurationUnit unit)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->delay.displayUnit = unit;
|
||||
switchData->delay = delay;
|
||||
}
|
||||
|
||||
void SequenceWidget::setExtendedSequenceStartScene()
|
||||
@@ -805,3 +796,5 @@ void SequenceWidget::ReduceClicked()
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "switch-generic.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto round_trip_func = 1;
|
||||
|
||||
struct SceneSequenceSwitch : SceneSwitcherEntry {
|
||||
@@ -59,8 +61,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const QString &text);
|
||||
void DelayChanged(double delay);
|
||||
void DelayUnitsChanged(DurationUnit);
|
||||
void DelayChanged(const Duration &delay);
|
||||
void StartSceneChanged(const QString &text);
|
||||
void InterruptibleChanged(int state);
|
||||
void ExtendClicked();
|
||||
@@ -83,3 +84,5 @@ protected:
|
||||
|
||||
SceneSequenceSwitch *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
bool TimeSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
|
||||
@@ -179,7 +182,7 @@ void SwitcherData::loadTimeSwitches(obs_data_t *obj)
|
||||
obs_data_array_release(timeArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupTimeTab()
|
||||
void AdvSceneSwitcher::SetupTimeTab()
|
||||
{
|
||||
for (auto &s : switcher->timeSwitches) {
|
||||
QListWidgetItem *item;
|
||||
@@ -258,7 +261,7 @@ TimeSwitchWidget::TimeSwitchWidget(QWidget *parent, TimeSwitch *s)
|
||||
{"{{time}}", time},
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.timeTab.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.timeTab.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -306,3 +309,5 @@ void TimeSwitchWidget::TimeChanged(const QTime &time)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->time = time;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto time_func = 7;
|
||||
|
||||
typedef enum {
|
||||
@@ -54,3 +56,5 @@ private:
|
||||
|
||||
TimeSwitch *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
namespace advss {
|
||||
|
||||
constexpr auto default_def_transition_dealy = 300;
|
||||
bool DefaultSceneTransition::pause = false;
|
||||
@@ -169,40 +172,7 @@ void SwitcherData::checkDefaultSceneTransitions()
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state && !switcher->adjustActiveTransitionType) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError"));
|
||||
ui->adjustActiveTransitionType->setChecked(true);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->transitionOverrideOverride = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_adjustActiveTransitionType_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This option only makes sense if we are allowed to use transition overrides
|
||||
if (!state && !switcher->transitionOverrideOverride) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError"));
|
||||
ui->transitionOverridecheckBox->setChecked(true);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->adjustActiveTransitionType = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::defTransitionDelayValueChanged(int value)
|
||||
void AdvSceneSwitcher::DefTransitionDelayValueChanged(int value)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
@@ -212,104 +182,6 @@ void AdvSceneSwitcher::defTransitionDelayValueChanged(int value)
|
||||
DefaultSceneTransition::delay = value;
|
||||
}
|
||||
|
||||
std::pair<obs_weak_source_t *, int> getNextTransition(obs_weak_source_t *scene1,
|
||||
obs_weak_source_t *scene2)
|
||||
{
|
||||
obs_weak_source_t *ws = nullptr;
|
||||
int duration = 0;
|
||||
if (scene1 && scene2) {
|
||||
for (SceneTransition &t : switcher->sceneTransitions) {
|
||||
if (!t.initialized()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t.scene == scene1 && t.scene2 == scene2) {
|
||||
ws = t.transition;
|
||||
duration = t.duration * 1000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_pair(ws, duration);
|
||||
}
|
||||
|
||||
void overwriteTransitionOverride(const sceneSwitchInfo &ssi, transitionData &td)
|
||||
{
|
||||
obs_source_t *scene = obs_weak_source_get_source(ssi.scene);
|
||||
obs_data_t *data = obs_source_get_private_settings(scene);
|
||||
|
||||
td.name = obs_data_get_string(data, "transition");
|
||||
td.duration = obs_data_get_int(data, "transition_duration");
|
||||
|
||||
std::string name = GetWeakSourceName(ssi.transition);
|
||||
|
||||
obs_data_set_string(data, "transition", name.c_str());
|
||||
obs_data_set_int(data, "transition_duration", ssi.duration);
|
||||
|
||||
obs_data_release(data);
|
||||
obs_source_release(scene);
|
||||
}
|
||||
|
||||
void restoreTransitionOverride(obs_source_t *scene, const transitionData &td)
|
||||
{
|
||||
obs_data_t *data = obs_source_get_private_settings(scene);
|
||||
|
||||
obs_data_set_string(data, "transition", td.name.c_str());
|
||||
obs_data_set_int(data, "transition_duration", td.duration);
|
||||
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
void setNextTransition(const sceneSwitchInfo &sceneSwitch,
|
||||
obs_source_t *currentSource, transitionData &td)
|
||||
{
|
||||
// Priority:
|
||||
// 1. Transition tab
|
||||
// 2. Individual switcher entry
|
||||
// 3. Current transition settings
|
||||
|
||||
// Transition Tab
|
||||
obs_weak_source_t *currentScene =
|
||||
obs_source_get_weak_source(currentSource);
|
||||
auto tinfo = getNextTransition(currentScene, sceneSwitch.scene);
|
||||
obs_weak_source_release(currentScene);
|
||||
|
||||
OBSWeakSource nextTransition = tinfo.first;
|
||||
int nextTransitionDuration = tinfo.second;
|
||||
|
||||
// Individual switcher entry
|
||||
if (!nextTransition) {
|
||||
nextTransition = sceneSwitch.transition;
|
||||
}
|
||||
if (!nextTransitionDuration) {
|
||||
nextTransitionDuration = sceneSwitch.duration;
|
||||
}
|
||||
|
||||
// Current transition settings
|
||||
if (!nextTransition) {
|
||||
auto ct = obs_frontend_get_current_transition();
|
||||
nextTransition = obs_source_get_weak_source(ct);
|
||||
obs_weak_source_release(nextTransition);
|
||||
obs_source_release(ct);
|
||||
}
|
||||
if (!nextTransitionDuration) {
|
||||
nextTransitionDuration = obs_frontend_get_transition_duration();
|
||||
}
|
||||
|
||||
if (switcher->adjustActiveTransitionType) {
|
||||
obs_frontend_set_transition_duration(nextTransitionDuration);
|
||||
auto t = obs_weak_source_get_source(nextTransition);
|
||||
obs_frontend_set_current_transition(t);
|
||||
obs_source_release(t);
|
||||
}
|
||||
|
||||
if (switcher->transitionOverrideOverride) {
|
||||
overwriteTransitionOverride({sceneSwitch.scene, nextTransition,
|
||||
nextTransitionDuration},
|
||||
td);
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::saveSceneTransitions(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *sceneTransitionsArray = obs_data_array_create();
|
||||
@@ -335,13 +207,6 @@ void SwitcherData::saveSceneTransitions(obs_data_t *obj)
|
||||
}
|
||||
obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray);
|
||||
obs_data_array_release(defaultTransitionsArray);
|
||||
|
||||
obs_data_set_bool(obj, "tansitionOverrideOverride",
|
||||
transitionOverrideOverride);
|
||||
obs_data_set_default_bool(obj, "adjustActiveTransitionType",
|
||||
adjustActiveTransitionType);
|
||||
obs_data_set_bool(obj, "adjustActiveTransitionType",
|
||||
adjustActiveTransitionType);
|
||||
obs_data_set_default_int(obj, "defTransitionDelay",
|
||||
default_def_transition_dealy);
|
||||
obs_data_set_int(obj, "defTransitionDelay",
|
||||
@@ -380,11 +245,6 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
|
||||
}
|
||||
obs_data_array_release(defaultTransitionsArray);
|
||||
|
||||
transitionOverrideOverride =
|
||||
obs_data_get_bool(obj, "tansitionOverrideOverride");
|
||||
adjustActiveTransitionType =
|
||||
obs_data_get_bool(obj, "adjustActiveTransitionType");
|
||||
|
||||
// Check for invalid config
|
||||
if (!transitionOverrideOverride && !adjustActiveTransitionType) {
|
||||
adjustActiveTransitionType = true;
|
||||
@@ -394,7 +254,7 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
|
||||
obs_data_get_int(obj, "defTransitionDelay");
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupTransitionsTab()
|
||||
void AdvSceneSwitcher::SetupTransitionsTab()
|
||||
{
|
||||
for (auto &s : switcher->sceneTransitions) {
|
||||
QListWidgetItem *item;
|
||||
@@ -442,11 +302,11 @@ void AdvSceneSwitcher::setupTransitionsTab()
|
||||
"AdvSceneSwitcher.transitionTab.defaultTransition.delay.help"));
|
||||
|
||||
QWidget::connect(defTransitionDelay, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(defTransitionDelayValueChanged(int)));
|
||||
SLOT(DefTransitionDelayValueChanged(int)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{defTransitionDelay}}", defTransitionDelay}};
|
||||
placeWidgets(
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.transitionTab.defaultTransition.delay"),
|
||||
ui->defTransitionDelayLayout, widgetPlaceholders);
|
||||
@@ -494,7 +354,7 @@ TransitionSwitchWidget::TransitionSwitchWidget(QWidget *parent,
|
||||
QWidget::connect(duration, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
|
||||
populateSceneSelection(scenes2);
|
||||
PopulateSceneSelection(scenes2);
|
||||
|
||||
if (s) {
|
||||
scenes2->setCurrentText(GetWeakSourceName(s->scene2).c_str());
|
||||
@@ -507,7 +367,7 @@ TransitionSwitchWidget::TransitionSwitchWidget(QWidget *parent,
|
||||
{"{{scenes2}}", scenes2},
|
||||
{"{{duration}}", duration},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.transitionTab.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.transitionTab.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -563,7 +423,7 @@ DefTransitionSwitchWidget::DefTransitionSwitchWidget(QWidget *parent,
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", scenes}, {"{{transitions}}", transitions}};
|
||||
placeWidgets(
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.transitionTab.defaultTransitionEntry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
@@ -637,3 +497,5 @@ void DefaultSceneTransition::setTransition()
|
||||
t = std::thread(setTransitionDelayed, transition, delay);
|
||||
t.detach();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
struct SceneTransition : SceneSwitcherEntry {
|
||||
OBSWeakSource scene2 = nullptr;
|
||||
double duration = 0.3;
|
||||
@@ -77,3 +79,5 @@ public:
|
||||
private:
|
||||
DefaultSceneTransition *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QBuffer>
|
||||
#include <QToolTip>
|
||||
#include <thread>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
namespace advss {
|
||||
|
||||
bool VideoSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
@@ -182,7 +185,7 @@ void SwitcherData::loadVideoSwitches(obs_data_t *obj)
|
||||
obs_data_array_release(videoArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupVideoTab()
|
||||
void AdvSceneSwitcher::SetupVideoTab()
|
||||
{
|
||||
for (auto &s : switcher->videoSwitches) {
|
||||
QListWidgetItem *item;
|
||||
@@ -428,7 +431,7 @@ VideoSwitchWidget::VideoSwitchWidget(QWidget *parent, VideoSwitch *s)
|
||||
{"{{ignoreInactiveSource}}", ignoreInactiveSource},
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.videoTab.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.videoTab.entry"),
|
||||
switchLayout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
@@ -577,3 +580,5 @@ void VideoSwitchWidget::IgnoreInactiveChanged(int state)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->ignoreInactiveSource = state;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "switch-generic.hpp"
|
||||
#include "screenshot-helper.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto video_func = 9;
|
||||
|
||||
enum class videoSwitchType {
|
||||
@@ -80,3 +82,5 @@ private:
|
||||
|
||||
VideoSwitch *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "platform-funcs.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
namespace advss {
|
||||
|
||||
bool WindowSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
@@ -177,8 +181,8 @@ void checkWindowTitleSwitchDirect(WindowSwitch &s,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
bool focus = (!s.focus || s.window == currentWindowTitle);
|
||||
bool fullscreen = (!s.fullscreen || isFullscreen(s.window));
|
||||
bool max = (!s.maximized || isMaximized(s.window));
|
||||
bool fullscreen = (!s.fullscreen || IsFullscreen(s.window));
|
||||
bool max = (!s.maximized || IsMaximized(s.window));
|
||||
|
||||
if (focus && fullscreen && max) {
|
||||
match = true;
|
||||
@@ -203,8 +207,8 @@ void checkWindowTitleSwitchRegex(WindowSwitch &s,
|
||||
}
|
||||
|
||||
bool focus = (!s.focus || window == currentWindowTitle);
|
||||
bool fullscreen = (!s.fullscreen || isFullscreen(window));
|
||||
bool max = (!s.maximized || isMaximized(window));
|
||||
bool fullscreen = (!s.fullscreen || IsFullscreen(window));
|
||||
bool max = (!s.maximized || IsMaximized(window));
|
||||
|
||||
if (focus && fullscreen && max) {
|
||||
match = true;
|
||||
@@ -315,7 +319,7 @@ void SwitcherData::loadWindowTitleSwitches(obs_data_t *obj)
|
||||
obs_data_array_release(ignoreWindowsArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupTitleTab()
|
||||
void AdvSceneSwitcher::SetupTitleTab()
|
||||
{
|
||||
for (auto &s : switcher->windowSwitches) {
|
||||
QListWidgetItem *item;
|
||||
@@ -336,7 +340,7 @@ void AdvSceneSwitcher::setupTitleTab()
|
||||
ui->windowHelp->setVisible(false);
|
||||
}
|
||||
|
||||
populateWindowSelection(ui->ignoreWindowsWindows);
|
||||
PopulateWindowSelection(ui->ignoreWindowsWindows);
|
||||
|
||||
for (auto &window : switcher->ignoreWindowsSwitches) {
|
||||
QString text = QString::fromStdString(window);
|
||||
@@ -394,7 +398,7 @@ WindowSwitchWidget::WindowSwitchWidget(QWidget *parent, WindowSwitch *s)
|
||||
QWidget::connect(focused, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(FocusChanged(int)));
|
||||
|
||||
populateWindowSelection(windows);
|
||||
PopulateWindowSelection(windows);
|
||||
|
||||
windows->setEditable(true);
|
||||
windows->setMaxVisibleItems(20);
|
||||
@@ -414,7 +418,7 @@ WindowSwitchWidget::WindowSwitchWidget(QWidget *parent, WindowSwitch *s)
|
||||
{"{{fullscreen}}", fullscreen},
|
||||
{"{{maximized}}", maximized},
|
||||
{"{{focused}}", focused}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.windowTitleTab.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.windowTitleTab.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -482,3 +486,5 @@ void WindowSwitchWidget::FocusChanged(int state)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->focus = state;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#pragma once
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto window_title_func = 5;
|
||||
|
||||
struct WindowSwitch : SceneSwitcherEntry {
|
||||
@@ -47,3 +49,5 @@ private:
|
||||
|
||||
WindowSwitch *switchData;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "platform-funcs.hpp"
|
||||
#include "hotkey.hpp"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
@@ -24,10 +25,16 @@
|
||||
#include <QStringList>
|
||||
#include <QRegularExpression>
|
||||
#include <QLibrary>
|
||||
#ifdef USE_PROCPS
|
||||
#include <proc/readproc.h>
|
||||
#else
|
||||
#include <libproc2/pids.h>
|
||||
#endif
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace advss {
|
||||
|
||||
static Display *xdisplay = 0;
|
||||
|
||||
static QLibrary *libXtstHandle = nullptr;
|
||||
@@ -35,18 +42,31 @@ typedef int (*keyPressFunc)(Display *, unsigned int, bool, unsigned long);
|
||||
static keyPressFunc pressFunc = nullptr;
|
||||
bool canSimulateKeyPresses = false;
|
||||
|
||||
static QLibrary *libXssHandle = nullptr;
|
||||
typedef XScreenSaverInfo *(*XScreenSaverAllocInfoFunc)();
|
||||
typedef int (*XScreenSaverQueryInfoFunc)(Display *, Window, XScreenSaverInfo *);
|
||||
static XScreenSaverAllocInfoFunc allocSSFunc = nullptr;
|
||||
static XScreenSaverQueryInfoFunc querySSFunc = nullptr;
|
||||
bool canGetIdleTime = false;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point lastMouseLeftClickTime{};
|
||||
std::chrono::high_resolution_clock::time_point lastMouseMiddleClickTime{};
|
||||
std::chrono::high_resolution_clock::time_point lastMouseRightClickTime{};
|
||||
|
||||
Display *disp()
|
||||
{
|
||||
if (!xdisplay)
|
||||
if (!xdisplay) {
|
||||
xdisplay = XOpenDisplay(NULL);
|
||||
}
|
||||
|
||||
return xdisplay;
|
||||
}
|
||||
|
||||
void cleanupDisplay()
|
||||
{
|
||||
if (!xdisplay)
|
||||
if (!xdisplay) {
|
||||
return;
|
||||
}
|
||||
|
||||
XCloseDisplay(xdisplay);
|
||||
xdisplay = 0;
|
||||
@@ -55,22 +75,29 @@ void cleanupDisplay()
|
||||
static bool ewmhIsSupported()
|
||||
{
|
||||
Display *display = disp();
|
||||
if (!display) {
|
||||
return false;
|
||||
}
|
||||
auto window = DefaultRootWindow(display);
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Atom netSupportingWmCheck =
|
||||
XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", true);
|
||||
Atom actualType;
|
||||
int format = 0;
|
||||
unsigned long num = 0, bytes = 0;
|
||||
unsigned char *data = NULL;
|
||||
Window ewmh_window = 0;
|
||||
Window ewmhWindow = 0;
|
||||
|
||||
int status = XGetWindowProperty(display, DefaultRootWindow(display),
|
||||
netSupportingWmCheck, 0L, 1L, false,
|
||||
XA_WINDOW, &actualType, &format, &num,
|
||||
&bytes, &data);
|
||||
int status = XGetWindowProperty(display, window, netSupportingWmCheck,
|
||||
0L, 1L, false, XA_WINDOW, &actualType,
|
||||
&format, &num, &bytes, &data);
|
||||
|
||||
if (status == Success) {
|
||||
if (num > 0) {
|
||||
ewmh_window = ((Window *)data)[0];
|
||||
ewmhWindow = ((Window *)data)[0];
|
||||
}
|
||||
if (data) {
|
||||
XFree(data);
|
||||
@@ -78,29 +105,28 @@ static bool ewmhIsSupported()
|
||||
}
|
||||
}
|
||||
|
||||
if (ewmh_window) {
|
||||
status = XGetWindowProperty(display, ewmh_window,
|
||||
if (ewmhWindow) {
|
||||
status = XGetWindowProperty(display, ewmhWindow,
|
||||
netSupportingWmCheck, 0L, 1L, false,
|
||||
XA_WINDOW, &actualType, &format,
|
||||
&num, &bytes, &data);
|
||||
if (status != Success || num == 0 ||
|
||||
ewmh_window != ((Window *)data)[0]) {
|
||||
ewmh_window = 0;
|
||||
ewmhWindow != ((Window *)data)[0]) {
|
||||
ewmhWindow = 0;
|
||||
}
|
||||
if (status == Success && data) {
|
||||
XFree(data);
|
||||
}
|
||||
}
|
||||
|
||||
return ewmh_window != 0;
|
||||
return ewmhWindow != 0;
|
||||
}
|
||||
|
||||
static QStringList getStates(Window window)
|
||||
{
|
||||
QStringList states;
|
||||
|
||||
if (!ewmhIsSupported())
|
||||
if (!window || !ewmhIsSupported()) {
|
||||
return states;
|
||||
}
|
||||
|
||||
Atom wmState = XInternAtom(disp(), "_NET_WM_STATE", true), type;
|
||||
int format;
|
||||
@@ -111,10 +137,13 @@ static QStringList getStates(Window window)
|
||||
AnyPropertyType, &type, &format, &num,
|
||||
&bytes, &data);
|
||||
|
||||
if (status == Success)
|
||||
for (unsigned long i = 0; i < num; i++)
|
||||
if (status == Success) {
|
||||
for (unsigned long i = 0; i < num; i++) {
|
||||
states.append(QString(
|
||||
XGetAtomName(disp(), ((Atom *)data)[i])));
|
||||
}
|
||||
XFree(data);
|
||||
}
|
||||
|
||||
return states;
|
||||
}
|
||||
@@ -122,9 +151,6 @@ static QStringList getStates(Window window)
|
||||
static std::vector<Window> getTopLevelWindows()
|
||||
{
|
||||
std::vector<Window> res;
|
||||
|
||||
res.resize(0);
|
||||
|
||||
if (!ewmhIsSupported()) {
|
||||
return res;
|
||||
}
|
||||
@@ -136,10 +162,13 @@ static std::vector<Window> getTopLevelWindows()
|
||||
Window *data = 0;
|
||||
|
||||
for (int i = 0; i < ScreenCount(disp()); ++i) {
|
||||
Window rootWin = RootWindow(disp(), i);
|
||||
Window rootWindow = RootWindow(disp(), i);
|
||||
if (!rootWindow) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int status = XGetWindowProperty(disp(), rootWin, netClList, 0L,
|
||||
~0L, false, AnyPropertyType,
|
||||
int status = XGetWindowProperty(disp(), rootWindow, netClList,
|
||||
0L, ~0L, false, AnyPropertyType,
|
||||
&actualType, &format, &num,
|
||||
&bytes, (uint8_t **)&data);
|
||||
|
||||
@@ -147,8 +176,9 @@ static std::vector<Window> getTopLevelWindows()
|
||||
continue;
|
||||
}
|
||||
|
||||
for (unsigned long i = 0; i < num; ++i)
|
||||
for (unsigned long i = 0; i < num; ++i) {
|
||||
res.emplace_back(data[i]);
|
||||
}
|
||||
|
||||
XFree(data);
|
||||
}
|
||||
@@ -156,24 +186,27 @@ static std::vector<Window> getTopLevelWindows()
|
||||
return res;
|
||||
}
|
||||
|
||||
static std::string GetWindowTitle(size_t i)
|
||||
std::string getWindowName(Window window)
|
||||
{
|
||||
Window w = getTopLevelWindows().at(i);
|
||||
auto display = disp();
|
||||
if (!display || !window) {
|
||||
return "";
|
||||
}
|
||||
std::string windowTitle;
|
||||
char *name;
|
||||
|
||||
XTextProperty text;
|
||||
int status = XGetTextProperty(
|
||||
disp(), w, &text, XInternAtom(disp(), "_NET_WM_NAME", true));
|
||||
if (status == 0)
|
||||
status = XGetTextProperty(disp(), w, &text,
|
||||
XInternAtom(disp(), "WM_NAME", true));
|
||||
name = reinterpret_cast<char *>(text.value);
|
||||
|
||||
if (status != 0 && name != nullptr) {
|
||||
int status = XFetchName(display, window, &name);
|
||||
if (status >= Success && name != nullptr) {
|
||||
std::string str(name);
|
||||
windowTitle = str;
|
||||
XFree(name);
|
||||
} else {
|
||||
XTextProperty xtp_new_name;
|
||||
if (XGetWMName(display, window, &xtp_new_name) != 0 &&
|
||||
xtp_new_name.value != nullptr) {
|
||||
std::string str((const char *)xtp_new_name.value);
|
||||
windowTitle = str;
|
||||
XFree(xtp_new_name.value);
|
||||
}
|
||||
}
|
||||
|
||||
return windowTitle;
|
||||
@@ -182,187 +215,130 @@ static std::string GetWindowTitle(size_t i)
|
||||
void GetWindowList(std::vector<std::string> &windows)
|
||||
{
|
||||
windows.resize(0);
|
||||
|
||||
for (size_t i = 0; i < getTopLevelWindows().size(); ++i) {
|
||||
if (GetWindowTitle(i) != "")
|
||||
windows.emplace_back(GetWindowTitle(i));
|
||||
for (auto window : getTopLevelWindows()) {
|
||||
auto name = getWindowName(window);
|
||||
if (name.empty()) {
|
||||
continue;
|
||||
}
|
||||
windows.emplace_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Overloaded
|
||||
void GetWindowList(QStringList &windows)
|
||||
{
|
||||
windows.clear();
|
||||
|
||||
for (size_t i = 0; i < getTopLevelWindows().size(); ++i) {
|
||||
if (GetWindowTitle(i) != "")
|
||||
windows << QString::fromStdString(GetWindowTitle(i));
|
||||
for (auto window : getTopLevelWindows()) {
|
||||
auto name = getWindowName(window);
|
||||
if (name.empty()) {
|
||||
continue;
|
||||
}
|
||||
windows << QString::fromStdString(name);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCurrentWindowTitle(std::string &title)
|
||||
int getActiveWindow(Window *&window)
|
||||
{
|
||||
if (!ewmhIsSupported()) {
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Atom active = XInternAtom(disp(), "_NET_ACTIVE_WINDOW", true);
|
||||
Atom actualType;
|
||||
int format;
|
||||
unsigned long num, bytes;
|
||||
|
||||
auto rootWindow = DefaultRootWindow(disp());
|
||||
if (!rootWindow) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
return XGetWindowProperty(disp(), rootWindow, active, 0L, ~0L, false,
|
||||
AnyPropertyType, &actualType, &format, &num,
|
||||
&bytes, (uint8_t **)&window);
|
||||
}
|
||||
|
||||
void GetCurrentWindowTitle(std::string &title)
|
||||
{
|
||||
Window *data = 0;
|
||||
char *name;
|
||||
|
||||
Window rootWin = RootWindow(disp(), 0);
|
||||
|
||||
int xstatus = XGetWindowProperty(disp(), rootWin, active, 0L, ~0L,
|
||||
false, AnyPropertyType, &actualType,
|
||||
&format, &num, &bytes,
|
||||
(uint8_t **)&data);
|
||||
|
||||
if (data == nullptr) {
|
||||
if (getActiveWindow(data) != Success || !data) {
|
||||
return;
|
||||
}
|
||||
if (!data[0]) {
|
||||
XFree(data);
|
||||
return;
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
XTextProperty text;
|
||||
if (xstatus == Success && data[0]) {
|
||||
status = XGetTextProperty(disp(), data[0], &text,
|
||||
XInternAtom(disp(), "_NET_WM_NAME",
|
||||
true));
|
||||
if (status == 0)
|
||||
status = XGetTextProperty(disp(), data[0], &text,
|
||||
XInternAtom(disp(), "WM_NAME",
|
||||
true));
|
||||
}
|
||||
name = reinterpret_cast<char *>(text.value);
|
||||
|
||||
if (status != 0 && name != nullptr) {
|
||||
std::string str(name);
|
||||
title = str;
|
||||
XFree(name);
|
||||
auto name = getWindowName(data[0]);
|
||||
XFree(data);
|
||||
if (name.empty()) {
|
||||
return;
|
||||
}
|
||||
title = name;
|
||||
}
|
||||
|
||||
std::pair<int, int> getCursorPos()
|
||||
bool windowStatesAreSet(const std::string &windowTitle,
|
||||
std::vector<QString> &expectedStates)
|
||||
{
|
||||
std::pair<int, int> pos(0, 0);
|
||||
Display *dpy;
|
||||
Window root;
|
||||
Window ret_root;
|
||||
Window ret_child;
|
||||
int root_x;
|
||||
int root_y;
|
||||
int win_x;
|
||||
int win_y;
|
||||
unsigned int mask;
|
||||
|
||||
dpy = disp();
|
||||
root = XDefaultRootWindow(dpy);
|
||||
|
||||
if (XQueryPointer(dpy, root, &ret_root, &ret_child, &root_x, &root_y,
|
||||
&win_x, &win_y, &mask)) {
|
||||
pos = std::pair<int, int>(root_x, root_y);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool isMaximized(const std::string &title)
|
||||
{
|
||||
if (!ewmhIsSupported())
|
||||
if (!ewmhIsSupported()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find switch in top level windows
|
||||
std::vector<Window> windows = getTopLevelWindows();
|
||||
for (auto &window : windows) {
|
||||
XTextProperty text;
|
||||
int status = XGetTextProperty(
|
||||
disp(), window, &text,
|
||||
XInternAtom(disp(), "_NET_WM_NAME", true));
|
||||
if (status == 0)
|
||||
status = XGetTextProperty(disp(), window, &text,
|
||||
XInternAtom(disp(), "WM_NAME",
|
||||
true));
|
||||
char *name = reinterpret_cast<char *>(text.value);
|
||||
|
||||
if (status == 0 || name == nullptr)
|
||||
auto name = getWindowName(window);
|
||||
if (name.empty()) {
|
||||
continue;
|
||||
|
||||
// True if switch equals window
|
||||
bool equals = (title == name);
|
||||
// True if switch matches window
|
||||
bool matches = QString::fromStdString(name).contains(
|
||||
QRegularExpression(QString::fromStdString(title)));
|
||||
|
||||
// If found, check if switch is maximized
|
||||
if (equals || matches) {
|
||||
QStringList states = getStates(window);
|
||||
|
||||
if (!states.isEmpty()) {
|
||||
// True if window is maximized vertically
|
||||
bool vertical = states.contains(
|
||||
"_NET_WM_STATE_MAXIMIZED_VERT");
|
||||
// True if window is maximized horizontally
|
||||
bool horizontal = states.contains(
|
||||
"_NET_WM_STATE_MAXIMIZED_HORZ");
|
||||
|
||||
return (vertical && horizontal);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool equals = windowTitle == name;
|
||||
bool matches = QString::fromStdString(name).contains(
|
||||
QRegularExpression(
|
||||
QString::fromStdString(windowTitle)));
|
||||
|
||||
if (!(equals || matches)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QStringList states = getStates(window);
|
||||
if (states.isEmpty()) {
|
||||
if (expectedStates.empty()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto &state : expectedStates) {
|
||||
if (!states.contains(state)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isFullscreen(const std::string &title)
|
||||
bool IsMaximized(const std::string &title)
|
||||
{
|
||||
if (!ewmhIsSupported())
|
||||
return false;
|
||||
|
||||
// Find switch in top level windows
|
||||
std::vector<Window> windows = getTopLevelWindows();
|
||||
for (auto &window : windows) {
|
||||
XTextProperty text;
|
||||
int status = XGetTextProperty(
|
||||
disp(), window, &text,
|
||||
XInternAtom(disp(), "_NET_WM_NAME", true));
|
||||
if (status == 0)
|
||||
status = XGetTextProperty(disp(), window, &text,
|
||||
XInternAtom(disp(), "WM_NAME",
|
||||
true));
|
||||
char *name = reinterpret_cast<char *>(text.value);
|
||||
|
||||
if (status == 0 || name == nullptr)
|
||||
continue;
|
||||
|
||||
// True if switch equals window
|
||||
bool equals = (title == name);
|
||||
// True if switch matches window
|
||||
bool matches = QString::fromStdString(name).contains(
|
||||
QRegularExpression(QString::fromStdString(title)));
|
||||
|
||||
// If found, check if switch is fullscreen
|
||||
if (equals || matches) {
|
||||
QStringList states = getStates(window);
|
||||
|
||||
if (!states.isEmpty()) {
|
||||
// True if window is fullscreen
|
||||
bool fullscreen = states.contains(
|
||||
"_NET_WM_STATE_FULLSCREEN");
|
||||
|
||||
return (fullscreen);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
std::vector<QString> states;
|
||||
states.emplace_back("_NET_WM_STATE_MAXIMIZED_VERT");
|
||||
states.emplace_back("_NET_WM_STATE_MAXIMIZED_HORZ");
|
||||
return windowStatesAreSet(title, states);
|
||||
}
|
||||
|
||||
//exe switch is not quite what is expected but it works for now
|
||||
bool IsFullscreen(const std::string &title)
|
||||
{
|
||||
std::vector<QString> states;
|
||||
states.emplace_back("_NET_WM_STATE_FULLSCREEN");
|
||||
return windowStatesAreSet(title, states);
|
||||
}
|
||||
|
||||
std::optional<std::string> GetTextInWindow(const std::string &window)
|
||||
{
|
||||
// Not implemented
|
||||
return {};
|
||||
}
|
||||
|
||||
#ifdef USE_PROCPS
|
||||
void GetProcessList(QStringList &processes)
|
||||
{
|
||||
processes.clear();
|
||||
@@ -371,48 +347,56 @@ void GetProcessList(QStringList &processes)
|
||||
memset(&proc_info, 0, sizeof(proc_info));
|
||||
while (readproc(proc, &proc_info) != NULL) {
|
||||
QString procName(proc_info.cmd);
|
||||
if (!procName.isEmpty() && !processes.contains(proc_info.cmd)) {
|
||||
processes << QString(proc_info.cmd);
|
||||
if (!procName.isEmpty() && !processes.contains(procName)) {
|
||||
processes << procName;
|
||||
}
|
||||
}
|
||||
closeproc(proc);
|
||||
}
|
||||
|
||||
int getForegroundProcessPid()
|
||||
#else
|
||||
void GetProcessList(QStringList &processes)
|
||||
{
|
||||
if (!ewmhIsSupported()) {
|
||||
return -1;
|
||||
processes.clear();
|
||||
struct pids_info *info = NULL;
|
||||
struct pids_stack *stack;
|
||||
enum pids_item Items[] = {
|
||||
PIDS_CMD,
|
||||
};
|
||||
|
||||
if (procps_pids_new(&info, Items, sizeof(Items) / sizeof(Items[0])) <
|
||||
0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dpy = disp();
|
||||
Atom active = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", true);
|
||||
Atom actualType;
|
||||
int format;
|
||||
unsigned long num, bytes;
|
||||
Window *window = 0;
|
||||
int pid = -1;
|
||||
while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
|
||||
auto cmd = PIDS_VAL(0, str, stack, info);
|
||||
QString procName(cmd);
|
||||
if (!procName.isEmpty() && !processes.contains(procName)) {
|
||||
processes << procName;
|
||||
}
|
||||
}
|
||||
procps_pids_unref(&info);
|
||||
}
|
||||
#endif
|
||||
|
||||
Window rootWin = RootWindow(dpy, 0);
|
||||
|
||||
int xstatus = XGetWindowProperty(dpy, rootWin, active, 0L, ~0L, false,
|
||||
AnyPropertyType, &actualType, &format,
|
||||
&num, &bytes, (uint8_t **)&window);
|
||||
|
||||
if (xstatus == 0 && window == nullptr) {
|
||||
long getForegroundProcessPid()
|
||||
{
|
||||
Window *window;
|
||||
if (getActiveWindow(window) != Success || !window || !*window) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Atom atom, actual_type;
|
||||
int actual_format;
|
||||
unsigned long nitems;
|
||||
unsigned long bytes_after;
|
||||
unsigned char *prop;
|
||||
atom = XInternAtom(dpy, "_NET_WM_PID", True);
|
||||
auto status = XGetWindowProperty(dpy, *window, atom, 0, 1024, False,
|
||||
AnyPropertyType, &actual_type,
|
||||
long long pid;
|
||||
atom = XInternAtom(disp(), "_NET_WM_PID", True);
|
||||
auto status = XGetWindowProperty(disp(), *window, atom, 0, 1024, False,
|
||||
XA_CARDINAL, &actual_type,
|
||||
&actual_format, &nitems, &bytes_after,
|
||||
&prop);
|
||||
|
||||
XFree(window);
|
||||
if (status != 0) {
|
||||
return -2;
|
||||
}
|
||||
@@ -420,24 +404,42 @@ int getForegroundProcessPid()
|
||||
return -3;
|
||||
}
|
||||
|
||||
pid = prop[1] * 256;
|
||||
pid += prop[0];
|
||||
pid = *((long *)prop);
|
||||
XFree(prop);
|
||||
return pid;
|
||||
}
|
||||
|
||||
std::string getProcNameFromPid(int pid)
|
||||
std::string getProcNameFromPid(long pid)
|
||||
{
|
||||
std::string path = "/proc/" + std::to_string(pid) + "/comm";
|
||||
std::ifstream t(path);
|
||||
std::stringstream buffer;
|
||||
buffer << t.rdbuf();
|
||||
return buffer.str();
|
||||
std::string name = buffer.str();
|
||||
if (!name.empty() && name[name.length() - 1] == '\n') {
|
||||
name.erase(name.length() - 1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
bool isInFocus(const QString &executable)
|
||||
void GetForegroundProcessName(QString &proc)
|
||||
{
|
||||
std::string temp;
|
||||
GetForegroundProcessName(temp);
|
||||
proc = QString::fromStdString(temp);
|
||||
}
|
||||
|
||||
void GetForegroundProcessName(std::string &proc)
|
||||
{
|
||||
proc.resize(0);
|
||||
auto pid = getForegroundProcessPid();
|
||||
std::string current = getProcNameFromPid(pid);
|
||||
proc = getProcNameFromPid(pid);
|
||||
}
|
||||
|
||||
bool IsInFocus(const QString &executable)
|
||||
{
|
||||
std::string current;
|
||||
GetForegroundProcessName(current);
|
||||
|
||||
// True if executable switch equals current window
|
||||
bool equals = (executable.toStdString() == current);
|
||||
@@ -448,24 +450,30 @@ bool isInFocus(const QString &executable)
|
||||
return (equals || matches);
|
||||
}
|
||||
|
||||
int secondsSinceLastInput()
|
||||
int SecondsSinceLastInput()
|
||||
{
|
||||
time_t idle_time;
|
||||
static XScreenSaverInfo *mit_info;
|
||||
Display *display;
|
||||
int screen;
|
||||
|
||||
mit_info = XScreenSaverAllocInfo();
|
||||
|
||||
if ((display = disp()) == NULL) {
|
||||
return -1;
|
||||
if (!canGetIdleTime) {
|
||||
return 0;
|
||||
}
|
||||
screen = DefaultScreen(display);
|
||||
XScreenSaverQueryInfo(display, RootWindow(display, screen), mit_info);
|
||||
|
||||
auto display = disp();
|
||||
if (!display) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto window = DefaultRootWindow(display);
|
||||
if (!window) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
time_t idle_time;
|
||||
auto mit_info = allocSSFunc();
|
||||
|
||||
auto status = querySSFunc(display, window, mit_info);
|
||||
idle_time = (mit_info->idle) / 1000;
|
||||
XFree(mit_info);
|
||||
|
||||
return idle_time;
|
||||
return status != 0 ? idle_time : 0;
|
||||
}
|
||||
|
||||
static std::unordered_map<HotkeyType, long> keyTable = {
|
||||
@@ -595,8 +603,9 @@ void PressKeys(const std::vector<HotkeyType> keys, int duration)
|
||||
}
|
||||
|
||||
Display *display = disp();
|
||||
if (display == NULL)
|
||||
if (!display) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Press keys
|
||||
for (auto &key : keys) {
|
||||
@@ -626,17 +635,37 @@ void PressKeys(const std::vector<HotkeyType> keys, int duration)
|
||||
|
||||
void PlatformInit()
|
||||
{
|
||||
libXtstHandle = new QLibrary("libXtst.so", nullptr);
|
||||
auto display = disp();
|
||||
if (!display) {
|
||||
return;
|
||||
}
|
||||
|
||||
libXtstHandle = new QLibrary("libXtst", nullptr);
|
||||
pressFunc = (keyPressFunc)libXtstHandle->resolve("XTestFakeKeyEvent");
|
||||
int _;
|
||||
canSimulateKeyPresses = pressFunc &&
|
||||
!XQueryExtension(disp(), "XTEST", &_, &_, &_);
|
||||
XQueryExtension(disp(), "XTEST", &_, &_, &_);
|
||||
|
||||
libXssHandle = new QLibrary("libXss", nullptr);
|
||||
allocSSFunc = (XScreenSaverAllocInfoFunc)libXssHandle->resolve(
|
||||
"XScreenSaverAllocInfo");
|
||||
querySSFunc = (XScreenSaverQueryInfoFunc)libXssHandle->resolve(
|
||||
"XScreenSaverQueryInfo");
|
||||
canGetIdleTime = allocSSFunc && querySSFunc &&
|
||||
XQueryExtension(disp(), ScreenSaverName, &_, &_, &_);
|
||||
}
|
||||
|
||||
void PlatformCleanup()
|
||||
{
|
||||
delete libXtstHandle;
|
||||
libXtstHandle = nullptr;
|
||||
|
||||
if (libXtstHandle) {
|
||||
delete libXtstHandle;
|
||||
libXtstHandle = nullptr;
|
||||
}
|
||||
if (libXssHandle) {
|
||||
delete libXssHandle;
|
||||
libXssHandle = nullptr;
|
||||
}
|
||||
cleanupDisplay();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "macro-action-audio.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr int64_t nsPerMs = 1000000;
|
||||
const std::string MacroActionAudio::id = "audio";
|
||||
|
||||
bool MacroActionAudio::_registered = MacroActionFactory::Register(
|
||||
@@ -9,19 +12,28 @@ bool MacroActionAudio::_registered = MacroActionFactory::Register(
|
||||
{MacroActionAudio::Create, MacroActionAudioEdit::Create,
|
||||
"AdvSceneSwitcher.action.audio"});
|
||||
|
||||
const static std::map<AudioAction, std::string> actionTypes = {
|
||||
{AudioAction::MUTE, "AdvSceneSwitcher.action.audio.type.mute"},
|
||||
{AudioAction::UNMUTE, "AdvSceneSwitcher.action.audio.type.unmute"},
|
||||
{AudioAction::SOURCE_VOLUME,
|
||||
const static std::map<MacroActionAudio::Action, std::string> actionTypes = {
|
||||
{MacroActionAudio::Action::MUTE,
|
||||
"AdvSceneSwitcher.action.audio.type.mute"},
|
||||
{MacroActionAudio::Action::UNMUTE,
|
||||
"AdvSceneSwitcher.action.audio.type.unmute"},
|
||||
{MacroActionAudio::Action::SOURCE_VOLUME,
|
||||
"AdvSceneSwitcher.action.audio.type.sourceVolume"},
|
||||
{AudioAction::MASTER_VOLUME,
|
||||
{MacroActionAudio::Action::MASTER_VOLUME,
|
||||
"AdvSceneSwitcher.action.audio.type.masterVolume"},
|
||||
{MacroActionAudio::Action::SYNC_OFFSET,
|
||||
"AdvSceneSwitcher.action.audio.type.syncOffset"},
|
||||
{MacroActionAudio::Action::MONITOR,
|
||||
"AdvSceneSwitcher.action.audio.type.monitor"},
|
||||
{MacroActionAudio::Action::BALANCE,
|
||||
"AdvSceneSwitcher.action.audio.type.balance"},
|
||||
};
|
||||
|
||||
const static std::map<FadeType, std::string> fadeTypes = {
|
||||
{FadeType::DURATION,
|
||||
const static std::map<MacroActionAudio::FadeType, std::string> fadeTypes = {
|
||||
{MacroActionAudio::FadeType::DURATION,
|
||||
"AdvSceneSwitcher.action.audio.fade.type.duration"},
|
||||
{FadeType::RATE, "AdvSceneSwitcher.action.audio.fade.type.rate"},
|
||||
{MacroActionAudio::FadeType::RATE,
|
||||
"AdvSceneSwitcher.action.audio.fade.type.rate"},
|
||||
};
|
||||
|
||||
constexpr auto fadeInterval = std::chrono::milliseconds(100);
|
||||
@@ -29,9 +41,9 @@ constexpr float minFade = 0.000001f;
|
||||
|
||||
void MacroActionAudio::SetFadeActive(bool value)
|
||||
{
|
||||
if (_action == AudioAction::SOURCE_VOLUME) {
|
||||
switcher->activeAudioFades[GetWeakSourceName(_audioSource)]
|
||||
.active = value;
|
||||
if (_action == Action::SOURCE_VOLUME) {
|
||||
switcher->activeAudioFades[_audioSource.ToString()].active =
|
||||
value;
|
||||
} else {
|
||||
switcher->masterAudioFade.active = value;
|
||||
}
|
||||
@@ -40,9 +52,9 @@ void MacroActionAudio::SetFadeActive(bool value)
|
||||
bool MacroActionAudio::FadeActive()
|
||||
{
|
||||
bool active = true;
|
||||
if (_action == AudioAction::SOURCE_VOLUME) {
|
||||
if (_action == Action::SOURCE_VOLUME) {
|
||||
auto it = switcher->activeAudioFades.find(
|
||||
GetWeakSourceName(_audioSource));
|
||||
_audioSource.ToString());
|
||||
if (it == switcher->activeAudioFades.end()) {
|
||||
return false;
|
||||
}
|
||||
@@ -57,9 +69,9 @@ bool MacroActionAudio::FadeActive()
|
||||
std::atomic_int *MacroActionAudio::GetFadeIdPtr()
|
||||
{
|
||||
|
||||
if (_action == AudioAction::SOURCE_VOLUME) {
|
||||
if (_action == Action::SOURCE_VOLUME) {
|
||||
auto it = switcher->activeAudioFades.find(
|
||||
GetWeakSourceName(_audioSource));
|
||||
_audioSource.ToString());
|
||||
if (it == switcher->activeAudioFades.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -70,8 +82,8 @@ std::atomic_int *MacroActionAudio::GetFadeIdPtr()
|
||||
|
||||
void MacroActionAudio::SetVolume(float vol)
|
||||
{
|
||||
if (_action == AudioAction::SOURCE_VOLUME) {
|
||||
auto s = obs_weak_source_get_source(_audioSource);
|
||||
if (_action == Action::SOURCE_VOLUME) {
|
||||
auto s = obs_weak_source_get_source(_audioSource.GetSource());
|
||||
obs_source_set_volume(s, vol);
|
||||
obs_source_release(s);
|
||||
} else {
|
||||
@@ -82,8 +94,8 @@ void MacroActionAudio::SetVolume(float vol)
|
||||
float MacroActionAudio::GetVolume()
|
||||
{
|
||||
float curVol;
|
||||
if (_action == AudioAction::SOURCE_VOLUME) {
|
||||
auto s = obs_weak_source_get_source(_audioSource);
|
||||
if (_action == Action::SOURCE_VOLUME) {
|
||||
auto s = obs_weak_source_get_source(_audioSource.GetSource());
|
||||
if (!s) {
|
||||
return 0.;
|
||||
}
|
||||
@@ -104,7 +116,7 @@ void MacroActionAudio::FadeVolume()
|
||||
int nrSteps = 0;
|
||||
float volStep = 0.;
|
||||
if (_fadeType == FadeType::DURATION) {
|
||||
nrSteps = _duration.seconds * 1000 / fadeInterval.count();
|
||||
nrSteps = _duration.Milliseconds() / fadeInterval.count();
|
||||
volStep = volDiff / nrSteps;
|
||||
} else {
|
||||
volStep = _rate / 1000.0f;
|
||||
@@ -139,15 +151,15 @@ void MacroActionAudio::FadeVolume()
|
||||
|
||||
void MacroActionAudio::StartFade()
|
||||
{
|
||||
if (_action == AudioAction::SOURCE_VOLUME && !_audioSource) {
|
||||
if (_action == Action::SOURCE_VOLUME && !_audioSource.GetSource()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (FadeActive() && !_abortActiveFade) {
|
||||
blog(LOG_WARNING,
|
||||
"Audio fade for volume of %s already active! New fade request will be ignored!",
|
||||
(_action == AudioAction::SOURCE_VOLUME)
|
||||
? GetWeakSourceName(_audioSource).c_str()
|
||||
(_action == Action::SOURCE_VOLUME)
|
||||
? _audioSource.ToString(true).c_str()
|
||||
: "master volume");
|
||||
return;
|
||||
}
|
||||
@@ -163,22 +175,31 @@ void MacroActionAudio::StartFade()
|
||||
|
||||
bool MacroActionAudio::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_audioSource);
|
||||
auto s = obs_weak_source_get_source(_audioSource.GetSource());
|
||||
switch (_action) {
|
||||
case AudioAction::MUTE:
|
||||
case Action::MUTE:
|
||||
obs_source_set_muted(s, true);
|
||||
break;
|
||||
case AudioAction::UNMUTE:
|
||||
case Action::UNMUTE:
|
||||
obs_source_set_muted(s, false);
|
||||
break;
|
||||
case AudioAction::SOURCE_VOLUME:
|
||||
case AudioAction::MASTER_VOLUME:
|
||||
case Action::SOURCE_VOLUME:
|
||||
case Action::MASTER_VOLUME:
|
||||
if (_fade) {
|
||||
StartFade();
|
||||
} else {
|
||||
SetVolume((float)_volume / 100.0f);
|
||||
}
|
||||
break;
|
||||
case Action::SYNC_OFFSET:
|
||||
obs_source_set_sync_offset(s, _syncOffset * nsPerMs);
|
||||
break;
|
||||
case Action::MONITOR:
|
||||
obs_source_set_monitoring_type(s, _monitorType);
|
||||
break;
|
||||
case Action::BALANCE:
|
||||
obs_source_set_balance_value(s, _balance);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -186,34 +207,36 @@ bool MacroActionAudio::PerformAction()
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionAudio::LogAction()
|
||||
void MacroActionAudio::LogAction() const
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO,
|
||||
"performed action \"%s\" for source \"%s\" with volume %d with fade %d %f",
|
||||
it->second.c_str(),
|
||||
GetWeakSourceName(_audioSource).c_str(), _volume, _fade,
|
||||
_duration.seconds);
|
||||
it->second.c_str(), _audioSource.ToString(true).c_str(),
|
||||
_volume.GetValue(), _fade, _duration.Seconds());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown audio action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionAudio::Save(obs_data_t *obj)
|
||||
bool MacroActionAudio::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_duration.Save(obj);
|
||||
obs_data_set_string(obj, "audioSource",
|
||||
GetWeakSourceName(_audioSource).c_str());
|
||||
_audioSource.Save(obj, "audioSource");
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "volume", _volume);
|
||||
obs_data_set_double(obj, "rate", _rate);
|
||||
obs_data_set_int(obj, "monitor", _monitorType);
|
||||
_syncOffset.Save(obj, "syncOffset");
|
||||
_balance.Save(obj, "balance");
|
||||
_volume.Save(obj, "volume");
|
||||
_rate.Save(obj, "rate");
|
||||
obs_data_set_bool(obj, "fade", _fade);
|
||||
obs_data_set_int(obj, "fadeType", static_cast<int>(_fadeType));
|
||||
obs_data_set_bool(obj, "wait", _wait);
|
||||
obs_data_set_bool(obj, "abortActiveFade", _abortActiveFade);
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -221,11 +244,21 @@ bool MacroActionAudio::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_duration.Load(obj);
|
||||
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
||||
_audioSource = GetWeakSourceByName(audioSourceName);
|
||||
_action = static_cast<AudioAction>(obs_data_get_int(obj, "action"));
|
||||
_volume = obs_data_get_int(obj, "volume");
|
||||
_rate = obs_data_get_double(obj, "rate");
|
||||
_audioSource.Load(obj, "audioSource");
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
_monitorType = static_cast<obs_monitoring_type>(
|
||||
obs_data_get_int(obj, "monitor"));
|
||||
if (!obs_data_has_user_value(obj, "version")) {
|
||||
_syncOffset = obs_data_get_int(obj, "syncOffset");
|
||||
_balance = obs_data_get_double(obj, "balance");
|
||||
_volume = obs_data_get_int(obj, "volume");
|
||||
_rate = obs_data_get_double(obj, "rate");
|
||||
} else {
|
||||
_syncOffset.Load(obj, "syncOffset");
|
||||
_balance.Load(obj, "balance");
|
||||
_volume.Load(obj, "volume");
|
||||
_rate.Load(obj, "rate");
|
||||
}
|
||||
_fade = obs_data_get_bool(obj, "fade");
|
||||
if (obs_data_has_user_value(obj, "wait")) {
|
||||
_wait = obs_data_get_bool(obj, "wait");
|
||||
@@ -246,18 +279,23 @@ bool MacroActionAudio::Load(obs_data_t *obj)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionAudio::GetShortDesc()
|
||||
std::string MacroActionAudio::GetShortDesc() const
|
||||
{
|
||||
if (_audioSource) {
|
||||
return GetWeakSourceName(_audioSource);
|
||||
}
|
||||
return "";
|
||||
return _audioSource.ToString();
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
for (const auto &[action, name] : actionTypes) {
|
||||
if (action == MacroActionAudio::Action::MONITOR) {
|
||||
if (obs_audio_monitoring_available()) {
|
||||
list->addItem(obs_module_text(name.c_str()),
|
||||
static_cast<int>(action));
|
||||
}
|
||||
} else {
|
||||
list->addItem(obs_module_text(name.c_str()),
|
||||
static_cast<int>(action));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,13 +309,19 @@ static inline void populateFadeTypeSelection(QComboBox *list)
|
||||
MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionAudio> entryData)
|
||||
: QWidget(parent),
|
||||
_audioSources(new QComboBox),
|
||||
_sources(new SourceSelectionWidget(this, QStringList(), true)),
|
||||
_actions(new QComboBox),
|
||||
_fadeTypes(new QComboBox),
|
||||
_volumePercent(new QSpinBox),
|
||||
_syncOffset(new VariableSpinBox),
|
||||
_monitorTypes(new QComboBox),
|
||||
_balance(new SliderSpinBox(
|
||||
0., 1., "",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.audio.balance.description"))),
|
||||
_volumePercent(new VariableSpinBox),
|
||||
_fade(new QCheckBox),
|
||||
_duration(new DurationSelection(parent, false)),
|
||||
_rate(new QDoubleSpinBox),
|
||||
_rate(new VariableDoubleSpinBox),
|
||||
_wait(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.audio.fade.wait"))),
|
||||
_abortActiveFade(new QCheckBox(
|
||||
@@ -285,6 +329,10 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
_fadeTypeLayout(new QHBoxLayout),
|
||||
_fadeOptionsLayout(new QVBoxLayout)
|
||||
{
|
||||
_syncOffset->setMinimum(-950);
|
||||
_syncOffset->setMaximum(20000);
|
||||
_syncOffset->setSuffix("ms");
|
||||
|
||||
_volumePercent->setMinimum(0);
|
||||
_volumePercent->setMaximum(2000);
|
||||
_volumePercent->setSuffix("%");
|
||||
@@ -294,22 +342,39 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
_rate->setSuffix("%");
|
||||
|
||||
populateActionSelection(_actions);
|
||||
populateAudioSelection(_audioSources);
|
||||
auto sources = GetAudioSourceNames();
|
||||
sources.sort();
|
||||
_sources->SetSourceNameList(sources);
|
||||
populateFadeTypeSelection(_fadeTypes);
|
||||
PopulateMonitorTypeSelection(_monitorTypes);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_audioSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_volumePercent, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(VolumeChanged(int)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SourceChanged(const SourceSelection &)), this,
|
||||
SLOT(SourceChanged(const SourceSelection &)));
|
||||
QWidget::connect(
|
||||
_syncOffset,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(SyncOffsetChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(
|
||||
_balance,
|
||||
SIGNAL(DoubleValueChanged(const NumberVariable<double> &)),
|
||||
this, SLOT(BalanceChanged(const NumberVariable<double> &)));
|
||||
QWidget::connect(_monitorTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(MonitorTypeChanged(int)));
|
||||
QWidget::connect(
|
||||
_volumePercent,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(VolumeChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(_fade, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(FadeChanged(int)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_rate, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(RateChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(const Duration &)),
|
||||
this, SLOT(DurationChanged(const Duration &)));
|
||||
QWidget::connect(
|
||||
_rate,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
|
||||
this, SLOT(RateChanged(const NumberVariable<double> &)));
|
||||
QWidget::connect(_wait, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(WaitChanged(int)));
|
||||
QWidget::connect(_abortActiveFade, SIGNAL(stateChanged(int)), this,
|
||||
@@ -318,8 +383,11 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
SLOT(FadeTypeChanged(int)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{audioSources}}", _audioSources},
|
||||
{"{{audioSources}}", _sources},
|
||||
{"{{actions}}", _actions},
|
||||
{"{{syncOffset}}", _syncOffset},
|
||||
{"{{monitorTypes}}", _monitorTypes},
|
||||
{"{{balance}}", _balance},
|
||||
{"{{volume}}", _volumePercent},
|
||||
{"{{fade}}", _fade},
|
||||
{"{{duration}}", _duration},
|
||||
@@ -329,10 +397,10 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
{"{{fadeTypes}}", _fadeTypes},
|
||||
};
|
||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.audio.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.audio.entry"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
_fadeTypeLayout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.audio.fade.duration"),
|
||||
_fadeTypeLayout, widgetPlaceholders);
|
||||
|
||||
@@ -342,6 +410,7 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addWidget(_balance);
|
||||
mainLayout->addLayout(_fadeOptionsLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -350,57 +419,61 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
bool hasVolumeControl(AudioAction action)
|
||||
static bool hasVolumeControl(MacroActionAudio::Action action)
|
||||
{
|
||||
return action == AudioAction::SOURCE_VOLUME ||
|
||||
action == AudioAction::MASTER_VOLUME;
|
||||
}
|
||||
|
||||
bool hasSourceControl(AudioAction action)
|
||||
{
|
||||
return action != AudioAction::MASTER_VOLUME;
|
||||
return action == MacroActionAudio::Action::SOURCE_VOLUME ||
|
||||
action == MacroActionAudio::Action::MASTER_VOLUME;
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::SetWidgetVisibility()
|
||||
{
|
||||
_volumePercent->setVisible(hasVolumeControl(_entryData->_action));
|
||||
_audioSources->setVisible(hasSourceControl(_entryData->_action));
|
||||
|
||||
_fadeTypes->setDisabled(!_entryData->_fade);
|
||||
_wait->setDisabled(!_entryData->_fade);
|
||||
_abortActiveFade->setDisabled(!_entryData->_fade);
|
||||
_duration->setDisabled(!_entryData->_fade);
|
||||
_rate->setDisabled(!_entryData->_fade);
|
||||
_sources->setVisible(_entryData->_action !=
|
||||
MacroActionAudio::Action::MASTER_VOLUME);
|
||||
_syncOffset->setVisible(_entryData->_action ==
|
||||
MacroActionAudio::Action::SYNC_OFFSET);
|
||||
_monitorTypes->setVisible(_entryData->_action ==
|
||||
MacroActionAudio::Action::MONITOR);
|
||||
_balance->setVisible(_entryData->_action ==
|
||||
MacroActionAudio::Action::BALANCE);
|
||||
|
||||
_fadeTypeLayout->removeWidget(_fade);
|
||||
_fadeTypeLayout->removeWidget(_fadeTypes);
|
||||
_fadeTypeLayout->removeWidget(_duration);
|
||||
_fadeTypeLayout->removeWidget(_rate);
|
||||
clearLayout(_fadeTypeLayout);
|
||||
ClearLayout(_fadeTypeLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{fade}}", _fade},
|
||||
{"{{duration}}", _duration},
|
||||
{"{{rate}}", _rate},
|
||||
{"{{fadeTypes}}", _fadeTypes},
|
||||
};
|
||||
if (_entryData->_fadeType == FadeType::DURATION) {
|
||||
placeWidgets(
|
||||
if (_entryData->_fadeType == MacroActionAudio::FadeType::DURATION) {
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.audio.fade.duration"),
|
||||
_fadeTypeLayout, widgetPlaceholders);
|
||||
} else {
|
||||
placeWidgets(obs_module_text(
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.audio.fade.rate"),
|
||||
_fadeTypeLayout, widgetPlaceholders);
|
||||
}
|
||||
|
||||
_duration->setVisible(_entryData->_fadeType == FadeType::DURATION);
|
||||
_rate->setVisible(_entryData->_fadeType == FadeType::RATE);
|
||||
_duration->setVisible(_entryData->_fadeType ==
|
||||
MacroActionAudio::FadeType::DURATION);
|
||||
_rate->setVisible(_entryData->_fadeType ==
|
||||
MacroActionAudio::FadeType::RATE);
|
||||
|
||||
setLayoutVisible(_fadeTypeLayout,
|
||||
SetLayoutVisible(_fadeTypeLayout,
|
||||
hasVolumeControl(_entryData->_action));
|
||||
setLayoutVisible(_fadeOptionsLayout,
|
||||
SetLayoutVisible(_fadeOptionsLayout,
|
||||
hasVolumeControl(_entryData->_action));
|
||||
_abortActiveFade->setVisible(hasVolumeControl(_entryData->_action) &&
|
||||
_entryData->_fade);
|
||||
_wait->setVisible(hasVolumeControl(_entryData->_action) &&
|
||||
_entryData->_fade);
|
||||
|
||||
updateGeometry();
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
@@ -410,49 +483,83 @@ void MacroActionAudioEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_audioSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_audioSource).c_str());
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_volumePercent->setValue(_entryData->_volume);
|
||||
_sources->SetSource(_entryData->_audioSource);
|
||||
_actions->setCurrentIndex(
|
||||
_actions->findData(static_cast<int>(_entryData->_action)));
|
||||
_syncOffset->SetValue(_entryData->_syncOffset);
|
||||
_monitorTypes->setCurrentIndex(_entryData->_monitorType);
|
||||
_balance->SetDoubleValue(_entryData->_balance);
|
||||
_volumePercent->SetValue(_entryData->_volume);
|
||||
_fade->setChecked(_entryData->_fade);
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_rate->setValue(_entryData->_rate);
|
||||
_rate->SetValue(_entryData->_rate);
|
||||
_wait->setChecked(_entryData->_wait);
|
||||
_abortActiveFade->setChecked(_entryData->_abortActiveFade);
|
||||
_fadeTypes->setCurrentIndex(static_cast<int>(_entryData->_fadeType));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::SourceChanged(const QString &text)
|
||||
void MacroActionAudioEdit::SourceChanged(const SourceSelection &source)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_audioSource = GetWeakSourceByQString(text);
|
||||
auto lock = LockContext();
|
||||
_entryData->_audioSource = source;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::ActionChanged(int value)
|
||||
void MacroActionAudioEdit::ActionChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<AudioAction>(value);
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<MacroActionAudio::Action>(
|
||||
_actions->itemData(idx).toInt());
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::VolumeChanged(int value)
|
||||
void MacroActionAudioEdit::SyncOffsetChanged(const NumberVariable<int> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_syncOffset = value;
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::MonitorTypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_monitorType = static_cast<obs_monitoring_type>(value);
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::BalanceChanged(const NumberVariable<double> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_balance = value;
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::VolumeChanged(const NumberVariable<int> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_volume = value;
|
||||
}
|
||||
|
||||
@@ -462,28 +569,28 @@ void MacroActionAudioEdit::FadeChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_fade = value;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::DurationChanged(double seconds)
|
||||
void MacroActionAudioEdit::DurationChanged(const Duration &dur)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
auto lock = LockContext();
|
||||
_entryData->_duration = dur;
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::RateChanged(double value)
|
||||
void MacroActionAudioEdit::RateChanged(const NumberVariable<double> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_rate = value;
|
||||
}
|
||||
|
||||
@@ -493,7 +600,7 @@ void MacroActionAudioEdit::WaitChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_wait = value;
|
||||
}
|
||||
|
||||
@@ -503,7 +610,7 @@ void MacroActionAudioEdit::AbortActiveFadeChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_abortActiveFade = value;
|
||||
}
|
||||
|
||||
@@ -513,7 +620,9 @@ void MacroActionAudioEdit::FadeTypeChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_fadeType = static_cast<FadeType>(value);
|
||||
auto lock = LockContext();
|
||||
_entryData->_fadeType = static_cast<MacroActionAudio::FadeType>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,45 +1,56 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "slider-spinbox.hpp"
|
||||
#include "source-selection.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
enum class AudioAction {
|
||||
MUTE,
|
||||
UNMUTE,
|
||||
SOURCE_VOLUME,
|
||||
MASTER_VOLUME,
|
||||
};
|
||||
|
||||
enum class FadeType {
|
||||
DURATION,
|
||||
RATE,
|
||||
};
|
||||
namespace advss {
|
||||
|
||||
class MacroActionAudio : public MacroAction {
|
||||
public:
|
||||
MacroActionAudio(Macro *m) : MacroAction(m) {}
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionAudio>(m);
|
||||
}
|
||||
|
||||
OBSWeakSource _audioSource;
|
||||
AudioAction _action = AudioAction::MUTE;
|
||||
SourceSelection _audioSource;
|
||||
|
||||
enum class Action {
|
||||
MUTE,
|
||||
UNMUTE,
|
||||
SOURCE_VOLUME,
|
||||
MASTER_VOLUME,
|
||||
SYNC_OFFSET,
|
||||
MONITOR,
|
||||
BALANCE,
|
||||
};
|
||||
|
||||
enum class FadeType {
|
||||
DURATION,
|
||||
RATE,
|
||||
};
|
||||
|
||||
Action _action = Action::MUTE;
|
||||
FadeType _fadeType = FadeType::DURATION;
|
||||
int _volume = 0;
|
||||
NumberVariable<int> _syncOffset = 0;
|
||||
obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_NONE;
|
||||
NumberVariable<double> _balance = 0.5;
|
||||
NumberVariable<int> _volume = 0;
|
||||
bool _fade = false;
|
||||
Duration _duration;
|
||||
double _rate = 100.;
|
||||
NumberVariable<double> _rate = 100.;
|
||||
bool _wait = false;
|
||||
bool _abortActiveFade = false;
|
||||
|
||||
@@ -73,12 +84,15 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void SourceChanged(const SourceSelection &);
|
||||
void ActionChanged(int value);
|
||||
void VolumeChanged(int value);
|
||||
void SyncOffsetChanged(const NumberVariable<int> &value);
|
||||
void MonitorTypeChanged(int value);
|
||||
void BalanceChanged(const NumberVariable<double> &value);
|
||||
void VolumeChanged(const NumberVariable<int> &value);
|
||||
void FadeChanged(int value);
|
||||
void DurationChanged(double seconds);
|
||||
void RateChanged(double value);
|
||||
void DurationChanged(const Duration &seconds);
|
||||
void RateChanged(const NumberVariable<double> &value);
|
||||
void WaitChanged(int value);
|
||||
void AbortActiveFadeChanged(int value);
|
||||
void FadeTypeChanged(int value);
|
||||
@@ -86,13 +100,16 @@ signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_audioSources;
|
||||
SourceSelectionWidget *_sources;
|
||||
QComboBox *_actions;
|
||||
QComboBox *_fadeTypes;
|
||||
QSpinBox *_volumePercent;
|
||||
VariableSpinBox *_syncOffset;
|
||||
QComboBox *_monitorTypes;
|
||||
SliderSpinBox *_balance;
|
||||
VariableSpinBox *_volumePercent;
|
||||
QCheckBox *_fade;
|
||||
DurationSelection *_duration;
|
||||
QDoubleSpinBox *_rate;
|
||||
VariableDoubleSpinBox *_rate;
|
||||
QCheckBox *_wait;
|
||||
QCheckBox *_abortActiveFade;
|
||||
QHBoxLayout *_fadeTypeLayout;
|
||||
@@ -104,3 +121,5 @@ private:
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-action-scene-switch.hpp"
|
||||
#include "section.hpp"
|
||||
#include "switch-button.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QGraphicsOpacityEffect>
|
||||
|
||||
namespace advss {
|
||||
|
||||
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
|
||||
{
|
||||
static std::map<std::string, MacroActionInfo> _methods;
|
||||
@@ -75,26 +81,32 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> *entryData,
|
||||
const std::string &id)
|
||||
: MacroSegmentEdit(switcher->macroProperties._highlightActions, parent),
|
||||
_actionSelection(new QComboBox()),
|
||||
_actionSelection(new FilterComboBox()),
|
||||
_enable(new SwitchButton()),
|
||||
_entryData(entryData)
|
||||
{
|
||||
QWidget::connect(_actionSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ActionSelectionChanged(const QString &)));
|
||||
QWidget::connect(_enable, SIGNAL(checked(bool)), this,
|
||||
SLOT(ActionEnableChanged(bool)));
|
||||
QWidget::connect(window(), SIGNAL(HighlightActionsChanged(bool)), this,
|
||||
SLOT(EnableHighlight(bool)));
|
||||
QWidget::connect(&_actionStateTimer, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateActionState()));
|
||||
|
||||
populateActionSelection(_actionSelection);
|
||||
|
||||
_section->AddHeaderWidget(_enable);
|
||||
_section->AddHeaderWidget(_actionSelection);
|
||||
_section->AddHeaderWidget(_headerInfo);
|
||||
|
||||
QVBoxLayout *actionLayout = new QVBoxLayout;
|
||||
auto actionLayout = new QVBoxLayout;
|
||||
actionLayout->setContentsMargins({7, 7, 7, 7});
|
||||
actionLayout->addWidget(_section);
|
||||
_contentLayout->addLayout(actionLayout);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
auto mainLayout = new QHBoxLayout;
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->setSpacing(0);
|
||||
mainLayout->addWidget(_frame);
|
||||
@@ -103,6 +115,7 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
_entryData = entryData;
|
||||
UpdateEntryData(id);
|
||||
|
||||
_actionStateTimer.start(300);
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
@@ -112,10 +125,14 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
|
||||
return;
|
||||
}
|
||||
|
||||
std::string id = MacroActionFactory::GetIdByName(text);
|
||||
if (id.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
HeaderInfoChanged("");
|
||||
auto idx = _entryData->get()->GetIndex();
|
||||
auto macro = _entryData->get()->GetMacro();
|
||||
std::string id = MacroActionFactory::GetIdByName(text);
|
||||
HeaderInfoChanged("");
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->reset();
|
||||
@@ -133,6 +150,9 @@ void MacroActionEdit::UpdateEntryData(const std::string &id)
|
||||
{
|
||||
_actionSelection->setCurrentText(
|
||||
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
|
||||
const bool enabled = (*_entryData)->Enabled();
|
||||
_enable->setChecked(enabled);
|
||||
SetDisableEffect(!enabled);
|
||||
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
|
||||
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
||||
this, SLOT(HeaderInfoChanged(const QString &)));
|
||||
@@ -147,14 +167,51 @@ void MacroActionEdit::SetEntryData(std::shared_ptr<MacroAction> *data)
|
||||
_entryData = data;
|
||||
}
|
||||
|
||||
MacroSegment *MacroActionEdit::Data()
|
||||
void MacroActionEdit::SetDisableEffect(bool value)
|
||||
{
|
||||
return _entryData->get();
|
||||
if (value) {
|
||||
auto effect = new QGraphicsOpacityEffect(this);
|
||||
effect->setOpacity(0.5);
|
||||
_section->setGraphicsEffect(effect);
|
||||
} else {
|
||||
_section->setGraphicsEffect(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionEdit::ActionEnableChanged(bool value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
(*_entryData)->SetEnabled(value);
|
||||
SetDisableEffect(!value);
|
||||
}
|
||||
|
||||
void MacroActionEdit::UpdateActionState()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetEnableAppearance((*_entryData)->Enabled());
|
||||
}
|
||||
|
||||
void MacroActionEdit::SetEnableAppearance(bool value)
|
||||
{
|
||||
_enable->setChecked(value);
|
||||
SetDisableEffect(!value);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroSegment> MacroActionEdit::Data()
|
||||
{
|
||||
return *_entryData;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::AddMacroAction(int idx)
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
@@ -172,8 +229,9 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
macro->Actions().emplace(macro->Actions().begin() + idx,
|
||||
MacroActionFactory::Create(id, macro));
|
||||
macro->Actions().emplace(
|
||||
macro->Actions().begin() + idx,
|
||||
MacroActionFactory::Create(id, macro.get()));
|
||||
if (idx - 1 >= 0) {
|
||||
auto data = obs_data_create();
|
||||
macro->Actions().at(idx - 1)->Save(data);
|
||||
@@ -187,20 +245,17 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
|
||||
SetActionData(*macro);
|
||||
}
|
||||
HighlightAction(idx);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionAdd_clicked()
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentActionIdx == -1) {
|
||||
auto macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
AddMacroAction((int)macro->Actions().size());
|
||||
} else {
|
||||
AddMacroAction(currentActionIdx + 1);
|
||||
@@ -213,7 +268,7 @@ void AdvSceneSwitcher::on_actionAdd_clicked()
|
||||
|
||||
void AdvSceneSwitcher::RemoveMacroAction(int idx)
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
@@ -233,12 +288,13 @@ void AdvSceneSwitcher::RemoveMacroAction(int idx)
|
||||
}
|
||||
MacroActionSelectionChanged(-1);
|
||||
lastInteracted = MacroSection::ACTIONS;
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionRemove_clicked()
|
||||
{
|
||||
if (currentActionIdx == -1) {
|
||||
auto macro = getSelectedMacro();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
@@ -249,23 +305,43 @@ void AdvSceneSwitcher::on_actionRemove_clicked()
|
||||
MacroActionSelectionChanged(-1);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionUp_clicked()
|
||||
void AdvSceneSwitcher::on_actionTop_clicked()
|
||||
{
|
||||
if (currentActionIdx == -1) {
|
||||
return;
|
||||
}
|
||||
MacroActionReorder(0, currentActionIdx);
|
||||
MacroActionSelectionChanged(0);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionUp_clicked()
|
||||
{
|
||||
if (currentActionIdx == -1 || currentActionIdx == 0) {
|
||||
return;
|
||||
}
|
||||
MoveMacroActionUp(currentActionIdx);
|
||||
MacroActionSelectionChanged(currentActionIdx - 1);
|
||||
}
|
||||
void AdvSceneSwitcher::on_actionDown_clicked()
|
||||
{
|
||||
if (currentActionIdx == -1) {
|
||||
if (currentActionIdx == -1 ||
|
||||
currentActionIdx == actionsList->ContentLayout()->count() - 1) {
|
||||
return;
|
||||
}
|
||||
MoveMacroActionDown(currentActionIdx);
|
||||
MacroActionSelectionChanged(currentActionIdx + 1);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionBottom_clicked()
|
||||
{
|
||||
if (currentActionIdx == -1) {
|
||||
return;
|
||||
}
|
||||
const int newIdx = actionsList->ContentLayout()->count() - 1;
|
||||
MacroActionReorder(newIdx, currentActionIdx);
|
||||
MacroActionSelectionChanged(newIdx);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
|
||||
{
|
||||
if (pos1 == pos2) {
|
||||
@@ -285,11 +361,12 @@ void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
|
||||
actionsList->Insert(pos1, widget2);
|
||||
actionsList->Insert(pos2, widget1);
|
||||
SetActionData(*m);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::MoveMacroActionUp(int idx)
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
@@ -298,13 +375,13 @@ void AdvSceneSwitcher::MoveMacroActionUp(int idx)
|
||||
return;
|
||||
}
|
||||
|
||||
SwapActions(macro, idx, idx - 1);
|
||||
SwapActions(macro.get(), idx, idx - 1);
|
||||
HighlightAction(idx - 1);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::MoveMacroActionDown(int idx)
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
@@ -313,13 +390,13 @@ void AdvSceneSwitcher::MoveMacroActionDown(int idx)
|
||||
return;
|
||||
}
|
||||
|
||||
SwapActions(macro, idx, idx + 1);
|
||||
SwapActions(macro.get(), idx, idx + 1);
|
||||
HighlightAction(idx + 1);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::MacroActionSelectionChanged(int idx)
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
@@ -339,13 +416,13 @@ void AdvSceneSwitcher::MacroActionSelectionChanged(int idx)
|
||||
|
||||
void AdvSceneSwitcher::MacroActionReorder(int to, int from)
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (from < 0 || from > (int)macro->Actions().size() || to < 0 ||
|
||||
to > (int)macro->Actions().size()) {
|
||||
if (to == from || from < 0 || from > (int)macro->Actions().size() ||
|
||||
to < 0 || to > (int)macro->Actions().size()) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
@@ -359,4 +436,7 @@ void AdvSceneSwitcher::MacroActionReorder(int to, int from)
|
||||
SetActionData(*macro);
|
||||
}
|
||||
HighlightAction(to);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#pragma once
|
||||
#include "macro-action.hpp"
|
||||
#include "filter-combo-box.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <memory>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class SwitchButton;
|
||||
|
||||
struct MacroActionInfo {
|
||||
using TCreateMethod = std::shared_ptr<MacroAction> (*)(Macro *m);
|
||||
using TCreateWidgetMethod = QWidget *(*)(QWidget *parent,
|
||||
@@ -42,12 +46,20 @@ public:
|
||||
|
||||
private slots:
|
||||
void ActionSelectionChanged(const QString &text);
|
||||
void ActionEnableChanged(bool);
|
||||
void UpdateActionState();
|
||||
|
||||
private:
|
||||
MacroSegment *Data();
|
||||
std::shared_ptr<MacroSegment> Data();
|
||||
void SetDisableEffect(bool);
|
||||
void SetEnableAppearance(bool);
|
||||
|
||||
QComboBox *_actionSelection;
|
||||
FilterComboBox *_actionSelection;
|
||||
SwitchButton *_enable;
|
||||
|
||||
std::shared_ptr<MacroAction> *_entryData;
|
||||
QTimer _actionStateTimer;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#include "macro-action-file.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QFileDialog>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionFile::id = "file";
|
||||
|
||||
bool MacroActionFile::_registered = MacroActionFactory::Register(
|
||||
@@ -40,7 +41,7 @@ bool MacroActionFile::PerformAction()
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionFile::LogAction()
|
||||
void MacroActionFile::LogAction() const
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
@@ -52,11 +53,11 @@ void MacroActionFile::LogAction()
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionFile::Save(obs_data_t *obj)
|
||||
bool MacroActionFile::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "file", _file.c_str());
|
||||
obs_data_set_string(obj, "text", _text.c_str());
|
||||
_file.Save(obj, "file");
|
||||
_text.Save(obj, "text");
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
@@ -64,15 +65,15 @@ bool MacroActionFile::Save(obs_data_t *obj)
|
||||
bool MacroActionFile::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_file = obs_data_get_string(obj, "file");
|
||||
_text = obs_data_get_string(obj, "text");
|
||||
_file.Load(obj, "file");
|
||||
_text.Load(obj, "text");
|
||||
_action = static_cast<FileAction>(obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionFile::GetShortDesc()
|
||||
std::string MacroActionFile::GetShortDesc() const
|
||||
{
|
||||
return _file;
|
||||
return _file.UnresolvedValue();
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
@@ -87,7 +88,7 @@ MacroActionFileEdit::MacroActionFileEdit(
|
||||
: QWidget(parent)
|
||||
{
|
||||
_filePath = new FileSelection(FileSelection::Type::WRITE);
|
||||
_text = new ResizingPlainTextEdit(this);
|
||||
_text = new VariableTextEdit(this);
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
@@ -106,7 +107,7 @@ MacroActionFileEdit::MacroActionFileEdit(
|
||||
{"{{matchText}}", _text},
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.file.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.file.entry"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
@@ -127,7 +128,7 @@ void MacroActionFileEdit::UpdateEntryData()
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_filePath->SetPath(QString::fromStdString(_entryData->_file));
|
||||
_text->setPlainText(QString::fromStdString(_entryData->_text));
|
||||
_text->setPlainText(_entryData->_text);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
@@ -139,7 +140,7 @@ void MacroActionFileEdit::PathChanged(const QString &text)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_file = text.toUtf8().constData();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
@@ -151,7 +152,7 @@ void MacroActionFileEdit::TextChanged()
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_text = _text->toPlainText().toStdString();
|
||||
|
||||
adjustSize();
|
||||
@@ -164,6 +165,8 @@ void MacroActionFileEdit::ActionChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<FileAction>(value);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "file-selection.hpp"
|
||||
#include "resizing-text-edit.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
|
||||
namespace advss {
|
||||
|
||||
enum class FileAction {
|
||||
WRITE,
|
||||
APPEND,
|
||||
@@ -14,18 +16,18 @@ class MacroActionFile : public MacroAction {
|
||||
public:
|
||||
MacroActionFile(Macro *m) : MacroAction(m) {}
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionFile>(m);
|
||||
}
|
||||
|
||||
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
std::string _text = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
StringVariable _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
StringVariable _text = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
FileAction _action = FileAction::WRITE;
|
||||
|
||||
private:
|
||||
@@ -58,10 +60,12 @@ signals:
|
||||
|
||||
protected:
|
||||
FileSelection *_filePath;
|
||||
ResizingPlainTextEdit *_text;
|
||||
VariableTextEdit *_text;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionFile> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "macro-action-filter.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionFilter::id = "filter";
|
||||
|
||||
bool MacroActionFilter::_registered = MacroActionFactory::Register(
|
||||
@@ -9,25 +10,32 @@ bool MacroActionFilter::_registered = MacroActionFactory::Register(
|
||||
{MacroActionFilter::Create, MacroActionFilterEdit::Create,
|
||||
"AdvSceneSwitcher.action.filter"});
|
||||
|
||||
const static std::map<FilterAction, std::string> actionTypes = {
|
||||
{FilterAction::ENABLE, "AdvSceneSwitcher.action.filter.type.enable"},
|
||||
{FilterAction::DISABLE, "AdvSceneSwitcher.action.filter.type.disable"},
|
||||
{FilterAction::SETTINGS,
|
||||
const static std::map<MacroActionFilter::Action, std::string> actionTypes = {
|
||||
{MacroActionFilter::Action::ENABLE,
|
||||
"AdvSceneSwitcher.action.filter.type.enable"},
|
||||
{MacroActionFilter::Action::DISABLE,
|
||||
"AdvSceneSwitcher.action.filter.type.disable"},
|
||||
{MacroActionFilter::Action::TOGGLE,
|
||||
"AdvSceneSwitcher.action.filter.type.toggle"},
|
||||
{MacroActionFilter::Action::SETTINGS,
|
||||
"AdvSceneSwitcher.action.filter.type.settings"},
|
||||
};
|
||||
|
||||
bool MacroActionFilter::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_filter);
|
||||
auto s = obs_weak_source_get_source(_filter.GetFilter(_source));
|
||||
switch (_action) {
|
||||
case FilterAction::ENABLE:
|
||||
case Action::ENABLE:
|
||||
obs_source_set_enabled(s, true);
|
||||
break;
|
||||
case FilterAction::DISABLE:
|
||||
case Action::DISABLE:
|
||||
obs_source_set_enabled(s, false);
|
||||
break;
|
||||
case FilterAction::SETTINGS:
|
||||
setSourceSettings(s, _settings);
|
||||
case Action::TOGGLE:
|
||||
obs_source_set_enabled(s, !obs_source_enabled(s));
|
||||
break;
|
||||
case Action::SETTINGS:
|
||||
SetSourceSettings(s, _settings);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -36,47 +44,55 @@ bool MacroActionFilter::PerformAction()
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionFilter::LogAction()
|
||||
void MacroActionFilter::LogAction() const
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO,
|
||||
"performed action \"%s\" for filter \"%s\" on source \"%s\"",
|
||||
it->second.c_str(), GetWeakSourceName(_filter).c_str(),
|
||||
GetWeakSourceName(_source).c_str());
|
||||
it->second.c_str(), _filter.ToString().c_str(),
|
||||
_source.ToString(true).c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown filter action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionFilter::Save(obs_data_t *obj)
|
||||
bool MacroActionFilter::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
obs_data_set_string(obj, "filter", GetWeakSourceName(_filter).c_str());
|
||||
_source.Save(obj);
|
||||
_filter.Save(obj, "filter");
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_string(obj, "settings", _settings.c_str());
|
||||
_settings.Save(obj, "settings");
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionFilter::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
const char *filterName = obs_data_get_string(obj, "filter");
|
||||
_filter = GetWeakFilterByQString(_source, filterName);
|
||||
_action = static_cast<FilterAction>(obs_data_get_int(obj, "action"));
|
||||
_settings = obs_data_get_string(obj, "settings");
|
||||
_source.Load(obj);
|
||||
_filter.Load(obj, _source, "filter");
|
||||
// TODO: Remove this fallback in future version
|
||||
if (!obs_data_has_user_value(obj, "version")) {
|
||||
const auto value = obs_data_get_int(obj, "action");
|
||||
if (value == 2) {
|
||||
_action = Action::SETTINGS;
|
||||
} else {
|
||||
_action = static_cast<Action>(value);
|
||||
}
|
||||
} else {
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
}
|
||||
_settings.Load(obj, "settings");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionFilter::GetShortDesc()
|
||||
std::string MacroActionFilter::GetShortDesc() const
|
||||
{
|
||||
if (_filter && _source) {
|
||||
return GetWeakSourceName(_source) + " - " +
|
||||
GetWeakSourceName(_filter);
|
||||
if (!_filter.ToString().empty() && !_source.ToString().empty()) {
|
||||
return _source.ToString() + " - " + _filter.ToString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -90,25 +106,29 @@ static inline void populateActionSelection(QComboBox *list)
|
||||
|
||||
MacroActionFilterEdit::MacroActionFilterEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionFilter> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_sources(new SourceSelectionWidget(this, QStringList(), true)),
|
||||
_filters(new FilterSelectionWidget(this, _sources, true)),
|
||||
_actions(new QComboBox()),
|
||||
_getSettings(new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.action.filter.getSettings"))),
|
||||
_settings(new VariableTextEdit(this))
|
||||
{
|
||||
_sources = new QComboBox();
|
||||
_filters = new QComboBox();
|
||||
_filters->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_actions = new QComboBox();
|
||||
_getSettings = new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.action.filter.getSettings"));
|
||||
_settings = new ResizingPlainTextEdit(this);
|
||||
|
||||
populateActionSelection(_actions);
|
||||
populateSourcesWithFilterSelection(_sources);
|
||||
auto sources = GetSourcesWithFilterNames();
|
||||
sources.sort();
|
||||
_sources->SetSourceNameList(sources);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_filters, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(FilterChanged(const QString &)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SourceChanged(const SourceSelection &)), this,
|
||||
SLOT(SourceChanged(const SourceSelection &)));
|
||||
QWidget::connect(_filters,
|
||||
SIGNAL(FilterChanged(const FilterSelection &)), this,
|
||||
SLOT(FilterChanged(const FilterSelection &)));
|
||||
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
|
||||
SLOT(GetSettingsClicked()));
|
||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||
@@ -121,12 +141,13 @@ MacroActionFilterEdit::MacroActionFilterEdit(
|
||||
{"{{actions}}", _actions}, {"{{settings}}", _settings},
|
||||
{"{{getSettings}}", _getSettings},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.filter.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.filter.entry"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->addWidget(_getSettings);
|
||||
buttonLayout->addStretch();
|
||||
buttonLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
@@ -146,40 +167,34 @@ void MacroActionFilterEdit::UpdateEntryData()
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
populateFilterSelection(_filters, _entryData->_source);
|
||||
_filters->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_filter).c_str());
|
||||
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
||||
SetWidgetVisibility(_entryData->_action == FilterAction::SETTINGS);
|
||||
_sources->SetSource(_entryData->_source);
|
||||
_filters->SetFilter(_entryData->_source, _entryData->_filter);
|
||||
_settings->setPlainText(_entryData->_settings);
|
||||
SetWidgetVisibility(_entryData->_action ==
|
||||
MacroActionFilter::Action::SETTINGS);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::SourceChanged(const QString &text)
|
||||
void MacroActionFilterEdit::SourceChanged(const SourceSelection &source)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
}
|
||||
_filters->clear();
|
||||
populateFilterSelection(_filters, _entryData->_source);
|
||||
_filters->adjustSize();
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_source = source;
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::FilterChanged(const QString &text)
|
||||
void MacroActionFilterEdit::FilterChanged(const FilterSelection &filter)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_filter = GetWeakFilterByQString(_entryData->_source, text);
|
||||
auto lock = LockContext();
|
||||
_entryData->_filter = filter;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -190,20 +205,21 @@ void MacroActionFilterEdit::ActionChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<FilterAction>(value);
|
||||
SetWidgetVisibility(_entryData->_action == FilterAction::SETTINGS);
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<MacroActionFilter::Action>(value);
|
||||
SetWidgetVisibility(_entryData->_action ==
|
||||
MacroActionFilter::Action::SETTINGS);
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::GetSettingsClicked()
|
||||
{
|
||||
if (_loading || !_entryData || !_entryData->_source ||
|
||||
!_entryData->_filter) {
|
||||
if (_loading || !_entryData ||
|
||||
!_entryData->_filter.GetFilter(_entryData->_source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_settings->setPlainText(
|
||||
formatJsonString(getSourceSettings(_entryData->_filter)));
|
||||
_settings->setPlainText(FormatJsonString(GetSourceSettings(
|
||||
_entryData->_filter.GetFilter(_entryData->_source))));
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::SettingsChanged()
|
||||
@@ -212,7 +228,7 @@ void MacroActionFilterEdit::SettingsChanged()
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_settings = _settings->toPlainText().toStdString();
|
||||
|
||||
adjustSize();
|
||||
@@ -224,4 +240,7 @@ void MacroActionFilterEdit::SetWidgetVisibility(bool showSettings)
|
||||
_settings->setVisible(showSettings);
|
||||
_getSettings->setVisible(showSettings);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,35 +1,40 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "resizing-text-edit.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
#include "source-selection.hpp"
|
||||
#include "filter-selection.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QSpinBox>
|
||||
#include <QPushButton>
|
||||
|
||||
enum class FilterAction {
|
||||
ENABLE,
|
||||
DISABLE,
|
||||
SETTINGS,
|
||||
};
|
||||
namespace advss {
|
||||
|
||||
class MacroActionFilter : public MacroAction {
|
||||
public:
|
||||
MacroActionFilter(Macro *m) : MacroAction(m) {}
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionFilter>(m);
|
||||
}
|
||||
|
||||
OBSWeakSource _source;
|
||||
OBSWeakSource _filter;
|
||||
FilterAction _action = FilterAction::ENABLE;
|
||||
std::string _settings = "";
|
||||
enum class Action {
|
||||
ENABLE,
|
||||
DISABLE,
|
||||
TOGGLE,
|
||||
SETTINGS,
|
||||
};
|
||||
|
||||
SourceSelection _source;
|
||||
FilterSelection _filter;
|
||||
Action _action = Action::ENABLE;
|
||||
StringVariable _settings = "";
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -53,8 +58,8 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void FilterChanged(const QString &text);
|
||||
void SourceChanged(const SourceSelection &);
|
||||
void FilterChanged(const FilterSelection &);
|
||||
void ActionChanged(int value);
|
||||
void GetSettingsClicked();
|
||||
void SettingsChanged();
|
||||
@@ -62,14 +67,16 @@ signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_sources;
|
||||
QComboBox *_filters;
|
||||
SourceSelectionWidget *_sources;
|
||||
FilterSelectionWidget *_filters;
|
||||
QComboBox *_actions;
|
||||
QPushButton *_getSettings;
|
||||
ResizingPlainTextEdit *_settings;
|
||||
VariableTextEdit *_settings;
|
||||
std::shared_ptr<MacroActionFilter> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility(bool);
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#include "macro-action-hotkey.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "platform-funcs.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <obs-interaction.h>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionHotkey::id = "hotkey";
|
||||
|
||||
bool MacroActionHotkey::_registered = MacroActionFactory::Register(
|
||||
@@ -10,6 +13,26 @@ bool MacroActionHotkey::_registered = MacroActionFactory::Register(
|
||||
{MacroActionHotkey::Create, MacroActionHotkeyEdit::Create,
|
||||
"AdvSceneSwitcher.action.hotkey"});
|
||||
|
||||
const static std::map<MacroActionHotkey::Action, std::string> actionTypes = {
|
||||
{MacroActionHotkey::Action::OBS_HOTKEY,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs"},
|
||||
{MacroActionHotkey::Action::CUSTOM,
|
||||
"AdvSceneSwitcher.action.hotkey.type.custom"},
|
||||
};
|
||||
|
||||
const static std::map<obs_hotkey_registerer_type, std::string> hotkeyTypes = {
|
||||
{OBS_HOTKEY_REGISTERER_FRONTEND,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs.type.frontend"},
|
||||
{OBS_HOTKEY_REGISTERER_SOURCE,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs.type.soruce"},
|
||||
{OBS_HOTKEY_REGISTERER_OUTPUT,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs.type.output"},
|
||||
{OBS_HOTKEY_REGISTERER_ENCODER,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs.type.encoder"},
|
||||
{OBS_HOTKEY_REGISTERER_SERVICE,
|
||||
"AdvSceneSwitcher.action.hotkey.type.obs.type.service"},
|
||||
};
|
||||
|
||||
static std::unordered_map<HotkeyType, long> keyTable = {
|
||||
// Chars
|
||||
{HotkeyType::Key_A, OBS_KEY_A},
|
||||
@@ -130,7 +153,8 @@ static std::unordered_map<HotkeyType, long> keyTable = {
|
||||
//{HotkeyType::Key_NumpadEnter, ???},
|
||||
};
|
||||
|
||||
obs_key_combination keysToOBSKeycombo(const std::vector<HotkeyType> &keys)
|
||||
static obs_key_combination
|
||||
keysToOBSKeycombo(const std::vector<HotkeyType> &keys)
|
||||
{
|
||||
obs_key_combination combo{};
|
||||
auto it = keyTable.find(keys.back());
|
||||
@@ -178,7 +202,7 @@ obs_key_combination keysToOBSKeycombo(const std::vector<HotkeyType> &keys)
|
||||
return combo;
|
||||
}
|
||||
|
||||
void InjectKeys(const std::vector<HotkeyType> &keys, int duration)
|
||||
static void InjectKeys(const std::vector<HotkeyType> &keys, int duration)
|
||||
{
|
||||
auto combo = keysToOBSKeycombo(keys);
|
||||
if (obs_key_combination_is_empty(combo)) {
|
||||
@@ -192,7 +216,92 @@ void InjectKeys(const std::vector<HotkeyType> &keys, int duration)
|
||||
obs_hotkey_inject_event(combo, false);
|
||||
}
|
||||
|
||||
bool MacroActionHotkey::PerformAction()
|
||||
static void addNamePrefix(std::string &name, obs_hotkey_t *hotkey)
|
||||
{
|
||||
const auto type = obs_hotkey_get_registerer_type(hotkey);
|
||||
std::string prefix;
|
||||
if (type == OBS_HOTKEY_REGISTERER_SOURCE) {
|
||||
auto source = reinterpret_cast<obs_weak_source_t *>(
|
||||
obs_hotkey_get_registerer(hotkey));
|
||||
prefix = "[" + GetWeakSourceName(source) + "] ";
|
||||
} else if (type == OBS_HOTKEY_REGISTERER_OUTPUT) {
|
||||
auto weakOutput = reinterpret_cast<obs_weak_output_t *>(
|
||||
obs_hotkey_get_registerer(hotkey));
|
||||
std::string name;
|
||||
auto output = obs_weak_output_get_output(weakOutput);
|
||||
if (output) {
|
||||
name = obs_output_get_name(output);
|
||||
obs_output_release(output);
|
||||
}
|
||||
prefix = "[" + name + "] ";
|
||||
} else if (type == OBS_HOTKEY_REGISTERER_ENCODER) {
|
||||
auto weakEncoder = reinterpret_cast<obs_weak_encoder_t *>(
|
||||
obs_hotkey_get_registerer(hotkey));
|
||||
std::string name;
|
||||
auto encoder = obs_weak_encoder_get_encoder(weakEncoder);
|
||||
if (encoder) {
|
||||
name = obs_encoder_get_name(encoder);
|
||||
obs_encoder_release(encoder);
|
||||
}
|
||||
prefix = "[" + name + "] ";
|
||||
} else if (type == OBS_HOTKEY_REGISTERER_SERVICE) {
|
||||
auto weakService = reinterpret_cast<obs_weak_service_t *>(
|
||||
obs_hotkey_get_registerer(hotkey));
|
||||
std::string name;
|
||||
auto service = obs_weak_service_get_service(weakService);
|
||||
if (service) {
|
||||
name = obs_service_get_name(service);
|
||||
obs_service_release(service);
|
||||
}
|
||||
prefix = "[" + name + "] ";
|
||||
}
|
||||
name = prefix + name;
|
||||
}
|
||||
|
||||
static obs_hotkey_id getHotkeyIdByName(const std::string &name)
|
||||
{
|
||||
struct Parameters {
|
||||
std::string name;
|
||||
obs_hotkey_id id = OBS_INVALID_HOTKEY_ID;
|
||||
} params;
|
||||
|
||||
auto func = [](void *param, obs_hotkey_id id, obs_hotkey_t *hotkey) {
|
||||
auto params = static_cast<Parameters *>(param);
|
||||
std::string name = obs_hotkey_get_name(hotkey);
|
||||
addNamePrefix(name, hotkey);
|
||||
if (name == params->name) {
|
||||
params->id = id;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
params.name = name;
|
||||
obs_enum_hotkeys(func, ¶ms);
|
||||
return params.id;
|
||||
}
|
||||
|
||||
void MacroActionHotkey::SendOBSHotkey()
|
||||
{
|
||||
auto id = getHotkeyIdByName(_hotkeyName);
|
||||
if (id == OBS_INVALID_HOTKEY_ID) {
|
||||
blog(LOG_WARNING,
|
||||
"failed to get hotkey id for \"%s\" - key will not be pressed",
|
||||
_hotkeyName.c_str());
|
||||
return;
|
||||
}
|
||||
obs_queue_task(
|
||||
OBS_TASK_UI,
|
||||
[](void *param) {
|
||||
auto id = (size_t *)param;
|
||||
obs_hotkey_trigger_routed_callback(*id, false);
|
||||
obs_hotkey_trigger_routed_callback(*id, true);
|
||||
obs_hotkey_trigger_routed_callback(*id, false);
|
||||
},
|
||||
&id, true);
|
||||
}
|
||||
|
||||
void MacroActionHotkey::SendCustomHotkey()
|
||||
{
|
||||
std::vector<HotkeyType> keys;
|
||||
if (_leftShift) {
|
||||
@@ -224,8 +333,8 @@ bool MacroActionHotkey::PerformAction()
|
||||
}
|
||||
|
||||
if (!keys.empty()) {
|
||||
int dur = _duration;
|
||||
if (_onlySendToObs) {
|
||||
int dur = _duration.Milliseconds();
|
||||
if (_onlySendToObs || !canSimulateKeyPresses) {
|
||||
std::thread t([keys, dur]() { InjectKeys(keys, dur); });
|
||||
t.detach();
|
||||
} else {
|
||||
@@ -233,18 +342,35 @@ bool MacroActionHotkey::PerformAction()
|
||||
t.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionHotkey::PerformAction()
|
||||
{
|
||||
switch (_action) {
|
||||
case advss::MacroActionHotkey::Action::OBS_HOTKEY:
|
||||
SendOBSHotkey();
|
||||
break;
|
||||
case advss::MacroActionHotkey::Action::CUSTOM:
|
||||
SendCustomHotkey();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionHotkey::LogAction()
|
||||
void MacroActionHotkey::LogAction() const
|
||||
{
|
||||
vblog(LOG_INFO, "sent hotkey");
|
||||
vblog(LOG_INFO, "sent hotkey type %d", static_cast<int>(_action));
|
||||
}
|
||||
|
||||
bool MacroActionHotkey::Save(obs_data_t *obj)
|
||||
bool MacroActionHotkey::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "hotkeyType", _hotkeyType);
|
||||
obs_data_set_string(obj, "hotkeyName", _hotkeyName.c_str());
|
||||
obs_data_set_int(obj, "key", static_cast<int>(_key));
|
||||
obs_data_set_bool(obj, "left_shift", _leftShift);
|
||||
obs_data_set_bool(obj, "right_shift", _rightShift);
|
||||
@@ -254,14 +380,24 @@ bool MacroActionHotkey::Save(obs_data_t *obj)
|
||||
obs_data_set_bool(obj, "right_alt", _rightAlt);
|
||||
obs_data_set_bool(obj, "left_meta", _leftMeta);
|
||||
obs_data_set_bool(obj, "right_meta", _rightMeta);
|
||||
obs_data_set_int(obj, "duration", _duration);
|
||||
_duration.Save(obj);
|
||||
obs_data_set_bool(obj, "onlyOBS", _onlySendToObs);
|
||||
obs_data_set_int(obj, "version", 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionHotkey::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
int version = obs_data_get_int(obj, "version");
|
||||
if (version != 2) {
|
||||
_action = Action::CUSTOM;
|
||||
} else {
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
}
|
||||
_hotkeyType = static_cast<obs_hotkey_registerer_type>(
|
||||
obs_data_get_int(obj, "hotkeyType"));
|
||||
_hotkeyName = obs_data_get_string(obj, "hotkeyName");
|
||||
_key = static_cast<HotkeyType>(obs_data_get_int(obj, "key"));
|
||||
_leftShift = obs_data_get_bool(obj, "left_shift");
|
||||
_rightShift = obs_data_get_bool(obj, "right_shift");
|
||||
@@ -271,7 +407,11 @@ bool MacroActionHotkey::Load(obs_data_t *obj)
|
||||
_rightAlt = obs_data_get_bool(obj, "right_alt");
|
||||
_leftMeta = obs_data_get_bool(obj, "left_meta");
|
||||
_rightMeta = obs_data_get_bool(obj, "right_meta");
|
||||
_duration = obs_data_get_int(obj, "duration");
|
||||
if (version == 0) {
|
||||
_duration = obs_data_get_int(obj, "duration") / 1000.0;
|
||||
} else {
|
||||
_duration.Load(obj);
|
||||
}
|
||||
_onlySendToObs = obs_data_get_bool(obj, "onlyOBS");
|
||||
return true;
|
||||
}
|
||||
@@ -386,36 +526,100 @@ static inline void populateKeySelection(QComboBox *list)
|
||||
list->addItem("NumpadEnter");
|
||||
}
|
||||
|
||||
static void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, value] : actionTypes) {
|
||||
list->addItem(obs_module_text(value.c_str()));
|
||||
}
|
||||
}
|
||||
static void populateHotkeyTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, value] : hotkeyTypes) {
|
||||
list->addItem(obs_module_text(value.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static void addNamePrefix(QString &name, obs_hotkey_t *hotkey)
|
||||
{
|
||||
auto temp = name.toStdString();
|
||||
addNamePrefix(temp, hotkey);
|
||||
name = QString::fromStdString(temp);
|
||||
}
|
||||
|
||||
static void populateHotkeyNames(QComboBox *list,
|
||||
obs_hotkey_registerer_type type)
|
||||
{
|
||||
struct Parameters {
|
||||
QStringList names;
|
||||
QStringList descriptions;
|
||||
obs_hotkey_registerer_type type;
|
||||
} params;
|
||||
|
||||
auto func = [](void *param, obs_hotkey_id, obs_hotkey_t *hotkey) {
|
||||
auto params = static_cast<Parameters *>(param);
|
||||
if (obs_hotkey_get_registerer_type(hotkey) == params->type) {
|
||||
QString description(obs_hotkey_get_description(hotkey));
|
||||
addNamePrefix(description, hotkey);
|
||||
params->descriptions.append(description);
|
||||
QString name(obs_hotkey_get_name(hotkey));
|
||||
addNamePrefix(name, hotkey);
|
||||
params->names.append(name);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
params.type = type;
|
||||
obs_enum_hotkeys(func, ¶ms);
|
||||
|
||||
list->clear();
|
||||
for (int i = 0; i < params.names.size(); i++) {
|
||||
list->addItem(params.descriptions.at(i), params.names.at(i));
|
||||
}
|
||||
AddSelectionEntry(list,
|
||||
obs_module_text("AdvSceneSwitcher.selectHotkey"));
|
||||
}
|
||||
|
||||
MacroActionHotkeyEdit::MacroActionHotkeyEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionHotkey> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_actionType(new QComboBox()),
|
||||
_hotkeyType(new QComboBox()),
|
||||
_obsHotkeys(new QComboBox()),
|
||||
_keys(new QComboBox()),
|
||||
_leftShift(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.leftShift"))),
|
||||
_rightShift(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.action.hotkey.rightShift"))),
|
||||
_leftCtrl(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.leftCtrl"))),
|
||||
_rightCtrl(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.rightCtrl"))),
|
||||
_leftAlt(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.leftAlt"))),
|
||||
_rightAlt(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.rightAlt"))),
|
||||
_leftMeta(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.leftMeta"))),
|
||||
_rightMeta(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.rightMeta"))),
|
||||
_duration(new DurationSelection(this, false)),
|
||||
_onlySendToOBS(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.onlyOBS"))),
|
||||
_noKeyPressSimulationWarning(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.disabled"))),
|
||||
_entryLayout(new QHBoxLayout()),
|
||||
_keyConfigLayout(new QHBoxLayout())
|
||||
{
|
||||
_keys = new QComboBox();
|
||||
_leftShift = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.leftShift"));
|
||||
_rightShift = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.rightShift"));
|
||||
_leftCtrl = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.leftCtrl"));
|
||||
_rightCtrl = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.rightCtrl"));
|
||||
_leftAlt = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.leftAlt"));
|
||||
_rightAlt = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.rightAlt"));
|
||||
_leftMeta = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.leftMeta"));
|
||||
_rightMeta = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.rightMeta"));
|
||||
_duration = new QSpinBox();
|
||||
_duration->setMaximum(5000);
|
||||
_onlySendToOBS = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.onlyOBS"));
|
||||
_noKeyPressSimulationWarning = new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.disabled"));
|
||||
|
||||
populateKeySelection(_keys);
|
||||
populateActionSelection(_actionType);
|
||||
populateHotkeyTypeSelection(_hotkeyType);
|
||||
|
||||
QWidget::connect(_actionType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_hotkeyType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(HotkeyTypeChanged(int)));
|
||||
QWidget::connect(_obsHotkeys, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(OBSHotkeyChanged(int)));
|
||||
QWidget::connect(_keys, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(KeyChanged(int)));
|
||||
QWidget::connect(_leftShift, SIGNAL(stateChanged(int)), this,
|
||||
@@ -434,47 +638,107 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
|
||||
SLOT(LMetaChanged(int)));
|
||||
QWidget::connect(_rightMeta, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(RMetaChanged(int)));
|
||||
QWidget::connect(_duration, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(DurationChanged(int)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(const Duration &)),
|
||||
this, SLOT(DurationChanged(const Duration &)));
|
||||
QWidget::connect(_onlySendToOBS, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(OnlySendToOBSChanged(int)));
|
||||
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{keys}}", _keys},
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.hotkey.entry"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
_entryLayout->setContentsMargins(0, 0, 0, 0);
|
||||
_keyConfigLayout->setContentsMargins(0, 0, 0, 0);
|
||||
_keyConfigLayout->addWidget(_leftShift);
|
||||
_keyConfigLayout->addWidget(_rightShift);
|
||||
_keyConfigLayout->addWidget(_leftCtrl);
|
||||
_keyConfigLayout->addWidget(_rightCtrl);
|
||||
_keyConfigLayout->addWidget(_leftAlt);
|
||||
_keyConfigLayout->addWidget(_rightAlt);
|
||||
_keyConfigLayout->addWidget(_leftMeta);
|
||||
_keyConfigLayout->addWidget(_rightMeta);
|
||||
_keyConfigLayout->addStretch();
|
||||
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
line2Layout->addWidget(_leftShift);
|
||||
line2Layout->addWidget(_rightShift);
|
||||
line2Layout->addWidget(_leftCtrl);
|
||||
line2Layout->addWidget(_rightCtrl);
|
||||
line2Layout->addWidget(_leftAlt);
|
||||
line2Layout->addWidget(_rightAlt);
|
||||
line2Layout->addWidget(_leftMeta);
|
||||
line2Layout->addWidget(_rightMeta);
|
||||
line2Layout->addStretch();
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(_entryLayout);
|
||||
mainLayout->addLayout(_keyConfigLayout);
|
||||
mainLayout->addWidget(_onlySendToOBS);
|
||||
mainLayout->addWidget(_noKeyPressSimulationWarning);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
_onlySendToOBS->setEnabled(canSimulateKeyPresses);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::SetWarningVisibility()
|
||||
void MacroActionHotkeyEdit::RepopulateOBSHotkeySelection()
|
||||
{
|
||||
populateHotkeyNames(_obsHotkeys, _entryData->_hotkeyType);
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::SetWidgetVisibility()
|
||||
{
|
||||
_entryLayout->removeWidget(_actionType);
|
||||
_entryLayout->removeWidget(_hotkeyType);
|
||||
_entryLayout->removeWidget(_obsHotkeys);
|
||||
_entryLayout->removeWidget(_keys);
|
||||
_entryLayout->removeWidget(_duration);
|
||||
ClearLayout(_entryLayout);
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
_entryData->_action ==
|
||||
MacroActionHotkey::Action::OBS_HOTKEY
|
||||
? "AdvSceneSwitcher.action.hotkey.entry.obs"
|
||||
: "AdvSceneSwitcher.action.hotkey.entry.custom"),
|
||||
_entryLayout,
|
||||
{
|
||||
{"{{actionType}}", _actionType},
|
||||
{"{{hotkeyType}}", _hotkeyType},
|
||||
{"{{obsHotkeys}}", _obsHotkeys},
|
||||
{"{{keys}}", _keys},
|
||||
{"{{duration}}", _duration},
|
||||
});
|
||||
|
||||
_noKeyPressSimulationWarning->setVisible(!_entryData->_onlySendToObs &&
|
||||
!canSimulateKeyPresses);
|
||||
SetLayoutVisible(_keyConfigLayout,
|
||||
_entryData->_action ==
|
||||
MacroActionHotkey::Action::CUSTOM);
|
||||
_duration->setVisible(_entryData->_action ==
|
||||
MacroActionHotkey::Action::CUSTOM);
|
||||
_keys->setVisible(_entryData->_action ==
|
||||
MacroActionHotkey::Action::CUSTOM);
|
||||
_onlySendToOBS->setVisible(_entryData->_action ==
|
||||
MacroActionHotkey::Action::CUSTOM);
|
||||
_hotkeyType->setVisible(_entryData->_action ==
|
||||
MacroActionHotkey::Action::OBS_HOTKEY);
|
||||
_obsHotkeys->setVisible(_entryData->_action ==
|
||||
MacroActionHotkey::Action::OBS_HOTKEY);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
static QString getHotkeyDescriptionByName(const std::string &name)
|
||||
{
|
||||
struct Parameters {
|
||||
std::string name;
|
||||
QString description = "";
|
||||
} params;
|
||||
|
||||
auto func = [](void *param, obs_hotkey_id id, obs_hotkey_t *hotkey) {
|
||||
auto params = static_cast<Parameters *>(param);
|
||||
std::string name = obs_hotkey_get_name(hotkey);
|
||||
addNamePrefix(name, hotkey);
|
||||
if (name == params->name) {
|
||||
params->description =
|
||||
obs_hotkey_get_description(hotkey);
|
||||
addNamePrefix(params->description, hotkey);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
params.name = name;
|
||||
obs_enum_hotkeys(func, ¶ms);
|
||||
return params.description;
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::UpdateEntryData()
|
||||
@@ -483,6 +747,11 @@ void MacroActionHotkeyEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_actionType->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_hotkeyType->setCurrentIndex(static_cast<int>(_entryData->_hotkeyType));
|
||||
RepopulateOBSHotkeySelection();
|
||||
_obsHotkeys->setCurrentText(
|
||||
getHotkeyDescriptionByName(_entryData->_hotkeyName));
|
||||
_keys->setCurrentIndex(static_cast<int>(_entryData->_key));
|
||||
_leftShift->setChecked(_entryData->_leftShift);
|
||||
_rightShift->setChecked(_entryData->_rightShift);
|
||||
@@ -492,9 +761,10 @@ void MacroActionHotkeyEdit::UpdateEntryData()
|
||||
_rightAlt->setChecked(_entryData->_rightAlt);
|
||||
_leftMeta->setChecked(_entryData->_leftMeta);
|
||||
_rightMeta->setChecked(_entryData->_rightMeta);
|
||||
_duration->setValue(_entryData->_duration);
|
||||
_onlySendToOBS->setChecked(_entryData->_onlySendToObs);
|
||||
SetWarningVisibility();
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_onlySendToOBS->setChecked(_entryData->_onlySendToObs ||
|
||||
!canSimulateKeyPresses);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::LShiftChanged(int state)
|
||||
@@ -503,7 +773,7 @@ void MacroActionHotkeyEdit::LShiftChanged(int state)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_leftShift = state;
|
||||
}
|
||||
|
||||
@@ -513,7 +783,7 @@ void MacroActionHotkeyEdit::RShiftChanged(int state)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_rightShift = state;
|
||||
}
|
||||
|
||||
@@ -523,7 +793,7 @@ void MacroActionHotkeyEdit::LCtrlChanged(int state)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_leftCtrl = state;
|
||||
}
|
||||
|
||||
@@ -533,7 +803,7 @@ void MacroActionHotkeyEdit::RCtrlChanged(int state)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_rightCtrl = state;
|
||||
}
|
||||
|
||||
@@ -543,7 +813,7 @@ void MacroActionHotkeyEdit::LAltChanged(int state)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_leftAlt = state;
|
||||
}
|
||||
|
||||
@@ -553,7 +823,7 @@ void MacroActionHotkeyEdit::RAltChanged(int state)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_rightAlt = state;
|
||||
}
|
||||
|
||||
@@ -563,7 +833,7 @@ void MacroActionHotkeyEdit::LMetaChanged(int state)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_leftMeta = state;
|
||||
}
|
||||
|
||||
@@ -573,18 +843,18 @@ void MacroActionHotkeyEdit::RMetaChanged(int state)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_rightMeta = state;
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::DurationChanged(int ms)
|
||||
void MacroActionHotkeyEdit::DurationChanged(const Duration &dur)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration = ms;
|
||||
auto lock = LockContext();
|
||||
_entryData->_duration = dur;
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::OnlySendToOBSChanged(int state)
|
||||
@@ -593,9 +863,9 @@ void MacroActionHotkeyEdit::OnlySendToOBSChanged(int state)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_onlySendToObs = state;
|
||||
SetWarningVisibility();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::KeyChanged(int key)
|
||||
@@ -604,6 +874,47 @@ void MacroActionHotkeyEdit::KeyChanged(int key)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_key = static_cast<HotkeyType>(key);
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<MacroActionHotkey::Action>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::HotkeyTypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
auto lock = LockContext();
|
||||
_entryData->_hotkeyType =
|
||||
static_cast<obs_hotkey_registerer_type>(value);
|
||||
}
|
||||
RepopulateOBSHotkeySelection();
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::OBSHotkeyChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
if (idx == -1) {
|
||||
_entryData->_hotkeyName = "";
|
||||
return;
|
||||
}
|
||||
_entryData->_hotkeyName =
|
||||
_obsHotkeys->itemData(idx).toString().toStdString();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,26 +1,37 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "hotkey.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
#include <QSpinBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroActionHotkey : public MacroAction {
|
||||
public:
|
||||
MacroActionHotkey(Macro *m) : MacroAction(m) {}
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionHotkey>(m);
|
||||
}
|
||||
|
||||
OBSWeakSource _HotkeySource;
|
||||
enum class Action {
|
||||
OBS_HOTKEY,
|
||||
CUSTOM,
|
||||
};
|
||||
Action _action = Action::OBS_HOTKEY;
|
||||
|
||||
obs_hotkey_registerer_type _hotkeyType = OBS_HOTKEY_REGISTERER_FRONTEND;
|
||||
std::string _hotkeyName;
|
||||
|
||||
HotkeyType _key = HotkeyType::Key_NoKey;
|
||||
bool _leftShift = false;
|
||||
bool _rightShift = false;
|
||||
@@ -30,7 +41,7 @@ public:
|
||||
bool _rightAlt = false;
|
||||
bool _leftMeta = false;
|
||||
bool _rightMeta = false;
|
||||
int _duration = 300; // in ms
|
||||
Duration _duration = 0.3;
|
||||
#ifdef __APPLE__
|
||||
bool _onlySendToObs = true;
|
||||
#else
|
||||
@@ -38,6 +49,9 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
void SendOBSHotkey();
|
||||
void SendCustomHotkey();
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
@@ -59,6 +73,9 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int key);
|
||||
void HotkeyTypeChanged(int key);
|
||||
void OBSHotkeyChanged(int);
|
||||
void KeyChanged(int key);
|
||||
void LShiftChanged(int state);
|
||||
void RShiftChanged(int state);
|
||||
@@ -68,10 +85,13 @@ private slots:
|
||||
void RAltChanged(int state);
|
||||
void LMetaChanged(int state);
|
||||
void RMetaChanged(int state);
|
||||
void DurationChanged(int ms);
|
||||
void DurationChanged(const Duration &);
|
||||
void OnlySendToOBSChanged(int state);
|
||||
|
||||
protected:
|
||||
QComboBox *_actionType;
|
||||
QComboBox *_hotkeyType;
|
||||
QComboBox *_obsHotkeys;
|
||||
QComboBox *_keys;
|
||||
QCheckBox *_leftShift;
|
||||
QCheckBox *_rightShift;
|
||||
@@ -81,14 +101,19 @@ protected:
|
||||
QCheckBox *_rightAlt;
|
||||
QCheckBox *_leftMeta;
|
||||
QCheckBox *_rightMeta;
|
||||
QSpinBox *_duration;
|
||||
DurationSelection *_duration;
|
||||
QCheckBox *_onlySendToOBS;
|
||||
QLabel *_noKeyPressSimulationWarning;
|
||||
|
||||
std::shared_ptr<MacroActionHotkey> _entryData;
|
||||
|
||||
private:
|
||||
void SetWarningVisibility();
|
||||
QHBoxLayout *_mainLayout;
|
||||
void RepopulateOBSHotkeySelection();
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QHBoxLayout *_entryLayout;
|
||||
QHBoxLayout *_keyConfigLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "macro-action-http.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "curl-helper.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionHttp::id = "http";
|
||||
|
||||
bool MacroActionHttp::_registered = MacroActionFactory::Register(
|
||||
@@ -16,30 +18,56 @@ const static std::map<MacroActionHttp::Method, std::string> methods = {
|
||||
"AdvSceneSwitcher.action.http.type.post"},
|
||||
};
|
||||
|
||||
size_t WriteCB(void *, size_t size, size_t nmemb, std::string *)
|
||||
static size_t DropCB(void *, size_t size, size_t nmemb, std::string *)
|
||||
{
|
||||
// Just drop the data
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static size_t WriteCB(void *ptr, size_t size, size_t nmemb, std::string *buffer)
|
||||
{
|
||||
buffer->append((char *)ptr, nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
void MacroActionHttp::SetupHeaders()
|
||||
{
|
||||
if (!_setHeaders) {
|
||||
return;
|
||||
}
|
||||
struct curl_slist *headers = nullptr;
|
||||
for (auto &header : _headers) {
|
||||
headers = switcher->curl.SlistAppend(headers, header.c_str());
|
||||
}
|
||||
if (!_headers.empty()) {
|
||||
switcher->curl.SetOpt(CURLOPT_HTTPHEADER, headers);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionHttp::Get()
|
||||
{
|
||||
switcher->curl.SetOpt(CURLOPT_URL, _url.c_str());
|
||||
switcher->curl.SetOpt(CURLOPT_HTTPGET, 1L);
|
||||
switcher->curl.SetOpt(CURLOPT_TIMEOUT_MS, _timeout.seconds * 1000);
|
||||
switcher->curl.SetOpt(CURLOPT_TIMEOUT_MS, _timeout.Milliseconds());
|
||||
SetupHeaders();
|
||||
|
||||
std::string response;
|
||||
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, WriteCB);
|
||||
if (IsReferencedInVars()) {
|
||||
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, WriteCB);
|
||||
} else {
|
||||
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, DropCB);
|
||||
}
|
||||
switcher->curl.SetOpt(CURLOPT_WRITEDATA, &response);
|
||||
|
||||
switcher->curl.Perform();
|
||||
|
||||
SetVariableValue(response);
|
||||
}
|
||||
|
||||
void MacroActionHttp::Post()
|
||||
{
|
||||
switcher->curl.SetOpt(CURLOPT_URL, _url.c_str());
|
||||
switcher->curl.SetOpt(CURLOPT_POSTFIELDS, _data.c_str());
|
||||
switcher->curl.SetOpt(CURLOPT_TIMEOUT_MS, _timeout.seconds * 1000);
|
||||
switcher->curl.SetOpt(CURLOPT_TIMEOUT_MS, _timeout.Milliseconds());
|
||||
SetupHeaders();
|
||||
switcher->curl.Perform();
|
||||
}
|
||||
|
||||
@@ -64,7 +92,7 @@ bool MacroActionHttp::PerformAction()
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionHttp::LogAction()
|
||||
void MacroActionHttp::LogAction() const
|
||||
{
|
||||
auto it = methods.find(_method);
|
||||
if (it != methods.end()) {
|
||||
@@ -77,11 +105,13 @@ void MacroActionHttp::LogAction()
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionHttp::Save(obs_data_t *obj)
|
||||
bool MacroActionHttp::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "url", _url.c_str());
|
||||
obs_data_set_string(obj, "data", _data.c_str());
|
||||
_url.Save(obj, "url");
|
||||
_data.Save(obj, "data");
|
||||
obs_data_set_bool(obj, "setHeaders", _setHeaders);
|
||||
_headers.Save(obj, "headers", "header");
|
||||
obs_data_set_int(obj, "method", static_cast<int>(_method));
|
||||
_timeout.Save(obj);
|
||||
return true;
|
||||
@@ -90,16 +120,18 @@ bool MacroActionHttp::Save(obs_data_t *obj)
|
||||
bool MacroActionHttp::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_url = obs_data_get_string(obj, "url");
|
||||
_data = obs_data_get_string(obj, "data");
|
||||
_url.Load(obj, "url");
|
||||
_data.Load(obj, "data");
|
||||
_setHeaders = obs_data_get_bool(obj, "setHeaders");
|
||||
_headers.Load(obj, "headers", "header");
|
||||
_method = static_cast<Method>(obs_data_get_int(obj, "method"));
|
||||
_timeout.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionHttp::GetShortDesc()
|
||||
std::string MacroActionHttp::GetShortDesc() const
|
||||
{
|
||||
return _url;
|
||||
return _url.UnresolvedValue();
|
||||
}
|
||||
|
||||
static inline void populateMethodSelection(QComboBox *list)
|
||||
@@ -112,12 +144,19 @@ static inline void populateMethodSelection(QComboBox *list)
|
||||
MacroActionHttpEdit::MacroActionHttpEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionHttp> entryData)
|
||||
: QWidget(parent),
|
||||
_url(new QLineEdit()),
|
||||
_url(new VariableLineEdit(this)),
|
||||
_methods(new QComboBox()),
|
||||
_data(new ResizingPlainTextEdit(this)),
|
||||
_data(new VariableTextEdit(this)),
|
||||
_setHeaders(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.http.setHeaders"))),
|
||||
_headerListLayout(new QVBoxLayout()),
|
||||
_headerList(new StringListEdit(
|
||||
this, obs_module_text("AdvSceneSwitcher.action.http.headers"),
|
||||
obs_module_text("AdvSceneSwitcher.action.http.addHeader"))),
|
||||
_timeout(new DurationSelection(this, false))
|
||||
{
|
||||
populateMethodSelection(_methods);
|
||||
_headerList->SetMaxStringSize(4096);
|
||||
|
||||
QWidget::connect(_url, SIGNAL(editingFinished()), this,
|
||||
SLOT(URLChanged()));
|
||||
@@ -125,8 +164,13 @@ MacroActionHttpEdit::MacroActionHttpEdit(
|
||||
SLOT(DataChanged()));
|
||||
QWidget::connect(_methods, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(MethodChanged(int)));
|
||||
QWidget::connect(_timeout, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(TimeoutChanged(double)));
|
||||
QWidget::connect(_setHeaders, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(SetHeadersChanged(int)));
|
||||
QWidget::connect(_headerList,
|
||||
SIGNAL(StringListChanged(const StringList &)), this,
|
||||
SLOT(HeadersChanged(const StringList &)));
|
||||
QWidget::connect(_timeout, SIGNAL(DurationChanged(const Duration &)),
|
||||
this, SLOT(TimeoutChanged(const Duration &)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{url}}", _url},
|
||||
@@ -135,16 +179,22 @@ MacroActionHttpEdit::MacroActionHttpEdit(
|
||||
{"{{timeout}}", _timeout},
|
||||
};
|
||||
auto *actionLayout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.http.entry.line1"),
|
||||
actionLayout, widgetPlaceholders);
|
||||
auto *timeoutLayout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.http.entry.line2"),
|
||||
timeoutLayout, widgetPlaceholders);
|
||||
|
||||
_headerListLayout->addWidget(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.http.headers")));
|
||||
_headerListLayout->addWidget(_headerList);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(actionLayout);
|
||||
mainLayout->addWidget(_setHeaders);
|
||||
mainLayout->addLayout(_headerListLayout);
|
||||
mainLayout->addWidget(_data);
|
||||
mainLayout->addLayout(timeoutLayout);
|
||||
setLayout(mainLayout);
|
||||
@@ -160,8 +210,10 @@ void MacroActionHttpEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_url->setText(QString::fromStdString(_entryData->_url));
|
||||
_data->setPlainText(QString::fromStdString(_entryData->_data));
|
||||
_url->setText(_entryData->_url);
|
||||
_data->setPlainText(_entryData->_data);
|
||||
_setHeaders->setChecked(_entryData->_setHeaders);
|
||||
_headerList->SetStringList(_entryData->_headers);
|
||||
_methods->setCurrentIndex(static_cast<int>(_entryData->_method));
|
||||
_timeout->SetDuration(_entryData->_timeout);
|
||||
SetWidgetVisibility();
|
||||
@@ -173,7 +225,7 @@ void MacroActionHttpEdit::URLChanged()
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_url = _url->text().toStdString();
|
||||
emit(HeaderInfoChanged(_url->text()));
|
||||
}
|
||||
@@ -184,33 +236,48 @@ void MacroActionHttpEdit::MethodChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_method = static_cast<MacroActionHttp::Method>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::TimeoutChanged(double seconds)
|
||||
void MacroActionHttpEdit::TimeoutChanged(const Duration &dur)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_timeout.seconds = seconds;
|
||||
auto lock = LockContext();
|
||||
_entryData->_timeout = dur;
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::SetHeadersChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_setHeaders = value;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::HeadersChanged(const StringList &headers)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_headers = headers;
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::SetWidgetVisibility()
|
||||
{
|
||||
switch (_entryData->_method) {
|
||||
case MacroActionHttp::Method::GET:
|
||||
_data->hide();
|
||||
break;
|
||||
case MacroActionHttp::Method::POST:
|
||||
_data->show();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_data->setVisible(_entryData->_method == MacroActionHttp::Method::POST);
|
||||
SetLayoutVisible(_headerListLayout, _entryData->_setHeaders);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
@@ -222,9 +289,11 @@ void MacroActionHttpEdit::DataChanged()
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_data = _data->toPlainText().toUtf8().constData();
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "resizing-text-edit.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "string-list.hpp"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroActionHttp : public MacroAction {
|
||||
public:
|
||||
MacroActionHttp(Macro *m) : MacroAction(m) {}
|
||||
MacroActionHttp(Macro *m) : MacroAction(m, true) {}
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionHttp>(m);
|
||||
@@ -25,12 +30,15 @@ public:
|
||||
POST,
|
||||
};
|
||||
|
||||
std::string _url = obs_module_text("AdvSceneSwitcher.enterURL");
|
||||
std::string _data = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
StringVariable _url = obs_module_text("AdvSceneSwitcher.enterURL");
|
||||
StringVariable _data = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
bool _setHeaders = false;
|
||||
StringList _headers;
|
||||
Method _method = Method::GET;
|
||||
Duration _timeout;
|
||||
Duration _timeout = Duration(1.0);
|
||||
|
||||
private:
|
||||
void SetupHeaders();
|
||||
void Get();
|
||||
void Post();
|
||||
|
||||
@@ -58,7 +66,9 @@ private slots:
|
||||
void DataChanged();
|
||||
void URLChanged();
|
||||
void MethodChanged(int);
|
||||
void TimeoutChanged(double seconds);
|
||||
void TimeoutChanged(const Duration &seconds);
|
||||
void SetHeadersChanged(int);
|
||||
void HeadersChanged(const StringList &);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -68,9 +78,14 @@ protected:
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QLineEdit *_url;
|
||||
VariableLineEdit *_url;
|
||||
QComboBox *_methods;
|
||||
ResizingPlainTextEdit *_data;
|
||||
VariableTextEdit *_data;
|
||||
QCheckBox *_setHeaders;
|
||||
QVBoxLayout *_headerListLayout;
|
||||
StringListEdit *_headerList;
|
||||
DurationSelection *_timeout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "macro-action-macro.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionMacro::id = "macro";
|
||||
|
||||
bool MacroActionMacro::_registered = MacroActionFactory::Register(
|
||||
@@ -9,37 +11,68 @@ bool MacroActionMacro::_registered = MacroActionFactory::Register(
|
||||
{MacroActionMacro::Create, MacroActionMacroEdit::Create,
|
||||
"AdvSceneSwitcher.action.macro"});
|
||||
|
||||
const static std::map<PerformMacroAction, std::string> actionTypes = {
|
||||
{PerformMacroAction::PAUSE, "AdvSceneSwitcher.action.macro.type.pause"},
|
||||
{PerformMacroAction::UNPAUSE,
|
||||
const static std::map<MacroActionMacro::Action, std::string> actionTypes = {
|
||||
{MacroActionMacro::Action::PAUSE,
|
||||
"AdvSceneSwitcher.action.macro.type.pause"},
|
||||
{MacroActionMacro::Action::UNPAUSE,
|
||||
"AdvSceneSwitcher.action.macro.type.unpause"},
|
||||
{PerformMacroAction::RESET_COUNTER,
|
||||
{MacroActionMacro::Action::RESET_COUNTER,
|
||||
"AdvSceneSwitcher.action.macro.type.resetCounter"},
|
||||
{PerformMacroAction::RUN, "AdvSceneSwitcher.action.macro.type.run"},
|
||||
{PerformMacroAction::STOP, "AdvSceneSwitcher.action.macro.type.stop"},
|
||||
{MacroActionMacro::Action::RUN,
|
||||
"AdvSceneSwitcher.action.macro.type.run"},
|
||||
{MacroActionMacro::Action::STOP,
|
||||
"AdvSceneSwitcher.action.macro.type.stop"},
|
||||
{MacroActionMacro::Action::DISABLE_ACTION,
|
||||
"AdvSceneSwitcher.action.macro.type.disableAction"},
|
||||
{MacroActionMacro::Action::ENABLE_ACTION,
|
||||
"AdvSceneSwitcher.action.macro.type.enableAction"},
|
||||
{MacroActionMacro::Action::TOGGLE_ACTION,
|
||||
"AdvSceneSwitcher.action.macro.type.toggleAction"},
|
||||
};
|
||||
|
||||
bool MacroActionMacro::PerformAction()
|
||||
{
|
||||
if (!_macro.get()) {
|
||||
auto macro = _macro.GetMacro();
|
||||
if (!macro) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_action) {
|
||||
case PerformMacroAction::PAUSE:
|
||||
_macro->SetPaused();
|
||||
case Action::PAUSE:
|
||||
macro->SetPaused();
|
||||
break;
|
||||
case PerformMacroAction::UNPAUSE:
|
||||
_macro->SetPaused(false);
|
||||
case Action::UNPAUSE:
|
||||
macro->SetPaused(false);
|
||||
break;
|
||||
case PerformMacroAction::RESET_COUNTER:
|
||||
_macro->ResetCount();
|
||||
case Action::RESET_COUNTER:
|
||||
macro->ResetRunCount();
|
||||
break;
|
||||
case PerformMacroAction::RUN:
|
||||
_macro->PerformActions();
|
||||
case Action::RUN:
|
||||
if (!macro->Paused()) {
|
||||
macro->PerformActions();
|
||||
}
|
||||
break;
|
||||
case PerformMacroAction::STOP:
|
||||
_macro->Stop();
|
||||
case Action::STOP:
|
||||
macro->Stop();
|
||||
break;
|
||||
case Action::DISABLE_ACTION:
|
||||
if (IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1,
|
||||
false)) {
|
||||
macro->Actions().at(_actionIndex - 1)->SetEnabled(false);
|
||||
}
|
||||
break;
|
||||
case Action::ENABLE_ACTION:
|
||||
if (IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1,
|
||||
false)) {
|
||||
macro->Actions().at(_actionIndex - 1)->SetEnabled(true);
|
||||
}
|
||||
break;
|
||||
case Action::TOGGLE_ACTION:
|
||||
if (IsValidMacroSegmentIndex(macro.get(), _actionIndex - 1,
|
||||
false)) {
|
||||
auto action = macro->Actions().at(_actionIndex - 1);
|
||||
action->SetEnabled(!action->Enabled());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -47,38 +80,52 @@ bool MacroActionMacro::PerformAction()
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionMacro::LogAction()
|
||||
void MacroActionMacro::LogAction() const
|
||||
{
|
||||
if (!_macro.get()) {
|
||||
auto macro = _macro.GetMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
switch (_action) {
|
||||
case PerformMacroAction::PAUSE:
|
||||
vblog(LOG_INFO, "paused \"%s\"", _macro->Name().c_str());
|
||||
case Action::PAUSE:
|
||||
vblog(LOG_INFO, "paused \"%s\"", macro->Name().c_str());
|
||||
break;
|
||||
case PerformMacroAction::UNPAUSE:
|
||||
vblog(LOG_INFO, "unpaused \"%s\"", _macro->Name().c_str());
|
||||
case Action::UNPAUSE:
|
||||
vblog(LOG_INFO, "unpaused \"%s\"", macro->Name().c_str());
|
||||
break;
|
||||
case PerformMacroAction::RESET_COUNTER:
|
||||
case Action::RESET_COUNTER:
|
||||
vblog(LOG_INFO, "reset counter for \"%s\"",
|
||||
_macro->Name().c_str());
|
||||
macro->Name().c_str());
|
||||
break;
|
||||
case PerformMacroAction::RUN:
|
||||
case Action::RUN:
|
||||
vblog(LOG_INFO, "run nested macro \"%s\"",
|
||||
_macro->Name().c_str());
|
||||
macro->Name().c_str());
|
||||
break;
|
||||
case PerformMacroAction::STOP:
|
||||
vblog(LOG_INFO, "stopped macro \"%s\"", _macro->Name().c_str());
|
||||
case Action::STOP:
|
||||
vblog(LOG_INFO, "stopped macro \"%s\"", macro->Name().c_str());
|
||||
break;
|
||||
case Action::DISABLE_ACTION:
|
||||
vblog(LOG_INFO, "disabled action %d of macro \"%s\"",
|
||||
_actionIndex.GetValue(), macro->Name().c_str());
|
||||
break;
|
||||
case Action::ENABLE_ACTION:
|
||||
vblog(LOG_INFO, "enabled action %d of macro \"%s\"",
|
||||
_actionIndex.GetValue(), macro->Name().c_str());
|
||||
break;
|
||||
case Action::TOGGLE_ACTION:
|
||||
vblog(LOG_INFO, "toggled action %d of macro \"%s\"",
|
||||
_actionIndex.GetValue(), macro->Name().c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionMacro::Save(obs_data_t *obj)
|
||||
bool MacroActionMacro::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_macro.Save(obj);
|
||||
_actionIndex.Save(obj, "actionIndex");
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
@@ -87,17 +134,15 @@ bool MacroActionMacro::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_macro.Load(obj);
|
||||
_action = static_cast<PerformMacroAction>(
|
||||
_actionIndex.Load(obj, "actionIndex");
|
||||
_action = static_cast<MacroActionMacro::Action>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionMacro::GetShortDesc()
|
||||
std::string MacroActionMacro::GetShortDesc() const
|
||||
{
|
||||
if (_macro.get()) {
|
||||
return _macro->Name();
|
||||
}
|
||||
return "";
|
||||
return _macro.Name();
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
@@ -109,27 +154,28 @@ static inline void populateActionSelection(QComboBox *list)
|
||||
|
||||
MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionMacro> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_macros(new MacroSelection(parent)),
|
||||
_actionIndex(new MacroSegmentSelection(
|
||||
this, MacroSegmentSelection::Type::ACTION)),
|
||||
_actions(new QComboBox())
|
||||
{
|
||||
_macros = new MacroSelection(parent);
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(MacroChanged(const QString &)));
|
||||
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
|
||||
SLOT(MacroRemove(const QString &)));
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_actionIndex,
|
||||
SIGNAL(SelectionChanged(const IntVariable &)), this,
|
||||
SLOT(ActionIndexChanged(const IntVariable &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
{"{{macros}}", _macros},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
auto mainLayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"),
|
||||
mainLayout,
|
||||
{{"{{actions}}", _actions},
|
||||
{"{{actionIndex}}", _actionIndex},
|
||||
{"{{macros}}", _macros}});
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -143,11 +189,10 @@ void MacroActionMacroEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_macros->SetCurrentMacro(_entryData->_macro.get());
|
||||
if (_entryData->_action == PerformMacroAction::RUN ||
|
||||
_entryData->_action == PerformMacroAction::STOP) {
|
||||
_macros->HideSelectedMacro();
|
||||
}
|
||||
_actionIndex->SetValue(_entryData->_actionIndex);
|
||||
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
|
||||
_macros->SetCurrentMacro(_entryData->_macro);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::MacroChanged(const QString &text)
|
||||
@@ -156,8 +201,9 @@ void MacroActionMacroEdit::MacroChanged(const QString &text)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_macro.UpdateRef(text);
|
||||
auto lock = LockContext();
|
||||
_entryData->_macro = text;
|
||||
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -168,20 +214,37 @@ void MacroActionMacroEdit::ActionChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<PerformMacroAction>(value);
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<MacroActionMacro::Action>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
if (_entryData->_action == PerformMacroAction::RUN ||
|
||||
_entryData->_action == PerformMacroAction::STOP) {
|
||||
void MacroActionMacroEdit::ActionIndexChanged(const IntVariable &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_actionIndex = value;
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (_entryData->_action == MacroActionMacro::Action::RUN ||
|
||||
_entryData->_action == MacroActionMacro::Action::STOP) {
|
||||
_macros->HideSelectedMacro();
|
||||
} else {
|
||||
_macros->ShowAllMacros();
|
||||
}
|
||||
|
||||
const bool isModifyingActionState =
|
||||
_entryData->_action ==
|
||||
MacroActionMacro::Action::DISABLE_ACTION ||
|
||||
_entryData->_action ==
|
||||
MacroActionMacro::Action::ENABLE_ACTION ||
|
||||
_entryData->_action == MacroActionMacro::Action::TOGGLE_ACTION;
|
||||
_actionIndex->setVisible(isModifyingActionState);
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::MacroRemove(const QString &)
|
||||
{
|
||||
if (_entryData) {
|
||||
_entryData->_macro.UpdateRef();
|
||||
}
|
||||
}
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,32 +1,38 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-selection.hpp"
|
||||
#include "macro-segment-selection.hpp"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
|
||||
enum class PerformMacroAction {
|
||||
PAUSE,
|
||||
UNPAUSE,
|
||||
RESET_COUNTER,
|
||||
RUN,
|
||||
STOP,
|
||||
};
|
||||
namespace advss {
|
||||
|
||||
class MacroActionMacro : public MacroRefAction {
|
||||
public:
|
||||
MacroActionMacro(Macro *m) : MacroAction(m), MacroRefAction(m) {}
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionMacro>(m);
|
||||
}
|
||||
|
||||
PerformMacroAction _action = PerformMacroAction::PAUSE;
|
||||
enum class Action {
|
||||
PAUSE,
|
||||
UNPAUSE,
|
||||
RESET_COUNTER,
|
||||
RUN,
|
||||
STOP,
|
||||
DISABLE_ACTION,
|
||||
ENABLE_ACTION,
|
||||
TOGGLE_ACTION,
|
||||
};
|
||||
Action _action = Action::PAUSE;
|
||||
IntVariable _actionIndex = 1;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -51,17 +57,21 @@ public:
|
||||
|
||||
private slots:
|
||||
void MacroChanged(const QString &text);
|
||||
void MacroRemove(const QString &name);
|
||||
void ActionChanged(int value);
|
||||
void ActionIndexChanged(const IntVariable &value);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
MacroSelection *_macros;
|
||||
MacroSegmentSelection *_actionIndex;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionMacro> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
void SetWidgetVisibility();
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "macro-action-media.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionMedia::id = "media";
|
||||
|
||||
bool MacroActionMedia::_registered = MacroActionFactory::Register(
|
||||
@@ -21,7 +22,7 @@ const static std::map<MediaAction, std::string> actionTypes = {
|
||||
|
||||
bool MacroActionMedia::PerformAction()
|
||||
{
|
||||
auto source = obs_weak_source_get_source(_mediaSource);
|
||||
auto source = obs_weak_source_get_source(_mediaSource.GetSource());
|
||||
obs_media_state state = obs_source_media_get_state(source);
|
||||
switch (_action) {
|
||||
case MediaAction::PLAY:
|
||||
@@ -48,7 +49,7 @@ bool MacroActionMedia::PerformAction()
|
||||
obs_source_media_previous(source);
|
||||
break;
|
||||
case MediaAction::SEEK:
|
||||
obs_source_media_set_time(source, _seek.seconds * 1000);
|
||||
obs_source_media_set_time(source, _seek.Milliseconds());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -57,24 +58,22 @@ bool MacroActionMedia::PerformAction()
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionMedia::LogAction()
|
||||
void MacroActionMedia::LogAction() const
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO, "performed action \"%s\" for source \"%s\"",
|
||||
it->second.c_str(),
|
||||
GetWeakSourceName(_mediaSource).c_str());
|
||||
it->second.c_str(), _mediaSource.ToString(true).c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown media action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionMedia::Save(obs_data_t *obj)
|
||||
bool MacroActionMedia::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "mediaSource",
|
||||
GetWeakSourceName(_mediaSource).c_str());
|
||||
_mediaSource.Save(obj, "mediaSource");
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
_seek.Save(obj);
|
||||
return true;
|
||||
@@ -83,19 +82,15 @@ bool MacroActionMedia::Save(obs_data_t *obj)
|
||||
bool MacroActionMedia::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
const char *MediaSourceName = obs_data_get_string(obj, "mediaSource");
|
||||
_mediaSource = GetWeakSourceByName(MediaSourceName);
|
||||
_mediaSource.Load(obj, "mediaSource");
|
||||
_action = static_cast<MediaAction>(obs_data_get_int(obj, "action"));
|
||||
_seek.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionMedia::GetShortDesc()
|
||||
std::string MacroActionMedia::GetShortDesc() const
|
||||
{
|
||||
if (_mediaSource) {
|
||||
return GetWeakSourceName(_mediaSource);
|
||||
}
|
||||
return "";
|
||||
return _mediaSource.ToString();
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
@@ -108,30 +103,30 @@ static inline void populateActionSelection(QComboBox *list)
|
||||
MacroActionMediaEdit::MacroActionMediaEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionMedia> entryData)
|
||||
: QWidget(parent),
|
||||
_mediaSources(new QComboBox()),
|
||||
_sources(new SourceSelectionWidget(this, QStringList(), true)),
|
||||
_actions(new QComboBox()),
|
||||
_seek(new DurationSelection())
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
populateMediaSelection(_mediaSources);
|
||||
auto sources = GetMediaSourceNames();
|
||||
sources.sort();
|
||||
_sources->SetSourceNameList(sources);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_mediaSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_seek, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_seek, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SourceChanged(const SourceSelection &)), this,
|
||||
SLOT(SourceChanged(const SourceSelection &)));
|
||||
QWidget::connect(_seek, SIGNAL(DurationChanged(const Duration &)), this,
|
||||
SLOT(DurationChanged(const Duration &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{mediaSources}}", _mediaSources},
|
||||
{"{{mediaSources}}", _sources},
|
||||
{"{{actions}}", _actions},
|
||||
{"{{duration}}", _seek},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.media.entry"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.media.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -146,21 +141,20 @@ void MacroActionMediaEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_mediaSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_mediaSource).c_str());
|
||||
_sources->SetSource(_entryData->_mediaSource);
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_seek->SetDuration(_entryData->_seek);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionMediaEdit::SourceChanged(const QString &text)
|
||||
void MacroActionMediaEdit::SourceChanged(const SourceSelection &source)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_mediaSource = GetWeakSourceByQString(text);
|
||||
auto lock = LockContext();
|
||||
_entryData->_mediaSource = source;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -171,29 +165,19 @@ void MacroActionMediaEdit::ActionChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<MediaAction>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionMediaEdit::DurationChanged(double seconds)
|
||||
void MacroActionMediaEdit::DurationChanged(const Duration &dur)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_seek.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroActionMediaEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_seek.displayUnit = unit;
|
||||
auto lock = LockContext();
|
||||
_entryData->_seek = dur;
|
||||
}
|
||||
|
||||
void MacroActionMediaEdit::SetWidgetVisibility()
|
||||
@@ -204,3 +188,5 @@ void MacroActionMediaEdit::SetWidgetVisibility()
|
||||
_seek->setVisible(_entryData->_action == MediaAction::SEEK);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "source-selection.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
namespace advss {
|
||||
|
||||
enum class MediaAction {
|
||||
PLAY,
|
||||
PAUSE,
|
||||
@@ -19,17 +22,17 @@ class MacroActionMedia : public MacroAction {
|
||||
public:
|
||||
MacroActionMedia(Macro *m) : MacroAction(m) {}
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionMedia>(m);
|
||||
}
|
||||
|
||||
OBSWeakSource _mediaSource;
|
||||
SourceSelection _mediaSource;
|
||||
MediaAction _action = MediaAction::PLAY;
|
||||
Duration _seek;
|
||||
|
||||
@@ -55,15 +58,14 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void SourceChanged(const SourceSelection &);
|
||||
void ActionChanged(int value);
|
||||
void DurationChanged(double value);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
void DurationChanged(const Duration &value);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_mediaSources;
|
||||
SourceSelectionWidget *_sources;
|
||||
QComboBox *_actions;
|
||||
DurationSelection *_seek;
|
||||
std::shared_ptr<MacroActionMedia> _entryData;
|
||||
@@ -74,3 +76,5 @@ private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
318
src/macro-core/macro-action-osc.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#include "macro-action-osc.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <system_error>
|
||||
#include <QGroupBox>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionOSC::id = "osc";
|
||||
|
||||
bool MacroActionOSC::_registered = MacroActionFactory::Register(
|
||||
MacroActionOSC::id, {MacroActionOSC::Create, MacroActionOSCEdit::Create,
|
||||
"AdvSceneSwitcher.action.osc"});
|
||||
|
||||
MacroActionOSC::MacroActionOSC(Macro *m)
|
||||
: MacroAction(m),
|
||||
_ctx(asio::io_context()),
|
||||
_tcpSocket(asio::ip::tcp::socket(_ctx)),
|
||||
_udpSocket(asio::ip::udp::socket(_ctx))
|
||||
{
|
||||
}
|
||||
|
||||
void MacroActionOSC::SendOSCTCPMessage(const asio::mutable_buffer &buffer)
|
||||
{
|
||||
try {
|
||||
asio::write(_tcpSocket, asio::buffer(buffer));
|
||||
} catch (const std::exception &e) {
|
||||
blog(LOG_WARNING,
|
||||
"failed to send OSC message \"%s\" via TCP %s %d: %s",
|
||||
_message.ToString().c_str(), _ip.c_str(), _port.GetValue(),
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionOSC::SendOSCUDPMessage(const asio::mutable_buffer &buffer)
|
||||
{
|
||||
try {
|
||||
_udpSocket.send_to(buffer, _updEndpoint);
|
||||
} catch (const std::exception &e) {
|
||||
blog(LOG_WARNING,
|
||||
"failed to send OSC message \"%s\" via UDP %s %d: %s",
|
||||
_message.ToString().c_str(), _ip.c_str(), _port.GetValue(),
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionOSC::UDPReconnect()
|
||||
{
|
||||
asio::error_code ec;
|
||||
asio::ip::udp::resolver resolver(_ctx);
|
||||
auto endpoints = resolver.resolve(asio::ip::udp::v4(), _ip.c_str(),
|
||||
std::to_string(_port.GetValue()), ec);
|
||||
if (ec) {
|
||||
endpoints = resolver.resolve(asio::ip::udp::v6(), _ip.c_str(),
|
||||
std::to_string(_port.GetValue()),
|
||||
ec);
|
||||
}
|
||||
if (ec) {
|
||||
blog(LOG_WARNING, "failed to get IP for \"%s\": %s",
|
||||
_ip.c_str(), ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_updEndpoint = endpoints.begin()->endpoint();
|
||||
_udpSocket = asio::ip::udp::socket(_ctx);
|
||||
_udpSocket.open(endpoints.begin()->endpoint().protocol());
|
||||
} catch (const std::exception &e) {
|
||||
blog(LOG_WARNING, "failed to connect to UDP %s %d: %s",
|
||||
_ip.c_str(), _port.GetValue(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionOSC::TCPReconnect()
|
||||
{
|
||||
asio::error_code ec;
|
||||
asio::ip::tcp::resolver resolver(_ctx);
|
||||
auto endpoints = resolver.resolve(asio::ip::tcp::v4(), _ip.c_str(),
|
||||
std::to_string(_port.GetValue()), ec);
|
||||
if (ec) {
|
||||
endpoints = resolver.resolve(asio::ip::tcp::v6(), _ip.c_str(),
|
||||
std::to_string(_port.GetValue()),
|
||||
ec);
|
||||
}
|
||||
if (ec) {
|
||||
blog(LOG_WARNING, "failed to get IP for \"%s\": %s",
|
||||
_ip.c_str(), ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
_tcpSocket = asio::ip::tcp::socket(_ctx);
|
||||
_tcpSocket.connect(endpoints.begin()->endpoint());
|
||||
} catch (const std::exception &e) {
|
||||
blog(LOG_WARNING, "failed to connect to TCP %s %d: %s",
|
||||
_ip.c_str(), _port.GetValue(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionOSC::CheckReconnect()
|
||||
{
|
||||
if (_protocol == Protocol::TCP &&
|
||||
(_reconnect || !_tcpSocket.is_open())) {
|
||||
TCPReconnect();
|
||||
}
|
||||
|
||||
if (_protocol == Protocol::UDP &&
|
||||
(_reconnect || !_udpSocket.is_open())) {
|
||||
UDPReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionOSC::PerformAction()
|
||||
{
|
||||
auto buffer = _message.GetBuffer();
|
||||
if (!buffer.has_value()) {
|
||||
blog(LOG_WARNING, "failed to create or fill OSC buffer!");
|
||||
return true;
|
||||
}
|
||||
|
||||
CheckReconnect();
|
||||
|
||||
if (_protocol == Protocol::TCP &&
|
||||
(_reconnect || !_tcpSocket.is_open())) {
|
||||
TCPReconnect();
|
||||
}
|
||||
|
||||
if (_protocol == Protocol::UDP &&
|
||||
(_reconnect || !_udpSocket.is_open())) {
|
||||
UDPReconnect();
|
||||
}
|
||||
|
||||
auto rawMessage = asio::buffer(*buffer);
|
||||
|
||||
switch (_protocol) {
|
||||
case MacroActionOSC::Protocol::TCP:
|
||||
SendOSCTCPMessage(rawMessage);
|
||||
break;
|
||||
case MacroActionOSC::Protocol::UDP:
|
||||
SendOSCUDPMessage(rawMessage);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionOSC::LogAction() const
|
||||
{
|
||||
vblog(LOG_INFO, "sending OSC message '%s' to %s %s %d",
|
||||
_message.ToString().c_str(),
|
||||
_protocol == Protocol::UDP ? "UDP" : "TCP", _ip.c_str(),
|
||||
_port.GetValue());
|
||||
}
|
||||
|
||||
bool MacroActionOSC::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "protocol", static_cast<int>(_protocol));
|
||||
_ip.Save(obj, "ip");
|
||||
_port.Save(obj, "port");
|
||||
_message.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionOSC::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_protocol = static_cast<Protocol>(obs_data_get_int(obj, "protocol"));
|
||||
_ip.Load(obj, "ip");
|
||||
_port.Load(obj, "port");
|
||||
_message.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionOSC::SetProtocol(Protocol p)
|
||||
{
|
||||
_protocol = p;
|
||||
_reconnect = true;
|
||||
}
|
||||
|
||||
void MacroActionOSC::SetIP(const std::string &ip)
|
||||
{
|
||||
_ip = ip;
|
||||
_reconnect = true;
|
||||
}
|
||||
|
||||
void MacroActionOSC::SetPortNr(IntVariable port)
|
||||
{
|
||||
_port = port;
|
||||
_reconnect = true;
|
||||
}
|
||||
|
||||
static void populateProtocolSelection(QComboBox *list)
|
||||
{
|
||||
list->addItem("TCP");
|
||||
list->addItem("UDP");
|
||||
}
|
||||
|
||||
MacroActionOSCEdit::MacroActionOSCEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionOSC> entryData)
|
||||
: QWidget(parent),
|
||||
_protocol(new QComboBox(this)),
|
||||
_ip(new VariableLineEdit(this)),
|
||||
_port(new VariableSpinBox(this)),
|
||||
_message(new OSCMessageEdit(this))
|
||||
{
|
||||
populateProtocolSelection(_protocol);
|
||||
_port->setMaximum(65535);
|
||||
|
||||
auto networkGroup =
|
||||
new QGroupBox(obs_module_text("AdvSceneSwitcher.osc.network"));
|
||||
auto networkLayout = new QGridLayout;
|
||||
int row = 0;
|
||||
networkLayout->addWidget(
|
||||
new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.osc.network.protocol")),
|
||||
row, 0);
|
||||
networkLayout->addWidget(_protocol, row, 1);
|
||||
++row;
|
||||
networkLayout->addWidget(
|
||||
new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.osc.network.address")),
|
||||
row, 0);
|
||||
networkLayout->addWidget(_ip, row, 1);
|
||||
++row;
|
||||
networkLayout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.osc.network.port")),
|
||||
row, 0);
|
||||
networkLayout->addWidget(_port, row, 1);
|
||||
networkGroup->setLayout(networkLayout);
|
||||
|
||||
auto messageGroup =
|
||||
new QGroupBox(obs_module_text("AdvSceneSwitcher.osc.message"));
|
||||
auto messageLayout = new QHBoxLayout();
|
||||
messageLayout->addWidget(_message);
|
||||
messageGroup->setLayout(messageLayout);
|
||||
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(networkGroup);
|
||||
mainLayout->addWidget(messageGroup);
|
||||
setLayout(mainLayout);
|
||||
|
||||
QWidget::connect(_ip, SIGNAL(editingFinished()), this,
|
||||
SLOT(IpChanged()));
|
||||
QWidget::connect(_protocol, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ProtocolChanged(int)));
|
||||
QWidget::connect(
|
||||
_port,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(PortChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(_message, SIGNAL(MessageChanged(const OSCMessage &)),
|
||||
this, SLOT(MessageChanged(const OSCMessage &)));
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionOSCEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_protocol->setCurrentIndex(static_cast<int>(_entryData->GetProtocol()));
|
||||
_ip->setText(_entryData->GetIP());
|
||||
_port->SetValue(_entryData->GetPortNr());
|
||||
_message->SetMessage(_entryData->_message);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionOSCEdit::IpChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->SetIP(_ip->text().toStdString());
|
||||
}
|
||||
|
||||
void MacroActionOSCEdit::ProtocolChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->SetProtocol(static_cast<MacroActionOSC::Protocol>(value));
|
||||
}
|
||||
|
||||
void MacroActionOSCEdit::PortChanged(const NumberVariable<int> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->SetPortNr(value);
|
||||
}
|
||||
|
||||
void MacroActionOSCEdit::MessageChanged(const OSCMessage &m)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_message = m;
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
94
src/macro-core/macro-action-osc.hpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "osc-helpers.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <asio.hpp>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroActionOSC : public MacroAction {
|
||||
public:
|
||||
MacroActionOSC(Macro *m);
|
||||
bool PerformAction();
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionOSC>(m);
|
||||
}
|
||||
|
||||
enum class Protocol {
|
||||
TCP,
|
||||
UDP,
|
||||
};
|
||||
|
||||
void SetProtocol(Protocol);
|
||||
Protocol GetProtocol() const { return _protocol; }
|
||||
void SetIP(const std::string &);
|
||||
StringVariable GetIP() const { return _ip; }
|
||||
void SetPortNr(IntVariable);
|
||||
IntVariable GetPortNr() { return _port; }
|
||||
|
||||
OSCMessage _message;
|
||||
|
||||
private:
|
||||
void SendOSCTCPMessage(const asio::mutable_buffer &);
|
||||
void SendOSCUDPMessage(const asio::mutable_buffer &);
|
||||
|
||||
void CheckReconnect();
|
||||
void TCPReconnect();
|
||||
void UDPReconnect();
|
||||
|
||||
Protocol _protocol = Protocol::UDP;
|
||||
StringVariable _ip = "localhost";
|
||||
IntVariable _port = 12345;
|
||||
bool _reconnect = true;
|
||||
|
||||
asio::io_context _ctx;
|
||||
asio::ip::tcp::socket _tcpSocket;
|
||||
asio::ip::udp::socket _udpSocket;
|
||||
asio::ip::udp::endpoint _updEndpoint;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionOSCEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionOSCEdit(QWidget *parent,
|
||||
std::shared_ptr<MacroActionOSC> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionOSCEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionOSC>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void IpChanged();
|
||||
void MessageChanged(const OSCMessage &);
|
||||
void ProtocolChanged(int);
|
||||
void PortChanged(const NumberVariable<int> &value);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<MacroActionOSC> _entryData;
|
||||
|
||||
private:
|
||||
QComboBox *_protocol;
|
||||
VariableLineEdit *_ip;
|
||||
VariableSpinBox *_port;
|
||||
OSCMessageEdit *_message;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||