Skip to content

Commit a665e26

Browse files
authored
chore: Add support for parsing key-value pairs in URL source response (#97)
1 parent 802803c commit a665e26

File tree

9 files changed

+119
-40
lines changed

9 files changed

+119
-40
lines changed

buildspec.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
}
4646
},
4747
"name": "obs-urlsource",
48-
"version": "0.3.0",
48+
"version": "0.3.1",
4949
"author": "Roy Shilkrot",
5050
"website": "https://github.com/occ-ai/obs-urlsource",
5151
"email": "roy.shil@gmail.com",

src/parsers/CMakeLists.txt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
target_sources(
22
${CMAKE_PROJECT_NAME}
3-
PRIVATE jsonpointer.cpp
4-
jsonpath.cpp
5-
regex.cpp
6-
xml.cpp
3+
PRIVATE binary-data.cpp
74
errors.cpp
85
html.cpp
9-
binary-data.cpp)
6+
jsonpath.cpp
7+
jsonpointer.cpp
8+
key-value.cpp
9+
regex.cpp
10+
xml.cpp)
1011

1112
# on linux, disable conversion errors
1213
if(UNIX AND NOT APPLE)

src/parsers/key-value.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include "errors.h"
2+
#include "request-data.h"
3+
4+
#include <obs-module.h>
5+
6+
#include <sstream>
7+
#include <string>
8+
9+
struct request_data_handler_response parse_key_value(struct request_data_handler_response response,
10+
const url_source_request_data *request_data)
11+
{
12+
13+
UNUSED_PARAMETER(request_data);
14+
15+
// Create a string stream from the body
16+
std::istringstream body_stream(response.body);
17+
std::string line;
18+
19+
// Iterate through each line of the body
20+
while (std::getline(body_stream, line)) {
21+
// Skip empty lines
22+
if (line.empty())
23+
continue;
24+
25+
// Split the line by the delimiter
26+
// look for the first occurrence of the delimiter from the beginning of the line
27+
size_t delimiter_pos = line.find(request_data->kv_delimiter);
28+
if (delimiter_pos != std::string::npos) {
29+
std::string key = line.substr(0, delimiter_pos);
30+
std::string value = line.substr(delimiter_pos + 1);
31+
response.key_value_pairs[key] = value;
32+
response.body_parts_parsed.push_back(line);
33+
}
34+
}
35+
36+
return response;
37+
}

src/parsers/parsers.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ struct request_data_handler_response
3232
parse_xml_by_xquery(struct request_data_handler_response response,
3333
const url_source_request_data *request_data);
3434

35+
struct request_data_handler_response parse_key_value(struct request_data_handler_response response,
36+
const url_source_request_data *request_data);
37+
3538
#endif // PARSERS_H

