diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c624b58..f6f95ffe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ if(BUILD_OUT_OF_TREE) find_package(LibObs REQUIRED) find_package(Qt5Core REQUIRED) find_package(Qt5Widgets REQUIRED) + find_package(Libcurl REQUIRED) find_path(LIBOBS_FRONTEND_INCLUDE_DIR HINTS ${LIBOBS_INCLUDE_DIRS}) find_file(LIBOBS_FRONTEND_API_LIB NAMES libobs-frontend-api.* HINTS ${LIBOBS_LIB} ) @@ -96,14 +97,16 @@ if(BUILD_OUT_OF_TREE) "${LIBOBS_INCLUDE_DIRS}" "${LIBOBS_FRONTEND_INCLUDE_DIR}" ${Qt5Core_INCLUDES} - ${Qt5Widgets_INCLUDES}) + ${Qt5Widgets_INCLUDES} + ${LIBCURL_INCLUDE_DIRS}) target_link_libraries(advanced-scene-switcher ${advanced-scene-switcher_PLATFORM_LIBS} ${LIBOBS_LIB} ${LIBOBS_FRONTEND_API_LIB} Qt5::Core - Qt5::Widgets) + Qt5::Widgets + ${LIBCURL_LIBRARIES}) # Additional commands to install the module in the correct place. # Find all the translation files so we can copy them to the correct @@ -224,7 +227,8 @@ else () ${advanced-scene-switcher_PLATFORM_LIBS} obs-frontend-api Qt5::Widgets - libobs) + libobs + ${LIBCURL_LIBRARIES}) install_obs_plugin(advanced-scene-switcher data) endif() diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index 091e18eb..b40f4708 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -2234,6 +2234,13 @@ + + + + Please note that if you choose the remote option the scene switcher will try to access the remote location every x ms as specified on the General tab! + + + diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index ad3b7865..9ec2944b 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -291,6 +291,9 @@ void SceneSwitcher::loadUI() ui->fileType->addItem("local"); ui->fileType->addItem("remote"); + ui->remoteFileWarningLabel->setText( + "Note that the scene switcher will try to access the remote location every " + + QString::number(switcher->interval) + "ms"); for (auto &s : switcher->fileSwitches) { std::string sceneName = GetWeakSourceName(s.scene); diff --git a/src/file-switch.cpp b/src/file-switch.cpp index d193856c..0f72a719 100644 --- a/src/file-switch.cpp +++ b/src/file-switch.cpp @@ -115,48 +115,103 @@ void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene, } } +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; +} + +std::string getRemoteData(std::string &url) +{ + CURL *curl; + CURLcode res; + std::string readBuffer; + + curl = curl_easy_init(); + if (curl) { + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + } + return readBuffer; +} + +bool compareIgnoringLineEnding(QString &s1, QString &s2) +{ + /* + Im using QTextStream here so the conversion between different lineendings is done by QT. + QT itself uses only the linefeed internally so the input by the user is always using that, + but the files selected by the user might use different line endings. + If you are reading this and know of a cleaner way to do this, please let me know :) + */ + QTextStream s1stream(&s1); + QTextStream s2stream(&s2); + + while (!s1stream.atEnd() && !s2stream.atEnd()) { + QString s1s = s1stream.readLine(); + QString s2s = s2stream.readLine(); + if (QString::compare(s1s, s2s, Qt::CaseSensitive) != 0) { + return false; + } + } + return true; +} + +bool checkRemoteFileContent(FileSwitch &s) +{ + std::string data = getRemoteData(s.file); + bool ret = false; + if (s.useRegex) { + ret = QString::fromStdString(data).contains( + QRegularExpression(QString::fromStdString(s.text))); + return ret; + } + QString t = QString::fromStdString(s.text); + QString d = QString::fromStdString(data); + + return compareIgnoringLineEnding(t, d); +} + +bool checkLocalFileContent(FileSwitch &s) +{ + bool equal = false; + QString t = QString::fromStdString(s.text); + QFile file(QString::fromStdString(s.file)); + if (!file.open(QIODevice::ReadOnly)) + return false; + + if (s.useTime) { + QDateTime newLastMod = QFileInfo(file).lastModified(); + if (s.lastMod == newLastMod) + return false; + s.lastMod = newLastMod; + } + + if (s.useRegex) { + QTextStream in(&file); + QRegExp rx(t); + equal = rx.exactMatch(in.readAll()); + } else { + QTextStream in(&file); + equal = compareIgnoringLineEnding(in.readAll(), t); + } + file.close(); + return equal; +} + void SwitcherData::checkFileContent(bool &match, OBSWeakSource &scene, OBSWeakSource &transition) { for (FileSwitch &s : fileSwitches) { bool equal = false; - QString t = QString::fromStdString(s.text); - QFile file(QString::fromStdString(s.file)); - if (!file.open(QIODevice::ReadOnly)) - continue; - - if (s.useTime) { - QDateTime newLastMod = QFileInfo(file).lastModified(); - if (s.lastMod == newLastMod) - continue; - s.lastMod = newLastMod; - } - - if (s.useRegex) { - QTextStream in(&file); - QRegExp rx(t); - equal = rx.exactMatch(in.readAll()); + if (s.remote) { + equal = checkRemoteFileContent(s); } else { - /*Im using QTextStream here so the conversion between different lineendings is done by QT. - *QT itself uses only the linefeed internally so the input by the user is always using that, - *but the files selected by the user might use different line endings. - *If you are reading this and know of a cleaner way to do this, please let me know :) - */ - QTextStream in(&file); - QTextStream text(&t); - while (!in.atEnd() && !text.atEnd()) { - QString fileLine = in.readLine(); - QString textLine = text.readLine(); - if (QString::compare(fileLine, textLine, - Qt::CaseSensitive) != 0) { - equal = false; - break; - } else { - equal = true; - } - } + equal = checkLocalFileContent(s); } - file.close(); if (equal) { scene = s.scene; @@ -181,13 +236,20 @@ void SceneSwitcher::on_browseButton_3_clicked() ui->filePathLineEdit->setText(path); } -void SceneSwitcher::on_fileType_currentIndexChanged(int idx) { +void SceneSwitcher::on_fileType_currentIndexChanged(int idx) +{ if (idx == -1) return; - if (idx == LOCAL_FILE_IDX) + if (idx == LOCAL_FILE_IDX) { ui->browseButton_3->setDisabled(false); - if (idx == REMOTE_FILE_IDX) + ui->fileContentTimeCheckBox->setDisabled(false); + ui->remoteFileWarningLabel->hide(); + } + if (idx == REMOTE_FILE_IDX) { ui->browseButton_3->setDisabled(true); + ui->fileContentTimeCheckBox->setDisabled(true); + ui->remoteFileWarningLabel->show(); + } } void SceneSwitcher::on_fileAdd_clicked() @@ -196,6 +258,7 @@ void SceneSwitcher::on_fileAdd_clicked() QString transitionName = ui->fileTransitions->currentText(); QString fileName = ui->filePathLineEdit->text(); QString text = ui->fileTextEdit->toPlainText(); + bool remote = (ui->fileType->currentIndex() == REMOTE_FILE_IDX); bool useRegex = ui->fileContentRegExCheckBox->isChecked(); bool useTime = ui->fileContentTimeCheckBox->isChecked(); @@ -217,8 +280,8 @@ void SceneSwitcher::on_fileAdd_clicked() std::lock_guard lock(switcher->m); switcher->fileSwitches.emplace_back(source, transition, fileName.toUtf8().constData(), - text.toUtf8().constData(), useRegex, - useTime); + text.toUtf8().constData(), remote, + useRegex, useTime); } void SceneSwitcher::on_fileRemove_clicked() @@ -260,6 +323,10 @@ void SceneSwitcher::on_fileScenesList_currentRowChanged(int idx) ui->fileTransitions->setCurrentText(transitionName.c_str()); ui->fileTextEdit->setPlainText(s.text.c_str()); ui->filePathLineEdit->setText(s.file.c_str()); + if (s.remote) + ui->fileType->setCurrentIndex(REMOTE_FILE_IDX); + else + ui->fileType->setCurrentIndex(LOCAL_FILE_IDX); ui->fileContentRegExCheckBox->setChecked(s.useRegex); ui->fileContentTimeCheckBox->setChecked(s.useTime); } @@ -283,6 +350,7 @@ void SwitcherData::saveFileSwitches(obs_data_t *obj) transitionName); obs_data_set_string(array_obj, "file", s.file.c_str()); obs_data_set_string(array_obj, "text", s.text.c_str()); + obs_data_set_bool(array_obj, "remote", s.remote); obs_data_set_bool(array_obj, "useRegex", s.useRegex); obs_data_set_bool(array_obj, "useTime", s.useTime); obs_data_array_push_back(fileArray, array_obj); @@ -316,12 +384,13 @@ void SwitcherData::loadFileSwitches(obs_data_t *obj) obs_data_get_string(array_obj, "transition"); const char *file = obs_data_get_string(array_obj, "file"); const char *text = obs_data_get_string(array_obj, "text"); + bool remote = obs_data_get_bool(array_obj, "remote"); bool useRegex = obs_data_get_bool(array_obj, "useRegex"); bool useTime = obs_data_get_bool(array_obj, "useTime"); switcher->fileSwitches.emplace_back( GetWeakSourceByName(scene), - GetWeakTransitionByName(transition), file, text, + GetWeakTransitionByName(transition), file, text, remote, useRegex, useTime); obs_data_release(array_obj); diff --git a/src/headers/switcher-data-structs.hpp b/src/headers/switcher-data-structs.hpp index 45e5d568..6260f17c 100644 --- a/src/headers/switcher-data-structs.hpp +++ b/src/headers/switcher-data-structs.hpp @@ -169,18 +169,19 @@ struct FileSwitch { OBSWeakSource transition; std::string file; std::string text; - bool local = true; + bool remote = false; bool useRegex = false; bool useTime = false; QDateTime lastMod; inline FileSwitch(OBSWeakSource scene_, OBSWeakSource transition_, - const char *file_, const char *text_, bool useRegex_, - bool useTime_) + const char *file_, const char *text_, bool remote_, + bool useRegex_, bool useTime_) : scene(scene_), transition(transition_), file(file_), text(text_), + remote(remote_), useRegex(useRegex_), useTime(useTime_), lastMod()