Add Run action/condition temp vars

This commit is contained in:
Przemek Pawlas
2024-03-23 02:51:51 +01:00
committed by WarmUpTill
parent c8545dea4d
commit 74dc0c871b
7 changed files with 175 additions and 9 deletions

View File

@@ -691,7 +691,8 @@ 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.entry.wait="{{wait}}Wait for process exit or at most {{timeout}}"
AdvSceneSwitcher.action.run.wait.entry="{{wait}}Wait for process exit or at most {{timeout}}{{waitHelp}}"
AdvSceneSwitcher.action.run.wait.help.tooltip="Note that macro properties won't work if you leave this unticked, as the process spawns detached from the rest of logic and there's no control over it."
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
@@ -1619,6 +1620,15 @@ AdvSceneSwitcher.tempVar.macro.matchedCount.description="The number of macros of
AdvSceneSwitcher.tempVar.process.name="Process name"
AdvSceneSwitcher.tempVar.run.process.id="Process ID"
AdvSceneSwitcher.tempVar.run.process.id.description="PID of the process assigned by the system."
AdvSceneSwitcher.tempVar.run.process.exitCode="Process exit code"
AdvSceneSwitcher.tempVar.run.process.exitCode.description="Numeric code with which the process exited. Usually 0 is positive and other values are error codes."
AdvSceneSwitcher.tempVar.run.process.stream.output="Process standard output stream"
AdvSceneSwitcher.tempVar.run.process.stream.output.description="Full standard output stream, usually numbered 1 and what you see in CLI after running the command."
AdvSceneSwitcher.tempVar.run.process.stream.error="Process standard error stream"
AdvSceneSwitcher.tempVar.run.process.stream.error.description="Full standard error stream, usually numbered 2."
AdvSceneSwitcher.tempVar.recording.durationSeconds="Recording duration"
AdvSceneSwitcher.tempVar.recording.durationSeconds.description="Recording duration in seconds.\nThis value does not change while the recording is paused and will be reset to zero if the recording is stopped."

View File