src/request-data.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,8 @@ struct request_data_handler_response request_data_handler(url_source_request_dat
464464
// attempt to parse as json and return the whole object
465465
response = parse_json(response, request_data);
466466
}
467+
} else if (request_data->output_type == "Key-Value") {
468+
response = parse_key_value(response, request_data);
467469
} else if (request_data->output_type == "XML (XPath)") {
468470
response = parse_xml(response, request_data);
469471
} else if (request_data->output_type == "XML (XQuery)") {
@@ -545,6 +547,7 @@ std::string serialize_request_data(url_source_request_data *request_data)
545547
json["output_regex_flags"] = request_data->output_regex_flags;
546548
json["output_regex_group"] = request_data->output_regex_group;
547549
json["output_cssselector"] = request_data->output_cssselector;
550+
json["kv_delimiter"] = request_data->kv_delimiter;
548551
// postprocess options
549552
json["post_process_regex"] = request_data->post_process_regex;
550553
json["post_process_regex_is_replace"] = request_data->post_process_regex_is_replace;
@@ -622,6 +625,7 @@ url_source_request_data unserialize_request_data(std::string serialized_request_
622625
request_data.output_regex_flags = json["output_regex_flags"].get<std::string>();
623626
request_data.output_regex_group = json["output_regex_group"].get<std::string>();
624627
request_data.output_cssselector = json.value("output_cssselector", "");
628+
request_data.kv_delimiter = json.value("kv_delimiter", "=");
625629

626630
// postprocess options
627631
request_data.post_process_regex = json.value("post_process_regex", "");

src/request-data.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ struct url_source_request_data {
119119
std::string post_process_regex;
120120
bool post_process_regex_is_replace;
121121
std::string post_process_regex_replace;
122+
std::string kv_delimiter;
122123

123124
// default constructor
124125
url_source_request_data()
@@ -149,6 +150,10 @@ struct url_source_request_data {
149150
output_regex_flags = std::string("");
150151
output_regex_group = std::string("0");
151152
output_cssselector = std::string("");
153+
post_process_regex = std::string("");
154+
post_process_regex_is_replace = false;
155+
post_process_regex_replace = std::string("");
156+
kv_delimiter = std::string("=");
152157
}
153158
};
154159

@@ -158,6 +163,7 @@ struct request_data_handler_response {
158163
nlohmann::json body_json;
159164
std::string content_type;
160165
std::vector<std::string> body_parts_parsed;
166+
std::map<std::string, std::string> key_value_pairs;
161167
std::map<std::string, std::string> headers;
162168
int status_code = URL_SOURCE_REQUEST_SUCCESS;
163169
long http_status_code;

src/ui/RequestBuilder.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,18 +434,23 @@ RequestBuilder::RequestBuilder(url_source_request_data *request_data,
434434
ui->outputRegexGroupLineEdit->setText(
435435
QString::fromStdString(request_data->output_regex_group));
436436
ui->cssSelectorLineEdit->setText(QString::fromStdString(request_data->output_cssselector));
437+
ui->lineEdit_delimiter->setText(QString::fromStdString(request_data->kv_delimiter));
437438
auto setVisibilityOfOutputParsingOptions = [=]() {
438439
// Hide all output parsing options
439440
for (const auto &widget :
440441
{ui->outputJSONPathLineEdit, ui->outputXPathLineEdit, ui->outputXQueryLineEdit,
441442
ui->outputRegexLineEdit, ui->outputRegexFlagsLineEdit,
442443
ui->outputRegexGroupLineEdit, ui->outputJSONPointerLineEdit,
443-
ui->cssSelectorLineEdit, ui->postProcessRegexLineEdit}) {
444+
ui->cssSelectorLineEdit, ui->postProcessRegexLineEdit,
445+
ui->lineEdit_delimiter}) {
444446
set_form_row_visibility(ui->formOutputParsing, widget, false);
445447
}
446448

447449
// Show the output parsing options for the selected output type
448-
if (ui->outputTypeComboBox->currentText() == "JSON") {
450+
if (ui->outputTypeComboBox->currentText() == "Key-Value") {
451+
set_form_row_visibility(ui->formOutputParsing, ui->lineEdit_delimiter,
452+
true);
453+
} else if (ui->outputTypeComboBox->currentText() == "JSON") {
449454
set_form_row_visibility(ui->formOutputParsing, ui->outputJSONPathLineEdit,
450455
true);
451456
set_form_row_visibility(ui->formOutputParsing,
@@ -577,6 +582,8 @@ RequestBuilder::RequestBuilder(url_source_request_data *request_data,
577582
ui->outputRegexGroupLineEdit->text().toStdString();
578583
request_data_for_saving->output_cssselector =
579584
ui->cssSelectorLineEdit->text().toStdString();
585+
request_data_for_saving->kv_delimiter =
586+
ui->lineEdit_delimiter->text().toStdString();
580587

581588
// Save the postprocess regex options
582589
request_data_for_saving->post_process_regex =

src/ui/requestbuilder.ui

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<x>0</x>
88
<y>0</y>
99
<width>493</width>
10-
<height>809</height>
10+
<height>931</height>
1111
</rect>
1212
</property>
1313
<property name="sizePolicy">
@@ -747,7 +747,7 @@
747747
<item row="0" column="0">
748748
<widget class="QLabel" name="label_7">
749749
<property name="text">
750-
<string>Content-Type</string>
750+
<string>Parsing Type</string>
751751
</property>
752752
</widget>
753753
</item>
@@ -758,6 +758,11 @@
758758
<string>Text</string>
759759
</property>
760760
</item>
761+
<item>
762+
<property name="text">
763+
<string>Key-Value</string>
764+
</property>
765+
</item>
761766
<item>
762767
<property name="text">
763768
<string>Image (data)</string>
@@ -790,102 +795,102 @@
790795
</item>
791796
</widget>
792797
</item>
793-
<item row="1" column="0">
798+
<item row="2" column="0">
794799
<widget class="QLabel" name="label_8">
795800
<property name="text">
796801
<string>JSON Pointer</string>
797802
</property>
798803
</widget>
799804
</item>
800-
<item row="1" column="1">
805+
<item row="2" column="1">
801806
<widget class="QLineEdit" name="outputJSONPointerLineEdit">
802807
<property name="placeholderText">
803808
<string/>
804809
</property>
805810
</widget>
806811
</item>
807-
<item row="2" column="0">
812+
<item row="3" column="0">
808813
<widget class="QLabel" name="label_9">
809814
<property name="text">
810815
<string>JSON Path</string>
811816
</property>
812817
</widget>
813818
</item>
814-
<item row="2" column="1">
819+
<item row="3" column="1">
815820
<widget class="QLineEdit" name="outputJSONPathLineEdit"/>
816821
</item>
817-
<item row="3" column="0">
822+
<item row="4" column="0">
818823
<widget class="QLabel" name="label_10">
819824
<property name="text">
820825
<string>XPath</string>
821826
</property>
822827
</widget>
823828
</item>
824-
<item row="3" column="1">
829+
<item row="4" column="1">
825830
<widget class="QLineEdit" name="outputXPathLineEdit"/>
826831
</item>
827-
<item row="4" column="0">
832+
<item row="5" column="0">
828833
<widget class="QLabel" name="label_11">
829834
<property name="text">
830835
<string>XQuery</string>
831836
</property>
832837
</widget>
833838
</item>
834-
<item row="4" column="1">
839+
<item row="5" column="1">
835840
<widget class="QLineEdit" name="outputXQueryLineEdit"/>
836841
</item>
837-
<item row="5" column="0">
842+
<item row="6" column="0">
838843
<widget class="QLabel" name="label_12">
839844
<property name="text">
840845
<string>Regex</string>
841846
</property>
842847
</widget>
843848
</item>
844-
<item row="5" column="1">
849+
<item row="6" column="1">
845850
<widget class="QLineEdit" name="outputRegexLineEdit"/>
846851
</item>
847-
<item row="6" column="0">
852+
<item row="7" column="0">
848853
<widget class="QLabel" name="label_13">
849854
<property name="text">
850855
<string>Regex Flags</string>
851856
</property>
852857
</widget>
853858
</item>
854-
<item row="6" column="1">
859+
<item row="7" column="1">
855860
<widget class="QLineEdit" name="outputRegexFlagsLineEdit"/>
856861
</item>
857-
<item row="7" column="0">
862+
<item row="8" column="0">
858863
<widget class="QLabel" name="label_14">
859864
<property name="text">
860865
<string>Regex Group</string>
861866
</property>
862867
</widget>
863868
</item>
864-
<item row="7" column="1">
869+
<item row="8" column="1">
865870
<widget class="QLineEdit" name="outputRegexGroupLineEdit">
866871
<property name="text">
867872
<string>0</string>
868873
</property>
869874
</widget>
870875
</item>
871-
<item row="8" column="0">
876+
<item row="9" column="0">
872877
<widget class="QLabel" name="label_17">
873878
<property name="text">
874879
<string>CSS Selector</string>
875880
</property>
876881
</widget>
877882
</item>
878-
<item row="8" column="1">
883+
<item row="9" column="1">
879884
<widget class="QLineEdit" name="cssSelectorLineEdit"/>
880885
</item>
881-
<item row="9" column="0">
886+
<item row="10" column="0">
882887
<widget class="QLabel" name="label_15">
883888
<property name="text">
884889
<string>Postprocess Regex</string>
885890
</property>
886891
</widget>
887892
</item>
888-
<item row="9" column="1">
893+
<item row="10" column="1">
889894
<widget class="QWidget" name="widget_9" native="true">
890895
<layout class="QHBoxLayout" name="horizontalLayout_7">
891896
<property name="leftMargin">
@@ -916,6 +921,20 @@
916921
</layout>
917922
</widget>
918923
</item>
924+
<item row="1" column="1">
925+
<widget class="QLineEdit" name="lineEdit_delimiter">
926+
<property name="text">
927+
<string>=</string>
928+
</property>
929+
</widget>
930+
</item>
931+
<item row="1" column="0">
932+
<widget class="QLabel" name="label_18">
933+
<property name="text">
934+
<string>Delimiter</string>
935+
</property>
936+
</widget>
937+
</item>
919938
</layout>
920939
</widget>
921940
</item>

src/url-source-callbacks.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -100,21 +100,25 @@ void setAudioCallback(const std::string &str, const output_mapping &mapping)
100100
};
101101

102102
std::string renderOutputTemplate(inja::Environment &env, const std::string &input,
103-
const std::vector<std::string> &outputs,
104-
const nlohmann::json &body)
103+
const request_data_handler_response &response)
105104
try {
106105
// Use Inja to render the template
107106
nlohmann::json data;
108-
if (outputs.size() > 1) {
109-
for (size_t i = 0; i < outputs.size(); i++) {
110-
data["output" + std::to_string(i)] = outputs[i];
107+
if (response.body_parts_parsed.size() > 1) {
108+
for (size_t i = 0; i < response.body_parts_parsed.size(); i++) {
109+
data["output" + std::to_string(i)] = response.body_parts_parsed[i];
111110
}
112111
// in "output" add an array of all the outputs
113-
data["output"] = outputs;
112+
data["output"] = response.body_parts_parsed;
114113
} else {
115-
data["output"] = outputs[0];
114+
data["output"] = response.body_parts_parsed[0];
116115
}
117-
data["body"] = body;
116+
if (response.key_value_pairs.size() > 0) {
117+
for (const auto &pair : response.key_value_pairs) {
118+
data[pair.first] = pair.second;
119+
}
120+
}
121+
data["body"] = response.body_json;
118122
return env.render(input, data);
119123
} catch (std::exception &e) {
120124
obs_log(LOG_ERROR, "Failed to parse template: %s", e.what());
@@ -172,8 +176,7 @@ std::string prepare_text_from_template(const output_mapping &mapping,
172176
mime_type = response.headers.at("content-type");
173177
}
174178
} else {
175-
text = renderOutputTemplate(env, text, response.body_parts_parsed,
176-
response.body_json);
179+
text = renderOutputTemplate(env, text, response);
177180
// use fetch_image to get the image
178181
image_data = fetch_image(text.c_str(), mime_type);
179182
}
@@ -182,8 +185,7 @@ std::string prepare_text_from_template(const output_mapping &mapping,
182185
// build an image tag with the base64 image
183186
text = "<img src=\"data:" + mime_type + ";base64," + base64_image + "\" />";
184187
} else {
185-
text = renderOutputTemplate(env, text, response.body_parts_parsed,
186-
response.body_json);
188+
text = renderOutputTemplate(env, text, response);
187189
}
188190
return text;
189191
}

0 commit comments

Comments
 (0)