#include <common/diagnostics/graph.h>
#include <common/os/windows/current_version.h>
#include <common/os/windows/system_info.h>
+#include <common/base64.h>
#include <core/producer/frame_producer.h>
#include <core/video_format.h>
#include <core/producer/layer.h>
#include <core/mixer/mixer.h>
#include <core/consumer/output.h>
+#include <core/thumbnail_generator.h>
#include <modules/reroute/producer/reroute_producer.h>
#include <modules/bluefish/bluefish.h>
#include <boost/locale.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/copy.hpp>
+#include <boost/archive/iterators/base64_from_binary.hpp>
+#include <boost/archive/iterators/insert_linebreaks.hpp>
+#include <boost/archive/iterators/transform_width.hpp>
#include <tbb/concurrent_unordered_map.h>
/* Return codes
-100 [action] Information om att något har hänt
-101 [action] Information om att något har hänt, en rad data skickas
+102 [action] Information that [action] has happened
+101 [action] Information that [action] has happened plus one row of data
-202 [kommando] OK Kommandot har utförts
-201 [kommando] OK Kommandot har utförts, och en rad data skickas tillbaka
-200 [kommando] OK Kommandot har utförts, och flera rader data skickas tillbaka. Avslutas med tomrad
+202 [command] OK [command] has been executed
+201 [command] OK [command] has been executed, plus one row of data
+200 [command] OK [command] has been executed, plus multiple lines of data. ends with an empty line
-400 ERROR Kommandot kunde inte förstås
-401 [kommando] ERROR Ogiltig kanal
-402 [kommando] ERROR Parameter saknas
-403 [kommando] ERROR Ogiltig parameter
-404 [kommando] ERROR Mediafilen hittades inte
+400 ERROR the command could not be understood
+401 [command] ERROR invalid/missing channel
+402 [command] ERROR parameter missing
+403 [command] ERROR invalid parameter
+404 [command] ERROR file not found
-500 FAILED Internt configurationfel
-501 [kommando] FAILED Internt configurationfel
-502 [kommando] FAILED Oläslig mediafil
+500 FAILED internal error
+501 [command] FAILED internal error
+502 [command] FAILED could not read file
+503 [command] FAILED access denied
-600 [kommando] FAILED funktion ej implementerad
+600 [command] FAILED [command] not implemented
*/
namespace caspar { namespace protocol {
using namespace core;
+std::wstring read_file_base64(const boost::filesystem::wpath& file)
+{
+ using namespace boost::archive::iterators;
+
+ boost::filesystem::ifstream filestream(file, std::ios::binary);
+
+ if (!filestream)
+ return L"";
+
+ auto length = boost::filesystem::file_size(file);
+ std::vector<char> bytes;
+ bytes.resize(length);
+ filestream.read(bytes.data(), length);
+
+ std::string result(to_base64(bytes.data(), length));
+ return std::wstring(result.begin(), result.end());
+}
+
std::wstring read_utf8_file(const boost::filesystem::wpath& file)
{
std::wstringstream result;
namespace amcp {
-AMCPCommand::AMCPCommand() : channelIndex_(0), layerIndex_(-1)
-{}
-
void AMCPCommand::SendReply()
{
- if(!pClientInfo_)
- return;
-
if(replyString_.empty())
return;
- pClientInfo_->Send(replyString_);
-}
-
-void AMCPCommand::Clear()
-{
- pChannel_->stage().clear();
- pClientInfo_.reset();
- channelIndex_ = 0;
- parameters_.clear();
+ client_->send(std::move(replyString_));
}
bool DiagnosticsCommand::DoExecute()
CASPAR_THROW_EXCEPTION(not_implemented());
//int index = 1;
- //auto self = GetChannels().back();
+ //auto self = channels().back();
//
//std::vector<std::wstring> params;
//params.push_back(L"SCREEN");
//self->output().add(screen);
- //BOOST_FOREACH(auto channel, GetChannels())
+ //BOOST_FOREACH(auto channel, channels())
//{
// if(channel != self)
// {
// }
//}
- //int n = GetChannels().size()-1;
+ //int n = channels().size()-1;
//double delta = 1.0/static_cast<double>(n);
//for(int x = 0; x < n; ++x)
//{
//Perform loading of the clip
try
{
- auto result = GetChannel()->stage().call(GetLayerIndex(), parameters());
+ auto result = channel()->stage().call(layer_index(), parameters());
if(!result.timed_wait(boost::posix_time::seconds(2)))
CASPAR_THROW_EXCEPTION(timed_out());
if(boost::iequals(parameters()[0], L"KEYER") || boost::iequals(parameters()[0], L"IS_KEY"))
{
bool value = boost::lexical_cast<int>(parameters().at(1));
- transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+ transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
{
transform.image_transform.is_key = value;
return transform;
double value = boost::lexical_cast<double>(parameters().at(1));
- transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+ transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
{
transform.image_transform.opacity = value;
return transform;
double x_s = boost::lexical_cast<double>(parameters().at(3));
double y_s = boost::lexical_cast<double>(parameters().at(4));
- transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) mutable -> frame_transform
+ transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) mutable -> frame_transform
{
transform.image_transform.fill_translation[0] = x;
transform.image_transform.fill_translation[1] = y;
double x_s = boost::lexical_cast<double>(parameters().at(3));
double y_s = boost::lexical_cast<double>(parameters().at(4));
- transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+ transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
{
transform.image_transform.clip_translation[0] = x;
transform.image_transform.clip_translation[1] = y;
else if(boost::iequals(parameters()[0], L"BLEND"))
{
auto blend_str = parameters().at(1);
- int layer = GetLayerIndex();
- GetChannel()->mixer().set_blend_mode(GetLayerIndex(), get_blend_mode(blend_str));
+ int layer = layer_index();
+ channel()->mixer().set_blend_mode(layer_index(), get_blend_mode(blend_str));
}
else if(boost::iequals(parameters()[0], L"MASTERVOLUME"))
{
float master_volume = boost::lexical_cast<float>(parameters().at(1));
- GetChannel()->mixer().set_master_volume(master_volume);
+ channel()->mixer().set_master_volume(master_volume);
}
else if(boost::iequals(parameters()[0], L"BRIGHTNESS"))
{
auto value = boost::lexical_cast<double>(parameters().at(1));
int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
- transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+ transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
{
transform.image_transform.brightness = value;
return transform;
auto value = boost::lexical_cast<double>(parameters().at(1));
int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
- transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+ transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
{
transform.image_transform.saturation = value;
return transform;
auto value = boost::lexical_cast<double>(parameters().at(1));
int duration = parameters().size() > 2 ? boost::lexical_cast<int>(parameters()[2]) : 0;
std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
- transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+ transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
{
transform.image_transform.contrast = value;
return transform;
int duration = parameters().size() > 6 ? boost::lexical_cast<int>(parameters()[6]) : 0;
std::wstring tween = parameters().size() > 7 ? parameters()[7] : L"linear";
- transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+ transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
{
transform.image_transform.levels = value;
return transform;
std::wstring tween = parameters().size() > 3 ? parameters()[3] : L"linear";
double value = boost::lexical_cast<double>(parameters()[1]);
- transforms.push_back(stage::transform_tuple_t(GetLayerIndex(), [=](frame_transform transform) -> frame_transform
+ transforms.push_back(stage::transform_tuple_t(layer_index(), [=](frame_transform transform) -> frame_transform
{
transform.audio_transform.volume = value;
return transform;
}
else if(boost::iequals(parameters()[0], L"CLEAR"))
{
- int layer = GetLayerIndex(std::numeric_limits<int>::max());
+ int layer = layer_index(std::numeric_limits<int>::max());
if (layer == std::numeric_limits<int>::max())
{
- GetChannel()->stage().clear_transforms();
- GetChannel()->mixer().clear_blend_modes();
+ channel()->stage().clear_transforms();
+ channel()->mixer().clear_blend_modes();
}
else
{
- GetChannel()->stage().clear_transforms(layer);
- GetChannel()->mixer().clear_blend_mode(layer);
+ channel()->stage().clear_transforms(layer);
+ channel()->mixer().clear_blend_mode(layer);
}
}
else if(boost::iequals(parameters()[0], L"COMMIT"))
{
- transforms = std::move(deferred_transforms[GetChannelIndex()]);
+ transforms = std::move(deferred_transforms[channel_index()]);
}
else
{
if(defer)
{
- auto& defer_tranforms = deferred_transforms[GetChannelIndex()];
+ auto& defer_tranforms = deferred_transforms[channel_index()];
defer_tranforms.insert(defer_tranforms.end(), transforms.begin(), transforms.end());
}
else
- GetChannel()->stage().apply_transforms(transforms);
+ channel()->stage().apply_transforms(transforms);
SetReplyString(TEXT("202 MIXER OK\r\n"));
//Perform loading of the clip
try
{
- if(GetLayerIndex(-1) != -1)
+ if(layer_index(-1) != -1)
{
std::vector<std::string> strs;
boost::split(strs, parameters()[0], boost::is_any_of("-"));
- auto ch1 = GetChannel();
- auto ch2 = GetChannels().at(boost::lexical_cast<int>(strs.at(0))-1);
+ auto ch1 = channel();
+ auto ch2 = channels().at(boost::lexical_cast<int>(strs.at(0))-1);
- int l1 = GetLayerIndex();
+ int l1 = layer_index();
int l2 = boost::lexical_cast<int>(strs.at(1));
- ch1->stage().swap_layer(l1, l2, ch2->stage());
+ ch1->stage().swap_layer(l1, l2, ch2.channel->stage());
}
else
{
- auto ch1 = GetChannel();
- auto ch2 = GetChannels().at(boost::lexical_cast<int>(parameters()[0])-1);
- ch1->stage().swap_layers(ch2->stage());
+ auto ch1 = channel();
+ auto ch2 = channels().at(boost::lexical_cast<int>(parameters()[0])-1);
+ ch1->stage().swap_layers(ch2.channel->stage());
}
SetReplyString(TEXT("202 SWAP OK\r\n"));
}
auto consumer = create_consumer(parameters());
- GetChannel()->output().add(GetLayerIndex(consumer->index()), consumer);
+ channel()->output().add(layer_index(consumer->index()), consumer);
SetReplyString(TEXT("202 ADD OK\r\n"));
//Perform loading of the clip
try
{
- auto index = GetLayerIndex(std::numeric_limits<int>::min());
+ auto index = layer_index(std::numeric_limits<int>::min());
if(index == std::numeric_limits<int>::min())
{
//create_consumer still expects all parameters to be uppercase
index = create_consumer(parameters())->index();
}
- GetChannel()->output().remove(index);
+ channel()->output().remove(index);
SetReplyString(TEXT("202 REMOVE OK\r\n"));
//Perform loading of the clip
try
{
- auto pFP = create_producer(GetChannel()->frame_factory(), GetChannel()->video_format_desc(), parameters());
- GetChannel()->stage().load(GetLayerIndex(), pFP, true);
+ auto pFP = create_producer(channel()->frame_factory(), channel()->video_format_desc(), parameters());
+ channel()->stage().load(layer_index(), pFP, true);
SetReplyString(TEXT("202 LOAD OK\r\n"));
if(boost::regex_match(parameters().at(0), what, expr))
{
auto channel_index = boost::lexical_cast<int>(what["CHANNEL"].str());
- pFP = reroute::create_producer(*GetChannels().at(channel_index-1));
+ pFP = reroute::create_producer(*channels().at(channel_index-1).channel);
}
else
{
- pFP = create_producer(GetChannel()->frame_factory(), GetChannel()->video_format_desc(), parameters());
+ pFP = create_producer(channel()->frame_factory(), channel()->video_format_desc(), parameters());
}
if(pFP == frame_producer::empty())
bool auto_play = contains_param(L"AUTO", parameters());
- auto pFP2 = create_transition_producer(GetChannel()->video_format_desc().field_mode, spl::make_shared_ptr(pFP), transitionInfo);
+ auto pFP2 = create_transition_producer(channel()->video_format_desc().field_mode, spl::make_shared_ptr(pFP), transitionInfo);
if(auto_play)
- GetChannel()->stage().load(GetLayerIndex(), pFP2, false, transitionInfo.duration); // TODO: LOOP
+ channel()->stage().load(layer_index(), pFP2, false, transitionInfo.duration); // TODO: LOOP
else
- GetChannel()->stage().load(GetLayerIndex(), pFP2, false); // TODO: LOOP
+ channel()->stage().load(layer_index(), pFP2, false); // TODO: LOOP
SetReplyString(TEXT("202 LOADBG OK\r\n"));
{
try
{
- GetChannel()->stage().pause(GetLayerIndex());
+ channel()->stage().pause(layer_index());
SetReplyString(TEXT("202 PAUSE OK\r\n"));
return true;
}
{
if(!parameters().empty())
{
- LoadbgCommand lbg;
- lbg.SetChannel(GetChannel());
- lbg.SetChannels(GetChannels());
- lbg.SetChannelIndex(GetChannelIndex());
- lbg.SetLayerIntex(GetLayerIndex());
- lbg.SetClientInfo(GetClientInfo());
- for(auto it = parameters().begin(); it != parameters().end(); ++it)
- lbg.AddParameter(*it);
+ LoadbgCommand lbg(*this);
+
if(!lbg.Execute())
throw std::exception();
}
- GetChannel()->stage().play(GetLayerIndex());
+ channel()->stage().play(layer_index());
SetReplyString(TEXT("202 PLAY OK\r\n"));
return true;
{
try
{
- GetChannel()->stage().stop(GetLayerIndex());
+ channel()->stage().stop(layer_index());
SetReplyString(TEXT("202 STOP OK\r\n"));
return true;
}
bool ClearCommand::DoExecute()
{
- int index = GetLayerIndex(std::numeric_limits<int>::min());
+ int index = layer_index(std::numeric_limits<int>::min());
if(index != std::numeric_limits<int>::min())
- GetChannel()->stage().clear(index);
+ channel()->stage().clear(index);
else
- GetChannel()->stage().clear();
+ channel()->stage().clear();
SetReplyString(TEXT("202 CLEAR OK\r\n"));
bool PrintCommand::DoExecute()
{
- GetChannel()->output().add(create_consumer(boost::assign::list_of(L"IMAGE")));
+ channel()->output().add(create_consumer(boost::assign::list_of(L"IMAGE")));
SetReplyString(TEXT("202 PRINT OK\r\n"));
std::wstring filename = parameters()[2];
filename.append(extension);
- flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).add(layer, filename, bDoStart, label, (pDataString!=0) ? pDataString : TEXT(""));
+ flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).add(layer, filename, bDoStart, label, (pDataString!=0) ? pDataString : TEXT(""));
SetReplyString(TEXT("202 CG OK\r\n"));
}
else
return false;
}
int layer = boost::lexical_cast<int>(parameters()[1]);
- flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).play(layer);
+ flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).play(layer);
}
else
{
return false;
}
int layer = boost::lexical_cast<int>(parameters()[1]);
- flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).stop(layer, 0);
+ flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).stop(layer, 0);
}
else
{
}
int layer = boost::lexical_cast<int>(parameters()[1]);
- flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).next(layer);
+ flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).next(layer);
}
else
{
}
int layer = boost::lexical_cast<int>(parameters()[1]);
- flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).remove(layer);
+ flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).remove(layer);
}
else
{
bool CGCommand::DoExecuteClear()
{
- GetChannel()->stage().clear(GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER));
+ channel()->stage().clear(layer_index(flash::cg_proxy::DEFAULT_LAYER));
SetReplyString(TEXT("202 CG OK\r\n"));
return true;
}
}
int layer = boost::lexical_cast<int>(parameters()[1]);
- flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).update(layer, dataString);
+ flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).update(layer, dataString);
}
catch(...)
{
return false;
}
int layer = boost::lexical_cast<int>(parameters()[1]);
- auto result = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).invoke(layer, parameters()[2]);
+ auto result = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).invoke(layer, parameters()[2]);
replyString << result << TEXT("\r\n");
}
else
}
int layer = boost::lexical_cast<int>(parameters()[1]);
- auto desc = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).description(layer);
+ auto desc = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).description(layer);
replyString << desc << TEXT("\r\n");
}
else
{
- auto info = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(GetChannel()), GetLayerIndex(flash::cg_proxy::DEFAULT_LAYER)).template_host_info();
+ auto info = flash::create_cg_proxy(spl::shared_ptr<core::video_channel>(channel()), layer_index(flash::cg_proxy::DEFAULT_LAYER)).template_host_info();
replyString << info << TEXT("\r\n");
}
std::wstringstream reply(TEXT("201 DATA RETRIEVE OK\r\n"));
- reply << file_contents;
+ std::wstringstream file_contents_stream(file_contents);
+ std::wstring line;
+
+ bool firstLine = true;
+ while(std::getline(file_contents_stream, line))
+ {
+ if(firstLine)
+ firstLine = false;
+ else
+ reply << "\n";
+
+ reply << line;
+ }
+
reply << "\r\n";
SetReplyString(reply.str());
return true;
boost::property_tree::wptree info;
int index = 0;
- BOOST_FOREACH(auto channel, channels_)
- info.add_child(L"channels.channel", channel->info())
+ BOOST_FOREACH(auto channel, channels())
+ info.add_child(L"channels.channel", channel.channel->info())
.add(L"index", ++index);
boost::property_tree::write_xml(replyString, info, w);
if(layer == std::numeric_limits<int>::min())
{
- info.add_child(L"channel", channels_.at(channel)->info())
+ info.add_child(L"channel", channels().at(channel).channel->info())
.add(L"index", channel);
}
else
if(parameters().size() >= 2)
{
if(boost::iequals(parameters()[1], L"B"))
- info.add_child(L"producer", channels_.at(channel)->stage().background(layer).get()->info());
+ info.add_child(L"producer", channels().at(channel).channel->stage().background(layer).get()->info());
else
- info.add_child(L"producer", channels_.at(channel)->stage().foreground(layer).get()->info());
+ info.add_child(L"producer", channels().at(channel).channel->stage().foreground(layer).get()->info());
}
else
{
- info.add_child(L"layer", channels_.at(channel)->stage().info(layer).get())
+ info.add_child(L"layer", channels().at(channel).channel->stage().info(layer).get())
.add(L"index", layer);
}
}
{
// This is needed for backwards compatibility with old clients
replyString << TEXT("200 INFO OK\r\n");
- for(size_t n = 0; n < channels_.size(); ++n)
- GenerateChannelInfo(n, channels_[n], replyString);
+ for(size_t n = 0; n < channels().size(); ++n)
+ GenerateChannelInfo(n, channels()[n].channel, replyString);
}
}
bool ByeCommand::DoExecute()
{
- GetClientInfo()->Disconnect();
+ client()->disconnect();
return true;
}
auto format_desc = core::video_format_desc(value);
if(format_desc.format != core::video_format::invalid)
{
- GetChannel()->video_format_desc(format_desc);
+ channel()->video_format_desc(format_desc);
SetReplyString(TEXT("202 SET MODE OK\r\n"));
}
else
return true;
}
+bool LockCommand::DoExecute()
+{
+ try
+ {
+ auto it = parameters().begin();
+
+ std::shared_ptr<caspar::IO::lock_container> lock;
+ try
+ {
+ int channel_index = boost::lexical_cast<int>(*it) - 1;
+ lock = channels().at(channel_index).lock;
+ }
+ catch(const boost::bad_lexical_cast&) {}
+ catch(...)
+ {
+ SetReplyString(L"401 LOCK ERROR\r\n");
+ return false;
+ }
+
+ if(lock)
+ ++it;
+
+ if(it == parameters().end()) //too few parameters
+ {
+ SetReplyString(L"402 LOCK ERROR\r\n");
+ return false;
+ }
+
+ std::wstring command = boost::to_upper_copy(*it);
+ if(command == L"ACQUIRE")
+ {
+ ++it;
+ if(it == parameters().end()) //too few parameters
+ {
+ SetReplyString(L"402 LOCK ACQUIRE ERROR\r\n");
+ return false;
+ }
+ std::wstring lock_phrase = (*it);
+
+ //TODO: read options
+
+ if(lock)
+ {
+ //just lock one channel
+ if(!lock->try_lock(lock_phrase, client()))
+ {
+ SetReplyString(L"503 LOCK ACQUIRE FAILED\r\n");
+ return false;
+ }
+ }
+ else
+ {
+ //TODO: lock all channels
+ CASPAR_THROW_EXCEPTION(not_implemented());
+ }
+ SetReplyString(L"202 LOCK ACQUIRE OK\r\n");
+
+ }
+ else if(command == L"RELEASE")
+ {
+ if(lock)
+ {
+ lock->release_lock(client());
+ }
+ else
+ {
+ //TODO: release all channels
+ CASPAR_THROW_EXCEPTION(not_implemented());
+ }
+ SetReplyString(L"202 LOCK RELEASE OK\r\n");
+ }
+ else if(command == L"CLEAR")
+ {
+ std::wstring override_phrase = env::properties().get(L"configuration.lock-clear-phrase", L"");
+ std::wstring client_override_phrase;
+ if(!override_phrase.empty())
+ {
+ ++it;
+ if(it == parameters().end())
+ {
+ SetReplyString(L"402 LOCK CLEAR ERROR\r\n");
+ return false;
+ }
+ client_override_phrase = (*it);
+ }
+
+ if(lock)
+ {
+ //just clear one channel
+ if(client_override_phrase != override_phrase)
+ {
+ SetReplyString(L"503 LOCK CLEAR FAILED\r\n");
+ return false;
+ }
+
+ lock->clear_locks();
+ }
+ else
+ {
+ //TODO: clear all channels
+ CASPAR_THROW_EXCEPTION(not_implemented());
+ }
+
+ SetReplyString(L"202 LOCK CLEAR OK\r\n");
+ }
+ else
+ {
+ SetReplyString(L"403 LOCK ERROR\r\n");
+ return false;
+ }
+ }
+ catch(not_implemented&)
+ {
+ SetReplyString(L"600 LOCK FAILED\r\n");
+ return false;
+ }
+ catch(...)
+ {
+ CASPAR_LOG_CURRENT_EXCEPTION();
+ SetReplyString(L"501 LOCK FAILED\r\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool ThumbnailCommand::DoExecute()
+{
+ std::wstring command = boost::to_upper_copy(parameters()[0]);
+
+ if (command == TEXT("RETRIEVE"))
+ return DoExecuteRetrieve();
+ else if (command == TEXT("LIST"))
+ return DoExecuteList();
+ else if (command == TEXT("GENERATE"))
+ return DoExecuteGenerate();
+ else if (command == TEXT("GENERATE_ALL"))
+ return DoExecuteGenerateAll();
+
+ SetReplyString(TEXT("403 THUMBNAIL ERROR\r\n"));
+ return false;
+}
+
+bool ThumbnailCommand::DoExecuteRetrieve()
+{
+ if(parameters().size() < 2)
+ {
+ SetReplyString(TEXT("402 THUMBNAIL RETRIEVE ERROR\r\n"));
+ return false;
+ }
+
+ std::wstring filename = env::thumbnails_folder();
+ filename.append(parameters()[1]);
+ filename.append(TEXT(".png"));
+
+ std::wstring file_contents = read_file_base64(boost::filesystem::wpath(filename));
+
+ if (file_contents.empty())
+ {
+ SetReplyString(TEXT("404 THUMBNAIL RETRIEVE ERROR\r\n"));
+ return false;
+ }
+
+ std::wstringstream reply;
+
+ reply << L"201 THUMBNAIL RETRIEVE OK\r\n";
+ reply << file_contents;
+ reply << L"\r\n";
+ SetReplyString(reply.str());
+ return true;
+}
+
+bool ThumbnailCommand::DoExecuteList()
+{
+ std::wstringstream replyString;
+ replyString << TEXT("200 THUMBNAIL LIST OK\r\n");
+
+ for (boost::filesystem::recursive_directory_iterator itr(env::thumbnails_folder()), end; itr != end; ++itr)
+ {
+ if(boost::filesystem::is_regular_file(itr->path()))
+ {
+ if(!boost::iequals(itr->path().extension().wstring(), L".png"))
+ continue;
+
+ auto relativePath = boost::filesystem::wpath(itr->path().wstring().substr(env::thumbnails_folder().size()-1, itr->path().wstring().size()));
+
+ auto str = relativePath.replace_extension(L"").native();
+ if(str[0] == '\\' || str[0] == '/')
+ str = std::wstring(str.begin() + 1, str.end());
+
+ auto mtime = boost::filesystem::last_write_time(itr->path());
+ auto mtime_readable = boost::posix_time::to_iso_wstring(boost::posix_time::from_time_t(mtime));
+ auto file_size = boost::filesystem::file_size(itr->path());
+
+ replyString << L"\"" << str << L"\" " << mtime_readable << L" " << file_size << L"\r\n";
+ }
+ }
+
+ replyString << TEXT("\r\n");
+
+ SetReplyString(boost::to_upper_copy(replyString.str()));
+ return true;
+}
+
+bool ThumbnailCommand::DoExecuteGenerate()
+{
+ if (parameters().size() < 2)
+ {
+ SetReplyString(L"402 THUMBNAIL GENERATE ERROR\r\n");
+ return false;
+ }
+
+ if (thumb_gen_)
+ {
+ thumb_gen_->generate(parameters()[1]);
+ SetReplyString(L"202 THUMBNAIL GENERATE OK\r\n");
+ return true;
+ }
+ else
+ {
+ SetReplyString(L"500 THUMBNAIL GENERATE ERROR\r\n");
+ return false;
+ }
+}
+
+bool ThumbnailCommand::DoExecuteGenerateAll()
+{
+ if (thumb_gen_)
+ {
+ thumb_gen_->generate_all();
+ SetReplyString(L"202 THUMBNAIL GENERATE_ALL OK\r\n");
+ return true;
+ }
+ else
+ {
+ SetReplyString(L"500 THUMBNAIL GENERATE_ALL ERROR\r\n");
+ return false;
+ }
+}
+
+bool KillCommand::DoExecute()
+{
+ shutdown_server_now_->set_value(false); //false for not attempting to restart
+ SetReplyString(TEXT("202 KILL OK\r\n"));
+ return true;
+}
+
+bool RestartCommand::DoExecute()
+{
+ shutdown_server_now_->set_value(true); //true for attempting to restart
+ SetReplyString(TEXT("202 RESTART OK\r\n"));
+ return true;
+}
} //namespace amcp
}} //namespace caspar
\ No newline at end of file