@@ -1,5 +1,6 @@
#include "macro-action-run.hpp"
#include "layout-helpers.hpp"
#include "ui-helpers.hpp"
#include <QProcess>
#include <QDesktopServices>
@@ -16,6 +17,8 @@ bool MacroActionRun::PerformAction()
{
if (_wait) {
_procConfig.StartProcessAndWait(_timeout.Milliseconds());
SetTempVarValues();
return true;
}
@@ -28,6 +31,7 @@ bool MacroActionRun::PerformAction()
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(_procConfig.Path())));
}
return true;
}
@@ -36,6 +40,45 @@ void MacroActionRun::LogAction() const
ablog(LOG_INFO, "run \"%s\"", _procConfig.UnresolvedPath().c_str());
}
void MacroActionRun::SetupTempVars()
{
MacroAction::SetupTempVars();
AddTempvar(
"process.id",
obs_module_text("AdvSceneSwitcher.tempVar.run.process.id"),
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.id.description"));
AddTempvar(
"process.exitCode",
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.exitCode"),
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.exitCode.description"));
AddTempvar(
"process.stream.output",
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.stream.output"),
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.stream.output.description"));
AddTempvar(
"process.stream.error",
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.stream.error"),
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.stream.error.description"));
}
void MacroActionRun::SetTempVarValues()
{
SetTempVarValue("process.id", _procConfig.GetProcessId());
SetTempVarValue("process.exitCode", _procConfig.GetProcessExitCode());
SetTempVarValue("process.stream.output",
_procConfig.GetProcessOutputStream());
SetTempVarValue("process.stream.error",
_procConfig.GetProcessErrorStream());
}
bool MacroActionRun::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
@@ -86,8 +129,18 @@ MacroActionRunEdit::MacroActionRunEdit(
_procConfig(new ProcessConfigEdit(this)),
_waitLayout(new QHBoxLayout()),
_wait(new QCheckBox()),
_timeout(new DurationSelection(this, true, 0.1))
_timeout(new DurationSelection(this, true, 0.1)),
_waitHelp(new QLabel())
{
QString helpIconPath = GetThemeTypeName() == "Light"
? ":/res/images/help.svg"
: ":/res/images/help_light.svg";
QIcon helpIcon(helpIconPath);
_waitHelp->setPixmap(helpIcon.pixmap(QSize(16, 16)));
_waitHelp->hide();
_waitHelp->setToolTip(obs_module_text(
"AdvSceneSwitcher.action.run.wait.help.tooltip"));
QWidget::connect(_procConfig,
SIGNAL(ConfigChanged(const ProcessConfig &)), this,
SLOT(ProcessConfigChanged(const ProcessConfig &)));
@@ -98,9 +151,11 @@ MacroActionRunEdit::MacroActionRunEdit(
QWidget::connect(_timeout, SIGNAL(DurationChanged(const Duration &)),
this, SLOT(TimeoutChanged(const Duration &)));
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.run.entry.wait"),
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.run.wait.entry"),
_waitLayout,
{{"{{wait}}", _wait}, {"{{timeout}}", _timeout}});
{{"{{wait}}", _wait},
{"{{timeout}}", _timeout},
{"{{waitHelp}}", _waitHelp}});
SetLayoutVisible(_waitLayout, false);
auto layout = new QVBoxLayout;

View File

@@ -25,6 +25,9 @@ public:
Duration _timeout = 1;
private:
void SetupTempVars();
void SetTempVarValues();
static bool _registered;
static const std::string id;
};
@@ -57,6 +60,7 @@ private:
QHBoxLayout *_waitLayout;
QCheckBox *_wait;
DurationSelection *_timeout;
QLabel *_waitHelp;
std::shared_ptr<MacroActionRun> _entryData;
bool _loading = true;

View File

@@ -48,6 +48,7 @@ bool MacroConditionRun::CheckCondition()
if (_thread.joinable()) {
_thread.join();
}
_threadDone = false;
_thread = std::thread(&MacroConditionRun::RunProcess, this);
@@ -64,7 +65,48 @@ void MacroConditionRun::RunProcess()
_error = ProcessConfig::ProcStartError::NONE;
_procExitCode = std::get<int>(result);
}
_threadDone = true;
SetTempVarValues();
}
void MacroConditionRun::SetupTempVars()
{
MacroCondition::SetupTempVars();
AddTempvar(
"process.id",
obs_module_text("AdvSceneSwitcher.tempVar.run.process.id"),
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.id.description"));
AddTempvar(
"process.exitCode",
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.exitCode"),
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.exitCode.description"));
AddTempvar(
"process.stream.output",
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.stream.output"),
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.stream.output.description"));
AddTempvar(
"process.stream.error",
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.stream.error"),
obs_module_text(
"AdvSceneSwitcher.tempVar.run.process.stream.error.description"));
}
void MacroConditionRun::SetTempVarValues()
{
SetTempVarValue("process.id", _procConfig.GetProcessId());
SetTempVarValue("process.exitCode", _procConfig.GetProcessExitCode());
SetTempVarValue("process.stream.output",
_procConfig.GetProcessOutputStream());
SetTempVarValue("process.stream.error",
_procConfig.GetProcessErrorStream());
}
bool MacroConditionRun::Save(obs_data_t *obj) const

View File

@@ -30,6 +30,10 @@ public:
private:
void RunProcess();
void SetupTempVars();
void SetTempVarValues();
std::thread _thread;
std::atomic_bool _threadDone{true};
ProcessConfig::ProcStartError _error =

View File

@@ -4,7 +4,6 @@
#include "name-dialog.hpp"
#include <QFileDialog>
#include <QProcess>
namespace advss {
@@ -16,6 +15,7 @@ bool ProcessConfig::Save(obs_data_t *obj) const
_args.Save(data, "args", "arg");
obs_data_set_obj(obj, "processConfig", data);
obs_data_release(data);
return true;
}
@@ -27,6 +27,7 @@ bool ProcessConfig::Load(obs_data_t *obj)
_workingDirectory =
obs_data_get_string(obj, "workingDirectory");
_args.Load(obj, "args", "arg");
return true;
}
@@ -35,6 +36,7 @@ bool ProcessConfig::Load(obs_data_t *obj)
_workingDirectory.Load(data, "workingDirectory");
_args.Load(data, "args", "arg");
obs_data_release(data);
return true;
}
@@ -44,6 +46,7 @@ QStringList ProcessConfig::Args() const
for (auto &arg : _args) {
result << QString::fromStdString(arg);
}
return result;
}
@@ -61,11 +64,14 @@ void ProcessConfig::ResolveVariables()
}
std::variant<int, ProcessConfig::ProcStartError>
ProcessConfig::StartProcessAndWait(int timeout) const
ProcessConfig::StartProcessAndWait(int timeout)
{
ResetFinishedProcessData();
QProcess process;
process.setWorkingDirectory(QString::fromStdString(WorkingDir()));
process.start(QString::fromStdString(Path()), Args());
SetProcessId(QString::number(process.processId()).toStdString());
vblog(LOG_INFO, "run \"%s\" with a timeout of %d ms", Path().c_str(),
timeout);
@@ -75,21 +81,47 @@ ProcessConfig::StartProcessAndWait(int timeout) const
Path().c_str());
return ProcStartError::FAILED_TO_START;
}
SetFinishedProcessData(process);
vblog(LOG_INFO,
"timeout while running \"%s\"\nAttempting to kill process!",
Path().c_str());
process.kill();
process.waitForFinished();
return ProcStartError::TIMEOUT;
}
SetFinishedProcessData(process);
if (process.exitStatus() == QProcess::NormalExit) {
return process.exitCode();
}
vblog(LOG_INFO, "process \"%s\" crashed!", Path().c_str());
return ProcStartError::CRASH;
}
void ProcessConfig::SetFinishedProcessData(QProcess &process)
{
static const QRegularExpression regex("(\\r\\n|\\r|\\n)$");
_processExitCode = QString::number(process.exitCode()).toStdString();
// Qt reads extra newline, at least on Windows, hence the workaround
_processOutputStream = QString(process.readAllStandardOutput())
.remove(regex)
.toStdString();
_processErrorStream = QString(process.readAllStandardError())
.remove(regex)
.toStdString();
}
void ProcessConfig::ResetFinishedProcessData()
{
_processExitCode = "";
_processOutputStream = "";
_processErrorStream = "";
}
ProcessConfigEdit::ProcessConfigEdit(QWidget *parent)
: QWidget(parent),
_filePath(new FileSelection()),

