diff --git a/src/mapping-data.cpp b/src/mapping-data.cpp index 253a0a5..39023e0 100644 --- a/src/mapping-data.cpp +++ b/src/mapping-data.cpp @@ -12,6 +12,7 @@ std::string serialize_output_mapping_data(const output_mapping_data &data) j_mapping["template_string"] = mapping.template_string; j_mapping["css_props"] = mapping.css_props; j_mapping["unhide_output_source"] = mapping.unhide_output_source; + j_mapping["file_path"] = mapping.file_path; j.push_back(j_mapping); } return j.dump(); @@ -28,6 +29,7 @@ output_mapping_data deserialize_output_mapping_data(const std::string &data) mapping.template_string = j_mapping.value("template_string", ""); mapping.css_props = j_mapping.value("css_props", ""); mapping.unhide_output_source = j_mapping.value("unhide_output_source", false); + mapping.file_path = j_mapping.value("file_path", ""); result.mappings.push_back(mapping); } return result; diff --git a/src/mapping-data.h b/src/mapping-data.h index e757987..5b52aae 100644 --- a/src/mapping-data.h +++ b/src/mapping-data.h @@ -8,6 +8,7 @@ const std::string none_internal_rendering = "None / Internal rendering"; const std::string save_to_setting = "Save to source settings"; +const std::string file_output_rendering = "File output"; struct output_mapping { std::string name; @@ -15,6 +16,7 @@ struct output_mapping { std::string template_string; std::string css_props; bool unhide_output_source = false; + std::string file_path; }; struct output_mapping_data { diff --git a/src/ui/outputmapping.cpp b/src/ui/outputmapping.cpp index 26661b8..f1b96c6 100644 --- a/src/ui/outputmapping.cpp +++ b/src/ui/outputmapping.cpp @@ -6,6 +6,11 @@ #include #include #include +#include +#include +#include +#include +#include namespace { @@ -39,6 +44,7 @@ OutputMapping::OutputMapping(const output_mapping_data &mapping_data_in, ui->plainTextEdit_cssProps->setEnabled(enable); ui->checkBox_unhide_Source->setEnabled(enable); + // populate the props with the data of the selected row if (enable) { // get the selected row const auto row = selected.indexes().first().row(); @@ -48,6 +54,7 @@ OutputMapping::OutputMapping(const output_mapping_data &mapping_data_in, ui->plainTextEdit_template->blockSignals(true); ui->plainTextEdit_cssProps->blockSignals(true); ui->checkBox_unhide_Source->blockSignals(true); + ui->lineEdit_file_output->blockSignals(true); // set the plainTextEdit_template and plainTextEdit_cssProps to the template_string and css_props of the selected row ui->plainTextEdit_template->setPlainText( this->mapping_data.mappings[row].template_string.c_str()); @@ -55,9 +62,12 @@ OutputMapping::OutputMapping(const output_mapping_data &mapping_data_in, this->mapping_data.mappings[row].css_props.c_str()); ui->checkBox_unhide_Source->setChecked( this->mapping_data.mappings[row].unhide_output_source); + ui->lineEdit_file_output->setText( + this->mapping_data.mappings[row].file_path.c_str()); ui->plainTextEdit_template->blockSignals(false); ui->plainTextEdit_cssProps->blockSignals(false); ui->checkBox_unhide_Source->blockSignals(false); + ui->lineEdit_file_output->blockSignals(false); } }); @@ -96,6 +106,21 @@ OutputMapping::OutputMapping(const output_mapping_data &mapping_data_in, this->update_handler(this->mapping_data); }); + // connect toolButton_selectFile to open a file dialog to select a file and update lineEdit_file_output + connect(ui->toolButton_selectFile, &QToolButton::clicked, [this]() { + // open a file dialog to select a file + const auto file = + QFileDialog::getSaveFileName(this, "Select file", "", "All files (*.*)"); + // set the lineEdit_file_output to the selected file + ui->lineEdit_file_output->setText(file); + // get the selected row + const auto row = ui->tableView->currentIndex().row(); + // set the output_source of the selected row to the selected file + this->mapping_data.mappings[row].file_path = file.toStdString(); + // call update_handler + this->update_handler(this->mapping_data); + }); + // populate the model with the mapping data for (const auto &mapping : mapping_data.mappings) { model.appendRow(QList() << new QStandardItem(mapping.name.c_str()) @@ -143,6 +168,8 @@ QComboBox *OutputMapping::createSourcesComboBox() QComboBox *comboBox = new QComboBox(this); // add "Internal Renderer" to the comboBox comboBox->addItem(QString::fromStdString(none_internal_rendering)); + // add "File output" to the comboBox + comboBox->addItem(QString::fromStdString(file_output_rendering)); // add "Save to source settings" to the comboBox comboBox->addItem(QString::fromStdString(save_to_setting)); // add all text and media sources to the comboBox @@ -153,6 +180,12 @@ QComboBox *OutputMapping::createSourcesComboBox() const auto row = ui->tableView->currentIndex().row(); // get the output_name of the selected item in the comboBox const auto output_name = comboBox->currentText().toStdString(); + // if output is file, enable the group box groupBox_outputFile + if (output_name == file_output_rendering) { + ui->widget_file_output->setEnabled(true); + } else { + ui->widget_file_output->setEnabled(false); + } // remove the prefix from the output_name if it exists std::string output_name_without_prefix = output_name; if (output_name.find("(Text) ") == 0) { diff --git a/src/ui/outputmapping.ui b/src/ui/outputmapping.ui index 869d07f..03a39a2 100644 --- a/src/ui/outputmapping.ui +++ b/src/ui/outputmapping.ui @@ -7,7 +7,7 @@ 0 0 457 - 387 + 423 @@ -146,16 +146,6 @@ Use {{body}} variable for unparsed object/array representation of the entire res - - - - false - - - Unhide output source on update - - - @@ -169,6 +159,55 @@ Use {{body}} variable for unparsed object/array representation of the entire res + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Output File + + + + + + + false + + + + + + + ... + + + + + + + + + + false + + + Unhide output source on update + + + diff --git a/src/url-source-callbacks.cpp b/src/url-source-callbacks.cpp index 8854968..aa0c184 100644 --- a/src/url-source-callbacks.cpp +++ b/src/url-source-callbacks.cpp @@ -194,6 +194,22 @@ std::string prepare_text_from_template(const output_mapping &mapping, return text; } +void save_text_to_file(const std::string &text, const std::string &file_path) +{ + // save the text to a file + if (file_path.empty()) { + obs_log(LOG_ERROR, "No file path specified"); + return; + } + std::ofstream file(file_path); + if (!file.is_open()) { + obs_log(LOG_ERROR, "Failed to open file '%s'", file_path.c_str()); + return; + } + file << text; + file.close(); +} + void output_with_mapping(const request_data_handler_response &response, struct url_source_data *usd) { std::vector mappings; @@ -237,6 +253,7 @@ void output_with_mapping(const request_data_handler_response &response, struct u if (is_valid_output_source_name(mapping.output_source.c_str()) && mapping.output_source != none_internal_rendering && + mapping.output_source != file_output_rendering && mapping.output_source != save_to_setting) { // If an output source is selected - use it for rendering setTextCallback(text, mapping); @@ -252,6 +269,9 @@ void output_with_mapping(const request_data_handler_response &response, struct u obs_source_update(usd->source, source_settings); obs_data_release(source_settings); } + } else if (mapping.output_source == file_output_rendering) { + // If the output source is set to file output - save the text to a file + save_text_to_file(text, mapping.file_path); } else { // render the text internally any_internal_rendering = true;