mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-11 19:16:19 -05:00
Add support for inline scripts
* Script can be defined in the macro segment or loaded from a file * Supports both LUA and Python * Can be used for actions and conditions * obs_script_create and obs_script_destroy are resolved at runtime (Let's hope the API remains stable)
This commit is contained in:
@@ -801,6 +801,7 @@ AdvSceneSwitcher.condition.screenshot.entry="A screenshot was taken"
|
|||||||
AdvSceneSwitcher.condition.mqtt="MQTT"
|
AdvSceneSwitcher.condition.mqtt="MQTT"
|
||||||
AdvSceneSwitcher.condition.mqtt.layout.match="Message was received from{{connection}} which matches{{regex}}:"
|
AdvSceneSwitcher.condition.mqtt.layout.match="Message was received from{{connection}} which matches{{regex}}:"
|
||||||
AdvSceneSwitcher.condition.mqtt.layout.listen="Set message selection to incoming message:{{listenButton}}"
|
AdvSceneSwitcher.condition.mqtt.layout.listen="Set message selection to incoming message:{{listenButton}}"
|
||||||
|
AdvSceneSwitcher.condition.script="Script"
|
||||||
|
|
||||||
# Macro Actions
|
# Macro Actions
|
||||||
AdvSceneSwitcher.action.unknown="Unknown action"
|
AdvSceneSwitcher.action.unknown="Unknown action"
|
||||||
@@ -1250,6 +1251,7 @@ AdvSceneSwitcher.action.obsSetting.action.setOutputCanvasX="Set output resolutio
|
|||||||
AdvSceneSwitcher.action.obsSetting.action.setOutputCanvasY="Set output resolution Y value"
|
AdvSceneSwitcher.action.obsSetting.action.setOutputCanvasY="Set output resolution Y value"
|
||||||
AdvSceneSwitcher.action.obsSetting.getCurrentValue="Get current value"
|
AdvSceneSwitcher.action.obsSetting.getCurrentValue="Get current value"
|
||||||
AdvSceneSwitcher.action.obsSettings.layout="{{actions}}{{fpsType}}{{fpsIntValue}}{{fpsStringValue}}{{canvasSizeValue}}{{getCurrentValue}}"
|
AdvSceneSwitcher.action.obsSettings.layout="{{actions}}{{fpsType}}{{fpsIntValue}}{{fpsStringValue}}{{canvasSizeValue}}{{getCurrentValue}}"
|
||||||
|
AdvSceneSwitcher.action.script="Script"
|
||||||
|
|
||||||
# Hotkey
|
# Hotkey
|
||||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
|
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
|
||||||
@@ -1514,6 +1516,17 @@ AdvSceneSwitcher.twitch.selection.points.reward.tooltip.noPermission="Can't sele
|
|||||||
AdvSceneSwitcher.twitch.selection.points.reward.tooltip.noChannel="Can't select points reward without entering a channel first!"
|
AdvSceneSwitcher.twitch.selection.points.reward.tooltip.noChannel="Can't select points reward without entering a channel first!"
|
||||||
AdvSceneSwitcher.twitch.selection.points.reward.tooltip.error="Can't select points reward because Twitch responded with an error! Check OBS logs for more details."
|
AdvSceneSwitcher.twitch.selection.points.reward.tooltip.error="Can't select points reward because Twitch responded with an error! Check OBS logs for more details."
|
||||||
|
|
||||||
|
AdvSceneSwitcher.script.type.inline="Inline"
|
||||||
|
AdvSceneSwitcher.script.type.file="File"
|
||||||
|
AdvSceneSwitcher.script.type.layout="Script type:{{scriptType}}"
|
||||||
|
AdvSceneSwitcher.script.language.python="Python"
|
||||||
|
AdvSceneSwitcher.script.language.lua="LUA"
|
||||||
|
AdvSceneSwitcher.script.language.select="--select language--"
|
||||||
|
AdvSceneSwitcher.script.language.layout="Script language:{{language}}"
|
||||||
|
AdvSceneSwitcher.script.file.open="Open"
|
||||||
|
AdvSceneSwitcher.script.file.open.failed="Could not open script file!"
|
||||||
|
AdvSceneSwitcher.script.file.layout="Script file:{{path}}{{open}}"
|
||||||
|
|
||||||
AdvSceneSwitcher.tempVar.select="--select value--"
|
AdvSceneSwitcher.tempVar.select="--select value--"
|
||||||
AdvSceneSwitcher.tempVar.selectionInfo.lastValues="Last values:"
|
AdvSceneSwitcher.tempVar.selectionInfo.lastValues="Last values:"
|
||||||
|
|
||||||
|
|||||||
@@ -9,16 +9,28 @@ target_sources(
|
|||||||
${PROJECT_NAME}
|
${PROJECT_NAME}
|
||||||
PRIVATE macro-action-script.cpp
|
PRIVATE macro-action-script.cpp
|
||||||
macro-action-script.hpp
|
macro-action-script.hpp
|
||||||
|
macro-action-script-inline.cpp
|
||||||
|
macro-action-script-inline.hpp
|
||||||
macro-condition-script.cpp
|
macro-condition-script.cpp
|
||||||
macro-condition-script.hpp
|
macro-condition-script.hpp
|
||||||
|
macro-condition-script-inline.cpp
|
||||||
|
macro-condition-script-inline.hpp
|
||||||
macro-script-handler.cpp
|
macro-script-handler.cpp
|
||||||
macro-script-handler.hpp
|
macro-script-handler.hpp
|
||||||
macro-segment-script.cpp
|
macro-segment-script.cpp
|
||||||
macro-segment-script.hpp)
|
macro-segment-script.hpp
|
||||||
|
macro-segment-script-inline.cpp
|
||||||
|
macro-segment-script-inline.hpp)
|
||||||
|
|
||||||
target_sources(
|
target_sources(
|
||||||
${PROJECT_NAME} PRIVATE utils/properties-view.cpp utils/properties-view.hpp
|
${PROJECT_NAME}
|
||||||
utils/properties-view.moc.hpp)
|
PRIVATE utils/inline-script.cpp
|
||||||
|
utils/inline-script.hpp
|
||||||
|
utils/obs-script-helpers.cpp
|
||||||
|
utils/obs-script-helpers.hpp
|
||||||
|
utils/properties-view.cpp
|
||||||
|
utils/properties-view.hpp
|
||||||
|
utils/properties-view.moc.hpp)
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME}
|
target_include_directories(${PROJECT_NAME}
|
||||||
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/utils")
|
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/utils")
|
||||||
|
|||||||
66
plugins/scripting/macro-action-script-inline.cpp
Normal file
66
plugins/scripting/macro-action-script-inline.cpp
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include "macro-action-script-inline.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
const std::string MacroActionScriptInline::_id = "script";
|
||||||
|
|
||||||
|
bool MacroActionScriptInline::_registered = MacroActionFactory::Register(
|
||||||
|
MacroActionScriptInline::_id,
|
||||||
|
{MacroActionScriptInline::Create, MacroActionScriptInlineEdit::Create,
|
||||||
|
"AdvSceneSwitcher.action.script"});
|
||||||
|
|
||||||
|
bool MacroActionScriptInline::PerformAction()
|
||||||
|
{
|
||||||
|
return _script.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionScriptInline::LogAction() const
|
||||||
|
{
|
||||||
|
ablog(LOG_INFO, "performing inline script action");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionScriptInline::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
MacroAction::Save(obj);
|
||||||
|
_script.Save(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionScriptInline::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
MacroAction::Load(obj);
|
||||||
|
_script.Load(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MacroAction> MacroActionScriptInline::Create(Macro *m)
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroActionScriptInline>(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MacroAction> MacroActionScriptInline::Copy() const
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroActionScriptInline>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionScriptInline::ResolveVariablesToFixedValues()
|
||||||
|
{
|
||||||
|
_script.ResolveVariablesToFixedValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroActionScriptInlineEdit::MacroActionScriptInlineEdit(
|
||||||
|
QWidget *parent, std::shared_ptr<MacroActionScriptInline> entryData)
|
||||||
|
: MacroSegmentScriptInlineEdit(parent, entryData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *
|
||||||
|
MacroActionScriptInlineEdit::Create(QWidget *parent,
|
||||||
|
std::shared_ptr<MacroAction> action)
|
||||||
|
{
|
||||||
|
return new MacroActionScriptInlineEdit(
|
||||||
|
parent,
|
||||||
|
std::dynamic_pointer_cast<MacroActionScriptInline>(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
39
plugins/scripting/macro-action-script-inline.hpp
Normal file
39
plugins/scripting/macro-action-script-inline.hpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "macro-action-edit.hpp"
|
||||||
|
#include "macro-segment-script-inline.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class MacroActionScriptInline : public MacroAction,
|
||||||
|
public MacroSegmentScriptInline {
|
||||||
|
public:
|
||||||
|
MacroActionScriptInline(Macro *m) : MacroAction(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);
|
||||||
|
std::shared_ptr<MacroAction> Copy() const;
|
||||||
|
|
||||||
|
void ResolveVariablesToFixedValues();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool _registered;
|
||||||
|
static const std::string _id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MacroActionScriptInlineEdit : public MacroSegmentScriptInlineEdit {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MacroActionScriptInlineEdit(
|
||||||
|
QWidget *, std::shared_ptr<MacroActionScriptInline> = nullptr);
|
||||||
|
static QWidget *Create(QWidget *, std::shared_ptr<MacroAction>);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
51
plugins/scripting/macro-condition-script-inline.cpp
Normal file
51
plugins/scripting/macro-condition-script-inline.cpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#include "macro-condition-script-inline.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
const std::string MacroConditionScriptInline::_id = "script";
|
||||||
|
|
||||||
|
bool MacroConditionScriptInline::_registered =
|
||||||
|
MacroConditionFactory::Register(MacroConditionScriptInline::_id,
|
||||||
|
{MacroConditionScriptInline::Create,
|
||||||
|
MacroConditionScriptInlineEdit::Create,
|
||||||
|
"AdvSceneSwitcher.condition.script"});
|
||||||
|
|
||||||
|
bool MacroConditionScriptInline::CheckCondition()
|
||||||
|
{
|
||||||
|
return _script.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionScriptInline::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
MacroCondition::Save(obj);
|
||||||
|
_script.Save(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionScriptInline::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
MacroCondition::Load(obj);
|
||||||
|
_script.Load(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MacroCondition> MacroConditionScriptInline::Create(Macro *m)
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroConditionScriptInline>(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroConditionScriptInlineEdit::MacroConditionScriptInlineEdit(
|
||||||
|
QWidget *parent, std::shared_ptr<MacroConditionScriptInline> entryData)
|
||||||
|
: MacroSegmentScriptInlineEdit(parent, entryData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *MacroConditionScriptInlineEdit::Create(
|
||||||
|
QWidget *parent, std::shared_ptr<MacroCondition> condition)
|
||||||
|
{
|
||||||
|
return new MacroConditionScriptInlineEdit(
|
||||||
|
parent, std::dynamic_pointer_cast<MacroConditionScriptInline>(
|
||||||
|
condition));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
36
plugins/scripting/macro-condition-script-inline.hpp
Normal file
36
plugins/scripting/macro-condition-script-inline.hpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "macro-condition-edit.hpp"
|
||||||
|
#include "macro-segment-script-inline.hpp"
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class MacroConditionScriptInline : public MacroCondition,
|
||||||
|
public MacroSegmentScriptInline {
|
||||||
|
public:
|
||||||
|
MacroConditionScriptInline(Macro *m) : MacroCondition(m) {}
|
||||||
|
|
||||||
|
bool CheckCondition();
|
||||||
|
|
||||||
|
bool Save(obs_data_t *obj) const;
|
||||||
|
bool Load(obs_data_t *obj);
|
||||||
|
|
||||||
|
std::string GetId() const { return _id; };
|
||||||
|
|
||||||
|
static std::shared_ptr<MacroCondition> Create(Macro *m);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool _registered;
|
||||||
|
static const std::string _id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MacroConditionScriptInlineEdit : public MacroSegmentScriptInlineEdit {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MacroConditionScriptInlineEdit(
|
||||||
|
QWidget *,
|
||||||
|
std::shared_ptr<MacroConditionScriptInline> = nullptr);
|
||||||
|
static QWidget *Create(QWidget *, std::shared_ptr<MacroCondition>);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
198
plugins/scripting/macro-segment-script-inline.cpp
Normal file
198
plugins/scripting/macro-segment-script-inline.cpp
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
#include "macro-segment-script-inline.hpp"
|
||||||
|
#include "layout-helpers.hpp"
|
||||||
|
#include "obs-module-helper.hpp"
|
||||||
|
#include "sync-helpers.hpp"
|
||||||
|
#include "ui-helpers.hpp"
|
||||||
|
|
||||||
|
#include <QDesktopServices>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
void MacroSegmentScriptInline::SetType(InlineScript::Type type)
|
||||||
|
{
|
||||||
|
_script.SetType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptInline::SetLanguage(obs_script_lang language)
|
||||||
|
{
|
||||||
|
_script.SetLanguage(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptInline::SetScript(const std::string &text)
|
||||||
|
{
|
||||||
|
_script.SetText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptInline::SetPath(const std::string &path)
|
||||||
|
{
|
||||||
|
_script.SetPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void populateLanguageSelection(QComboBox *list)
|
||||||
|
{
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.script.language.python"),
|
||||||
|
OBS_SCRIPT_LANG_PYTHON);
|
||||||
|
list->addItem(obs_module_text("AdvSceneSwitcher.script.language.lua"),
|
||||||
|
OBS_SCRIPT_LANG_LUA);
|
||||||
|
list->setPlaceholderText(
|
||||||
|
obs_module_text("AdvSceneSwitcher.script.language.select"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void populateScriptTypeSelection(QComboBox *list)
|
||||||
|
{
|
||||||
|
list->addItem(obs_module_text("AdvSceneSwitcher.script.type.inline"),
|
||||||
|
InlineScript::INLINE);
|
||||||
|
list->addItem(obs_module_text("AdvSceneSwitcher.script.type.file"),
|
||||||
|
InlineScript::FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroSegmentScriptInlineEdit::MacroSegmentScriptInlineEdit(
|
||||||
|
QWidget *parent, std::shared_ptr<MacroSegmentScriptInline> entryData)
|
||||||
|
: QWidget(parent),
|
||||||
|
_scriptType(new QComboBox(this)),
|
||||||
|
_language(new QComboBox(this)),
|
||||||
|
_script(new ScriptEditor(this)),
|
||||||
|
_path(new FileSelection(FileSelection::Type::WRITE, this)),
|
||||||
|
_openFile(new QPushButton(
|
||||||
|
obs_module_text("AdvSceneSwitcher.script.file.open"), this)),
|
||||||
|
_fileLayout(new QHBoxLayout()),
|
||||||
|
_entryData(entryData)
|
||||||
|
{
|
||||||
|
SetupLayout();
|
||||||
|
SetupWidgetConnections();
|
||||||
|
PopulateWidgets();
|
||||||
|
SetWidgetVisibility();
|
||||||
|
_loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptInlineEdit::ScriptTypeChanged(int idx)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->SetType(static_cast<InlineScript::Type>(
|
||||||
|
_scriptType->itemData(idx).toInt()));
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptInlineEdit::LanguageChanged(int idx)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->SetLanguage(static_cast<obs_script_lang>(
|
||||||
|
_language->itemData(idx).toInt()));
|
||||||
|
const QSignalBlocker b(_script);
|
||||||
|
_script->setPlainText(_entryData->GetScript());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entryData->GetType() == InlineScript::Type::FILE) {
|
||||||
|
PathChanged(QString::fromStdString(_entryData->GetPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptInlineEdit::ScriptChanged()
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
_entryData->SetScript(_script->toPlainText().toStdString());
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptInlineEdit::PathChanged(const QString &path)
|
||||||
|
{
|
||||||
|
GUARD_LOADING_AND_LOCK();
|
||||||
|
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
_entryData->SetPath(path.toStdString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Script language will be detected by OBS based on file extension so
|
||||||
|
// adjust it if necessary
|
||||||
|
QString pathAdjusted = path;
|
||||||
|
if (_entryData->GetLanguage() == OBS_SCRIPT_LANG_PYTHON &&
|
||||||
|
!path.endsWith(".py")) {
|
||||||
|
if (path.endsWith(".lua")) {
|
||||||
|
pathAdjusted.chop(4);
|
||||||
|
}
|
||||||
|
pathAdjusted += ".py";
|
||||||
|
}
|
||||||
|
if (_entryData->GetLanguage() == OBS_SCRIPT_LANG_LUA &&
|
||||||
|
!path.endsWith(".lua")) {
|
||||||
|
if (path.endsWith(".py")) {
|
||||||
|
pathAdjusted.chop(3);
|
||||||
|
}
|
||||||
|
pathAdjusted += ".lua";
|
||||||
|
}
|
||||||
|
|
||||||
|
const QSignalBlocker b(_path);
|
||||||
|
_path->SetPath(pathAdjusted);
|
||||||
|
_entryData->SetPath(pathAdjusted.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptInlineEdit::PopulateWidgets()
|
||||||
|
{
|
||||||
|
populateLanguageSelection(_language);
|
||||||
|
populateScriptTypeSelection(_scriptType);
|
||||||
|
|
||||||
|
if (!_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_scriptType->setCurrentIndex(
|
||||||
|
_scriptType->findData(_entryData->GetType()));
|
||||||
|
_language->setCurrentIndex(
|
||||||
|
_language->findData(_entryData->GetLanguage()));
|
||||||
|
_script->setPlainText(_entryData->GetScript());
|
||||||
|
_path->SetPath(_entryData->GetPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptInlineEdit::SetupWidgetConnections()
|
||||||
|
{
|
||||||
|
QWidget::connect(_scriptType, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(ScriptTypeChanged(int)));
|
||||||
|
QWidget::connect(_language, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(LanguageChanged(int)));
|
||||||
|
QWidget::connect(_script, SIGNAL(ScriptChanged()), this,
|
||||||
|
SLOT(ScriptChanged()));
|
||||||
|
QWidget::connect(_path, SIGNAL(PathChanged(const QString &)), this,
|
||||||
|
SLOT(PathChanged(const QString &)));
|
||||||
|
QWidget::connect(_openFile, &QPushButton::clicked, this, [this] {
|
||||||
|
QUrl fileUrl = QUrl::fromLocalFile(
|
||||||
|
QString::fromStdString(_entryData->GetPath()));
|
||||||
|
if (!QDesktopServices::openUrl(fileUrl)) {
|
||||||
|
DisplayMessage(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.script.file.open.failed"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptInlineEdit::SetupLayout()
|
||||||
|
{
|
||||||
|
auto languageLayout = new QHBoxLayout();
|
||||||
|
PlaceWidgets(obs_module_text("AdvSceneSwitcher.script.language.layout"),
|
||||||
|
languageLayout, {{"{{language}}", _language}});
|
||||||
|
auto typeLayout = new QHBoxLayout();
|
||||||
|
PlaceWidgets(obs_module_text("AdvSceneSwitcher.script.type.layout"),
|
||||||
|
typeLayout, {{"{{scriptType}}", _scriptType}});
|
||||||
|
PlaceWidgets(obs_module_text("AdvSceneSwitcher.script.file.layout"),
|
||||||
|
_fileLayout,
|
||||||
|
{{"{{path}}", _path}, {"{{open}}", _openFile}}, false);
|
||||||
|
auto layout = new QVBoxLayout();
|
||||||
|
layout->addLayout(typeLayout);
|
||||||
|
layout->addLayout(languageLayout);
|
||||||
|
layout->addLayout(_fileLayout);
|
||||||
|
layout->addWidget(_script);
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroSegmentScriptInlineEdit::SetWidgetVisibility()
|
||||||
|
{
|
||||||
|
_script->setVisible(_entryData->GetType() ==
|
||||||
|
InlineScript::Type::INLINE);
|
||||||
|
SetLayoutVisible(_fileLayout,
|
||||||
|
_entryData->GetType() == InlineScript::Type::FILE);
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
56
plugins/scripting/macro-segment-script-inline.hpp
Normal file
56
plugins/scripting/macro-segment-script-inline.hpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "file-selection.hpp"
|
||||||
|
#include "inline-script.hpp"
|
||||||
|
#include "variable-text-edit.hpp"
|
||||||
|
|
||||||
|
#include <QLayout>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class MacroSegmentScriptInline {
|
||||||
|
public:
|
||||||
|
InlineScript::Type GetType() const { return _script.GetType(); }
|
||||||
|
void SetType(InlineScript::Type);
|
||||||
|
obs_script_lang GetLanguage() const { return _script.GetLanguage(); }
|
||||||
|
void SetLanguage(obs_script_lang);
|
||||||
|
StringVariable GetScript() const { return _script.GetText(); }
|
||||||
|
void SetScript(const std::string &);
|
||||||
|
std::string GetPath() const { return _script.GetPath(); }
|
||||||
|
void SetPath(const std::string &);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
InlineScript _script;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MacroSegmentScriptInlineEdit : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MacroSegmentScriptInlineEdit(
|
||||||
|
QWidget *, std::shared_ptr<MacroSegmentScriptInline> = nullptr);
|
||||||
|
virtual ~MacroSegmentScriptInlineEdit() = default;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void ScriptTypeChanged(int);
|
||||||
|
void LanguageChanged(int);
|
||||||
|
void ScriptChanged();
|
||||||
|
void PathChanged(const QString &);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void PopulateWidgets();
|
||||||
|
void SetupWidgetConnections();
|
||||||
|
void SetupLayout();
|
||||||
|
void SetWidgetVisibility();
|
||||||
|
|
||||||
|
QComboBox *_scriptType;
|
||||||
|
QComboBox *_language;
|
||||||
|
ScriptEditor *_script;
|
||||||
|
FileSelection *_path;
|
||||||
|
QPushButton *_openFile;
|
||||||
|
QHBoxLayout *_fileLayout;
|
||||||
|
|
||||||
|
std::shared_ptr<MacroSegmentScriptInline> _entryData;
|
||||||
|
bool _loading = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
344
plugins/scripting/utils/inline-script.cpp
Normal file
344
plugins/scripting/utils/inline-script.cpp
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
#include "inline-script.hpp"
|
||||||
|
#include "log-helper.hpp"
|
||||||
|
#include "obs-module-helper.hpp"
|
||||||
|
|
||||||
|
#include <obs-module.h>
|
||||||
|
#include <obs.hpp>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
static constexpr std::string_view signalName = "advss_run_temp_script";
|
||||||
|
|
||||||
|
std::atomic_uint64_t InlineScript::_instanceIdCounter = 0;
|
||||||
|
const std::string_view InlineScript::_defaultPythonScript =
|
||||||
|
"import obspython as obs\n"
|
||||||
|
"\n"
|
||||||
|
"def run():\n"
|
||||||
|
" obs.script_log(obs.LOG_WARNING, \"Hello from Python!\")\n"
|
||||||
|
" return True\n";
|
||||||
|
const std::string_view InlineScript::_defaultLUAScript =
|
||||||
|
"obs = obslua\n"
|
||||||
|
"\n"
|
||||||
|
"function run()\n"
|
||||||
|
" obs.script_log(obs.LOG_WARNING, \"Hello from LUA!\")\n"
|
||||||
|
" return true\n"
|
||||||
|
"end";
|
||||||
|
;
|
||||||
|
|
||||||
|
static bool setup()
|
||||||
|
{
|
||||||
|
auto sh = obs_get_signal_handler();
|
||||||
|
auto signalDecl =
|
||||||
|
std::string("void ") + signalName.data() + "(string id)";
|
||||||
|
signal_handler_add(sh, signalDecl.c_str());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static bool setupDone = setup();
|
||||||
|
|
||||||
|
static void cleanupScriptFile(const std::string &path)
|
||||||
|
{
|
||||||
|
const QFileInfo fileInfo(QString::fromStdString(path));
|
||||||
|
if (!fileInfo.isFile()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QFile file(fileInfo.absoluteFilePath());
|
||||||
|
if (!file.remove()) {
|
||||||
|
vblog(LOG_INFO, "failed to clean up script file %s",
|
||||||
|
fileInfo.absoluteFilePath().toStdString().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<std::string>
|
||||||
|
getScriptTempFilePath(obs_script_lang language)
|
||||||
|
{
|
||||||
|
static int counter = 0;
|
||||||
|
++counter;
|
||||||
|
static const QString filenamePattern =
|
||||||
|
"scripting/advss-tmp-script%1.%2";
|
||||||
|
const QString filename = filenamePattern.arg(counter).arg(
|
||||||
|
language == OBS_SCRIPT_LANG_PYTHON ? "py" : "lua");
|
||||||
|
auto settingsFile =
|
||||||
|
obs_module_config_path(filename.toStdString().c_str());
|
||||||
|
|
||||||
|
if (!settingsFile) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"could not create temp script file! (obs_module_config_path)");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string path = settingsFile;
|
||||||
|
bfree(settingsFile);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool createScriptFile(const char *settingsFile, const char *content)
|
||||||
|
{
|
||||||
|
const QFileInfo fileInfo(settingsFile);
|
||||||
|
const QString dirPath = fileInfo.absolutePath();
|
||||||
|
const QDir dir(dirPath);
|
||||||
|
if (!dir.exists() && !dir.mkpath(dirPath)) {
|
||||||
|
blog(LOG_WARNING, "could not create script file! (mkpath)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file(settingsFile);
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto out = QTextStream(&file);
|
||||||
|
out << content;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineScript::InlineScript() : _instanceId(_instanceIdCounter++)
|
||||||
|
{
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineScript::InlineScript(const InlineScript &other)
|
||||||
|
: _language(other._language),
|
||||||
|
_textPython(other._textPython),
|
||||||
|
_textLUA(other._textLUA),
|
||||||
|
_instanceId(_instanceIdCounter++)
|
||||||
|
{
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineScript::Save(obs_data_t *data) const
|
||||||
|
{
|
||||||
|
OBSDataAutoRelease obj = obs_data_create();
|
||||||
|
obs_data_set_int(obj, "type", _type);
|
||||||
|
obs_data_set_int(obj, "language", _language);
|
||||||
|
_textPython.Save(obj, "scriptPython");
|
||||||
|
_textLUA.Save(obj, "scriptLUA");
|
||||||
|
obs_data_set_string(obj, "file", _file.c_str());
|
||||||
|
obs_data_set_obj(data, "script", obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineScript::Load(obs_data_t *data)
|
||||||
|
{
|
||||||
|
OBSDataAutoRelease obj = obs_data_get_obj(data, "script");
|
||||||
|
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
|
||||||
|
_language =
|
||||||
|
static_cast<obs_script_lang>(obs_data_get_int(obj, "language"));
|
||||||
|
_textPython.Load(obj, "scriptPython");
|
||||||
|
_textLUA.Load(obj, "scriptLUA");
|
||||||
|
_file = obs_data_get_string(obj, "file");
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineScript::SetType(Type type)
|
||||||
|
{
|
||||||
|
_type = type;
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineScript::SetLanguage(obs_script_lang language)
|
||||||
|
{
|
||||||
|
_language = language;
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineScript::SetText(const std::string &text)
|
||||||
|
{
|
||||||
|
switch (_language) {
|
||||||
|
case OBS_SCRIPT_LANG_UNKNOWN:
|
||||||
|
break;
|
||||||
|
case OBS_SCRIPT_LANG_LUA:
|
||||||
|
_textLUA = text;
|
||||||
|
break;
|
||||||
|
case OBS_SCRIPT_LANG_PYTHON:
|
||||||
|
_textPython = text;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
const StringVariable &InlineScript::GetText() const
|
||||||
|
{
|
||||||
|
static const StringVariable defaultRet;
|
||||||
|
|
||||||
|
switch (_language) {
|
||||||
|
case OBS_SCRIPT_LANG_UNKNOWN:
|
||||||
|
break;
|
||||||
|
case OBS_SCRIPT_LANG_LUA:
|
||||||
|
return _textLUA;
|
||||||
|
case OBS_SCRIPT_LANG_PYTHON:
|
||||||
|
return _textPython;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return defaultRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineScript::SetPath(const std::string &path)
|
||||||
|
{
|
||||||
|
_file = path;
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InlineScript::Run()
|
||||||
|
{
|
||||||
|
static auto sh = obs_get_signal_handler();
|
||||||
|
|
||||||
|
if (_type == INLINE && _lastResolvedText != std::string(GetText())) {
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_type == FILE && _lastPath != _file) {
|
||||||
|
Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cd = calldata_create();
|
||||||
|
calldata_set_string(cd, "id", GetID().c_str());
|
||||||
|
signal_handler_signal(sh, signalName.data(), cd);
|
||||||
|
bool result = calldata_bool(cd, "result");
|
||||||
|
calldata_destroy(cd);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineScript::ResolveVariablesToFixedValues()
|
||||||
|
{
|
||||||
|
_textPython.ResolveVariables();
|
||||||
|
_textLUA.ResolveVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string preprocessScriptText(const std::string &text,
|
||||||
|
obs_script_lang language,
|
||||||
|
const std::string &id)
|
||||||
|
{
|
||||||
|
const std::string footerPython =
|
||||||
|
std::string("\n\n"
|
||||||
|
"## AUTO GENERATED ##\n"
|
||||||
|
"def script_load(settings):\n"
|
||||||
|
" def run_wrapper(data):\n"
|
||||||
|
" id = obs.calldata_string(data, \"id\")\n"
|
||||||
|
" if id == \"") +
|
||||||
|
id +
|
||||||
|
"\":\n"
|
||||||
|
" ret = run()\n"
|
||||||
|
" obs.calldata_set_bool(data, \"result\", ret)\n"
|
||||||
|
" sh = obs.obs_get_signal_handler()\n"
|
||||||
|
" obs.signal_handler_connect(sh, \"" +
|
||||||
|
signalName.data() + "\", run_wrapper)\n\n";
|
||||||
|
|
||||||
|
const std::string footerLUA =
|
||||||
|
std::string(
|
||||||
|
"\n\n"
|
||||||
|
"-- AUTO GENERATED --\n"
|
||||||
|
"function script_load(settings)\n"
|
||||||
|
" local run_wrapper = (function(data)\n"
|
||||||
|
" local id = obs.calldata_string(data, \"id\")\n"
|
||||||
|
" if id == \"") +
|
||||||
|
id +
|
||||||
|
"\" then\n"
|
||||||
|
" local ret = run()\n"
|
||||||
|
" obs.calldata_set_bool(data, \"result\", ret)\n"
|
||||||
|
" end\n"
|
||||||
|
" end)\n"
|
||||||
|
" local sh = obs.obs_get_signal_handler()\n"
|
||||||
|
" obs.signal_handler_connect(sh, \"" +
|
||||||
|
signalName.data() +
|
||||||
|
"\" , run_wrapper)\n"
|
||||||
|
"end\n";
|
||||||
|
|
||||||
|
std::string scriptText =
|
||||||
|
language == OBS_SCRIPT_LANG_PYTHON ? footerPython : footerLUA;
|
||||||
|
return text + scriptText;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineScript::SetupFile()
|
||||||
|
{
|
||||||
|
const auto path = GetLUACompatiblePath(_file);
|
||||||
|
_fileId = path;
|
||||||
|
|
||||||
|
if (path.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!QFileInfo(QString::fromStdString(path)).exists()) {
|
||||||
|
const auto text = preprocessScriptText(
|
||||||
|
_language == OBS_SCRIPT_LANG_PYTHON
|
||||||
|
? _defaultPythonScript.data()
|
||||||
|
: _defaultLUAScript.data(),
|
||||||
|
_language, GetID());
|
||||||
|
(void)createScriptFile(_file.c_str(), text.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
_script = std::unique_ptr<obs_script_t, ScriptDeleter>(
|
||||||
|
CreateOBSScript(path.c_str(), nullptr), {});
|
||||||
|
_lastPath = _file;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineScript::SetupInline()
|
||||||
|
{
|
||||||
|
const StringVariable &text =
|
||||||
|
_language == OBS_SCRIPT_LANG_PYTHON ? _textPython : _textLUA;
|
||||||
|
const auto scriptText = preprocessScriptText(text, _language, GetID());
|
||||||
|
|
||||||
|
auto path_ = getScriptTempFilePath(_language);
|
||||||
|
if (!path_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto path = GetLUACompatiblePath(*path_);
|
||||||
|
if (!createScriptFile(path.c_str(), scriptText.c_str())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_script = std::unique_ptr<obs_script_t, ScriptDeleter>(
|
||||||
|
CreateOBSScript(path.c_str(), nullptr), {path});
|
||||||
|
_lastResolvedText = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineScript::Setup()
|
||||||
|
{
|
||||||
|
_script.reset();
|
||||||
|
_lastResolvedText = "";
|
||||||
|
_lastPath = "";
|
||||||
|
|
||||||
|
if (_type == FILE) {
|
||||||
|
SetupFile();
|
||||||
|
} else {
|
||||||
|
SetupInline();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string InlineScript::GetID() const
|
||||||
|
{
|
||||||
|
if (_type == FILE) {
|
||||||
|
return _fileId;
|
||||||
|
}
|
||||||
|
return std::to_string(_instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineScript::ScriptDeleter::operator()(obs_script_t *script)
|
||||||
|
{
|
||||||
|
DestroyOBSScript(script);
|
||||||
|
if (!tempScriptPath.empty()) {
|
||||||
|
cleanupScriptFile(tempScriptPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptEditor::ScriptEditor(QWidget *parent) : VariableTextEdit(parent, 15, 5)
|
||||||
|
{
|
||||||
|
installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptEditor::eventFilter(QObject *obj, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::FocusOut) {
|
||||||
|
emit ScriptChanged();
|
||||||
|
}
|
||||||
|
return QObject::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
76
plugins/scripting/utils/inline-script.hpp
Normal file
76
plugins/scripting/utils/inline-script.hpp
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "obs-script-helpers.hpp"
|
||||||
|
#include "variable-text-edit.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <obs-data.h>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class InlineScript {
|
||||||
|
public:
|
||||||
|
InlineScript();
|
||||||
|
InlineScript(const InlineScript &);
|
||||||
|
|
||||||
|
enum Type { INLINE, FILE };
|
||||||
|
|
||||||
|
void Save(obs_data_t *) const;
|
||||||
|
void Load(obs_data_t *);
|
||||||
|
|
||||||
|
void SetType(Type);
|
||||||
|
Type GetType() const { return _type; }
|
||||||
|
void SetLanguage(obs_script_lang);
|
||||||
|
obs_script_lang GetLanguage() const { return _language; }
|
||||||
|
void SetText(const std::string &);
|
||||||
|
const StringVariable &GetText() const;
|
||||||
|
void SetPath(const std::string &);
|
||||||
|
const std::string &GetPath() const { return _file; }
|
||||||
|
|
||||||
|
bool Run();
|
||||||
|
|
||||||
|
void ResolveVariablesToFixedValues();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Setup();
|
||||||
|
void SetupFile();
|
||||||
|
void SetupInline();
|
||||||
|
std::string GetID() const;
|
||||||
|
|
||||||
|
Type _type = INLINE;
|
||||||
|
obs_script_lang _language = OBS_SCRIPT_LANG_PYTHON;
|
||||||
|
std::string _file;
|
||||||
|
StringVariable _textPython = _defaultPythonScript.data();
|
||||||
|
StringVariable _textLUA = _defaultLUAScript.data();
|
||||||
|
std::string _lastResolvedText;
|
||||||
|
std::string _lastPath;
|
||||||
|
std::string _fileId;
|
||||||
|
const uint64_t _instanceId;
|
||||||
|
|
||||||
|
struct ScriptDeleter {
|
||||||
|
void operator()(obs_script_t *);
|
||||||
|
std::string tempScriptPath;
|
||||||
|
};
|
||||||
|
std::unique_ptr<obs_script_t, ScriptDeleter> _script;
|
||||||
|
|
||||||
|
static std::atomic_uint64_t _instanceIdCounter;
|
||||||
|
static const std::string_view _defaultPythonScript;
|
||||||
|
static const std::string_view _defaultLUAScript;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptEditor : public VariableTextEdit {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ScriptEditor(QWidget *parent);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ScriptChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
72
plugins/scripting/utils/obs-script-helpers.cpp
Normal file
72
plugins/scripting/utils/obs-script-helpers.cpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#include "obs-script-helpers.hpp"
|
||||||
|
#include "log-helper.hpp"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QLibrary>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
static const char *libName =
|
||||||
|
#if defined(WIN32)
|
||||||
|
"obs-scripting.dll";
|
||||||
|
#elif __APPLE__
|
||||||
|
"obs-scripting.dylib";
|
||||||
|
#else
|
||||||
|
"obs-scripting.so";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef obs_script_t *(*obs_script_create_t)(const char *, obs_data_t *);
|
||||||
|
typedef void (*obs_script_destroy_t)(obs_script_t *);
|
||||||
|
|
||||||
|
obs_script_create_t obs_script_create = nullptr;
|
||||||
|
obs_script_destroy_t obs_script_destroy = nullptr;
|
||||||
|
|
||||||
|
static bool setup()
|
||||||
|
{
|
||||||
|
QLibrary scriptingLib(libName);
|
||||||
|
|
||||||
|
obs_script_create =
|
||||||
|
(obs_script_create_t)scriptingLib.resolve("obs_script_create");
|
||||||
|
if (!obs_script_create) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"could not resolve obs_script_create symbol!");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_script_destroy = (obs_script_destroy_t)scriptingLib.resolve(
|
||||||
|
"obs_script_destroy");
|
||||||
|
if (!obs_script_destroy) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"could not resolve obs_script_destroy symbol!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static bool setupDone = setup();
|
||||||
|
|
||||||
|
obs_script_t *CreateOBSScript(const char *path, obs_data_t *settings)
|
||||||
|
{
|
||||||
|
if (!obs_script_create) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return obs_script_create(path, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroyOBSScript(obs_script_t *script)
|
||||||
|
{
|
||||||
|
if (!obs_script_destroy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
obs_script_destroy(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetLUACompatiblePath(const std::string &path)
|
||||||
|
{
|
||||||
|
// Can't use settingsFile here as LUA will complain if Windows style
|
||||||
|
// paths (C:\some\path) are used.
|
||||||
|
// QFileInfo::absoluteFilePath will convert those paths so LUA won't
|
||||||
|
// complain. (C:/some/path)
|
||||||
|
const QFileInfo fileInfo(QString::fromStdString(path));
|
||||||
|
return fileInfo.absoluteFilePath().toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
23
plugins/scripting/utils/obs-script-helpers.hpp
Normal file
23
plugins/scripting/utils/obs-script-helpers.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <obs-data.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Based on obs-scripting.h
|
||||||
|
|
||||||
|
struct obs_script;
|
||||||
|
typedef struct obs_script obs_script_t;
|
||||||
|
|
||||||
|
enum obs_script_lang {
|
||||||
|
OBS_SCRIPT_LANG_UNKNOWN,
|
||||||
|
OBS_SCRIPT_LANG_LUA,
|
||||||
|
OBS_SCRIPT_LANG_PYTHON
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
obs_script_t *CreateOBSScript(const char *path, obs_data_t *settings);
|
||||||
|
void DestroyOBSScript(obs_script_t *script);
|
||||||
|
std::string GetLUACompatiblePath(const std::string &path);
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
Reference in New Issue
Block a user