diff --git a/data/locale/de-DE.ini b/data/locale/de-DE.ini index a63569591..e0a9b69d7 100644 --- a/data/locale/de-DE.ini +++ b/data/locale/de-DE.ini @@ -145,11 +145,7 @@ AdvSceneSwitcher.condition.file="Datei" AdvSceneSwitcher.condition.file.type.match="entspricht" AdvSceneSwitcher.condition.file.type.contentChange="Inhalt geändert" AdvSceneSwitcher.condition.file.type.dateChange="Änderungsdatum geändert" -AdvSceneSwitcher.condition.file.remote="Entfernte Datei" -AdvSceneSwitcher.condition.file.local="Lokale Datei" -AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{useRegex}}" -AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" -AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}" +AdvSceneSwitcher.condition.file.layout="{{filePath}}{{conditions}}{{regex}}" AdvSceneSwitcher.condition.media="Medien" AdvSceneSwitcher.condition.media.source="Quelle" AdvSceneSwitcher.condition.media.anyOnScene="Beliebige Medienquelle in" diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 6d842c2fc..5e3cff9a7 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -368,11 +368,7 @@ AdvSceneSwitcher.condition.file.type.dateChange="modification date changed" AdvSceneSwitcher.condition.file.type.exists="exists" AdvSceneSwitcher.condition.file.type.isFile="is a file" AdvSceneSwitcher.condition.file.type.isFolder="is a folder" -AdvSceneSwitcher.condition.file.remote="Remote file" -AdvSceneSwitcher.condition.file.local="Local file" -AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{useRegex}}" -AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" -AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}" +AdvSceneSwitcher.condition.file.layout="{{filePath}}{{conditions}}{{regex}}" AdvSceneSwitcher.condition.media="Media" AdvSceneSwitcher.condition.media.checkType.state="State matches" AdvSceneSwitcher.condition.media.checkType.time="Time restriction matches" diff --git a/data/locale/es-ES.ini b/data/locale/es-ES.ini index 4fc8ce515..515eaaff2 100644 --- a/data/locale/es-ES.ini +++ b/data/locale/es-ES.ini @@ -117,9 +117,7 @@ AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Durante la tra AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="Durante la transición, verifique la escena de origen de la transición" AdvSceneSwitcher.condition.window="Ventana" AdvSceneSwitcher.condition.file="Archivo" -AdvSceneSwitcher.condition.file.entry.line1="Contenido de{{fileType}}{{filePath}}{{conditions}}{{useRegex}}" -AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" -AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}" +AdvSceneSwitcher.condition.file.layout="Contenido de{{filePath}}{{conditions}}{{regex}}" AdvSceneSwitcher.condition.media="Medios" AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activada" AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas" diff --git a/data/locale/fr-FR.ini b/data/locale/fr-FR.ini index ebed6ce5c..6af447bce 100644 --- a/data/locale/fr-FR.ini +++ b/data/locale/fr-FR.ini @@ -187,8 +187,6 @@ AdvSceneSwitcher.condition.file="Fichier" AdvSceneSwitcher.condition.file.type.match="correspond à" AdvSceneSwitcher.condition.file.type.contentChange="a changé de contenu" AdvSceneSwitcher.condition.file.type.dateChange="a changé de date de modification" -AdvSceneSwitcher.condition.file.remote="Fichier distant" -AdvSceneSwitcher.condition.file.local="Fichier local" AdvSceneSwitcher.condition.media="Média" AdvSceneSwitcher.condition.media.source="Source" AdvSceneSwitcher.condition.media.anyOnScene="Toute source média sur la scène" diff --git a/data/locale/ja-JP.ini b/data/locale/ja-JP.ini index 31eb6bdd8..f3d5e2331 100644 --- a/data/locale/ja-JP.ini +++ b/data/locale/ja-JP.ini @@ -331,14 +331,6 @@ AdvSceneSwitcher.condition.window.entry.text="{{checkText}}ウィンドウにテ AdvSceneSwitcher.condition.window.entry.text.note="このオプションは、ウィンドウに表示されているすべてのテキストに対して機能するとは限りません。\nその場合は、代わりにビデオ条件 OCR チェックの使用を検討してください。" AdvSceneSwitcher.condition.window.entry.currentFocus="現在のフォーカスウィンドウ:{{focusWindow}}" AdvSceneSwitcher.condition.file="ファイル" -; AdvSceneSwitcher.condition.file.type.match="matches" -AdvSceneSwitcher.condition.file.type.contentChange="内容変更しました" -AdvSceneSwitcher.condition.file.type.dateChange="更新日変更" -AdvSceneSwitcher.condition.file.remote="リモートファイル" -AdvSceneSwitcher.condition.file.local="ローカルファイル" -; AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{useRegex}}" -; AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" -; AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}" ; AdvSceneSwitcher.condition.media="Media" AdvSceneSwitcher.condition.media.checkType.state="状態が一致" AdvSceneSwitcher.condition.media.checkType.time="時間が一致" diff --git a/data/locale/pt-BR.ini b/data/locale/pt-BR.ini index 2ac561ca8..92c29e041 100644 --- a/data/locale/pt-BR.ini +++ b/data/locale/pt-BR.ini @@ -295,11 +295,6 @@ AdvSceneSwitcher.condition.file="Arquivo" AdvSceneSwitcher.condition.file.type.match="corresponde" AdvSceneSwitcher.condition.file.type.contentChange="conteúdo mudou" AdvSceneSwitcher.condition.file.type.dateChange="data de modificação mudou" -AdvSceneSwitcher.condition.file.remote="Arquivo remoto" -AdvSceneSwitcher.condition.file.local="Arquivo local" -AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{useRegex}}" -AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" -AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}" AdvSceneSwitcher.condition.media="Mídia" AdvSceneSwitcher.condition.media.checkType.state="Estado corresponde" AdvSceneSwitcher.condition.media.checkType.time="Restrição de tempo corresponde" diff --git a/data/locale/ru-RU.ini b/data/locale/ru-RU.ini index e1c163c89..e2273057f 100644 --- a/data/locale/ru-RU.ini +++ b/data/locale/ru-RU.ini @@ -85,8 +85,6 @@ AdvSceneSwitcher.condition.scene.type.current="Текущий" AdvSceneSwitcher.condition.scene.type.previous="Предыдущий" AdvSceneSwitcher.condition.window="Окно" AdvSceneSwitcher.condition.file="Файл" -AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" -AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}" AdvSceneSwitcher.condition.media="Медиа" AdvSceneSwitcher.condition.video="Видео" AdvSceneSwitcher.condition.video.condition.match="точно соответствует" diff --git a/data/locale/tr-TR.ini b/data/locale/tr-TR.ini index ff849725b..f1c590951 100644 --- a/data/locale/tr-TR.ini +++ b/data/locale/tr-TR.ini @@ -107,9 +107,7 @@ AdvSceneSwitcher.condition.scene.type.notChanged="Sahne değişmedi" AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Geçiş hedefi sahnesi için geçiş kontrolü sırasında" AdvSceneSwitcher.condition.window="Pencere" AdvSceneSwitcher.condition.file="Dosya" -AdvSceneSwitcher.condition.file.entry.line1="İçerik{{fileType}}{{filePath}}{{conditions}}{{useRegex}}" -AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" -AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}" +AdvSceneSwitcher.condition.file.layout="İçerik{{filePath}}{{conditions}}{{regex}}" AdvSceneSwitcher.condition.media="Medya" AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı" AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları " diff --git a/data/locale/zh-CN.ini b/data/locale/zh-CN.ini index 29dabc860..7918c560d 100644 --- a/data/locale/zh-CN.ini +++ b/data/locale/zh-CN.ini @@ -318,11 +318,7 @@ AdvSceneSwitcher.condition.file="文件" AdvSceneSwitcher.condition.file.type.match="内容匹配" AdvSceneSwitcher.condition.file.type.contentChange="内容发生改变" AdvSceneSwitcher.condition.file.type.dateChange="修改日期发生改变" -AdvSceneSwitcher.condition.file.remote="网络文件" -AdvSceneSwitcher.condition.file.local="本地文件" -AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{useRegex}}" -AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" -AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}" +AdvSceneSwitcher.condition.file.layout="{{filePath}}{{conditions}}{{regex}}" AdvSceneSwitcher.condition.media="媒体" AdvSceneSwitcher.condition.media.checkType.state="状态匹配" AdvSceneSwitcher.condition.media.checkType.time="时间限制匹配" diff --git a/lib/utils/temp-variable.cpp b/lib/utils/temp-variable.cpp index 15005af7e..f429fa737 100644 --- a/lib/utils/temp-variable.cpp +++ b/lib/utils/temp-variable.cpp @@ -723,13 +723,12 @@ TempVarSignalManager *TempVarSignalManager::Instance() void NotifyUIAboutTempVarChange(MacroSegment *segment) { IncrementTempVarInUseGeneration(); - obs_queue_task( - OBS_TASK_UI, + QueueUITask( [](void *segment) { TempVarSignalManager::Instance()->SegmentTempVarsChanged( (MacroSegment *)segment); }, - segment, false); + segment); } } // namespace advss diff --git a/plugins/base/macro-condition-file.cpp b/plugins/base/macro-condition-file.cpp index 2a2311e74..c8642c75f 100644 --- a/plugins/base/macro-condition-file.cpp +++ b/plugins/base/macro-condition-file.cpp @@ -1,12 +1,10 @@ #include "macro-condition-file.hpp" -#include "curl-helper.hpp" #include "layout-helpers.hpp" #include "plugin-state-helpers.hpp" #include "utility.hpp" -#include #include -#include +#include namespace advss { @@ -19,29 +17,6 @@ bool MacroConditionFile::_registered = MacroConditionFactory::Register( static std::hash strHash; -static size_t WriteCallback(void *contents, size_t size, size_t nmemb, - void *userp) -{ - ((std::string *)userp)->append((char *)contents, size * nmemb); - return size * nmemb; -} - -static std::string getRemoteData(std::string &url) -{ - std::string readBuffer; - CurlHelper::SetOpt(CURLOPT_URL, url.c_str()); - CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, WriteCallback); - CurlHelper::SetOpt(CURLOPT_WRITEDATA, &readBuffer); - // Set timeout to at least one second - int timeout = GetIntervalValue() / 1000; - if (timeout == 0) { - timeout = 1; - } - CurlHelper::SetOpt(CURLOPT_TIMEOUT, 1); - CurlHelper::Perform(); - return readBuffer; -} - void MacroConditionFile::SetCondition(Condition condition) { _condition = condition; @@ -50,14 +25,6 @@ void MacroConditionFile::SetCondition(Condition condition) bool MacroConditionFile::MatchFileContent(QString &filedata) { - if (_onlyMatchIfChanged) { - size_t newHash = strHash(filedata.toUtf8().constData()); - if (newHash == _lastHash) { - return false; - } - _lastHash = newHash; - } - if (_regex.Enabled()) { return _regex.Matches(filedata, QString::fromStdString(_text)); } @@ -66,31 +33,13 @@ bool MacroConditionFile::MatchFileContent(QString &filedata) return CompareIgnoringLineEnding(text, filedata); } -bool MacroConditionFile::CheckRemoteFileContent() -{ - std::string path = _file; - std::string data = getRemoteData(path); - SetVariableValue(data); - SetTempVarValue("content", data); - QString qdata = QString::fromStdString(data); - return MatchFileContent(qdata); -} - -bool MacroConditionFile::CheckLocalFileContent() +bool MacroConditionFile::CheckFileContent() { QFile file(QString::fromStdString(_file)); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { return false; } - if (_useTime) { - QDateTime newLastMod = QFileInfo(file).lastModified(); - if (_lastMod == newLastMod) { - return false; - } - _lastMod = newLastMod; - } - QString filedata = QTextStream(&file).readAll(); SetVariableValue(filedata.toStdString()); SetTempVarValue("content", filedata.toStdString()); @@ -103,42 +52,30 @@ bool MacroConditionFile::CheckLocalFileContent() 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; + + std::string path = _file; + QFile file(QString::fromStdString(path)); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return false; } + filedata = QTextStream(&file).readAll(); + file.close(); SetTempVarValue("content", filedata.toStdString()); size_t newHash = strHash(filedata.toUtf8().constData()); - const bool contentChanged = newHash != _lastHash; + const bool contentChanged = !_firstContentCheck && + (newHash != _lastHash); _lastHash = newHash; + _firstContentCheck = false; return contentChanged; } bool MacroConditionFile::CheckChangeDate() { - if (_fileType == FileType::REMOTE) { - return false; - } - QFile file(QString::fromStdString(_file)); QDateTime newLastMod = QFileInfo(file).lastModified(); SetVariableValue(newLastMod.toString().toStdString()); - const bool dateChanged = _lastMod != newLastMod; + const bool dateChanged = _lastMod.isValid() && (_lastMod != newLastMod); _lastMod = newLastMod; SetTempVarValue("date", newLastMod.toString(Qt::ISODate).toStdString()); return dateChanged; @@ -157,10 +94,6 @@ void MacroConditionFile::SetupTempVars() "AdvSceneSwitcher.tempVar.file.content")); } - if (_fileType == FileType::REMOTE) { - return; - } - AddTempvar( "basename", obs_module_text("AdvSceneSwitcher.tempVar.file.basename"), @@ -199,11 +132,7 @@ bool MacroConditionFile::CheckCondition() bool ret = false; switch (_condition) { case Condition::MATCH: - if (_fileType == FileType::REMOTE) { - ret = CheckRemoteFileContent(); - break; - } - ret = CheckLocalFileContent(); + ret = CheckFileContent(); break; case Condition::CONTENT_CHANGE: ret = CheckChangeContent(); @@ -264,10 +193,7 @@ bool MacroConditionFile::Save(obs_data_t *obj) const _regex.Save(obj); _file.Save(obj, "file"); _text.Save(obj, "text"); - obs_data_set_int(obj, "fileType", static_cast(_fileType)); obs_data_set_int(obj, "condition", static_cast(_condition)); - obs_data_set_bool(obj, "useTime", _useTime); - obs_data_set_bool(obj, "onlyMatchIfChanged", _onlyMatchIfChanged); return true; } @@ -275,18 +201,10 @@ bool MacroConditionFile::Load(obs_data_t *obj) { MacroCondition::Load(obj); _regex.Load(obj); - // TODO: remove in future version - if (obs_data_has_user_value(obj, "useRegex")) { - _regex.CreateBackwardsCompatibleRegex( - obs_data_get_bool(obj, "useRegex")); - } _file.Load(obj, "file"); _text.Load(obj, "text"); - _fileType = static_cast(obs_data_get_int(obj, "fileType")); SetCondition( static_cast(obs_data_get_int(obj, "condition"))); - _useTime = obs_data_get_bool(obj, "useTime"); - _onlyMatchIfChanged = obs_data_get_bool(obj, "onlyMatchIfChanged"); return true; } @@ -295,13 +213,6 @@ std::string MacroConditionFile::GetShortDesc() const return _file.UnresolvedValue(); } -static void populateFileTypes(QComboBox *list) -{ - list->addItem(obs_module_text("AdvSceneSwitcher.condition.file.local")); - list->addItem( - obs_module_text("AdvSceneSwitcher.condition.file.remote")); -} - static void populateConditions(QComboBox *list) { list->addItem( @@ -321,21 +232,13 @@ static void populateConditions(QComboBox *list) MacroConditionFileEdit::MacroConditionFileEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), - _fileTypes(new QComboBox()), _conditions(new QComboBox()), _filePath(new FileSelection()), _matchText(new VariableTextEdit(this)), - _regex(new RegexConfigWidget(parent)), - _checkModificationDate(new QCheckBox(obs_module_text( - "AdvSceneSwitcher.fileTab.checkfileContentTime"))), - _checkFileContent(new QCheckBox( - obs_module_text("AdvSceneSwitcher.fileTab.checkfileContent"))) + _regex(new RegexConfigWidget(parent)) { - populateFileTypes(_fileTypes); populateConditions(_conditions); - QWidget::connect(_fileTypes, SIGNAL(currentIndexChanged(int)), this, - SLOT(FileTypeChanged(int))); QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this, SLOT(ConditionChanged(int))); QWidget::connect(_filePath, SIGNAL(PathChanged(const QString &)), this, @@ -345,42 +248,20 @@ MacroConditionFileEdit::MacroConditionFileEdit( QWidget::connect(_regex, SIGNAL(RegexConfigChanged(const RegexConfig &)), this, SLOT(RegexChanged(const RegexConfig &))); - QWidget::connect(_checkModificationDate, SIGNAL(stateChanged(int)), - this, SLOT(CheckModificationDateChanged(int))); - QWidget::connect(_checkFileContent, SIGNAL(stateChanged(int)), this, - SLOT(OnlyMatchIfChangedChanged(int))); - - std::unordered_map widgetPlaceholders = { - {"{{fileType}}", _fileTypes}, - {"{{conditions}}", _conditions}, - {"{{filePath}}", _filePath}, - {"{{matchText}}", _matchText}, - {"{{useRegex}}", _regex}, - {"{{checkModificationDate}}", _checkModificationDate}, - {"{{checkFileContent}}", _checkFileContent}, - }; - - QVBoxLayout *mainLayout = new QVBoxLayout; - QHBoxLayout *line1Layout = new QHBoxLayout; - QHBoxLayout *line2Layout = new QHBoxLayout; - QHBoxLayout *line3Layout = new QHBoxLayout; - line1Layout->setContentsMargins(0, 0, 0, 0); - line2Layout->setContentsMargins(0, 0, 0, 0); - line3Layout->setContentsMargins(0, 0, 0, 0); - PlaceWidgets( - obs_module_text("AdvSceneSwitcher.condition.file.entry.line1"), - line1Layout, widgetPlaceholders); - PlaceWidgets( - obs_module_text("AdvSceneSwitcher.condition.file.entry.line2"), - line2Layout, widgetPlaceholders, false); - PlaceWidgets( - obs_module_text("AdvSceneSwitcher.condition.file.entry.line3"), - line3Layout, widgetPlaceholders); - mainLayout->addLayout(line1Layout); - mainLayout->addLayout(line2Layout); - mainLayout->addLayout(line3Layout); - - setLayout(mainLayout); + + auto widgetLayout = new QHBoxLayout; + widgetLayout->setContentsMargins(0, 0, 0, 0); + PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.file.layout"), + widgetLayout, + {{"{{conditions}}", _conditions}, + {"{{filePath}}", _filePath}, + {"{{regex}}", _regex}}); + + auto layout = new QVBoxLayout; + layout->addLayout(widgetLayout); + layout->addWidget(_matchText); + + setLayout(layout); _entryData = entryData; UpdateEntryData(); @@ -393,47 +274,15 @@ void MacroConditionFileEdit::UpdateEntryData() return; } - _fileTypes->setCurrentIndex(static_cast(_entryData->_fileType)); _conditions->setCurrentIndex( static_cast(_entryData->GetCondition())); _filePath->SetPath(_entryData->_file); _matchText->setPlainText(_entryData->_text); _regex->SetRegexConfig(_entryData->_regex); - _checkModificationDate->setChecked(_entryData->_useTime); - _checkFileContent->setChecked(_entryData->_onlyMatchIfChanged); - - // TODO: Remove in future version - if (!_entryData->_useTime) { - _checkModificationDate->hide(); - } - if (!_entryData->_onlyMatchIfChanged) { - _checkFileContent->hide(); - } SetWidgetVisibility(); } -void MacroConditionFileEdit::FileTypeChanged(int index) -{ - if (_loading || !_entryData) { - return; - } - - MacroConditionFile::FileType type = - static_cast(index); - - if (type == MacroConditionFile::FileType::LOCAL) { - _filePath->Button()->setDisabled(false); - _checkModificationDate->setDisabled(false); - } else { - _filePath->Button()->setDisabled(true); - _checkModificationDate->setDisabled(true); - } - - auto lock = LockContext(); - _entryData->_fileType = type; -} - void MacroConditionFileEdit::ConditionChanged(int index) { GUARD_LOADING_AND_LOCK(); @@ -467,18 +316,6 @@ void MacroConditionFileEdit::RegexChanged(const RegexConfig &conf) updateGeometry(); } -void MacroConditionFileEdit::CheckModificationDateChanged(int state) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_useTime = state; -} - -void MacroConditionFileEdit::OnlyMatchIfChangedChanged(int state) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_onlyMatchIfChanged = state; -} - void MacroConditionFileEdit::SetWidgetVisibility() { if (!_entryData) { @@ -489,20 +326,6 @@ void MacroConditionFileEdit::SetWidgetVisibility() MacroConditionFile::Condition::MATCH); _regex->setVisible(_entryData->GetCondition() == MacroConditionFile::Condition::MATCH); - _checkModificationDate->setVisible( - _entryData->_useTime && - _entryData->GetCondition() == - MacroConditionFile::Condition::MATCH); - _checkFileContent->setVisible( - _entryData->_onlyMatchIfChanged && - _entryData->GetCondition() == - MacroConditionFile::Condition::MATCH); - - // TODO: Remove remote file support in future version in favor of HTTP - // action. - // Hide the option for now, if it is not used already. - _fileTypes->setVisible(_entryData->_fileType == - MacroConditionFile::FileType::REMOTE); adjustSize(); updateGeometry(); diff --git a/plugins/base/macro-condition-file.hpp b/plugins/base/macro-condition-file.hpp index c6340608b..d606da69c 100644 --- a/plugins/base/macro-condition-file.hpp +++ b/plugins/base/macro-condition-file.hpp @@ -7,9 +7,6 @@ #include #include #include -#include -#include -#include namespace advss { @@ -26,11 +23,6 @@ class MacroConditionFile : public MacroCondition { return std::make_shared(m); } - enum class FileType { - LOCAL, - REMOTE, - }; - enum class Condition { MATCH, CONTENT_CHANGE, @@ -46,15 +38,9 @@ class MacroConditionFile : public MacroCondition { StringVariable _text = obs_module_text("AdvSceneSwitcher.enterText"); RegexConfig _regex; - // TODO: Remove in future version - bool _useTime = false; - bool _onlyMatchIfChanged = false; - FileType _fileType = FileType::LOCAL; - private: bool MatchFileContent(QString &filedata); - bool CheckRemoteFileContent(); - bool CheckLocalFileContent(); + bool CheckFileContent(); bool CheckChangeContent(); bool CheckChangeDate(); void SetupTempVars(); @@ -62,6 +48,7 @@ class MacroConditionFile : public MacroCondition { Condition _condition = Condition::MATCH; QDateTime _lastMod; size_t _lastHash = 0; + bool _firstContentCheck = true; std::string _lastFile; std::string _basename; std::string _basenameComplete; @@ -92,29 +79,22 @@ class MacroConditionFileEdit : public QWidget { } private slots: - void FileTypeChanged(int index); void ConditionChanged(int index); void PathChanged(const QString &text); void MatchTextChanged(); void RegexChanged(const RegexConfig &); - void CheckModificationDateChanged(int state); - void OnlyMatchIfChangedChanged(int state); signals: void HeaderInfoChanged(const QString &); -protected: - QComboBox *_fileTypes; +private: + void SetWidgetVisibility(); + QComboBox *_conditions; FileSelection *_filePath; VariableTextEdit *_matchText; RegexConfigWidget *_regex; - QCheckBox *_checkModificationDate; - QCheckBox *_checkFileContent; std::shared_ptr _entryData; -private: - void SetWidgetVisibility(); - bool _loading = true; }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9fab07ebf..d633d7328 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -175,6 +175,18 @@ target_sources( ${ADVSS_SOURCE_DIR}/lib/variables/variable-spinbox.cpp ${ADVSS_SOURCE_DIR}/lib/variables/variable-string.cpp) +# --- macro-condition-file --- # + +target_include_directories(${PROJECT_NAME} + PRIVATE ${ADVSS_SOURCE_DIR}/plugins/base) + +target_sources( + ${PROJECT_NAME} + PRIVATE test-macro-condition-file.cpp + ${ADVSS_SOURCE_DIR}/plugins/base/macro-condition-file.cpp + ${ADVSS_SOURCE_DIR}/lib/utils/file-selection.cpp + ${ADVSS_SOURCE_DIR}/lib/variables/variable-text-edit.cpp) + # --- Testing --- # enable_testing() diff --git a/tests/test-macro-condition-file.cpp b/tests/test-macro-condition-file.cpp new file mode 100644 index 000000000..495af16e6 --- /dev/null +++ b/tests/test-macro-condition-file.cpp @@ -0,0 +1,325 @@ +#include "catch.hpp" +#include "macro-condition-file.hpp" + +#include +#include +#include +#include +#include + +using advss::MacroConditionFile; + +// Write (or overwrite) a file with the given content. +static void writeFile(const QString &path, const QString &content) +{ + QFile f(path); + f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate); + QTextStream(&f) << content; +} + +// --------------------------------------------------------------------------- +// CONTENT_CHANGE +// --------------------------------------------------------------------------- + +TEST_CASE("CONTENT_CHANGE: first check does not trigger for non-empty file", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, "hello world"); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::CONTENT_CHANGE); + cond._file = path.toStdString(); + + REQUIRE_FALSE(cond.CheckCondition()); +} + +TEST_CASE("CONTENT_CHANGE: first check does not trigger for empty file", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, ""); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::CONTENT_CHANGE); + cond._file = path.toStdString(); + + REQUIRE_FALSE(cond.CheckCondition()); +} + +TEST_CASE("CONTENT_CHANGE: no trigger when content stays the same", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, "same content"); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::CONTENT_CHANGE); + cond._file = path.toStdString(); + + cond.CheckCondition(); // baseline + REQUIRE_FALSE(cond.CheckCondition()); +} + +TEST_CASE("CONTENT_CHANGE: triggers when content changes", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, "initial"); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::CONTENT_CHANGE); + cond._file = path.toStdString(); + + cond.CheckCondition(); // baseline + writeFile(path, "changed"); + REQUIRE(cond.CheckCondition()); +} + +TEST_CASE("CONTENT_CHANGE: no trigger after content stabilizes", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, "initial"); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::CONTENT_CHANGE); + cond._file = path.toStdString(); + + cond.CheckCondition(); // baseline + writeFile(path, "changed"); + cond.CheckCondition(); // change detected + REQUIRE_FALSE(cond.CheckCondition()); // same content again +} + +TEST_CASE("CONTENT_CHANGE: returns false when file cannot be opened", + "[macro-condition-file]") +{ + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::CONTENT_CHANGE); + cond._file = "/nonexistent/path/to/file.txt"; + + REQUIRE_FALSE(cond.CheckCondition()); +} + +// --------------------------------------------------------------------------- +// DATE_CHANGE +// --------------------------------------------------------------------------- + +TEST_CASE("DATE_CHANGE: first check does not trigger", "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, "content"); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::DATE_CHANGE); + cond._file = path.toStdString(); + + REQUIRE_FALSE(cond.CheckCondition()); +} + +TEST_CASE("DATE_CHANGE: no trigger when modification date stays the same", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, "content"); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::DATE_CHANGE); + cond._file = path.toStdString(); + + cond.CheckCondition(); // baseline + REQUIRE_FALSE(cond.CheckCondition()); +} + +TEST_CASE("DATE_CHANGE: triggers when modification date changes", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, "content"); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::DATE_CHANGE); + cond._file = path.toStdString(); + + cond.CheckCondition(); // baseline + + // Explicitly set the modification time to a known future value so the + // test is not sensitive to filesystem mtime resolution. + QFile f(path); + f.open(QIODevice::ReadWrite); + f.setFileTime(QDateTime::currentDateTime().addSecs(10), + QFileDevice::FileModificationTime); + f.close(); + + REQUIRE(cond.CheckCondition()); +} + +// --------------------------------------------------------------------------- +// MATCH +// --------------------------------------------------------------------------- + +TEST_CASE("MATCH: returns true when file content matches text", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, "hello"); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::MATCH); + cond._file = path.toStdString(); + cond._text = "hello"; + + REQUIRE(cond.CheckCondition()); +} + +TEST_CASE("MATCH: returns false when file content does not match text", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, "hello"); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::MATCH); + cond._file = path.toStdString(); + cond._text = "world"; + + REQUIRE_FALSE(cond.CheckCondition()); +} + +TEST_CASE("MATCH: returns false when file cannot be opened", + "[macro-condition-file]") +{ + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::MATCH); + cond._file = "/nonexistent/path/to/file.txt"; + cond._text = "anything"; + + REQUIRE_FALSE(cond.CheckCondition()); +} + +TEST_CASE("MATCH: regex enabled matches file content against pattern", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, "hello world"); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::MATCH); + cond._file = path.toStdString(); + cond._text = "h.*d"; // matches "hello world" as a regex + cond._regex.SetEnabled(true); + + REQUIRE(cond.CheckCondition()); +} + +TEST_CASE("MATCH: regex disabled treats pattern as literal text", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, "hello world"); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::MATCH); + cond._file = path.toStdString(); + cond._text = "h.*d"; // does not literally appear in "hello world" + cond._regex.SetEnabled(false); + + REQUIRE_FALSE(cond.CheckCondition()); +} + +// --------------------------------------------------------------------------- +// EXISTS +// --------------------------------------------------------------------------- + +TEST_CASE("EXISTS: returns true for an existing file", "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, ""); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::EXISTS); + cond._file = path.toStdString(); + + REQUIRE(cond.CheckCondition()); +} + +TEST_CASE("EXISTS: returns false for a non-existing path", + "[macro-condition-file]") +{ + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::EXISTS); + cond._file = "/nonexistent/path/to/file.txt"; + + REQUIRE_FALSE(cond.CheckCondition()); +} + +// --------------------------------------------------------------------------- +// IS_FILE +// --------------------------------------------------------------------------- + +TEST_CASE("IS_FILE: returns true for a regular file", "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, ""); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::IS_FILE); + cond._file = path.toStdString(); + + REQUIRE(cond.CheckCondition()); +} + +TEST_CASE("IS_FILE: returns false for a directory", "[macro-condition-file]") +{ + QTemporaryDir dir; + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::IS_FILE); + cond._file = dir.path().toStdString(); + + REQUIRE_FALSE(cond.CheckCondition()); +} + +// --------------------------------------------------------------------------- +// IS_FOLDER +// --------------------------------------------------------------------------- + +TEST_CASE("IS_FOLDER: returns true for a directory", "[macro-condition-file]") +{ + QTemporaryDir dir; + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::IS_FOLDER); + cond._file = dir.path().toStdString(); + + REQUIRE(cond.CheckCondition()); +} + +TEST_CASE("IS_FOLDER: returns false for a regular file", + "[macro-condition-file]") +{ + QTemporaryDir dir; + QString path = dir.filePath("test.txt"); + writeFile(path, ""); + + MacroConditionFile cond(nullptr); + cond.SetCondition(MacroConditionFile::Condition::IS_FOLDER); + cond._file = path.toStdString(); + + REQUIRE_FALSE(cond.CheckCondition()); +}