mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-21 17:34:57 -05:00
Add option to match changed content
This commit is contained in:
parent
51d121bda2
commit
7ada858046
|
|
@ -195,12 +195,16 @@ AdvSceneSwitcher.condition.window.entry.text="{{checkText}}Window contains text{
|
|||
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.type.match="Matches"
|
||||
AdvSceneSwitcher.condition.file.type.contentChange="Content changed"
|
||||
AdvSceneSwitcher.condition.file.type.dateChange="Modification date changed"
|
||||
AdvSceneSwitcher.condition.file.type.changesMatch="Changed lines match"
|
||||
AdvSceneSwitcher.condition.file.remote="Remote file"
|
||||
AdvSceneSwitcher.condition.file.local="Local file"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
|
||||
AdvSceneSwitcher.condition.file.changeMatchType.any="Any lines"
|
||||
AdvSceneSwitcher.condition.file.changeMatchType.added="Added lines"
|
||||
AdvSceneSwitcher.condition.file.changeMatchType.removed="Removed lines"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{changeMatchType}}{{useRegex}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.media="Media"
|
||||
AdvSceneSwitcher.condition.media.source="Source"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
|
||||
#include <QTextStream>
|
||||
#include <QFileDialog>
|
||||
#include <regex>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
|
||||
namespace advss {
|
||||
|
||||
|
|
@ -41,70 +42,63 @@ static std::string getRemoteData(std::string &url)
|
|||
return readBuffer;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::MatchFileContent(QString &filedata) const
|
||||
static bool stringsMatch(const QString &text, const std::string &pattern,
|
||||
const RegexConfig ®ex)
|
||||
{
|
||||
if (_regex.Enabled()) {
|
||||
auto expr = _regex.GetRegularExpression(_text);
|
||||
if (!expr.isValid()) {
|
||||
return false;
|
||||
}
|
||||
auto match = expr.match(filedata);
|
||||
return match.hasMatch();
|
||||
auto qpattern = QString::fromStdString(pattern);
|
||||
if (regex.Enabled()) {
|
||||
return regex.Matches(text, qpattern);
|
||||
}
|
||||
|
||||
QString text = QString::fromStdString(_text);
|
||||
return CompareIgnoringLineEnding(text, filedata);
|
||||
auto textCopy = text;
|
||||
return CompareIgnoringLineEnding(qpattern, textCopy);
|
||||
}
|
||||
|
||||
bool MacroConditionFile::CheckRemoteFileContent()
|
||||
static std::optional<QString> getFileContent(MacroConditionFile::FileType type,
|
||||
const std::string &filePath)
|
||||
{
|
||||
std::string path = _file;
|
||||
std::string data = getRemoteData(path);
|
||||
SetVariableValue(data);
|
||||
QString qdata = QString::fromStdString(data);
|
||||
return MatchFileContent(qdata);
|
||||
QString filedata;
|
||||
switch (type) {
|
||||
case MacroConditionFile::FileType::LOCAL: {
|
||||
std::string path = filePath;
|
||||
QFile file(QString::fromStdString(path));
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return {};
|
||||
}
|
||||
filedata = QTextStream(&file).readAll();
|
||||
file.close();
|
||||
} break;
|
||||
case MacroConditionFile::FileType::REMOTE: {
|
||||
std::string path = filePath;
|
||||
std::string data = getRemoteData(path);
|
||||
filedata = QString::fromStdString(data);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return filedata;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::CheckLocalFileContent()
|
||||
bool MacroConditionFile::CheckFileContentMatch()
|
||||
{
|
||||
QFile file(QString::fromStdString(_file));
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
auto fileData = getFileContent(_fileType, _file);
|
||||
if (!fileData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString filedata = QTextStream(&file).readAll();
|
||||
SetVariableValue(filedata.toStdString());
|
||||
bool match = MatchFileContent(filedata);
|
||||
|
||||
file.close();
|
||||
const bool match = stringsMatch(*fileData, _text, _regex);
|
||||
SetVariableValue(fileData->toStdString());
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::CheckChangeContent()
|
||||
{
|
||||
QString filedata;
|
||||
switch (_fileType) {
|
||||
case FileType::LOCAL: {
|
||||
std::string path = _file;
|
||||
QFile file(QString::fromStdString(path));
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return false;
|
||||
}
|
||||
filedata = QTextStream(&file).readAll();
|
||||
file.close();
|
||||
} break;
|
||||
case FileType::REMOTE: {
|
||||
std::string path = _file;
|
||||
std::string data = getRemoteData(path);
|
||||
QString filedata = QString::fromStdString(data);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
auto filedata = getFileContent(_fileType, _file);
|
||||
if (!filedata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t newHash = strHash(filedata.toUtf8().constData());
|
||||
size_t newHash = strHash(filedata->toUtf8().constData());
|
||||
const bool contentChanged = newHash != _lastHash;
|
||||
_lastHash = newHash;
|
||||
SetVariableValue(contentChanged ? "true" : "false");
|
||||
return contentChanged;
|
||||
}
|
||||
|
||||
|
|
@ -119,35 +113,94 @@ bool MacroConditionFile::CheckChangeDate()
|
|||
SetVariableValue(newLastMod.toString().toStdString());
|
||||
const bool dateChanged = _lastMod != newLastMod;
|
||||
_lastMod = newLastMod;
|
||||
SetVariableValue(dateChanged ? "true" : "false");
|
||||
return dateChanged;
|
||||
}
|
||||
|
||||
static QStringList splitLines(const QString &string)
|
||||
{
|
||||
static auto regex = QRegularExpression("\n|\r\n|\r");
|
||||
return string.split(regex);
|
||||
}
|
||||
|
||||
bool MacroConditionFile::CheckChangedMatch()
|
||||
{
|
||||
static bool setupDone = false;
|
||||
if (!setupDone) {
|
||||
auto content = getFileContent(_fileType, _file);
|
||||
if (!content) {
|
||||
return false;
|
||||
}
|
||||
_previousContent = splitLines(*content);
|
||||
setupDone = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto currentFileContent = getFileContent(_fileType, _file);
|
||||
if (!currentFileContent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto currentContent = splitLines(*currentFileContent);
|
||||
auto newContent = currentContent;
|
||||
|
||||
QStringList addedLines;
|
||||
QStringList removedLines;
|
||||
|
||||
for (const auto &line : newContent) {
|
||||
if (auto it = std::find(_previousContent.begin(),
|
||||
_previousContent.end(),
|
||||
line) == _previousContent.end()) {
|
||||
addedLines.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &line : _previousContent) {
|
||||
if (std::find(newContent.begin(), newContent.end(), line) ==
|
||||
newContent.end()) {
|
||||
removedLines.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
_previousContent = currentContent;
|
||||
|
||||
if (_changeMatchType == ChangeMatchType::ADDED ||
|
||||
_changeMatchType == ChangeMatchType::ANY) {
|
||||
for (const auto &line : addedLines) {
|
||||
if (stringsMatch(line, _text, _regex)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_changeMatchType == ChangeMatchType::REMOVED ||
|
||||
_changeMatchType == ChangeMatchType::ANY) {
|
||||
for (const auto &line : removedLines) {
|
||||
if (stringsMatch(line, _text, _regex)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::CheckCondition()
|
||||
{
|
||||
bool ret = false;
|
||||
switch (_condition) {
|
||||
case MacroConditionFile::ConditionType::MATCH:
|
||||
if (_fileType == FileType::REMOTE) {
|
||||
ret = CheckRemoteFileContent();
|
||||
break;
|
||||
}
|
||||
ret = CheckLocalFileContent();
|
||||
break;
|
||||
return CheckFileContentMatch();
|
||||
case MacroConditionFile::ConditionType::CONTENT_CHANGE:
|
||||
ret = CheckChangeContent();
|
||||
break;
|
||||
return CheckChangeContent();
|
||||
case MacroConditionFile::ConditionType::DATE_CHANGE:
|
||||
ret = CheckChangeDate();
|
||||
break;
|
||||
return CheckChangeDate();
|
||||
case MacroConditionFile::ConditionType::CHANGES_MATCH:
|
||||
return CheckChangedMatch();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetVariableValue().empty()) {
|
||||
SetVariableValue(ret ? "true" : "false");
|
||||
}
|
||||
|
||||
return ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::Save(obs_data_t *obj) const
|
||||
|
|
@ -157,6 +210,8 @@ bool MacroConditionFile::Save(obs_data_t *obj) const
|
|||
_file.Save(obj, "file");
|
||||
_text.Save(obj, "text");
|
||||
obs_data_set_int(obj, "fileType", static_cast<int>(_fileType));
|
||||
obs_data_set_int(obj, "changeMatchType",
|
||||
static_cast<int>(_changeMatchType));
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
return true;
|
||||
}
|
||||
|
|
@ -173,6 +228,8 @@ bool MacroConditionFile::Load(obs_data_t *obj)
|
|||
_file.Load(obj, "file");
|
||||
_text.Load(obj, "text");
|
||||
_fileType = static_cast<FileType>(obs_data_get_int(obj, "fileType"));
|
||||
_changeMatchType = static_cast<ChangeMatchType>(
|
||||
obs_data_get_int(obj, "changeMatchType"));
|
||||
_condition =
|
||||
static_cast<ConditionType>(obs_data_get_int(obj, "condition"));
|
||||
return true;
|
||||
|
|
@ -185,25 +242,66 @@ std::string MacroConditionFile::GetShortDesc() const
|
|||
|
||||
static void populateFileTypes(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.condition.file.local"));
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.condition.file.remote"));
|
||||
static constexpr std::array<
|
||||
std::tuple<MacroConditionFile::FileType, std::string_view>, 2>
|
||||
fileTypes = {{{MacroConditionFile::FileType::LOCAL,
|
||||
"AdvSceneSwitcher.condition.file.local"},
|
||||
{MacroConditionFile::FileType::REMOTE,
|
||||
"AdvSceneSwitcher.condition.file.remote"}}};
|
||||
|
||||
for (const auto &[type, name] : fileTypes) {
|
||||
list->addItem(obs_module_text(name.data()),
|
||||
static_cast<int>(type));
|
||||
}
|
||||
}
|
||||
|
||||
static void populateConditions(QComboBox *list)
|
||||
{
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.condition.file.type.match"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.file.type.contentChange"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.file.type.dateChange"));
|
||||
static constexpr std::array<
|
||||
std::tuple<MacroConditionFile::ConditionType, std::string_view>,
|
||||
4>
|
||||
conditionTypes = {{
|
||||
{MacroConditionFile::ConditionType::MATCH,
|
||||
"AdvSceneSwitcher.condition.file.type.match"},
|
||||
{MacroConditionFile::ConditionType::CONTENT_CHANGE,
|
||||
"AdvSceneSwitcher.condition.file.type.contentChange"},
|
||||
{MacroConditionFile::ConditionType::DATE_CHANGE,
|
||||
"AdvSceneSwitcher.condition.file.type.dateChange"},
|
||||
{MacroConditionFile::ConditionType::CHANGES_MATCH,
|
||||
"AdvSceneSwitcher.condition.file.type.changesMatch"},
|
||||
}};
|
||||
|
||||
for (const auto &[type, name] : conditionTypes) {
|
||||
list->addItem(obs_module_text(name.data()),
|
||||
static_cast<int>(type));
|
||||
}
|
||||
}
|
||||
|
||||
static void populateChangeMatchTypes(QComboBox *list)
|
||||
{
|
||||
static constexpr std::array<
|
||||
std::tuple<MacroConditionFile::ChangeMatchType, std::string_view>,
|
||||
3>
|
||||
matchTypes = {{
|
||||
{MacroConditionFile::ChangeMatchType::ANY,
|
||||
"AdvSceneSwitcher.condition.file.changeMatchType.any"},
|
||||
{MacroConditionFile::ChangeMatchType::ADDED,
|
||||
"AdvSceneSwitcher.condition.file.changeMatchType.added"},
|
||||
{MacroConditionFile::ChangeMatchType::REMOVED,
|
||||
"AdvSceneSwitcher.condition.file.changeMatchType.removed"},
|
||||
}};
|
||||
|
||||
for (const auto &[type, name] : matchTypes) {
|
||||
list->addItem(obs_module_text(name.data()),
|
||||
static_cast<int>(type));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionFileEdit::MacroConditionFileEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionFile> entryData)
|
||||
: QWidget(parent),
|
||||
_fileTypes(new QComboBox()),
|
||||
_changeMatchTypes(new QComboBox()),
|
||||
_conditions(new QComboBox()),
|
||||
_filePath(new FileSelection()),
|
||||
_matchText(new VariableTextEdit(this)),
|
||||
|
|
@ -211,9 +309,12 @@ MacroConditionFileEdit::MacroConditionFileEdit(
|
|||
{
|
||||
populateFileTypes(_fileTypes);
|
||||
populateConditions(_conditions);
|
||||
populateChangeMatchTypes(_changeMatchTypes);
|
||||
|
||||
QWidget::connect(_fileTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(FileTypeChanged(int)));
|
||||
QWidget::connect(_changeMatchTypes, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(ChangeMatchTypeChanged(int)));
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_filePath, SIGNAL(PathChanged(const QString &)), this,
|
||||
|
|
@ -224,8 +325,11 @@ MacroConditionFileEdit::MacroConditionFileEdit(
|
|||
SLOT(RegexChanged(RegexConfig)));
|
||||
|
||||
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{fileType}}", _fileTypes}, {"{{conditions}}", _conditions},
|
||||
{"{{filePath}}", _filePath}, {"{{matchText}}", _matchText},
|
||||
{"{{fileType}}", _fileTypes},
|
||||
{"{{changeMatchType}}", _changeMatchTypes},
|
||||
{"{{conditions}}", _conditions},
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{matchText}}", _matchText},
|
||||
{"{{useRegex}}", _regex},
|
||||
};
|
||||
|
||||
|
|
@ -256,8 +360,12 @@ void MacroConditionFileEdit::UpdateEntryData()
|
|||
return;
|
||||
}
|
||||
|
||||
_fileTypes->setCurrentIndex(static_cast<int>(_entryData->_fileType));
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_fileTypes->setCurrentIndex(
|
||||
_fileTypes->findData(static_cast<int>(_entryData->_fileType)));
|
||||
_conditions->setCurrentIndex(_conditions->findData(
|
||||
static_cast<int>(_entryData->_condition)));
|
||||
_changeMatchTypes->setCurrentIndex(_changeMatchTypes->findData(
|
||||
static_cast<int>(_entryData->_changeMatchType)));
|
||||
_filePath->SetPath(_entryData->_file);
|
||||
_matchText->setPlainText(_entryData->_text);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
|
|
@ -271,13 +379,27 @@ void MacroConditionFileEdit::FileTypeChanged(int index)
|
|||
return;
|
||||
}
|
||||
|
||||
auto type = static_cast<MacroConditionFile::FileType>(index);
|
||||
auto type = static_cast<MacroConditionFile::FileType>(
|
||||
_fileTypes->itemData(index).toInt());
|
||||
_filePath->Button()->setEnabled(type ==
|
||||
MacroConditionFile::FileType::LOCAL);
|
||||
auto lock = LockContext();
|
||||
_entryData->_fileType = type;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::ChangeMatchTypeChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_changeMatchType =
|
||||
static_cast<MacroConditionFile::ChangeMatchType>(
|
||||
_changeMatchTypes->itemData(index).toInt());
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::ConditionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
|
|
@ -285,8 +407,8 @@ void MacroConditionFileEdit::ConditionChanged(int index)
|
|||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_condition =
|
||||
static_cast<MacroConditionFile::ConditionType>(index);
|
||||
_entryData->_condition = static_cast<MacroConditionFile::ConditionType>(
|
||||
_conditions->itemData(index).toInt());
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
|
|
@ -333,10 +455,19 @@ void MacroConditionFileEdit::SetWidgetVisibility()
|
|||
return;
|
||||
}
|
||||
|
||||
_matchText->setVisible(_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH);
|
||||
_regex->setVisible(_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH);
|
||||
_changeMatchTypes->setVisible(
|
||||
_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::CHANGES_MATCH);
|
||||
_matchText->setVisible(
|
||||
_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH ||
|
||||
_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::CHANGES_MATCH);
|
||||
_regex->setVisible(
|
||||
_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH ||
|
||||
_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::CHANGES_MATCH);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include <QStringList>
|
||||
|
||||
namespace advss {
|
||||
|
||||
|
|
@ -35,23 +36,31 @@ public:
|
|||
MATCH,
|
||||
CONTENT_CHANGE,
|
||||
DATE_CHANGE,
|
||||
CHANGES_MATCH,
|
||||
};
|
||||
|
||||
enum class ChangeMatchType {
|
||||
ANY,
|
||||
ADDED,
|
||||
REMOVED,
|
||||
};
|
||||
|
||||
StringVariable _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
StringVariable _text = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
FileType _fileType = FileType::LOCAL;
|
||||
ChangeMatchType _changeMatchType = ChangeMatchType::ANY;
|
||||
ConditionType _condition = ConditionType::MATCH;
|
||||
RegexConfig _regex;
|
||||
|
||||
private:
|
||||
bool MatchFileContent(QString &filedata) const;
|
||||
bool CheckRemoteFileContent();
|
||||
bool CheckLocalFileContent();
|
||||
bool CheckFileContentMatch();
|
||||
bool CheckChangeContent();
|
||||
bool CheckChangeDate();
|
||||
bool CheckChangedMatch();
|
||||
|
||||
QDateTime _lastMod;
|
||||
size_t _lastHash = 0;
|
||||
QStringList _previousContent;
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
|
@ -74,6 +83,7 @@ public:
|
|||
|
||||
private slots:
|
||||
void FileTypeChanged(int index);
|
||||
void ChangeMatchTypeChanged(int index);
|
||||
void ConditionChanged(int index);
|
||||
void PathChanged(const QString &text);
|
||||
void MatchTextChanged();
|
||||
|
|
@ -83,6 +93,7 @@ signals:
|
|||
|
||||
protected:
|
||||
QComboBox *_fileTypes;
|
||||
QComboBox *_changeMatchTypes;
|
||||
QComboBox *_conditions;
|
||||
FileSelection *_filePath;
|
||||
VariableTextEdit *_matchText;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user