#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
-503 [kommando] FAILED access denied
+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;
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;
{
if(lock)
{
- client()->remove_lifecycle_bound_object(lock->lifecycle_key());
+ lock->release_lock(client());
}
else
{
- //TODO: lock all channels
+ //TODO: release all channels
CASPAR_THROW_EXCEPTION(not_implemented());
}
SetReplyString(L"202 LOCK RELEASE OK\r\n");
}
else if(command == L"CLEAR")
{
- ++it;
- if(it == parameters().end())
+ std::wstring override_phrase = env::properties().get(L"configuration.lock-clear-phrase", L"");
+ std::wstring client_override_phrase;
+ if(!override_phrase.empty())
{
- SetReplyString(L"402 LOCK CLEAR ERROR\r\n");
- return false;
+ ++it;
+ if(it == parameters().end())
+ {
+ SetReplyString(L"402 LOCK CLEAR ERROR\r\n");
+ return false;
+ }
+ client_override_phrase = (*it);
}
- std::wstring override_phrase = (*it);
-
- //TODO: clear locks
+ 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");
}
return false;
}
}
+ catch(not_implemented&)
+ {
+ SetReplyString(L"600 LOCK FAILED\r\n");
+ return false;
+ }
catch(...)
{
CASPAR_LOG_CURRENT_EXCEPTION();
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