X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Ftools%2Fdcpomatic_batch.cc;h=64015a0e30179faa7a0dd7b78bf05a2109dc2e21;hb=b7e65adf286ce20918797a06a910ededf8f07b7b;hp=48c7c2da5c88f1ce9534659f617502f3bd3c0b68;hpb=f7b75c6ff5e62fff7c37ff4080625c5510a298da;p=dcpomatic.git diff --git a/src/tools/dcpomatic_batch.cc b/src/tools/dcpomatic_batch.cc index 48c7c2da5..64015a0e3 100644 --- a/src/tools/dcpomatic_batch.cc +++ b/src/tools/dcpomatic_batch.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2019 Carl Hetherington + Copyright (C) 2013-2021 Carl Hetherington This file is part of DCP-o-matic. @@ -18,54 +18,68 @@ */ -#include "wx/wx_util.h" + #include "wx/about_dialog.h" -#include "wx/wx_signal_manager.h" -#include "wx/job_manager_view.h" +#include "wx/dcpomatic_button.h" #include "wx/full_config_dialog.h" +#include "wx/job_manager_view.h" #include "wx/servers_list_dialog.h" -#include "wx/dcpomatic_button.h" -#include "lib/version.h" +#include "wx/wx_signal_manager.h" +#include "wx/wx_util.h" #include "lib/compose.hpp" #include "lib/config.h" -#include "lib/util.h" +#include "lib/dcpomatic_socket.h" #include "lib/film.h" -#include "lib/job_manager.h" #include "lib/job.h" -#include "lib/dcpomatic_socket.h" +#include "lib/job_manager.h" +#include "lib/make_dcp.h" +#include "lib/scope_guard.h" #include "lib/transcode_job.h" +#include "lib/util.h" +#include "lib/version.h" +#include +LIBDCP_DISABLE_WARNINGS #include -#include #include -#include +#include #include +#include +#include #include -#include +LIBDCP_ENABLE_WARNINGS #include #include -using std::exception; -using std::string; + using std::cout; +using std::dynamic_pointer_cast; +using std::exception; using std::list; +using std::make_shared; using std::set; -using boost::shared_ptr; -using boost::thread; +using std::shared_ptr; +using std::string; using boost::scoped_array; -using boost::dynamic_pointer_cast; +using boost::thread; +#if BOOST_VERSION >= 106100 +using namespace boost::placeholders; +#endif + static list films_to_load; + enum { ID_file_add_film = 1, ID_tools_encoding_servers, ID_help_about }; + void setup_menu (wxMenuBar* m) { - wxMenu* file = new wxMenu; + auto file = new wxMenu; file->Append (ID_file_add_film, _("&Add Film...\tCtrl-A")); #ifdef DCPOMATIC_OSX file->Append (wxID_EXIT, _("&Exit")); @@ -76,14 +90,14 @@ setup_menu (wxMenuBar* m) #ifdef DCPOMATIC_OSX file->Append (wxID_PREFERENCES, _("&Preferences...\tCtrl-P")); #else - wxMenu* edit = new wxMenu; + auto edit = new wxMenu; edit->Append (wxID_PREFERENCES, _("&Preferences...\tCtrl-P")); #endif - wxMenu* tools = new wxMenu; + auto tools = new wxMenu; tools->Append (ID_tools_encoding_servers, _("Encoding servers...")); - wxMenu* help = new wxMenu; + auto help = new wxMenu; help->Append (ID_help_about, _("About")); m->Append (file, _("&File")); @@ -94,16 +108,45 @@ setup_menu (wxMenuBar* m) m->Append (help, _("&Help")); } + class DOMFrame : public wxFrame { public: + enum class Tool { + ADD, + PAUSE + }; + + class DCPDropTarget : public wxFileDropTarget + { + public: + DCPDropTarget(DOMFrame* owner) + : _frame(owner) + {} + + bool OnDropFiles(wxCoord, wxCoord, wxArrayString const& filenames) override + { + if (filenames.GetCount() == 1) { + /* Try to load a directory */ + auto path = boost::filesystem::path(wx_to_std(filenames[0])); + if (boost::filesystem::is_directory(path)) { + _frame->start_job(wx_to_std(filenames[0])); + return true; + } + } + + return false; + } + + private: + DOMFrame* _frame; + }; + explicit DOMFrame (wxString const & title) - : wxFrame (NULL, -1, title) - , _sizer (new wxBoxSizer (wxVERTICAL)) - , _config_dialog (0) - , _servers_list_dialog (0) + : wxFrame (nullptr, -1, title) + , _sizer (new wxBoxSizer(wxVERTICAL)) { - wxMenuBar* bar = new wxMenuBar; + auto bar = new wxMenuBar; setup_menu (bar); SetMenuBar (bar); @@ -115,57 +158,58 @@ public: Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_encoding_servers, this), ID_tools_encoding_servers); Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_about, this), ID_help_about); - wxPanel* panel = new wxPanel (this); - wxSizer* s = new wxBoxSizer (wxHORIZONTAL); + auto panel = new wxPanel (this); + auto s = new wxBoxSizer (wxHORIZONTAL); s->Add (panel, 1, wxEXPAND); SetSizer (s); - JobManagerView* job_manager_view = new JobManagerView (panel, true); - _sizer->Add (job_manager_view, 1, wxALL | wxEXPAND, 6); + wxBitmap add(icon_path("add"), wxBITMAP_TYPE_PNG); + wxBitmap pause(icon_path("pause"), wxBITMAP_TYPE_PNG); - wxSizer* buttons = new wxBoxSizer (wxHORIZONTAL); - wxButton* add = new Button (panel, _("Add Film...")); - add->Bind (wxEVT_BUTTON, boost::bind (&DOMFrame::add_film, this)); - buttons->Add (add, 1, wxALL, 6); - _pause = new Button (panel, _("Pause")); - _pause->Bind (wxEVT_BUTTON, boost::bind(&DOMFrame::pause, this)); - buttons->Add (_pause, 1, wxALL, 6); - _resume = new Button (panel, _("Resume")); - _resume->Bind (wxEVT_BUTTON, boost::bind(&DOMFrame::resume, this)); - buttons->Add (_resume, 1, wxALL, 6); + auto toolbar = new wxToolBar(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL); + toolbar->SetMargins(4, 4); + toolbar->SetToolBitmapSize(wxSize(32, 32)); + toolbar->AddTool(static_cast(Tool::ADD), _("Add film"), add, _("Add film for conversion")); + toolbar->AddCheckTool(static_cast(Tool::PAUSE), _("Pause/resume"), pause, wxNullBitmap, _("Pause or resume conversion")); + toolbar->Realize(); + _sizer->Add(toolbar, 0, wxALL, 6); - setup_sensitivity (); + toolbar->Bind(wxEVT_TOOL, bind(&DOMFrame::tool_clicked, this, _1)); - _sizer->Add (buttons, 0, wxALL, 6); + auto job_manager_view = new JobManagerView (panel, true); + _sizer->Add (job_manager_view, 1, wxALL | wxEXPAND, 6); panel->SetSizer (_sizer); - Bind (wxEVT_CLOSE_WINDOW, boost::bind (&DOMFrame::close, this, _1)); - Bind (wxEVT_SIZE, boost::bind (&DOMFrame::sized, this, _1)); - } - - void setup_sensitivity () - { - _pause->Enable (!JobManager::instance()->paused()); - _resume->Enable (JobManager::instance()->paused()); - } + Bind (wxEVT_CLOSE_WINDOW, boost::bind(&DOMFrame::close, this, _1)); + Bind (wxEVT_SIZE, boost::bind(&DOMFrame::sized, this, _1)); - void pause () - { - JobManager::instance()->pause (); - setup_sensitivity (); + SetDropTarget(new DCPDropTarget(this)); } - void resume () + void tool_clicked(wxCommandEvent& ev) { - JobManager::instance()->resume (); - setup_sensitivity (); + switch (static_cast(ev.GetId())) { + case Tool::ADD: + add_film(); + break; + case Tool::PAUSE: + { + auto jm = JobManager::instance(); + if (jm->paused()) { + jm->resume(); + } else { + jm->pause(); + } + break; + } + } } void start_job (boost::filesystem::path path) { try { - shared_ptr film (new Film (path)); + auto film = make_shared(path); film->read_metadata (); double total_required; @@ -174,15 +218,15 @@ public: film->should_be_enough_disk_space (total_required, available, can_hard_link); - set > films; + set> films; - BOOST_FOREACH (shared_ptr i, JobManager::instance()->get()) { + for (auto i: JobManager::instance()->get()) { films.insert (i->film()); } - BOOST_FOREACH (shared_ptr i, films) { + for (auto i: films) { double progress = 0; - BOOST_FOREACH (shared_ptr j, JobManager::instance()->get()) { + for (auto j: JobManager::instance()->get()) { if (i == j->film() && dynamic_pointer_cast(j)) { progress = j->progress().get_value_or(0); } @@ -204,11 +248,11 @@ public: } } - film->make_dcp (); + make_dcp (film, TranscodeJob::ChangedBehaviour::STOP); } catch (std::exception& e) { - wxString p = std_to_wx (path.string ()); - wxCharBuffer b = p.ToUTF8 (); - error_dialog (this, wxString::Format (_("Could not open film at %s"), p.data()), std_to_wx(e.what())); + auto p = std_to_wx (path.string ()); + auto b = p.ToUTF8 (); + error_dialog (this, wxString::Format(_("Could not open film at %s"), p.data()), std_to_wx(e.what())); } } @@ -221,25 +265,24 @@ private: bool should_close () { - if (!JobManager::instance()->work_to_do ()) { + if (!JobManager::instance()->work_to_do()) { return true; } - wxMessageDialog* d = new wxMessageDialog ( + auto d = new wxMessageDialog ( 0, _("There are unfinished jobs; are you sure you want to quit?"), _("Unfinished jobs"), wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION ); + ScopeGuard sg = [d]{ d->Destroy(); }; - bool const r = d->ShowModal() == wxID_YES; - d->Destroy (); - return r; + return d->ShowModal() == wxID_YES; } void close (wxCloseEvent& ev) { - if (!should_close ()) { + if (!should_close()) { ev.Veto (); return; } @@ -254,7 +297,7 @@ private: void file_quit () { - if (should_close ()) { + if (should_close()) { Close (true); } } @@ -278,22 +321,23 @@ private: void help_about () { - AboutDialog* d = new AboutDialog (this); + auto d = new AboutDialog (this); + ScopeGuard sg = [d]() { d->Destroy(); }; d->ShowModal (); - d->Destroy (); } void add_film () { - wxDirDialog* c = new wxDirDialog (this, _("Select film to open"), wxStandardPaths::Get().GetDocumentsDir(), wxDEFAULT_DIALOG_STYLE | wxDD_DIR_MUST_EXIST); + auto dialog = new wxDirDialog(this, _("Select film to open"), wxStandardPaths::Get().GetDocumentsDir(), wxDEFAULT_DIALOG_STYLE | wxDD_DIR_MUST_EXIST); + ScopeGuard sg = [dialog]() { dialog->Destroy(); }; if (_last_parent) { - c->SetPath (std_to_wx (_last_parent.get().string ())); + dialog->SetPath(std_to_wx(_last_parent.get().string())); } int r; while (true) { - r = c->ShowModal (); - if (r == wxID_OK && c->GetPath() == wxStandardPaths::Get().GetDocumentsDir()) { + r = dialog->ShowModal(); + if (r == wxID_OK && dialog->GetPath() == wxStandardPaths::Get().GetDocumentsDir()) { error_dialog (this, _("You did not select a folder. Make sure that you select a folder before clicking Open.")); } else { break; @@ -301,12 +345,10 @@ private: } if (r == wxID_OK) { - start_job (wx_to_std (c->GetPath ())); + start_job(wx_to_std(dialog->GetPath())); } - _last_parent = boost::filesystem::path (wx_to_std (c->GetPath ())).parent_path (); - - c->Destroy (); + _last_parent = boost::filesystem::path(wx_to_std(dialog->GetPath())).parent_path(); } void config_changed (Config::Property what) @@ -318,7 +360,7 @@ private: } catch (exception& e) { error_dialog ( this, - wxString::Format ( + wxString::Format( _("Could not write to cinemas file at %s. Your changes have not been saved."), std_to_wx (Config::instance()->cinemas_file().string()).data() ) @@ -330,7 +372,7 @@ private: } catch (exception& e) { error_dialog ( this, - wxString::Format ( + wxString::Format( _("Could not write to config file at %s. Your changes have not been saved."), std_to_wx (Config::instance()->cinemas_file().string()).data() ) @@ -341,17 +383,17 @@ private: boost::optional _last_parent; wxSizer* _sizer; - wxPreferencesEditor* _config_dialog; - ServersListDialog* _servers_list_dialog; - wxButton* _pause; - wxButton* _resume; + wxPreferencesEditor* _config_dialog = nullptr; + ServersListDialog* _servers_list_dialog = nullptr; }; + static const wxCmdLineEntryDesc command_line_description[] = { { wxCMD_LINE_PARAM, 0, 0, "film to load", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL }, { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 } }; + class JobServer : public Server { public: @@ -360,15 +402,15 @@ public: , _frame (frame) {} - void handle (shared_ptr socket) + void handle (shared_ptr socket) override { try { int const length = socket->read_uint32 (); - scoped_array buffer (new char[length]); - socket->read (reinterpret_cast (buffer.get()), length); + scoped_array buffer(new char[length]); + socket->read (reinterpret_cast(buffer.get()), length); string s (buffer.get()); _frame->start_job (s); - socket->write (reinterpret_cast ("OK"), 3); + socket->write (reinterpret_cast("OK"), 3); } catch (...) { } @@ -378,17 +420,20 @@ private: DOMFrame* _frame; }; + class App : public wxApp { - bool OnInit () + bool OnInit () override { + wxInitAllImageHandlers (); + SetAppName (_("DCP-o-matic Batch Converter")); is_batch_converter = true; - Config::FailedToLoad.connect (boost::bind (&App::config_failed_to_load, this)); - Config::Warning.connect (boost::bind (&App::config_warning, this, _1)); + Config::FailedToLoad.connect(boost::bind(&App::config_failed_to_load, this, _1)); + Config::Warning.connect (boost::bind(&App::config_warning, this, _1)); - wxSplashScreen* splash = maybe_show_splash (); + auto splash = maybe_show_splash (); if (!wxApp::OnInit()) { return false; @@ -426,23 +471,27 @@ class App : public wxApp } _frame->Show (); - JobServer* server = new JobServer (_frame); - new thread (boost::bind (&JobServer::run, server)); + try { + auto server = new JobServer (_frame); + new thread (boost::bind (&JobServer::run, server)); + } catch (boost::system::system_error& e) { + error_dialog(_frame, _("Could not listen for new batch jobs. Perhaps another instance of the DCP-o-matic Batch Converter is running.")); + } signal_manager = new wxSignalManager (this); this->Bind (wxEVT_IDLE, boost::bind (&App::idle, this)); shared_ptr film; - BOOST_FOREACH (boost::filesystem::path i, films_to_load) { - if (boost::filesystem::is_directory (i)) { + for (auto i: films_to_load) { + if (boost::filesystem::is_directory(i)) { try { - film.reset (new Film (i)); + film = make_shared(i); film->read_metadata (); - film->make_dcp (); + make_dcp (film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP); } catch (exception& e) { error_dialog ( 0, - std_to_wx (String::compose (wx_to_std (_("Could not load film %1")), i.string())), + std_to_wx(String::compose(wx_to_std(_("Could not load film %1")), i.string())), std_to_wx(e.what()) ); } @@ -457,32 +506,33 @@ class App : public wxApp signal_manager->ui_idle (); } - void OnInitCmdLine (wxCmdLineParser& parser) + void OnInitCmdLine (wxCmdLineParser& parser) override { parser.SetDesc (command_line_description); parser.SetSwitchChars (wxT ("-")); } - bool OnCmdLineParsed (wxCmdLineParser& parser) + bool OnCmdLineParsed (wxCmdLineParser& parser) override { for (size_t i = 0; i < parser.GetParamCount(); ++i) { - films_to_load.push_back (wx_to_std (parser.GetParam(i))); + films_to_load.push_back (wx_to_std(parser.GetParam(i))); } return true; } - void config_failed_to_load () + void config_failed_to_load(Config::LoadFailure what) { - message_dialog (_frame, _("The existing configuration failed to load. Default values will be used instead. These may take a short time to create.")); + report_config_load_failure(_frame, what); } void config_warning (string m) { - message_dialog (_frame, std_to_wx (m)); + message_dialog (_frame, std_to_wx(m)); } DOMFrame* _frame; }; + IMPLEMENT_APP (App)