Skip to content

Commit

Permalink
[scene_producer] Add optional parameter to autoplay cg producer.
Browse files Browse the repository at this point in the history
Add optional parameter to send template data as json to cg producer.
Handle json formatted template data.
  • Loading branch information
Julusian committed Jan 28, 2018
1 parent 115b220 commit d7f6861
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 24 deletions.
47 changes: 36 additions & 11 deletions core/producer/cg_proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include <boost/algorithm/string/predicate.hpp>
#include <boost/optional.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>

#include <future>
Expand Down Expand Up @@ -321,19 +322,22 @@ class cg_proxy_as_producer : public frame_producer

std::map<std::wstring, std::shared_ptr<core::variable>> variables_;
std::vector<std::wstring> variable_names_;
bool template_data_as_json_;
core::binding<std::wstring> template_data_xml_ { [=] { return generate_template_data_xml(); } };
std::shared_ptr<void> template_data_change_subscription_;
bool is_playing_ = false;
public:
cg_proxy_as_producer(
spl::shared_ptr<frame_producer> producer,
spl::shared_ptr<cg_proxy> proxy,
const std::wstring& template_name,
const bool autoplay,
const bool data_as_json,
const std::vector<std::wstring>& parameter_specification)
: producer_(std::move(producer))
, proxy_(std::move(proxy))
, template_name_(std::move(template_name))
, producer_has_its_own_variables_defined_(!producer_->get_variables().empty())
, template_data_as_json_(data_as_json)
{
if (parameter_specification.size() % 2 != 0)
CASPAR_THROW_EXCEPTION(user_error() << msg_info("Parameter specification must be a sequence of type and parameter name pairs"));
Expand All @@ -356,9 +360,10 @@ class cg_proxy_as_producer : public frame_producer

template_data_change_subscription_ = template_data_xml_.on_change([=]
{
if (is_playing_)
proxy_->update(0, template_data_xml_.get());
proxy_->update(0, template_data_xml_.get());
});

proxy_->add(0, template_name_, autoplay, L"", template_data_xml_.get());
}

// frame_producer
Expand All @@ -368,10 +373,7 @@ class cg_proxy_as_producer : public frame_producer
auto& command = params.at(0);

if (command == L"play()")
{
proxy_->add(0, template_name_, true, L"", template_data_xml_.get());
is_playing_ = true;
}
proxy_->play(0);
else if (command == L"stop()")
proxy_->stop(0, 0);
else if (command == L"next()")
Expand Down Expand Up @@ -418,6 +420,21 @@ class cg_proxy_as_producer : public frame_producer
private:
std::wstring generate_template_data_xml() const
{
if (template_data_as_json_)
{
boost::property_tree::wptree document;

for (auto& parameter_name : variable_names_)
{
document.add_child(parameter_name, boost::property_tree::wptree(variables_.at(parameter_name)->to_string()));
}

std::wstringstream json;
boost::property_tree::json_parser::write_json(json, document, false);
CASPAR_LOG(warning) << json.str();
return json.str();
}

boost::property_tree::wptree document;
boost::property_tree::wptree template_data;

Expand Down Expand Up @@ -456,16 +473,18 @@ class cg_proxy_as_producer : public frame_producer
void describe_cg_proxy_as_producer(core::help_sink& sink, const core::help_repository& repo)
{
sink.short_description(L"Wraps any CG producer for compatibility with scene producer.");
sink.syntax(L"[CG] [template:string] {[param1_type:\"string\",\"number\"] [param1_name:string] {[param2_type:\"string\",\"number\"] [param2_name:string] {...}}}");
sink.syntax(L"[CG] [template:string] {[AUTOPLAY]} {[JSON]|[XML]} {[param1_type:\"string\",\"number\"] [param1_name:string] {[param2_type:\"string\",\"number\"] [param2_name:string] {...}}}");
sink.para()->text(L"Wraps any CG producer for compatibility with scene producer.");
sink.para()->text(L"It allows the user to specify what template parameters should be exposed to the parent scene. This is only required for Flash and HTML templates. PSD and Scene templates does this automatically.");
sink.para()->text(L"Examples only really usable from within the scene producer implementation:");
sink.example(L">> PLAY 1-10 [CG] folder/flas_template string f0 number f1");
sink.example(L">> PLAY 1-10 [CG] folder/flash_template string f0 number f1");
sink.para()->text(L"...followed by the scene producer setting variables causing the equivalent of a ")->code(L"CG ADD")->text(L". Then the following calls can be made:");
sink.example(L">> CALL 1-10 play()");
sink.example(L">> CALL 1-10 next()");
sink.example(L">> CALL 1-10 invoke() label");
sink.example(L">> CALL 1-10 stop()");
sink.para()->text(L"There is an optional autoplay parameter, and an optional parameter to specify what format to compile the variables data to (default xml):");
sink.example(L">> PLAY 1-10 [CG] folder/flash_template [AUTOPLAY] [JSON] string f0");
}

spl::shared_ptr<frame_producer> create_cg_proxy_as_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
Expand All @@ -478,13 +497,19 @@ spl::shared_ptr<frame_producer> create_cg_proxy_as_producer(const core::frame_pr
auto proxy = dependencies.cg_registry->get_proxy(producer);
auto params2 = params;

int skip_params = 2;
const bool autoplay = params.size() > skip_params && boost::iequals(params.at(skip_params), L"[AUTOPLAY]");
if (autoplay) skip_params++;
const bool json_data = params.size() > skip_params && boost::iequals(params.at(skip_params), L"[JSON]");
if (json_data || (params.size() > skip_params && boost::iequals(params.at(skip_params), L"[XML]"))) skip_params++;

if (proxy == cg_proxy::empty())
CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"No template with name " + template_name + L" found"));

