mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-21 17:34:57 -05:00
Use cpp-httplib based HTTP action type
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The goal is to remove the older, more limited version of the HTTP action at some point in the future.
This commit is contained in:
parent
1b05019acc
commit
aa87911b71
|
|
@ -1050,11 +1050,25 @@ AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.request="Send{{api}}of typ
|
|||
AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.event="Send{{api}}of type{{type}}to connected clients"
|
||||
AdvSceneSwitcher.action.websocket.entry.generic="Send{{api}}via{{connection}}"
|
||||
AdvSceneSwitcher.action.http="HTTP"
|
||||
AdvSceneSwitcher.action.http.legacy="HTTP (legacy)"
|
||||
AdvSceneSwitcher.action.http.setHeaders="Set headers"
|
||||
AdvSceneSwitcher.action.http.headers="Headers:"
|
||||
AdvSceneSwitcher.action.http.addHeader="Add header"
|
||||
AdvSceneSwitcher.action.http.addHeader.name="Header name"
|
||||
AdvSceneSwitcher.action.http.addHeader.value="Header value"
|
||||
AdvSceneSwitcher.action.http.setParams="Set parameters"
|
||||
AdvSceneSwitcher.action.http.params="Parameters:"
|
||||
AdvSceneSwitcher.action.http.addParam.name="Parameter name"
|
||||
AdvSceneSwitcher.action.http.addParam.value="Parameter value"
|
||||
AdvSceneSwitcher.action.http.body="Message body:"
|
||||
AdvSceneSwitcher.action.http.type.get="GET"
|
||||
AdvSceneSwitcher.action.http.type.post="POST"
|
||||
AdvSceneSwitcher.action.http.type.put="PUT"
|
||||
AdvSceneSwitcher.action.http.type.patch="PATCH"
|
||||
AdvSceneSwitcher.action.http.type.delete="DELETE"
|
||||
AdvSceneSwitcher.action.http.layout.method="Send{{method}}to URL{{url}}at path{{path}}"
|
||||
AdvSceneSwitcher.action.http.layout.contentType="Content type:{{contentType}}"
|
||||
AdvSceneSwitcher.action.http.layout.timeout="Timeout:{{timeout}}seconds"
|
||||
AdvSceneSwitcher.action.http.entry.line1="Send{{method}}to{{url}}"
|
||||
AdvSceneSwitcher.action.http.entry.line2="Timeout:{{timeout}}seconds"
|
||||
AdvSceneSwitcher.action.variable="Variable"
|
||||
|
|
@ -2040,6 +2054,11 @@ AdvSceneSwitcher.tempVar.gameCapture.class.description="Window class of the appl
|
|||
AdvSceneSwitcher.tempVar.gameCapture.executable="Executable"
|
||||
AdvSceneSwitcher.tempVar.gameCapture.executable.description="Executable name of the application captured by the source."
|
||||
|
||||
AdvSceneSwitcher.tempVar.http.status="Status code"
|
||||
AdvSceneSwitcher.tempVar.http.body="Message body"
|
||||
AdvSceneSwitcher.tempVar.http.error="Error"
|
||||
AdvSceneSwitcher.tempVar.http.error.description="Empty when no error occurred.\nOther possible values:\n\n * Could not establish connection\n * Failed to bind IP address\n * Failed to read connection\n * Failed to write connection\n * Maximum redirect count exceeded\n * Connection handling canceled\n * SSL connection failed\n * SSL certificate loading failed\n * SSL server verification failed\n * Unsupported HTTP multipart boundary characters\n * Compression failed\n * Connection timed out\n * Proxy connection failed\n * Unknown"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ void PreventMouseWheelAdjustWithoutFocus(QWidget *w)
|
|||
QString(w->metaObject()->className()) ==
|
||||
"advss::OSCMessageElementEdit" ||
|
||||
QString(w->metaObject()->className()) ==
|
||||
"advss::ChatMessagePropertyEdit") {
|
||||
"advss::ChatMessagePropertyEdit" ||
|
||||
QString(w->metaObject()->className()) ==
|
||||
"advss::KeyValueListContainerWidget") {
|
||||
return;
|
||||
}
|
||||
w->setFocusPolicy(Qt::StrongFocus);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ install_advss_plugin_dependency(...)
|
|||
... to install the plugin and its dependencies.
|
||||
#]]
|
||||
|
||||
add_plugin(http)
|
||||
add_plugin(midi)
|
||||
add_plugin(openvr)
|
||||
add_plugin(stream-deck)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ target_sources(
|
|||
macro-action-filter.hpp
|
||||
macro-action-hotkey.cpp
|
||||
macro-action-hotkey.hpp
|
||||
macro-action-http.cpp
|
||||
macro-action-http.hpp
|
||||
macro-action-http-legacy.cpp
|
||||
macro-action-http-legacy.hpp
|
||||
macro-action-log.cpp
|
||||
macro-action-log.hpp
|
||||
macro-action-media.cpp
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include "macro-action-http.hpp"
|
||||
#include "macro-action-http-legacy.hpp"
|
||||
#include "curl-helper.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ const std::string MacroActionHttp::id = "http";
|
|||
bool MacroActionHttp::_registered = MacroActionFactory::Register(
|
||||
MacroActionHttp::id,
|
||||
{MacroActionHttp::Create, MacroActionHttpEdit::Create,
|
||||
"AdvSceneSwitcher.action.http"});
|
||||
"AdvSceneSwitcher.action.http.legacy", true});
|
||||
|
||||
const static std::map<MacroActionHttp::Method, std::string> methods = {
|
||||
{MacroActionHttp::Method::GET, "AdvSceneSwitcher.action.http.type.get"},
|
||||
77
plugins/http/CMakeLists.txt
Normal file
77
plugins/http/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
cmake_minimum_required(VERSION 3.14)
|
||||
project(advanced-scene-switcher-http)
|
||||
|
||||
# --- Check requirements ---
|
||||
|
||||
get_target_property(ADVSS_SOURCE_DIR advanced-scene-switcher-lib SOURCE_DIR)
|
||||
set(CPP_HTTPLIB_DIR "${ADVSS_SOURCE_DIR}/deps/cpp-httplib")
|
||||
if(NOT EXISTS "${CPP_HTTPLIB_DIR}/CMakeLists.txt")
|
||||
message(WARNING "cpp-httplib directory \"${CPP_HTTPLIB_DIR}\" not found!\n"
|
||||
"HTTP support will be disabled!")
|
||||
return()
|
||||
endif()
|
||||
if(NOT TARGET httplib)
|
||||
add_subdirectory("${CPP_HTTPLIB_DIR}" "${CPP_HTTPLIB_DIR}/build"
|
||||
EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
if(NOT OPENSSL_INCLUDE_DIR OR NOT OPENSSL_LIBRARIES)
|
||||
find_package(OpenSSL)
|
||||
if(NOT OPENSSL_FOUND)
|
||||
message(WARNING "OpenSSL not found!\n" "HTTP support will be disabled!\n\n")
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(ZLIB)
|
||||
if(NOT ZLIB_FOUND)
|
||||
message(WARNING "zlib not found!\n" "HTTP support will be disabled!\n\n")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# --- End of section ---
|
||||
|
||||
add_library(${PROJECT_NAME} MODULE)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT=1)
|
||||
if(OS_MACOS)
|
||||
target_compile_definitions(
|
||||
${PROJECT_NAME} PRIVATE CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN=1)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework CoreFoundation")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework Security")
|
||||
endif()
|
||||
|
||||
target_sources(
|
||||
${PROJECT_NAME} PRIVATE macro-action-http.cpp macro-action-http.hpp
|
||||
key-value-list.cpp key-value-list.hpp)
|
||||
|
||||
setup_advss_plugin(${PROJECT_NAME})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE "${CPP_HTTPLIB_DIR}/"
|
||||
"${OPENSSL_INCLUDE_DIR}")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${OPENSSL_LIBRARIES} ZLIB::ZLIB)
|
||||
install_advss_plugin(${PROJECT_NAME})
|
||||
if(OS_WINDOWS)
|
||||
# Couldn't really find a better way to install runtime dependencies for
|
||||
# Windows TODO: Clean this up at some point
|
||||
function(FIND_FILES_WITH_PATTERN result pattern dir)
|
||||
execute_process(
|
||||
COMMAND
|
||||
powershell -Command
|
||||
"Get-ChildItem -Path '${dir}' -Recurse -Include ${pattern} |"
|
||||
"Select-Object -First 1 |"
|
||||
"ForEach-Object { $_.FullName -replace '\\\\', '\\\\' }"
|
||||
OUTPUT_VARIABLE files
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
set(${result}
|
||||
${files}
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
set(OPENSSL_DLL_SEARCH_DIR "${OPENSSL_INCLUDE_DIR}/..")
|
||||
find_files_with_pattern(CRYPTO_DLL_FILES "libcrypto*.dll"
|
||||
"${OPENSSL_DLL_SEARCH_DIR}")
|
||||
find_files_with_pattern(SSL_DLL_FILES "libssl*.dll"
|
||||
"${OPENSSL_DLL_SEARCH_DIR}")
|
||||
install_advss_plugin_dependency(TARGET ${PROJECT_NAME} DEPENDENCIES
|
||||
"${CRYPTO_DLL_FILES}" "${SSL_DLL_FILES}")
|
||||
endif()
|
||||
213
plugins/http/key-value-list.cpp
Normal file
213
plugins/http/key-value-list.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
#include "key-value-list.hpp"
|
||||
#include "name-dialog.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <QLayout>
|
||||
#include <QTimer>
|
||||
|
||||
namespace advss {
|
||||
|
||||
KeyValueListEdit::KeyValueListEdit(QWidget *parent, const QString &addKeyString,
|
||||
const QString &addKeyStringDescription,
|
||||
const QString &addValueString,
|
||||
const QString &addValueStringDescription)
|
||||
: ListEditor(parent),
|
||||
_addKeyString(addKeyString),
|
||||
_addKeyStringDescription(addKeyStringDescription),
|
||||
_addValueString(addValueString),
|
||||
_addValueStringDescription(addValueStringDescription)
|
||||
{
|
||||
_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
_list->setAutoScroll(false);
|
||||
}
|
||||
|
||||
void KeyValueListEdit::SetStringList(const StringList &list)
|
||||
{
|
||||
_stringList = list;
|
||||
_list->clear();
|
||||
|
||||
for (int i = 0; i < list.size(); i += 2) {
|
||||
AppendListEntryWidget(
|
||||
list.at(i), i + 1 >= list.size() ? "" : list.at(i + 1));
|
||||
}
|
||||
UpdateListSize();
|
||||
}
|
||||
|
||||
void KeyValueListEdit::Add()
|
||||
{
|
||||
StringVariable key;
|
||||
StringVariable value;
|
||||
bool accepted = AskForKeyValue(key, value);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
AppendListEntryWidget(key, value);
|
||||
_stringList << key << value;
|
||||
|
||||
// Delay resizing to make sure the list viewport was already updated
|
||||
QTimer::singleShot(0, this, [this]() { UpdateListSize(); });
|
||||
|
||||
StringListChanged(_stringList);
|
||||
}
|
||||
|
||||
void KeyValueListEdit::Remove()
|
||||
{
|
||||
int idx = _list->currentRow();
|
||||
if (idx == -1) {
|
||||
return;
|
||||
}
|
||||
_stringList.removeAt(idx);
|
||||
|
||||
QListWidgetItem *item = _list->currentItem();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
delete item;
|
||||
|
||||
// Delay resizing to make sure the list viewport was already updated
|
||||
QTimer::singleShot(0, this, [this]() { UpdateListSize(); });
|
||||
|
||||
StringListChanged(_stringList);
|
||||
}
|
||||
|
||||
void KeyValueListEdit::Up()
|
||||
{
|
||||
int idx = _list->currentRow();
|
||||
if (idx <= 0 || idx >= _list->count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MoveStringListIdxUp(idx);
|
||||
|
||||
auto row = _list->itemWidget(_list->currentItem());
|
||||
auto newItem = _list->currentItem()->clone();
|
||||
|
||||
_list->insertItem(idx - 1, newItem);
|
||||
_list->setItemWidget(newItem, row);
|
||||
|
||||
_list->takeItem(idx + 1);
|
||||
_list->setCurrentRow(idx - 1);
|
||||
|
||||
UpdateListSize();
|
||||
|
||||
StringListChanged(_stringList);
|
||||
}
|
||||
|
||||
void KeyValueListEdit::Down()
|
||||
{
|
||||
int idx = _list->currentRow();
|
||||
if (idx == -1 || idx == _list->count() - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
MoveStringListIdxUp(idx + 1);
|
||||
|
||||
auto row = _list->itemWidget(_list->currentItem());
|
||||
auto newItem = _list->currentItem()->clone();
|
||||
|
||||
_list->insertItem(idx + 2, newItem);
|
||||
_list->setItemWidget(newItem, row);
|
||||
|
||||
_list->takeItem(idx);
|
||||
_list->setCurrentRow(idx + 1);
|
||||
|
||||
UpdateListSize();
|
||||
|
||||
StringListChanged(_stringList);
|
||||
}
|
||||
|
||||
void KeyValueListEdit::Clicked(QListWidgetItem *item)
|
||||
{
|
||||
int idx = _list->currentRow();
|
||||
|
||||
StringVariable key = _stringList[idx * 2];
|
||||
StringVariable value = _stringList[idx * 2 + 1];
|
||||
bool accepted = AskForKeyValue(key, value);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto container = static_cast<KeyValueListContainerWidget *>(
|
||||
_list->itemWidget(item));
|
||||
container->_key->setText(QString::fromStdString(key.UnresolvedValue()));
|
||||
container->_value->setText(
|
||||
QString::fromStdString(value.UnresolvedValue()));
|
||||
container->adjustSize();
|
||||
container->updateGeometry();
|
||||
|
||||
_stringList[idx * 2] = key;
|
||||
_stringList[idx * 2 + 1] = value;
|
||||
|
||||
// Delay resizing to make sure the list viewport was already updated
|
||||
QTimer::singleShot(0, this, [this]() { UpdateListSize(); });
|
||||
StringListChanged(_stringList);
|
||||
}
|
||||
|
||||
void KeyValueListEdit::MoveStringListIdxUp(int idx)
|
||||
{
|
||||
if (idx <= 0 || idx >= _list->count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_stringList.move(idx * 2, idx * 2 - 2);
|
||||
_stringList.move(idx * 2 + 1, idx * 2 - 1);
|
||||
}
|
||||
|
||||
bool KeyValueListEdit::AskForKeyValue(StringVariable &keyVariable,
|
||||
StringVariable &valueVariable)
|
||||
{
|
||||
std::string key;
|
||||
bool accepted = NameDialog::AskForName(
|
||||
this, _addKeyString, _addKeyStringDescription, key,
|
||||
QString::fromStdString(keyVariable.UnresolvedValue()), 4096,
|
||||
false);
|
||||
if (!accepted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string value;
|
||||
accepted = NameDialog::AskForName(
|
||||
this, _addValueString, _addValueStringDescription, value,
|
||||
QString::fromStdString(valueVariable.UnresolvedValue()), 4096,
|
||||
false);
|
||||
if (!accepted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
keyVariable = key;
|
||||
valueVariable = value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KeyValueListEdit::AppendListEntryWidget(const StringVariable &key,
|
||||
const StringVariable &value)
|
||||
{
|
||||
QListWidgetItem *item = new QListWidgetItem(_list);
|
||||
auto container = new KeyValueListContainerWidget(this, _list->count());
|
||||
container->_key->setText(QString::fromStdString(key.UnresolvedValue()));
|
||||
container->_value->setText(
|
||||
QString::fromStdString(value.UnresolvedValue()));
|
||||
container->adjustSize();
|
||||
container->updateGeometry();
|
||||
_list->addItem(item);
|
||||
_list->setItemWidget(item, container);
|
||||
UpdateListSize();
|
||||
}
|
||||
|
||||
KeyValueListContainerWidget::KeyValueListContainerWidget(QWidget *parent,
|
||||
int index)
|
||||
: QWidget(parent),
|
||||
_key(new QLabel("Key", this)),
|
||||
_value(new QLabel("Value", this)),
|
||||
_index(index)
|
||||
{
|
||||
auto layout = new QHBoxLayout();
|
||||
layout->addWidget(_key);
|
||||
layout->addWidget(_value);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
53
plugins/http/key-value-list.hpp
Normal file
53
plugins/http/key-value-list.hpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
#include "string-list.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
class KeyValueListEdit final : public ListEditor {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
KeyValueListEdit(QWidget *parent, const QString &addKeyString,
|
||||
const QString &addKeyStringDescription,
|
||||
const QString &addValueString,
|
||||
const QString &addValueStringDescription);
|
||||
void SetStringList(const StringList &);
|
||||
|
||||
private slots:
|
||||
void Add();
|
||||
void Remove();
|
||||
void Up();
|
||||
void Down();
|
||||
void Clicked(QListWidgetItem *);
|
||||
signals:
|
||||
void StringListChanged(const StringList &);
|
||||
|
||||
private:
|
||||
void MoveStringListIdxUp(int);
|
||||
bool AskForKeyValue(StringVariable &key, StringVariable &value);
|
||||
void AppendListEntryWidget(const StringVariable &key,
|
||||
const StringVariable &value);
|
||||
|
||||
StringList _stringList;
|
||||
QString _addKeyString;
|
||||
QString _addKeyStringDescription;
|
||||
QString _addValueString;
|
||||
QString _addValueStringDescription;
|
||||
};
|
||||
|
||||
class KeyValueListContainerWidget final : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
KeyValueListContainerWidget(QWidget *parent, int index);
|
||||
|
||||
private:
|
||||
QLabel *_key;
|
||||
QLabel *_value;
|
||||
|
||||
int _index = -1;
|
||||
friend class KeyValueListEdit;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
463
plugins/http/macro-action-http.cpp
Normal file
463
plugins/http/macro-action-http.cpp
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
#include "macro-action-http.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
|
||||
#include <httplib.h>
|
||||
|
||||
#undef DELETE
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionHttp::id = "http_v2";
|
||||
|
||||
bool MacroActionHttp::_registered = MacroActionFactory::Register(
|
||||
MacroActionHttp::id,
|
||||
{MacroActionHttp::Create, MacroActionHttpEdit::Create,
|
||||
"AdvSceneSwitcher.action.http"});
|
||||
|
||||
static httplib::Headers getHeaders(const StringList &strings)
|
||||
{
|
||||
httplib::Headers headers;
|
||||
for (int i = 0; i < strings.size(); i = i + 2) {
|
||||
const auto pair =
|
||||
i + 1 >= strings.size()
|
||||
? std::make_pair(std::string(strings.at(i)), "")
|
||||
: std::make_pair(
|
||||
std::string(strings.at(i)),
|
||||
std::string(strings.at(i + 1)));
|
||||
headers.emplace(pair);
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
static httplib::Params getParams(const StringList &strings)
|
||||
{
|
||||
httplib::Params params;
|
||||
for (int i = 0; i < strings.size(); i = i + 2) {
|
||||
const auto pair =
|
||||
i + 1 >= strings.size()
|
||||
? std::make_pair(std::string(strings.at(i)), "")
|
||||
: std::make_pair(
|
||||
std::string(strings.at(i)),
|
||||
std::string(strings.at(i + 1)));
|
||||
params.emplace(pair);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
static void setTimeout(httplib::Client &client, const Duration &timeout)
|
||||
{
|
||||
const time_t seconds = timeout.Seconds();
|
||||
const time_t usecs = timeout.Milliseconds() * 1000;
|
||||
client.set_read_timeout(seconds, usecs);
|
||||
client.set_write_timeout(seconds, usecs);
|
||||
}
|
||||
|
||||
void MacroActionHttp::SetupTempVars()
|
||||
{
|
||||
MacroAction::SetupTempVars();
|
||||
AddTempvar("status",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.http.status"));
|
||||
AddTempvar("body",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.http.body"));
|
||||
AddTempvar("error",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.http.error"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.http.error.description"));
|
||||
}
|
||||
|
||||
bool MacroActionHttp::PerformAction()
|
||||
{
|
||||
httplib::Client cli(_url);
|
||||
setTimeout(cli, _timeout);
|
||||
const auto params = _setParams ? getParams(_params) : httplib::Params();
|
||||
const auto headers = _setHeaders ? getHeaders(_headers)
|
||||
: httplib::Headers();
|
||||
|
||||
httplib::Result response;
|
||||
switch (_method) {
|
||||
case MacroActionHttp::Method::GET:
|
||||
response = cli.Get(_path, params, headers);
|
||||
break;
|
||||
case MacroActionHttp::Method::POST: {
|
||||
const auto path = httplib::append_query_params(_path, params);
|
||||
response = cli.Post(path, headers, _body, _contentType);
|
||||
break;
|
||||
}
|
||||
case MacroActionHttp::Method::PUT: {
|
||||
const auto path = httplib::append_query_params(_path, params);
|
||||
response = cli.Put(path, headers, _body, _contentType);
|
||||
break;
|
||||
}
|
||||
case MacroActionHttp::Method::PATCH: {
|
||||
const auto path = httplib::append_query_params(_path, params);
|
||||
response = cli.Patch(path, headers, _body, _contentType);
|
||||
break;
|
||||
}
|
||||
case MacroActionHttp::Method::DELETE: {
|
||||
const auto path = httplib::append_query_params(_path, params);
|
||||
response = cli.Delete(path, headers, _body, _contentType);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
SetTempVarValue("status",
|
||||
response ? std::to_string(response->status) : "");
|
||||
SetTempVarValue("body", response ? response->body : "");
|
||||
SetTempVarValue("error",
|
||||
response ? "" : httplib::to_string(response.error()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr std::string_view methodToString(MacroActionHttp::Method method)
|
||||
{
|
||||
switch (method) {
|
||||
case MacroActionHttp::Method::GET:
|
||||
return "GET";
|
||||
case MacroActionHttp::Method::POST:
|
||||
return "POST";
|
||||
case MacroActionHttp::Method::PUT:
|
||||
return "PUT";
|
||||
case MacroActionHttp::Method::PATCH:
|
||||
return "PATCH";
|
||||
case MacroActionHttp::Method::DELETE:
|
||||
return "DELETE";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static std::string stringListToString(const StringList &list)
|
||||
{
|
||||
if (list.empty()) {
|
||||
return "[]";
|
||||
}
|
||||
std::string result = "[";
|
||||
for (const auto &string : list) {
|
||||
result += std::string(string) + ", ";
|
||||
}
|
||||
result.pop_back();
|
||||
return result + "]";
|
||||
}
|
||||
|
||||
void MacroActionHttp::LogAction() const
|
||||
{
|
||||
ablog(LOG_INFO,
|
||||
"sent HTTP request (%s) "
|
||||
"to URL \"%s\" "
|
||||
"to path \"%s\" "
|
||||
"with content type \"%s\" "
|
||||
"with body \"%s\" "
|
||||
"with headers \"%s\" "
|
||||
"with parameters \"%s\" "
|
||||
"with timeout \"%s\"",
|
||||
methodToString(_method).data(), _url.c_str(), _path.c_str(),
|
||||
_contentType.c_str(), _body.c_str(),
|
||||
_setHeaders ? stringListToString(_headers).c_str() : "-",
|
||||
_setParams ? stringListToString(_params).c_str() : "-",
|
||||
_timeout.ToString().c_str());
|
||||
}
|
||||
|
||||
bool MacroActionHttp::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_url.Save(obj, "url");
|
||||
_path.Save(obj, "path");
|
||||
_contentType.Save(obj, "contentType");
|
||||
_body.Save(obj, "body");
|
||||
obs_data_set_bool(obj, "setHeaders", _setHeaders);
|
||||
_headers.Save(obj, "headers", "header");
|
||||
obs_data_set_bool(obj, "setParams", _setParams);
|
||||
_params.Save(obj, "params", "param");
|
||||
obs_data_set_int(obj, "method", static_cast<int>(_method));
|
||||
_timeout.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionHttp::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_url.Load(obj, "url");
|
||||
_path.Load(obj, "path");
|
||||
_contentType.Load(obj, "contentType");
|
||||
_body.Load(obj, "body");
|
||||
_setHeaders = obs_data_get_bool(obj, "setHeaders");
|
||||
_headers.Load(obj, "headers", "header");
|
||||
_setParams = obs_data_get_bool(obj, "setParams");
|
||||
_params.Load(obj, "params", "param");
|
||||
_method = static_cast<Method>(obs_data_get_int(obj, "method"));
|
||||
_timeout.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionHttp::GetShortDesc() const
|
||||
{
|
||||
return _url.UnresolvedValue();
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionHttp::Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionHttp>(m);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionHttp::Copy() const
|
||||
{
|
||||
return std::make_shared<MacroActionHttp>(*this);
|
||||
}
|
||||
|
||||
void MacroActionHttp::ResolveVariablesToFixedValues()
|
||||
{
|
||||
_url.ResolveVariables();
|
||||
_path.ResolveVariables();
|
||||
_contentType.ResolveVariables();
|
||||
_body.ResolveVariables();
|
||||
_headers.ResolveVariables();
|
||||
_params.ResolveVariables();
|
||||
_timeout.ResolveVariables();
|
||||
}
|
||||
|
||||
static inline void populateMethodSelection(QComboBox *list)
|
||||
{
|
||||
const static std::map<MacroActionHttp::Method, std::string> methods = {
|
||||
{MacroActionHttp::Method::GET,
|
||||
"AdvSceneSwitcher.action.http.type.get"},
|
||||
{MacroActionHttp::Method::POST,
|
||||
"AdvSceneSwitcher.action.http.type.post"},
|
||||
{MacroActionHttp::Method::PUT,
|
||||
"AdvSceneSwitcher.action.http.type.put"},
|
||||
{MacroActionHttp::Method::PATCH,
|
||||
"AdvSceneSwitcher.action.http.type.patch"},
|
||||
{MacroActionHttp::Method::DELETE,
|
||||
"AdvSceneSwitcher.action.http.type.delete"},
|
||||
};
|
||||
|
||||
for (const auto &[value, name] : methods) {
|
||||
list->addItem(obs_module_text(name.c_str()),
|
||||
static_cast<int>(value));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionHttpEdit::MacroActionHttpEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionHttp> entryData)
|
||||
: QWidget(parent),
|
||||
_url(new VariableLineEdit(this)),
|
||||
_path(new VariableLineEdit(this)),
|
||||
_contentType(new VariableLineEdit(this)),
|
||||
_contentTypeLayout(new QHBoxLayout()),
|
||||
_methods(new QComboBox()),
|
||||
_body(new VariableTextEdit(this)),
|
||||
_bodyLayout(new QVBoxLayout()),
|
||||
_setHeaders(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.http.setHeaders"))),
|
||||
_headerList(new KeyValueListEdit(
|
||||
this, obs_module_text("AdvSceneSwitcher.action.http.headers"),
|
||||
obs_module_text("AdvSceneSwitcher.action.http.addHeader.name"),
|
||||
obs_module_text("AdvSceneSwitcher.action.http.headers"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.http.addHeader.value"))),
|
||||
_headerListLayout(new QVBoxLayout()),
|
||||
_setParams(new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.http.setParams"))),
|
||||
_paramList(new KeyValueListEdit(
|
||||
this, obs_module_text("AdvSceneSwitcher.action.http.params"),
|
||||
obs_module_text("AdvSceneSwitcher.action.http.addParam.name"),
|
||||
obs_module_text("AdvSceneSwitcher.action.http.params"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.http.addParam.value"))),
|
||||
_paramListLayout(new QVBoxLayout()),
|
||||
_timeout(new DurationSelection(this, false))
|
||||
{
|
||||
populateMethodSelection(_methods);
|
||||
|
||||
SetWidgetSignalConnections();
|
||||
SetWidgetLayout();
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_url->setText(_entryData->_url);
|
||||
_path->setText(_entryData->_path);
|
||||
_contentType->setText(_entryData->_contentType);
|
||||
_body->setPlainText(_entryData->_body);
|
||||
_setHeaders->setChecked(_entryData->_setHeaders);
|
||||
_headerList->SetStringList(_entryData->_headers);
|
||||
_setParams->setChecked(_entryData->_setParams);
|
||||
_paramList->SetStringList(_entryData->_params);
|
||||
_methods->setCurrentIndex(
|
||||
_methods->findData(static_cast<int>(_entryData->_method)));
|
||||
_timeout->SetDuration(_entryData->_timeout);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::URLChanged()
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_url = _url->text().toStdString();
|
||||
emit(HeaderInfoChanged(_url->text()));
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::PathChanged()
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_path = _path->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::ContentTypeChanged()
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_contentType = _contentType->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::BodyChanged()
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_body = _body->toPlainText().toUtf8().constData();
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::MethodChanged(int idx)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_method = static_cast<MacroActionHttp::Method>(
|
||||
_methods->itemData(idx).toInt());
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::TimeoutChanged(const Duration &dur)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_timeout = dur;
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::SetHeadersChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_setHeaders = value;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::HeadersChanged(const StringList &headers)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_headers = headers;
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::SetParamsChanged(int value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_setParams = value;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::ParamsChanged(const StringList ¶ms)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_params = params;
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::SetWidgetSignalConnections()
|
||||
{
|
||||
QWidget::connect(_url, SIGNAL(editingFinished()), this,
|
||||
SLOT(URLChanged()));
|
||||
QWidget::connect(_path, SIGNAL(editingFinished()), this,
|
||||
SLOT(PathChanged()));
|
||||
QWidget::connect(_contentType, SIGNAL(editingFinished()), this,
|
||||
SLOT(ContentTypeChanged()));
|
||||
QWidget::connect(_body, SIGNAL(textChanged()), this,
|
||||
SLOT(BodyChanged()));
|
||||
QWidget::connect(_methods, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(MethodChanged(int)));
|
||||
QWidget::connect(_setHeaders, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(SetHeadersChanged(int)));
|
||||
QWidget::connect(_headerList,
|
||||
SIGNAL(StringListChanged(const StringList &)), this,
|
||||
SLOT(HeadersChanged(const StringList &)));
|
||||
QWidget::connect(_setParams, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(SetParamsChanged(int)));
|
||||
QWidget::connect(_paramList,
|
||||
SIGNAL(StringListChanged(const StringList &)), this,
|
||||
SLOT(ParamsChanged(const StringList &)));
|
||||
QWidget::connect(_timeout, SIGNAL(DurationChanged(const Duration &)),
|
||||
this, SLOT(TimeoutChanged(const Duration &)));
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::SetWidgetLayout()
|
||||
{
|
||||
const std::unordered_map<std::string, QWidget *> widgets = {
|
||||
{"{{url}}", _url},
|
||||
{"{{path}}", _path},
|
||||
{"{{contentType}}", _contentType},
|
||||
{"{{method}}", _methods},
|
||||
{"{{body}}", _body},
|
||||
{"{{timeout}}", _timeout},
|
||||
};
|
||||
|
||||
auto actionLayout = new QHBoxLayout;
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.http.layout.method"),
|
||||
actionLayout, widgets);
|
||||
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.http.layout.contentType"),
|
||||
_contentTypeLayout, widgets);
|
||||
|
||||
_bodyLayout->addWidget(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.http.body")));
|
||||
_bodyLayout->addWidget(_body);
|
||||
|
||||
auto timeoutLayout = new QHBoxLayout;
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.http.layout.timeout"),
|
||||
timeoutLayout, widgets);
|
||||
|
||||
_headerListLayout->addWidget(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.http.headers")));
|
||||
_headerListLayout->addWidget(_headerList);
|
||||
|
||||
_paramListLayout->addWidget(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.http.params")));
|
||||
_paramListLayout->addWidget(_paramList);
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
layout->addLayout(actionLayout);
|
||||
layout->addWidget(_setHeaders);
|
||||
layout->addLayout(_headerListLayout);
|
||||
layout->addWidget(_setParams);
|
||||
layout->addLayout(_paramListLayout);
|
||||
layout->addLayout(_contentTypeLayout);
|
||||
layout->addLayout(_bodyLayout);
|
||||
layout->addLayout(timeoutLayout);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void MacroActionHttpEdit::SetWidgetVisibility()
|
||||
{
|
||||
SetLayoutVisible(_headerListLayout, _entryData->_setHeaders);
|
||||
SetLayoutVisible(_paramListLayout, _entryData->_setParams);
|
||||
SetLayoutVisible(_contentTypeLayout,
|
||||
_entryData->_method != MacroActionHttp::Method::GET);
|
||||
SetLayoutVisible(_bodyLayout,
|
||||
_entryData->_method != MacroActionHttp::Method::GET);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
107
plugins/http/macro-action-http.hpp
Normal file
107
plugins/http/macro-action-http.hpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "key-value-list.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroActionHttp final : public MacroAction {
|
||||
public:
|
||||
MacroActionHttp(Macro *m) : MacroAction(m, true) {}
|
||||
bool PerformAction();
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m);
|
||||
std::shared_ptr<MacroAction> Copy() const;
|
||||
void ResolveVariablesToFixedValues();
|
||||
|
||||
enum class Method {
|
||||
GET = 0,
|
||||
POST,
|
||||
PUT,
|
||||
PATCH,
|
||||
DELETE,
|
||||
};
|
||||
|
||||
StringVariable _url = "127.0.0.1:8080";
|
||||
StringVariable _path = "/";
|
||||
StringVariable _body = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
StringVariable _contentType = "application/json";
|
||||
bool _setHeaders = false;
|
||||
StringList _headers;
|
||||
bool _setParams = false;
|
||||
StringList _params;
|
||||
Method _method = Method::GET;
|
||||
Duration _timeout = Duration(1.0);
|
||||
|
||||
private:
|
||||
void SetupTempVars();
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionHttpEdit final : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionHttpEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionHttp> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionHttpEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionHttp>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void URLChanged();
|
||||
void PathChanged();
|
||||
void BodyChanged();
|
||||
void ContentTypeChanged();
|
||||
void MethodChanged(int);
|
||||
void TimeoutChanged(const Duration &seconds);
|
||||
void SetHeadersChanged(int);
|
||||
void HeadersChanged(const StringList &);
|
||||
void SetParamsChanged(int);
|
||||
void ParamsChanged(const StringList &);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
private:
|
||||
void SetWidgetSignalConnections();
|
||||
void SetWidgetLayout();
|
||||
void SetWidgetVisibility();
|
||||
|
||||
VariableLineEdit *_url;
|
||||
VariableLineEdit *_path;
|
||||
VariableLineEdit *_contentType;
|
||||
QHBoxLayout *_contentTypeLayout;
|
||||
QComboBox *_methods;
|
||||
VariableTextEdit *_body;
|
||||
QVBoxLayout *_bodyLayout;
|
||||
QCheckBox *_setHeaders;
|
||||
KeyValueListEdit *_headerList;
|
||||
QVBoxLayout *_headerListLayout;
|
||||
QCheckBox *_setParams;
|
||||
KeyValueListEdit *_paramList;
|
||||
QVBoxLayout *_paramListLayout;
|
||||
DurationSelection *_timeout;
|
||||
|
||||
std::shared_ptr<MacroActionHttp> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
|
@ -10,8 +10,10 @@ if(NOT EXISTS "${CPP_HTTPLIB_DIR}/CMakeLists.txt")
|
|||
"Twitch support will be disabled!")
|
||||
return()
|
||||
endif()
|
||||
add_subdirectory("${CPP_HTTPLIB_DIR}" "${CPP_HTTPLIB_DIR}/build"
|
||||
EXCLUDE_FROM_ALL)
|
||||
if(NOT TARGET httplib)
|
||||
add_subdirectory("${CPP_HTTPLIB_DIR}" "${CPP_HTTPLIB_DIR}/build"
|
||||
EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
if(NOT OPENSSL_INCLUDE_DIR OR NOT OPENSSL_LIBRARIES)
|
||||
find_package(OpenSSL)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user