Skip to content

Commit 79852bd

Browse files
authored
Refactor URL source callbacks and add mapping data (#91)
* Refactor URL source callbacks and add mapping data (#91) * Refactor URL source callbacks and fix error messages * Refactor URL source thread and remove unused variables
1 parent 71cc597 commit 79852bd

11 files changed

+703
-307
lines changed

CMakeLists.txt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,16 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE vendor/nlohmann-json)
7373
target_sources(
7474
${CMAKE_PROJECT_NAME}
7575
PRIVATE src/plugin-main.c
76-
src/url-source.cpp
77-
src/ui/RequestBuilder.cpp
76+
src/obs-source-util.cpp
77+
src/mapping-data.cpp
7878
src/request-data.cpp
79+
src/ui/RequestBuilder.cpp
7980
src/ui/text-render-helper.cpp
80-
src/url-source-info.c
81-
src/obs-source-util.cpp
81+
src/ui/outputmapping.cpp
8282
src/url-source-callbacks.cpp
83-
src/url-source-thread.cpp)
83+
src/url-source-info.c
84+
src/url-source-thread.cpp
85+
src/url-source.cpp)
8486
add_subdirectory(src/parsers)
8587

8688
set_target_properties_plugin(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${_name})

src/mapping-data.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
#include "mapping-data.h"
3+
#include <nlohmann/json.hpp>
4+
5+
std::string serialize_output_mapping_data(const output_mapping_data &data)
6+
{
7+
nlohmann::json j;
8+
for (const auto &mapping : data.mappings) {
9+
nlohmann::json j_mapping;
10+
j_mapping["name"] = mapping.name;
11+
j_mapping["output_source"] = mapping.output_source;
12+
j_mapping["template_string"] = mapping.template_string;
13+
j_mapping["css_props"] = mapping.css_props;
14+
j_mapping["unhide_output_source"] = mapping.unhide_output_source;
15+
j.push_back(j_mapping);
16+
}
17+
return j.dump();
18+
}
19+
20+
output_mapping_data deserialize_output_mapping_data(const std::string &data)
21+
{
22+
output_mapping_data result;
23+
nlohmann::json j = nlohmann::json::parse(data);
24+
for (const auto &j_mapping : j) {
25+
output_mapping mapping;
26+
mapping.name = j_mapping.value("name", "");
27+
mapping.output_source = j_mapping.value("output_source", "");
28+
mapping.template_string = j_mapping.value("template_string", "");
29+
mapping.css_props = j_mapping.value("css_props", "");
30+
mapping.unhide_output_source = j_mapping.value("unhide_output_source", false);
31+
result.mappings.push_back(mapping);
32+
}
33+
return result;
34+
}

src/mapping-data.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#ifndef MAPPING_DATA_H
2+
#define MAPPING_DATA_H
3+
4+
#include <string>
5+
#include <vector>
6+
7+
const std::string none_internal_rendering = "None / Internal rendering";
8+
9+
struct output_mapping {
10+
std::string name;
11+
std::string output_source;
12+
std::string template_string;
13+
std::string css_props;
14+
bool unhide_output_source = false;
15+
};
16+
17+
struct output_mapping_data {
18+
std::vector<output_mapping> mappings;
19+
};
20+
21+
std::string serialize_output_mapping_data(const output_mapping_data &data);
22+
output_mapping_data deserialize_output_mapping_data(const std::string &data);
23+
24+
#endif // MAPPING_DATA_H