// Remove "[CG]" and template_name to only leave the parameter specification part
params2.erase(params2.begin(), params2.begin() + 2);
params2.erase(params2.begin(), params2.begin() + skip_params);

return spl::make_shared<cg_proxy_as_producer>(std::move(producer), std::move(proxy), template_name, params2);
return spl::make_shared<cg_proxy_as_producer>(std::move(producer), std::move(proxy), template_name, autoplay, json_data, params2);
}

void init_cg_proxy_as_producer(core::module_dependencies dependencies)
Expand Down
51 changes: 39 additions & 12 deletions core/producer/scene/scene_cg_proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/property_tree/json_parser.hpp>

#include <sstream>
#include <future>
Expand Down Expand Up @@ -88,27 +89,53 @@ void scene_cg_proxy::update(int layer, const std::wstring& data)
if (data.empty())
return;

std::wstringstream stream(data);
boost::property_tree::wptree root;
boost::property_tree::read_xml(
if (data.at(0) == L'<') {

std::wstringstream stream(data);
boost::property_tree::wptree root;
boost::property_tree::read_xml(
stream,
root,
boost::property_tree::xml_parser::trim_whitespace | boost::property_tree::xml_parser::no_comments);

std::vector<std::wstring> parameters;
std::vector<std::wstring> parameters;

for (auto value : root | witerate_children(L"templateData") | welement_context_iteration)
{
ptree_verify_element_name(value, L"componentData");
for (const auto value : root | witerate_children(L"templateData") | welement_context_iteration)
{
ptree_verify_element_name(value, L"componentData");

auto id = ptree_get<std::wstring>(value.second, L"<xmlattr>.id");
auto val = ptree_get<std::wstring>(value.second, L"data.<xmlattr>.value");

auto id = ptree_get<std::wstring>(value.second, L"<xmlattr>.id");
auto val = ptree_get<std::wstring>(value.second, L"data.<xmlattr>.value");
parameters.push_back(std::move(id));
parameters.push_back(std::move(val));
}

parameters.push_back(std::move(id));
parameters.push_back(std::move(val));
impl_->producer_->call(parameters);
}
else if (data.at(0) == L'{')
{
std::wstringstream stream(data);
boost::property_tree::wptree root;
boost::property_tree::read_json(stream, root);

std::vector<std::wstring> parameters;

for (const auto value : root | welement_context_iteration)
{
std::wstring id = value.first;
std::wstring val = value.second.data();

impl_->producer_->call(parameters);
parameters.push_back(std::move(id));
parameters.push_back(std::move(val));
}

impl_->producer_->call(parameters);
}
else
{
CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Scene template data must be in XML or JSON format"));
}
}

std::wstring scene_cg_proxy::invoke(int layer, const std::wstring& label)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ namespace boost { namespace property_tree {
return new_tree();
}
assert(false);
throw(std::logic_error("reached passed assert!"));
}
string& new_value() {
if (stack.empty()) return new_tree().data();
Expand Down
5 changes: 4 additions & 1 deletion modules/html/producer/html_cg_proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ void html_cg_proxy::next(int layer)

void html_cg_proxy::update(int layer, const std::wstring& data)
{
impl_->producer->call({ (boost::wformat(L"update(\"%1%\")") % boost::algorithm::replace_all_copy(boost::algorithm::trim_copy_if(data, boost::is_any_of(" \"")), "\"", "\\\"")).str() });
auto str = boost::algorithm::trim_copy_if(data, boost::is_any_of("\r\n \""));
str = boost::algorithm::replace_all_copy(str, "\\", "\\\\");
str = boost::algorithm::replace_all_copy(str, "\"", "\\\"");
impl_->producer->call({ (boost::wformat(L"update(\"%1%\")") % str).str() });
}

std::wstring html_cg_proxy::invoke(int layer, const std::wstring& label)
Expand Down

0 comments on commit d7f6861

Please sign in to comment.