]> git.sesse.net Git - casparcg/blobdiff - protocol/amcp/AMCPCommandsImpl.cpp
manually merged 4a2171b from master
[casparcg] / protocol / amcp / AMCPCommandsImpl.cpp
index 38f89f7f5e56e2b834fcabeda15112e4e1ca40cb..cc877bada57287842370fc127bd19edd3622094c 100644 (file)
@@ -35,6 +35,7 @@
 #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>
@@ -44,6 +45,7 @@
 #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;
@@ -1332,7 +1355,20 @@ bool DataCommand::DoExecuteRetrieve()
 
        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;
@@ -1741,27 +1777,46 @@ bool LockCommand::DoExecute()
                {
                        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");
                }
@@ -1771,6 +1826,11 @@ bool LockCommand::DoExecute()
                        return false;
                }
        }
+       catch(not_implemented&)
+       {
+               SetReplyString(L"600 LOCK FAILED\r\n");
+               return false;
+       }
        catch(...)
        {
                CASPAR_LOG_CURRENT_EXCEPTION();
@@ -1781,6 +1841,133 @@ bool LockCommand::DoExecute()
        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