src/ui/outputmapping.cpp

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
#include "outputmapping.h"
2+
#include "ui_outputmapping.h"
3+
4+
#include <QComboBox>
5+
#include <QHeaderView>
6+
#include <QStandardItem>
7+
8+
#include <obs.h>
9+
#include <obs-module.h>
10+
11+
namespace {
12+
// add_sources_to_list is a helper function that adds all text and media sources to the list
13+
bool add_sources_to_list(void *list_property, obs_source_t *source)
14+
{
15+
// add all text and media sources to the list
16+
auto source_id = obs_source_get_id(source);
17+
if (strcmp(source_id, "text_ft2_source_v2") != 0 &&
18+
strcmp(source_id, "text_gdiplus_v2") != 0 && strcmp(source_id, "ffmpeg_source") != 0 &&
19+
strcmp(source_id, "image_source") != 0) {
20+
return true;
21+
}
22+
23+
QComboBox *sources = static_cast<QComboBox *>(list_property);
24+
const char *name = obs_source_get_name(source);
25+
std::string name_with_prefix;
26+
// add a prefix to the name to indicate the source type
27+
if (strcmp(source_id, "text_ft2_source_v2") == 0 ||
28+
strcmp(source_id, "text_gdiplus_v2") == 0) {
29+
name_with_prefix = std::string("(Text) ").append(name);
30+
} else if (strcmp(source_id, "image_source") == 0) {
31+
name_with_prefix = std::string("(Image) ").append(name);
32+
} else if (strcmp(source_id, "ffmpeg_source") == 0) {
33+
name_with_prefix = std::string("(Media) ").append(name);
34+
}
35+
sources->addItem(name_with_prefix.c_str());
36+
return true;
37+
}
38+
39+
const std::string default_css_props = R"(background-color: transparent;
40+
color: #FFFFFF;
41+
font-size: 48px;
42+
)";
43+
const std::string default_template_string = R"({{output}})";
44+
} // namespace
45+
46+
OutputMapping::OutputMapping(output_mapping_data *mapping_data_in,
47+
std::function<void()> update_handler_in, QWidget *parent)
48+
: QDialog(parent),
49+
ui(new Ui::OutputMapping),
50+
mapping_data(mapping_data_in),
51+
update_handler(update_handler_in)
52+
{
53+
ui->setupUi(this);
54+
55+
model.setHorizontalHeaderLabels(QStringList() << "Mapping Name"
56+
<< "Output");
57+
ui->tableView->setModel(&model);
58+
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
59+
60+
// if an item is selected in the tableView, enable the remove button
61+
connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
62+
[this](const QItemSelection &selected) {
63+
const bool enable = !selected.indexes().isEmpty();
64+
ui->toolButton_removeMapping->setEnabled(enable);
65+
ui->plainTextEdit_template->setEnabled(enable);
66+
ui->plainTextEdit_cssProps->setEnabled(enable);
67+
68+
if (enable) {
69+
// get the selected row
70+
const auto row = selected.indexes().first().row();
71+
// get the data and output_source of the selected row
72+
const auto data = model.item(row, 0)->text();
73+
const auto output_source = model.item(row, 1)->text();
74+
ui->plainTextEdit_template->blockSignals(true);
75+
ui->plainTextEdit_cssProps->blockSignals(true);
76+
ui->checkBox_unhide_Source->blockSignals(true);
77+
// set the plainTextEdit_template and plainTextEdit_cssProps to the template_string and css_props of the selected row
78+
ui->plainTextEdit_template->setPlainText(
79+
this->mapping_data->mappings[row].template_string.c_str());
80+
ui->plainTextEdit_cssProps->setPlainText(
81+
this->mapping_data->mappings[row].css_props.c_str());
82+
ui->checkBox_unhide_Source->setChecked(
83+
this->mapping_data->mappings[row].unhide_output_source);
84+
ui->plainTextEdit_template->blockSignals(false);
85+
ui->plainTextEdit_cssProps->blockSignals(false);
86+
ui->checkBox_unhide_Source->blockSignals(false);
87+
}
88+
});
89+
90+
// connect toolButton_addMapping to addMapping
91+
connect(ui->toolButton_addMapping, &QToolButton::clicked, this, &OutputMapping::addMapping);
92+
// connect toolButton_removeMapping to removeMapping
93+
connect(ui->toolButton_removeMapping, &QToolButton::clicked, this,
94+
&OutputMapping::removeMapping);
95+
96+
// connect plainTextEdit_template textChanged to update the mapping template_string
97+
connect(ui->plainTextEdit_template, &QPlainTextEdit::textChanged, [this]() {
98+
// get the selected row
99+
const auto row = ui->tableView->currentIndex().row();
100+
// set the template_string of the selected row to the plainTextEdit_template text
101+
this->mapping_data->mappings[row].template_string =
102+
ui->plainTextEdit_template->toPlainText().toStdString();
103+
// call update_handler
104+
this->update_handler();
105+
});
106+
connect(ui->plainTextEdit_cssProps, &QPlainTextEdit::textChanged, [this]() {
107+
// get the selected row
108+
const auto row = ui->tableView->currentIndex().row();
109+
// set the css_props of the selected row to the plainTextEdit_cssProps text
110+
this->mapping_data->mappings[row].css_props =
111+
ui->plainTextEdit_cssProps->toPlainText().toStdString();
112+
// call update_handler
113+
this->update_handler();
114+
});
115+
116+
// populate the model with the mapping data
117+
for (const auto &mapping : mapping_data->mappings) {
118+
model.appendRow(QList<QStandardItem *>() << new QStandardItem(mapping.name.c_str())
119+
<< new QStandardItem(""));
120+
QComboBox *comboBox = createSourcesComboBox();
121+
if (!mapping.output_source.empty()) {
122+
// select the output_source of the mapping in the comboBox
123+
comboBox->blockSignals(true);
124+
comboBox->setCurrentText(mapping.output_source.c_str());
125+
comboBox->blockSignals(false);
126+
}
127+
// add a row to the model with the data and output_source of the mapping
128+
// set comboBox as the index widget of the last item in the model
129+
ui->tableView->setIndexWidget(model.index(model.rowCount() - 1, 1), comboBox);
130+
}
131+
132+
// connect item edit on tableView
133+
connect(&model, &QStandardItemModel::itemChanged, [this](QStandardItem *item) {
134+
// update mapping name
135+
if (item->column() == 0) {
136+
this->mapping_data->mappings[item->row()].name = item->text().toStdString();
137+
}
138+
});
139+
}
140+
141+
OutputMapping::~OutputMapping()
142+
{
143+
delete ui;
144+
}
145+
146+
/// @brief a helper function that creates a comboBox and adds all text and media sources to the list.
147+
/// The sources are added with a prefix to indicate the source type (Text, Image, or Media).
148+
/// @return a pointer to the comboBox
149+
QComboBox *OutputMapping::createSourcesComboBox()
150+
{
151+
QComboBox *comboBox = new QComboBox(this);
152+
// add "Internal Renderer" to the comboBox
153+
comboBox->addItem(QString::fromStdString(none_internal_rendering));
154+
// add all text and media sources to the comboBox
155+
obs_enum_sources(add_sources_to_list, comboBox);
156+
// connect comboBox to update_handler
157+
connect(comboBox, &QComboBox::currentIndexChanged, [this, comboBox]() {
158+
// get the selected row
159+
const auto row = ui->tableView->currentIndex().row();
160+
// get the output_name of the selected item in the comboBox
161+
const auto output_name = comboBox->currentText().toStdString();
162+
// remove the prefix from the output_name if it exists
163+
std::string output_name_without_prefix = output_name;
164+
if (output_name.find("(Text) ") == 0) {
165+
output_name_without_prefix = output_name.substr(7);
166+
} else if (output_name.find("(Image) ") == 0) {
167+
output_name_without_prefix = output_name.substr(8);
168+
} else if (output_name.find("(Media) ") == 0) {
169+
output_name_without_prefix = output_name.substr(8);
170+
}
171+
// set the css_props of the selected row to the plainTextEdit_cssProps text
172+
this->mapping_data->mappings[row].output_source = output_name_without_prefix;
173+
// call update_handler
174+
this->update_handler();
175+
});
176+
177+
return comboBox;
178+
}
179+
180+
void OutputMapping::addMapping()
181+
{
182+
// add row to model
183+
model.appendRow(QList<QStandardItem *>()
184+
<< new QStandardItem("Mapping") << new QStandardItem("Output"));
185+
QComboBox *comboBox = createSourcesComboBox();
186+
// set comboBox as the index widget of the last item in the model
187+
ui->tableView->setIndexWidget(model.index(model.rowCount() - 1, 1), comboBox);
188+
// add a new mapping to the mapping_data
189+
this->mapping_data->mappings.push_back(output_mapping{
190+
"Mapping", none_internal_rendering, default_template_string, default_css_props});
191+
// call update_handler
192+
this->update_handler();
193+
}
194+
195+
void OutputMapping::removeMapping()
196+
{
197+
// remove the mapping from the mapping_data
198+
this->mapping_data->mappings.erase(this->mapping_data->mappings.begin() +
199+
ui->tableView->currentIndex().row());
200+
// remove row from model
201+
model.removeRow(ui->tableView->currentIndex().row());
202+
// call update_handler
203+
this->update_handler();
204+
}

src/ui/outputmapping.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#ifndef OUTPUTMAPPING_H
2+
#define OUTPUTMAPPING_H
3+
4+
#include <QDialog>
5+
#include <QStandardItemModel>
6+
#include <QComboBox>
7+
#include <functional>
8+
9+
#include "mapping-data.h"
10+
11+
namespace Ui {
12+
class OutputMapping;
13+
}
14+
15+
class OutputMapping : public QDialog {
16+
Q_OBJECT
17+
18+
public:
19+
explicit OutputMapping(output_mapping_data *mapping_data_in,
20+
std::function<void()> update_handler, QWidget *parent = nullptr);
21+
~OutputMapping();
22+
23+
OutputMapping(const OutputMapping &) = delete;
24+
OutputMapping &operator=(const OutputMapping &) = delete;
25+
26+
private:
27+
Ui::OutputMapping *ui;
28+
QStandardItemModel model;
29+
output_mapping_data *mapping_data;
30+
QComboBox *createSourcesComboBox();
31+
std::function<void()> update_handler;
32+
33+
private slots:
34+
void addMapping();
35+
void removeMapping();
36+
};
37+
38+
#endif // OUTPUTMAPPING_H

0 commit comments

Comments
 (0)