View File

@@ -5,8 +5,9 @@
#include <obs-data.h>
#include <obs-module-helper.hpp>
#include <QPushButton>
#include <QListWidget>
#include <QProcess>
#include <QPushButton>
#include <QStringList>
#include <QVBoxLayout>
#include <variant>
@@ -23,6 +24,20 @@ public:
std::string WorkingDir() const { return _workingDirectory; }
QStringList Args() const; // Resolves variables
void SetProcessId(std::string processId) { _processId = processId; }
std::string GetProcessId() const { return _processId; }
void SetFinishedProcessData(QProcess &process);
void ResetFinishedProcessData();
std::string GetProcessExitCode() const { return _processExitCode; }
std::string GetProcessOutputStream() const
{
return _processOutputStream;
}
std::string GetProcessErrorStream() const
{
return _processErrorStream;
}
enum class ProcStartError {
NONE,
FAILED_TO_START,
@@ -30,8 +45,7 @@ public:
CRASH,
};
std::variant<int, ProcStartError>
StartProcessAndWait(int timeoutInMs) const;
std::variant<int, ProcStartError> StartProcessAndWait(int timeoutInMs);
bool StartProcessDetached() const;
void ResolveVariables();
@@ -41,6 +55,11 @@ private:
StringVariable _workingDirectory = "";
StringList _args;
std::string _processId;
std::string _processExitCode;
std::string _processOutputStream;
std::string _processErrorStream;
friend class ProcessConfigEdit;
};