mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 12:45:28 -05:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c3c953c6b | ||
|
|
ae74f68db7 | ||
|
|
213f1bba36 | ||
|
|
5a2cb0bd68 | ||
|
|
23b461828b | ||
|
|
9944a1b03b | ||
|
|
eaad4d1bbd |
@@ -298,6 +298,13 @@ setup_obs_lib_dependency(${PROJECT_NAME})
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core Qt6::Widgets)
|
||||
target_link_libraries(${LIB_NAME} PRIVATE Qt6::Core Qt6::Widgets)
|
||||
|
||||
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported Qt
|
||||
# version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
|
||||
if(Qt6_VERSION VERSION_GREATER "6.0.0")
|
||||
target_compile_definitions(${LIB_NAME} PRIVATE QT_NO_DEPRECATED_WARNINGS)
|
||||
endif()
|
||||
|
||||
target_compile_options(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE
|
||||
|
||||
@@ -286,6 +286,12 @@ function(setup_advss_plugin target)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
|
||||
target_link_libraries(${target} PRIVATE Qt6::Core Qt6::Widgets)
|
||||
|
||||
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported
|
||||
# Qt version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
|
||||
if(Qt6_VERSION VERSION_GREATER "6.7.0")
|
||||
target_compile_definitions(${target} PRIVATE QT_NO_DEPRECATED_WARNINGS)
|
||||
endif()
|
||||
|
||||
set_target_properties(
|
||||
${target}
|
||||
PROPERTIES AUTOMOC ON
|
||||
|
||||
@@ -1113,6 +1113,8 @@ AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt
|
||||
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
|
||||
AdvSceneSwitcher.action.variable.entry.randomNumber="Generate random number in range from{{randomNumberStart}}to{{randomNumberEnd}}"
|
||||
AdvSceneSwitcher.action.projector="Projector"
|
||||
AdvSceneSwitcher.action.projector.action.open="Open"
|
||||
AdvSceneSwitcher.action.projector.action.close="Close"
|
||||
AdvSceneSwitcher.action.projector.type.source="Source"
|
||||
AdvSceneSwitcher.action.projector.type.scene="Scene"
|
||||
AdvSceneSwitcher.action.projector.type.preview="Preview"
|
||||
@@ -1121,8 +1123,9 @@ 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.projector.entry.close="{{actions}}projector with name{{projectorWindowName}}{{regex}}"
|
||||
AdvSceneSwitcher.action.projector.entry.open.windowed="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}"
|
||||
AdvSceneSwitcher.action.projector.entry.open.fullscreen="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}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}}"
|
||||
|
||||
@@ -718,8 +718,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivue"
|
||||
AdvSceneSwitcher.action.projector.display="Affichage"
|
||||
AdvSceneSwitcher.action.projector.windowed="Fenêtré"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="Plein écran"
|
||||
AdvSceneSwitcher.action.projector.entry="Ouvrir le projecteur{{windowTypes}}de{{types}}{{scenes}}{{sources}}"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="sur{{monitors}}"
|
||||
AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="Envoyer un message à{{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="Définir la sélection de messages MIDI sur les messages entrants de{{listenDevices}}:{{listenButton}}"
|
||||
|
||||
@@ -1068,8 +1068,6 @@ AdvSceneSwitcher.action.projector.type.multiview="マルチビュー"
|
||||
AdvSceneSwitcher.action.projector.display="ディスプレイ"
|
||||
AdvSceneSwitcher.action.projector.windowed="ウィンドウ"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="フルスクリーン"
|
||||
AdvSceneSwitcher.action.projector.entry="{{types}}{{scenes}}{{sources}}の{{windowTypes}}プロジェクターを開きます"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="{{monitors}}上で"
|
||||
; AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="メッセージを{{device}}に送信します:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="MIDI メッセージの選択を {{listenDevices}} で受信するメッセージに設定します:{{listenButton}}"
|
||||
|
||||
@@ -1049,8 +1049,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivisão"
|
||||
AdvSceneSwitcher.action.projector.display="Exibição"
|
||||
AdvSceneSwitcher.action.projector.windowed="Janela"
|
||||
AdvSceneSwitcher.action.projector.fullscreen="Tela cheia"
|
||||
AdvSceneSwitcher.action.projector.entry="Abrir{{windowTypes}}projetor de{{types}}{{scenes}}{{sources}}"
|
||||
AdvSceneSwitcher.action.projector.entry.monitor="em{{monitors}}"
|
||||
AdvSceneSwitcher.action.midi="MIDI"
|
||||
AdvSceneSwitcher.action.midi.entry="Enviar mensagem para{{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="Definir seleção de mensagem MIDI para mensagens recebidas em{{listenDevices}}:{{listenButton}}"
|
||||
|
||||
@@ -644,8 +644,6 @@ 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}}"
|
||||
|
||||
@@ -307,7 +307,7 @@ void SwitcherData::Thread()
|
||||
}
|
||||
}
|
||||
|
||||
ResetForNextInterval();
|
||||
RunIntervalResetSteps();
|
||||
|
||||
if (match) {
|
||||
if (macroMatch) {
|
||||
@@ -357,14 +357,6 @@ void SwitcherData::SetPreconditions()
|
||||
InvalidateMacroTempVarValues();
|
||||
}
|
||||
|
||||
void SwitcherData::ResetForNextInterval()
|
||||
{
|
||||
// Plugin reset functions
|
||||
for (const auto &func : resetIntervalSteps) {
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger,
|
||||
bool &setPrevSceneAfterLinger,
|
||||
@@ -445,7 +437,7 @@ void AutoStartActionQueues();
|
||||
void SwitcherData::Start()
|
||||
{
|
||||
if (!(th && th->isRunning())) {
|
||||
ResetForNextInterval();
|
||||
RunIntervalResetSteps();
|
||||
ResetMacros();
|
||||
AutoStartActionQueues();
|
||||
|
||||
@@ -453,10 +445,7 @@ void SwitcherData::Start()
|
||||
th = new SwitcherThread();
|
||||
th->start((QThread::Priority)threadPriority);
|
||||
|
||||
// Will be overwritten quickly but might be useful
|
||||
writeToStatusFile("Advanced Scene Switcher running");
|
||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
|
||||
nullptr);
|
||||
RunStartSteps();
|
||||
}
|
||||
|
||||
if (showSystemTrayNotifications) {
|
||||
@@ -483,11 +472,7 @@ void SwitcherData::Stop()
|
||||
th->wait();
|
||||
delete th;
|
||||
th = nullptr;
|
||||
writeToStatusFile("Advanced Scene Switcher stopped");
|
||||
if (!obsIsShuttingDown) {
|
||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
|
||||
nullptr);
|
||||
}
|
||||
RunStopSteps();
|
||||
}
|
||||
|
||||
if (showSystemTrayNotifications) {
|
||||
|
||||
@@ -21,6 +21,29 @@ bool FileSwitch::pause = false;
|
||||
static QObject *addPulse = nullptr;
|
||||
static std::hash<std::string> strHash;
|
||||
|
||||
static void writeToStatusFile(const QString &msg)
|
||||
{
|
||||
if (!GetSwitcher() || !GetSwitcher()->fileIO.writeEnabled ||
|
||||
GetSwitcher()->fileIO.writePath.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(QString::fromStdString(GetSwitcher()->fileIO.writePath));
|
||||
if (file.open(QIODevice::ReadWrite)) {
|
||||
QTextStream stream(&file);
|
||||
stream << msg << Qt::endl;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
static bool _ = []() {
|
||||
AddStartStep(
|
||||
[]() { writeToStatusFile("Advanced Scene Switcher running"); });
|
||||
AddStopStep(
|
||||
[]() { writeToStatusFile("Advanced Scene Switcher stopped"); });
|
||||
return true;
|
||||
}();
|
||||
|
||||
void AdvSceneSwitcher::on_browseButton_clicked()
|
||||
{
|
||||
QString path = QFileDialog::getOpenFileName(
|
||||
@@ -113,20 +136,6 @@ void SwitcherData::writeSceneInfoToFile()
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::writeToStatusFile(const QString &msg)
|
||||
{
|
||||
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(QString::fromStdString(fileIO.writePath));
|
||||
if (file.open(QIODevice::ReadWrite)) {
|
||||
QTextStream stream(&file);
|
||||
stream << msg << Qt::endl;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool SwitcherData::checkSwitchInfoFromFile(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
|
||||
@@ -67,9 +67,7 @@ void SetMacroSwitchedScene(bool value)
|
||||
{
|
||||
static bool setupDone = false;
|
||||
if (!setupDone) {
|
||||
// Will always be called with switcher lock already held
|
||||
AddIntervalResetStep([]() { macroSceneSwitched = false; },
|
||||
false);
|
||||
AddIntervalResetStep([]() { macroSceneSwitched = false; });
|
||||
setupDone = true;
|
||||
}
|
||||
macroSceneSwitched = value;
|
||||
|
||||
@@ -108,6 +108,24 @@ static const std::string setTempVarValueDeclString =
|
||||
tempVarIdParam.data() + ", in string " + valueParam.data() +
|
||||
", in int " + GetInstanceIdParamName().data() + ")";
|
||||
|
||||
/* Plugin status */
|
||||
|
||||
static constexpr std::string_view stopSignalName = "advss_plugin_stopped";
|
||||
static constexpr std::string_view startSignalName = "advss_plugin_started";
|
||||
static constexpr std::string_view getRunningStatusFuncName =
|
||||
"advss_plugin_running";
|
||||
static constexpr std::string_view resetIntervalSignalName =
|
||||
"advss_interval_reset";
|
||||
|
||||
static const std::string stopSignalDeclString =
|
||||
std::string("void ") + stopSignalName.data() + "()";
|
||||
static const std::string startSignalDeclString =
|
||||
std::string("void ") + startSignalName.data() + "()";
|
||||
static const std::string resetIntervalDeclString =
|
||||
std::string("void ") + resetIntervalSignalName.data() + "()";
|
||||
static const std::string getRunningStatusDeclString =
|
||||
std::string("bool ") + getRunningStatusFuncName.data() + "()";
|
||||
|
||||
static bool setup();
|
||||
static bool setupDone = setup();
|
||||
|
||||
@@ -134,6 +152,25 @@ static bool setup()
|
||||
&ScriptHandler::DeregisterAllTempVars, nullptr);
|
||||
proc_handler_add(ph, setTempVarValueDeclString.c_str(),
|
||||
&ScriptHandler::SetTempVarValue, nullptr);
|
||||
proc_handler_add(ph, getRunningStatusDeclString.c_str(),
|
||||
&ScriptHandler::GetRunningStatus, nullptr);
|
||||
|
||||
auto sh = obs_get_signal_handler();
|
||||
signal_handler_add(sh, stopSignalDeclString.c_str());
|
||||
signal_handler_add(sh, startSignalDeclString.c_str());
|
||||
signal_handler_add(sh, resetIntervalDeclString.c_str());
|
||||
|
||||
static constexpr auto triggerSignal = [](const std::string_view &name) {
|
||||
auto sh = obs_get_signal_handler();
|
||||
struct calldata data;
|
||||
calldata_init(&data);
|
||||
signal_handler_signal(sh, name.data(), &data);
|
||||
calldata_free(&data);
|
||||
};
|
||||
|
||||
AddIntervalResetStep([]() { triggerSignal(resetIntervalSignalName); });
|
||||
AddStartStep([]() { triggerSignal(startSignalName); });
|
||||
AddStopStep([]() { triggerSignal(stopSignalName); });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -476,6 +513,11 @@ void ScriptHandler::SetVariableValue(void *, calldata_t *data)
|
||||
RETURN_SUCCESS();
|
||||
}
|
||||
|
||||
void ScriptHandler::GetRunningStatus(void *ctx, calldata_t *data)
|
||||
{
|
||||
calldata_set_bool(data, "is_running", PluginIsRunning());
|
||||
}
|
||||
|
||||
void ScriptHandler::RegisterTempVar(void *, calldata_t *data)
|
||||
{
|
||||
const char *variableId;
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
static void DeregisterAllTempVars(void *ctx, calldata_t *data);
|
||||
static void SetTempVarValue(void *ctx, calldata_t *data);
|
||||
static void SetVariableValue(void *ctx, calldata_t *data);
|
||||
static void GetRunningStatus(void *ctx, calldata_t *data);
|
||||
static bool ActionIdIsValid(const std::string &id);
|
||||
static bool ConditionIdIsValid(const std::string &id);
|
||||
|
||||
|
||||
@@ -168,17 +168,6 @@ void SwitcherData::SaveVersion(obs_data_t *obj,
|
||||
obs_data_set_string(obj, "version", currentVersion.c_str());
|
||||
}
|
||||
|
||||
void SwitcherData::AddIntervalResetStep(std::function<void()> function,
|
||||
bool tryLock)
|
||||
{
|
||||
if (!tryLock) {
|
||||
resetIntervalSteps.emplace_back(function);
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
resetIntervalSteps.emplace_back(function);
|
||||
}
|
||||
|
||||
void SwitcherData::RunPostLoadSteps()
|
||||
{
|
||||
for (const auto &func : postLoadSteps) {
|
||||
|
||||
@@ -57,11 +57,9 @@ public:
|
||||
bool AnySceneTransitionStarted();
|
||||
|
||||
void SetPreconditions();
|
||||
void ResetForNextInterval();
|
||||
void AddSaveStep(std::function<void(obs_data_t *)>);
|
||||
void AddLoadStep(std::function<void(obs_data_t *)>);
|
||||
void AddPostLoadStep(std::function<void()>);
|
||||
void AddIntervalResetStep(std::function<void()>, bool lock = true);
|
||||
void RunPostLoadSteps();
|
||||
bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
int &linger, bool &setPreviousSceneAsMatch,
|
||||
@@ -100,7 +98,6 @@ public:
|
||||
std::vector<std::function<void(obs_data_t *)>> saveSteps;
|
||||
std::vector<std::function<void(obs_data_t *)>> loadSteps;
|
||||
std::vector<std::function<void()>> postLoadSteps;
|
||||
std::vector<std::function<void()>> resetIntervalSteps;
|
||||
|
||||
bool firstBoot = true;
|
||||
bool transitionActive = false;
|
||||
@@ -233,7 +230,6 @@ public:
|
||||
bool checkPause();
|
||||
void checkDefaultSceneTransitions();
|
||||
void writeSceneInfoToFile();
|
||||
void writeToStatusFile(const QString &msg);
|
||||
void checkSwitchCooldown(bool &match);
|
||||
|
||||
std::deque<WindowSwitch> windowSwitches;
|
||||
|
||||
@@ -21,6 +21,24 @@ static std::vector<std::function<void()>> &getPluginCleanupSteps()
|
||||
return steps;
|
||||
}
|
||||
|
||||
static std::vector<std::function<void()>> &getResetIntervalSteps()
|
||||
{
|
||||
static std::vector<std::function<void()>> steps;
|
||||
return steps;
|
||||
}
|
||||
|
||||
static std::vector<std::function<void()>> &getStartSteps()
|
||||
{
|
||||
static std::vector<std::function<void()>> steps;
|
||||
return steps;
|
||||
}
|
||||
|
||||
static std::vector<std::function<void()>> &getStopSteps()
|
||||
{
|
||||
static std::vector<std::function<void()>> steps;
|
||||
return steps;
|
||||
}
|
||||
|
||||
static std::mutex mutex;
|
||||
|
||||
void SavePluginSettings(obs_data_t *obj)
|
||||
@@ -48,9 +66,10 @@ void AddPostLoadStep(std::function<void()> step)
|
||||
GetSwitcher()->AddPostLoadStep(step);
|
||||
}
|
||||
|
||||
void AddIntervalResetStep(std::function<void()> step, bool lock)
|
||||
void AddIntervalResetStep(std::function<void()> step)
|
||||
{
|
||||
GetSwitcher()->AddIntervalResetStep(step, lock);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
getResetIntervalSteps().emplace_back(step);
|
||||
}
|
||||
|
||||
void RunPostLoadSteps()
|
||||
@@ -100,6 +119,42 @@ void RunPluginCleanupSteps()
|
||||
}
|
||||
}
|
||||
|
||||
void RunIntervalResetSteps()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (const auto &step : getResetIntervalSteps()) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
void AddStartStep(std::function<void()> step)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
getStartSteps().emplace_back(step);
|
||||
}
|
||||
|
||||
void AddStopStep(std::function<void()> step)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
getStopSteps().emplace_back(step);
|
||||
}
|
||||
|
||||
void RunStartSteps()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (const auto &step : getStartSteps()) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
void RunStopSteps()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (const auto &step : getStopSteps()) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
void StopPlugin()
|
||||
{
|
||||
GetSwitcher()->Stop();
|
||||
|
||||
@@ -11,7 +11,7 @@ EXPORT void LoadPluginSettings(obs_data_t *);
|
||||
EXPORT void AddSaveStep(std::function<void(obs_data_t *)>);
|
||||
EXPORT void AddLoadStep(std::function<void(obs_data_t *)>);
|
||||
EXPORT void AddPostLoadStep(std::function<void()>);
|
||||
EXPORT void AddIntervalResetStep(std::function<void()>, bool lock = true);
|
||||
EXPORT void AddIntervalResetStep(std::function<void()>);
|
||||
EXPORT void RunPostLoadSteps();
|
||||
|
||||
EXPORT void AddPluginInitStep(std::function<void()>);
|
||||
@@ -25,6 +25,11 @@ EXPORT void StopPlugin();
|
||||
EXPORT void StartPlugin();
|
||||
EXPORT bool PluginIsRunning();
|
||||
EXPORT int GetIntervalValue();
|
||||
void AddStartStep(std::function<void()>);
|
||||
void AddStopStep(std::function<void()>);
|
||||
void RunStartSteps();
|
||||
void RunStopSteps();
|
||||
void RunIntervalResetSteps();
|
||||
|
||||
enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 };
|
||||
EXPORT void SetPluginNoMatchBehavior(NoMatchBehavior);
|
||||
|
||||
@@ -23,6 +23,14 @@ static bool setupDone = setup();
|
||||
bool setup()
|
||||
{
|
||||
AddPluginPostLoadStep(registerWebsocketVendor);
|
||||
AddStartStep([]() {
|
||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
|
||||
nullptr);
|
||||
});
|
||||
AddStopStep([]() {
|
||||
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
|
||||
nullptr);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -109,6 +117,9 @@ void RegisterWebsocketRequest(
|
||||
|
||||
void SendWebsocketVendorEvent(const std::string &eventName, obs_data_t *data)
|
||||
{
|
||||
if (OBSIsShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
obs_websocket_vendor_emit_event(vendor, eventName.c_str(), data);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "source-helpers.hpp"
|
||||
|
||||
#include <obs-frontend-api.h>
|
||||
#include <QApplication>
|
||||
#include <QWindow>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -28,8 +30,34 @@ const static std::map<MacroActionProjector::Type, std::string> selectionTypes =
|
||||
"AdvSceneSwitcher.action.projector.type.multiview"},
|
||||
};
|
||||
|
||||
static void closeOBSProjectorWindows(const std::string expectedWindowTitle,
|
||||
const RegexConfig ®ex)
|
||||
{
|
||||
for (QWindow *widget : QApplication::allWindows()) {
|
||||
if (!widget->property("isOBSProjectorWindow").toBool()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const windowTitle = widget->title().toStdString();
|
||||
if (!regex.Enabled() && expectedWindowTitle != windowTitle) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!regex.Matches(windowTitle, expectedWindowTitle)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
widget->close();
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionProjector::PerformAction()
|
||||
{
|
||||
if (_action == Action::CLOSE) {
|
||||
closeOBSProjectorWindows(_projectorWindowName, _regex);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string name = "";
|
||||
const char *type = "";
|
||||
|
||||
@@ -78,10 +106,16 @@ bool MacroActionProjector::PerformAction()
|
||||
|
||||
void MacroActionProjector::LogAction() const
|
||||
{
|
||||
if (_action == Action::CLOSE) {
|
||||
ablog(LOG_INFO, "closing projector window \"%s\"",
|
||||
_projectorWindowName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = selectionTypes.find(_type);
|
||||
if (it != selectionTypes.end()) {
|
||||
ablog(LOG_INFO,
|
||||
"performed projector action \"%s\" with"
|
||||
"open projector \"%s\" with"
|
||||
"source \"%s\","
|
||||
"scene \"%s\","
|
||||
"monitor %d",
|
||||
@@ -96,24 +130,30 @@ void MacroActionProjector::LogAction() const
|
||||
bool MacroActionProjector::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
obs_data_set_int(obj, "monitor", _monitor);
|
||||
obs_data_set_string(obj, "monitorName", _monitorName.c_str());
|
||||
obs_data_set_bool(obj, "fullscreen", _fullscreen);
|
||||
_scene.Save(obj);
|
||||
_source.Save(obj);
|
||||
_projectorWindowName.Save(obj, "projectorWindowName");
|
||||
_regex.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionProjector::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
||||
_monitor = obs_data_get_int(obj, "monitor");
|
||||
_monitorName = obs_data_get_string(obj, "monitorName");
|
||||
_fullscreen = obs_data_get_bool(obj, "fullscreen");
|
||||
_scene.Load(obj);
|
||||
_source.Load(obj);
|
||||
_projectorWindowName.Load(obj, "projectorWindowName");
|
||||
_regex.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -131,6 +171,7 @@ void MacroActionProjector::ResolveVariablesToFixedValues()
|
||||
{
|
||||
_source.ResolveVariables();
|
||||
_scene.ResolveVariables();
|
||||
_projectorWindowName.ResolveVariables();
|
||||
}
|
||||
|
||||
void MacroActionProjector::SetMonitor(int idx)
|
||||
@@ -166,14 +207,22 @@ bool MacroActionProjector::MonitorSetupChanged() const
|
||||
QString::fromStdString(_monitorName);
|
||||
}
|
||||
|
||||
static inline void populateSelectionTypes(QComboBox *list)
|
||||
static void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : selectionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.projector.action.open"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.projector.action.close"));
|
||||
}
|
||||
|
||||
static void populateSelectionTypes(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : selectionTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateWindowTypes(QComboBox *list)
|
||||
static void populateWindowTypes(QComboBox *list)
|
||||
{
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.action.projector.windowed"));
|
||||
@@ -184,14 +233,18 @@ static inline void populateWindowTypes(QComboBox *list)
|
||||
MacroActionProjectorEdit::MacroActionProjectorEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionProjector> entryData)
|
||||
: QWidget(parent),
|
||||
_windowTypes(new QComboBox()),
|
||||
_actions(new QComboBox()),
|
||||
_types(new QComboBox()),
|
||||
_windowTypes(new QComboBox()),
|
||||
_scenes(new SceneSelectionWidget(window(), true, false, true, true,
|
||||
true)),
|
||||
_sources(new SourceSelectionWidget(window(), QStringList(), true)),
|
||||
_monitorSelection(new QHBoxLayout()),
|
||||
_monitors(new QComboBox())
|
||||
_monitors(new QComboBox()),
|
||||
_projectorWindowName(new VariableLineEdit(this)),
|
||||
_regex(new RegexConfigWidget(this)),
|
||||
_layout(new QHBoxLayout(this))
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
populateWindowTypes(_windowTypes);
|
||||
populateSelectionTypes(_types);
|
||||
auto sources = GetSourceNames();
|
||||
@@ -201,6 +254,8 @@ MacroActionProjectorEdit::MacroActionProjectorEdit(
|
||||
_monitors->setPlaceholderText(
|
||||
obs_module_text("AdvSceneSwitcher.selectDisplay"));
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_windowTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(WindowTypeChanged(int)));
|
||||
QWidget::connect(_types, SIGNAL(currentIndexChanged(int)), this,
|
||||
@@ -212,24 +267,15 @@ MacroActionProjectorEdit::MacroActionProjectorEdit(
|
||||
SLOT(SourceChanged(const SourceSelection &)));
|
||||
QWidget::connect(_monitors, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(MonitorChanged(int)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{windowTypes}}", _windowTypes}, {"{{types}}", _types},
|
||||
{"{{scenes}}", _scenes}, {"{{sources}}", _sources},
|
||||
{"{{monitors}}", _monitors},
|
||||
};
|
||||
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.projector.entry.monitor"),
|
||||
_monitorSelection, widgetPlaceholders);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.projector.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
mainLayout->insertLayout(mainLayout->count() - 1, _monitorSelection);
|
||||
setLayout(mainLayout);
|
||||
QWidget::connect(_projectorWindowName, SIGNAL(editingFinished()), this,
|
||||
SLOT(ProjectorWindowNameChanged()));
|
||||
QWidget::connect(_regex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(RegexChanged(const RegexConfig &)));
|
||||
|
||||
_entryData = entryData;
|
||||
SetWidgetLayout();
|
||||
setLayout(_layout);
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
@@ -239,11 +285,15 @@ void MacroActionProjectorEdit::UpdateEntryData()
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_windowTypes->setCurrentIndex(_entryData->_fullscreen ? 1 : 0);
|
||||
_types->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_sources->SetSource(_entryData->_source);
|
||||
_monitors->setCurrentIndex(_entryData->GetMonitor());
|
||||
_projectorWindowName->setText(_entryData->_projectorWindowName);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -265,12 +315,26 @@ void MacroActionProjectorEdit::MonitorChanged(int value)
|
||||
_entryData->SetMonitor(value);
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::ProjectorWindowNameChanged()
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_projectorWindowName =
|
||||
_projectorWindowName->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::RegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_regex = regex;
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::WindowTypeChanged(int)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_fullscreen =
|
||||
_windowTypes->currentText() ==
|
||||
obs_module_text("AdvSceneSwitcher.action.projector.fullscreen");
|
||||
SetWidgetLayout();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -281,17 +345,69 @@ void MacroActionProjectorEdit::TypeChanged(int value)
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::ActionChanged(int idx)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_action = static_cast<MacroActionProjector::Action>(idx);
|
||||
SetWidgetLayout();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::SetWidgetLayout()
|
||||
{
|
||||
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
{"{{windowTypes}}", _windowTypes},
|
||||
{"{{types}}", _types},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{sources}}", _sources},
|
||||
{"{{monitors}}", _monitors},
|
||||
{"{{projectorWindowName}}", _projectorWindowName},
|
||||
{"{{regex}}", _regex},
|
||||
};
|
||||
|
||||
for (const auto &[_, widget] : widgetPlaceholders) {
|
||||
_layout->removeWidget(widget);
|
||||
}
|
||||
ClearLayout(_layout);
|
||||
|
||||
const char *layoutText;
|
||||
if (_entryData->_action == MacroActionProjector::Action::CLOSE) {
|
||||
layoutText = "AdvSceneSwitcher.action.projector.entry.close";
|
||||
} else if (_entryData->_fullscreen) {
|
||||
layoutText =
|
||||
"AdvSceneSwitcher.action.projector.entry.open.fullscreen";
|
||||
} else {
|
||||
layoutText =
|
||||
"AdvSceneSwitcher.action.projector.entry.open.windowed";
|
||||
}
|
||||
|
||||
PlaceWidgets(obs_module_text(layoutText), _layout, widgetPlaceholders);
|
||||
}
|
||||
|
||||
void MacroActionProjectorEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_scenes->setVisible(_entryData->_type ==
|
||||
MacroActionProjector::Type::SCENE);
|
||||
_sources->setVisible(_entryData->_type ==
|
||||
MacroActionProjector::Type::SOURCE);
|
||||
SetLayoutVisible(_monitorSelection, _entryData->_fullscreen);
|
||||
_projectorWindowName->setVisible(_entryData->_action ==
|
||||
MacroActionProjector::Action::CLOSE);
|
||||
_regex->setVisible(_entryData->_action ==
|
||||
MacroActionProjector::Action::CLOSE);
|
||||
_types->setVisible(_entryData->_action ==
|
||||
MacroActionProjector::Action::OPEN);
|
||||
_windowTypes->setVisible(_entryData->_action ==
|
||||
MacroActionProjector::Action::OPEN);
|
||||
_scenes->setVisible(
|
||||
_entryData->_action == MacroActionProjector::Action::OPEN &&
|
||||
_entryData->_type == MacroActionProjector::Type::SCENE);
|
||||
_sources->setVisible(
|
||||
_entryData->_action == MacroActionProjector::Action::OPEN &&
|
||||
_entryData->_type == MacroActionProjector::Type::SOURCE);
|
||||
_monitors->setVisible(_entryData->_action ==
|
||||
MacroActionProjector::Action::OPEN &&
|
||||
_entryData->_fullscreen);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "regex-config.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "source-selection.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -19,6 +21,11 @@ public:
|
||||
void SetMonitor(int);
|
||||
int GetMonitor() const;
|
||||
|
||||
enum class Action {
|
||||
OPEN,
|
||||
CLOSE,
|
||||
};
|
||||
|
||||
enum class Type {
|
||||
SOURCE,
|
||||
SCENE,
|
||||
@@ -27,10 +34,13 @@ public:
|
||||
MULTIVIEW,
|
||||
};
|
||||
|
||||
Action _action = Action::OPEN;
|
||||
Type _type = Type::SCENE;
|
||||
SourceSelection _source;
|
||||
SceneSelection _scene;
|
||||
bool _fullscreen = true;
|
||||
StringVariable _projectorWindowName = "Windowed Projector";
|
||||
RegexConfig _regex = RegexConfig::PartialMatchRegexConfig(true);
|
||||
|
||||
private:
|
||||
bool MonitorSetupChanged() const;
|
||||
@@ -60,21 +70,28 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int value);
|
||||
void WindowTypeChanged(int value);
|
||||
void TypeChanged(int value);
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const SourceSelection &);
|
||||
void MonitorChanged(int value);
|
||||
void ProjectorWindowNameChanged();
|
||||
void RegexChanged(const RegexConfig &);
|
||||
|
||||
private:
|
||||
void SetWidgetLayout();
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QComboBox *_windowTypes;
|
||||
QComboBox *_actions;
|
||||
QComboBox *_types;
|
||||
QComboBox *_windowTypes;
|
||||
SceneSelectionWidget *_scenes;
|
||||
SourceSelectionWidget *_sources;
|
||||
QHBoxLayout *_monitorSelection;
|
||||
QComboBox *_monitors;
|
||||
VariableLineEdit *_projectorWindowName;
|
||||
RegexConfigWidget *_regex;
|
||||
QHBoxLayout *_layout;
|
||||
|
||||
std::shared_ptr<MacroActionProjector> _entryData;
|
||||
bool _loading = true;
|
||||
|
||||
@@ -826,7 +826,7 @@ MacroActionTwitchEdit::MacroActionTwitchEdit(
|
||||
_userInfoQueryType(new QComboBox(this)),
|
||||
_userLogin(new VariableLineEdit(this)),
|
||||
_userId(new VariableSpinBox(this)),
|
||||
_pointsReward(new TwitchPointsRewardWidget(this)),
|
||||
_pointsReward(new TwitchPointsRewardWidget(this, false)),
|
||||
_rewardVariable(new VariableSelection(this)),
|
||||
_toggleRewardSelection(new QPushButton())
|
||||
{
|
||||
|
||||
@@ -1450,7 +1450,7 @@ MacroConditionTwitchEdit::MacroConditionTwitchEdit(
|
||||
_tokens(new TwitchConnectionSelection()),
|
||||
_tokenWarning(new QLabel()),
|
||||
_channel(new TwitchChannelSelection(this)),
|
||||
_pointsReward(new TwitchPointsRewardWidget(this)),
|
||||
_pointsReward(new TwitchPointsRewardWidget(this, true)),
|
||||
_streamTitle(new VariableLineEdit(this)),
|
||||
_regexTitle(new RegexConfigWidget(parent)),
|
||||
_chatMesageEdit(new ChatMessageEdit(this)),
|
||||
|
||||
@@ -135,9 +135,10 @@ void TwitchPointsRewardSelection::SelectionChanged(int index)
|
||||
emit PointsRewardChanged(pointsReward);
|
||||
}
|
||||
|
||||
TwitchPointsRewardWidget::TwitchPointsRewardWidget(QWidget *parent)
|
||||
TwitchPointsRewardWidget::TwitchPointsRewardWidget(QWidget *parent,
|
||||
bool allowAny)
|
||||
: QWidget(parent),
|
||||
_selection(new TwitchPointsRewardSelection(this)),
|
||||
_selection(new TwitchPointsRewardSelection(this, allowAny)),
|
||||
_refreshButton(new QPushButton(this))
|
||||
{
|
||||
_refreshButton->setMaximumWidth(22);
|
||||
|
||||
@@ -20,7 +20,7 @@ class TwitchPointsRewardSelection : public FilterComboBox {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TwitchPointsRewardSelection(QWidget *parent, bool allowAny = true);
|
||||
TwitchPointsRewardSelection(QWidget *parent, bool allowAny);
|
||||
|
||||
void SetPointsReward(const TwitchPointsReward &pointsReward);
|
||||
void SetChannel(const TwitchChannel &channel);
|
||||
@@ -53,7 +53,7 @@ class TwitchPointsRewardWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TwitchPointsRewardWidget(QWidget *parent);
|
||||
TwitchPointsRewardWidget(QWidget *parent, bool allowAny);
|
||||
|
||||
void SetPointsReward(const TwitchPointsReward &pointsReward);
|
||||
void SetChannel(const TwitchChannel &channel);
|
||||
|
||||
@@ -29,17 +29,23 @@ public:
|
||||
_headers(headers)
|
||||
{
|
||||
}
|
||||
bool operator<(const Args &other) const
|
||||
bool operator==(const Args &other) const
|
||||
{
|
||||
bool ret = true;
|
||||
ret = ret && _uri < other._uri;
|
||||
ret = ret && _path < other._path;
|
||||
ret = ret && _params < other._params;
|
||||
ret = ret && _data < other._data;
|
||||
ret = ret && _headers < other._headers;
|
||||
ret = ret && _uri == other._uri;
|
||||
ret = ret && _path == other._path;
|
||||
ret = ret && _params == other._params;
|
||||
ret = ret && _data == other._data;
|
||||
ret = ret && _headers == other._headers;
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::string &uri() const { return _uri; }
|
||||
const std::string &path() const { return _path; }
|
||||
const std::string &data() const { return _data; }
|
||||
const httplib::Params ¶ms() const { return _params; }
|
||||
const httplib::Headers &headers() const { return _headers; }
|
||||
|
||||
private:
|
||||
std::string _uri;
|
||||
std::string _path;
|
||||
@@ -48,6 +54,37 @@ private:
|
||||
httplib::Headers _headers;
|
||||
};
|
||||
|
||||
}; // namespace advss
|
||||
|
||||
template<> struct std::hash<advss::Args> {
|
||||
inline std::size_t operator()(const advss::Args &args) const
|
||||
{
|
||||
static constexpr auto hash_combine = [](std::size_t &seed,
|
||||
std::size_t hashValue) {
|
||||
seed ^= hashValue + 0x9e3779b9 + (seed << 6) +
|
||||
(seed >> 2);
|
||||
};
|
||||
|
||||
std::size_t seed = 0;
|
||||
hash_combine(seed, std::hash<std::string>()(args.uri()));
|
||||
hash_combine(seed, std::hash<std::string>()(args.path()));
|
||||
hash_combine(seed, std::hash<std::string>()(args.data()));
|
||||
|
||||
for (const auto &[key, value] : args.params()) {
|
||||
hash_combine(seed, std::hash<std::string>()(key));
|
||||
hash_combine(seed, std::hash<std::string>()(value));
|
||||
}
|
||||
for (const auto &[key, value] : args.headers()) {
|
||||
hash_combine(seed, std::hash<std::string>()(key));
|
||||
hash_combine(seed, std::hash<std::string>()(value));
|
||||
}
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
namespace advss {
|
||||
|
||||
struct CacheEntry {
|
||||
RequestResult result;
|
||||
std::chrono::system_clock::time_point cacheTime =
|
||||
@@ -61,14 +98,15 @@ static bool cacheIsTooOld(const CacheEntry &cache)
|
||||
return diff >= std::chrono::seconds(cacheTimeoutSeconds);
|
||||
}
|
||||
|
||||
static bool cacheIsValid(const std::map<Args, CacheEntry> &cache,
|
||||
static bool cacheIsValid(const std::unordered_map<Args, CacheEntry> &cache,
|
||||
const Args &args)
|
||||
{
|
||||
auto it = cache.find(args);
|
||||
return it != cache.end() && !cacheIsTooOld(it->second);
|
||||
}
|
||||
|
||||
static void cleanupCache(std::map<Args, CacheEntry> &cache, std::mutex &mtx)
|
||||
static void cleanupCache(std::unordered_map<Args, CacheEntry> &cache,
|
||||
std::mutex &mtx)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
cache.clear();
|
||||
@@ -181,7 +219,7 @@ RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri,
|
||||
return {};
|
||||
}
|
||||
|
||||
static std::map<Args, CacheEntry> cache;
|
||||
static std::unordered_map<Args, CacheEntry> cache;
|
||||
static std::mutex mtx;
|
||||
[[maybe_unused]] static bool _ = []() {
|
||||
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
||||
@@ -251,7 +289,7 @@ RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri,
|
||||
return {};
|
||||
}
|
||||
|
||||
static std::map<Args, CacheEntry> cache;
|
||||
static std::unordered_map<Args, CacheEntry> cache;
|
||||
static std::mutex mtx;
|
||||
[[maybe_unused]] static bool _ = []() {
|
||||
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
||||
@@ -322,7 +360,7 @@ RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri,
|
||||
return {};
|
||||
}
|
||||
|
||||
static std::map<Args, CacheEntry> cache;
|
||||
static std::unordered_map<Args, CacheEntry> cache;
|
||||
static std::mutex mtx;
|
||||
[[maybe_unused]] static bool _ = []() {
|
||||
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
||||
@@ -393,7 +431,7 @@ RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri,
|
||||
return {};
|
||||
}
|
||||
|
||||
static std::map<Args, CacheEntry> cache;
|
||||
static std::unordered_map<Args, CacheEntry> cache;
|
||||
static std::mutex mtx;
|
||||
[[maybe_unused]] static bool _ = []() {
|
||||
AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });
|
||||
|
||||
@@ -148,6 +148,39 @@ def script_load(settings):
|
||||
],
|
||||
)
|
||||
|
||||
# Other signals and procedures, which might be useful
|
||||
def plugin_stop_handler(data):
|
||||
obs.script_log(obs.LOG_INFO, "Hello from stop handler!")
|
||||
get_running_status()
|
||||
|
||||
def plugin_start_handler(data):
|
||||
obs.script_log(obs.LOG_INFO, "Hello from start handler!")
|
||||
get_running_status()
|
||||
|
||||
def get_running_status():
|
||||
proc_handler = obs.obs_get_proc_handler()
|
||||
data = obs.calldata_create()
|
||||
obs.proc_handler_call(proc_handler, "advss_plugin_running", data)
|
||||
success = obs.calldata_bool(data, "is_running")
|
||||
obs.script_log(
|
||||
obs.LOG_INFO, f"The advanced scene switcher is currently running: {success}"
|
||||
)
|
||||
obs.calldata_destroy(data)
|
||||
|
||||
def interval_reset_handler(data):
|
||||
obs.script_log(obs.LOG_INFO, "Hello from reset handler!")
|
||||
|
||||
signal_handler = obs.obs_get_signal_handler()
|
||||
obs.signal_handler_connect(
|
||||
signal_handler, "advss_plugin_stopped", plugin_stop_handler
|
||||
)
|
||||
obs.signal_handler_connect(
|
||||
signal_handler, "advss_plugin_started", plugin_start_handler
|
||||
)
|
||||
obs.signal_handler_connect(
|
||||
signal_handler, "advss_interval_reset", interval_reset_handler
|
||||
)
|
||||
|
||||
|
||||
def script_unload():
|
||||
# Deregistering is useful if you plan on reloading the script files
|
||||
|
||||
Reference in New Issue
Block a user