X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=blobdiff_plain;f=src%2Ftools%2Fdcpomatic_batch.cc;h=9cb6ddc5546fc3887a598641e9bfc5c0d3ddd996;hp=403c1c21b7273556a805c6eb4950249fd0fec1f9;hb=a5d004b0773f633401528392fc28e66d70e13ac8;hpb=883d885dc8690519d205c8baa275385af8a39f4b diff --git a/src/tools/dcpomatic_batch.cc b/src/tools/dcpomatic_batch.cc index 403c1c21b..9cb6ddc55 100644 --- a/src/tools/dcpomatic_batch.cc +++ b/src/tools/dcpomatic_batch.cc @@ -1,40 +1,66 @@ /* - Copyright (C) 2013 Carl Hetherington + Copyright (C) 2013-2019 Carl Hetherington - This program is free software; you can redistribute it and/or modify + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, + DCP-o-matic is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with DCP-o-matic. If not, see . */ -#include -#include -#include +#include "wx/wx_util.h" +#include "wx/about_dialog.h" +#include "wx/wx_signal_manager.h" +#include "wx/job_manager_view.h" +#include "wx/full_config_dialog.h" +#include "wx/servers_list_dialog.h" +#include "wx/dcpomatic_button.h" #include "lib/version.h" #include "lib/compose.hpp" #include "lib/config.h" #include "lib/util.h" #include "lib/film.h" #include "lib/job_manager.h" -#include "wx/wx_util.h" -#include "wx/wx_ui_signaller.h" -#include "wx/job_manager_view.h" - -using boost::shared_ptr; +#include "lib/job.h" +#include "lib/dcpomatic_socket.h" +#include "lib/transcode_job.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using std::exception; +using std::string; +using std::cout; +using std::list; +using std::set; +using std::shared_ptr; +using boost::thread; +using boost::scoped_array; +using std::dynamic_pointer_cast; +#if BOOST_VERSION >= 106100 +using namespace boost::placeholders; +#endif + +static list films_to_load; enum { ID_file_add_film = 1, - ID_file_quit, + ID_tools_encoding_servers, ID_help_about }; @@ -42,53 +68,159 @@ void setup_menu (wxMenuBar* m) { wxMenu* file = new wxMenu; - file->Append (ID_file_add_film, _("&Add Film...")); - file->Append (ID_file_quit, _("&Quit")); + file->Append (ID_file_add_film, _("&Add Film...\tCtrl-A")); +#ifdef DCPOMATIC_OSX + file->Append (wxID_EXIT, _("&Exit")); +#else + file->Append (wxID_EXIT, _("&Quit")); +#endif + +#ifdef DCPOMATIC_OSX + file->Append (wxID_PREFERENCES, _("&Preferences...\tCtrl-P")); +#else + wxMenu* edit = new wxMenu; + edit->Append (wxID_PREFERENCES, _("&Preferences...\tCtrl-P")); +#endif + + wxMenu* tools = new wxMenu; + tools->Append (ID_tools_encoding_servers, _("Encoding servers...")); wxMenu* help = new wxMenu; help->Append (ID_help_about, _("About")); m->Append (file, _("&File")); +#ifndef DCPOMATIC_OSX + m->Append (edit, _("&Edit")); +#endif + m->Append (tools, _("&Tools")); m->Append (help, _("&Help")); } -class Frame : public wxFrame +class DOMFrame : public wxFrame { public: - Frame (wxString const & title) + explicit DOMFrame (wxString const & title) : wxFrame (NULL, -1, title) + , _sizer (new wxBoxSizer (wxVERTICAL)) + , _config_dialog (0) + , _servers_list_dialog (0) { wxMenuBar* bar = new wxMenuBar; setup_menu (bar); SetMenuBar (bar); - Connect (ID_file_add_film, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_add_film)); - Connect (ID_file_quit, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_quit)); - Connect (ID_help_about, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::help_about)); + Config::instance()->Changed.connect (boost::bind (&DOMFrame::config_changed, this, _1)); + + Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_add_film, this), ID_file_add_film); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_quit, this), wxID_EXIT); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::edit_preferences, this), wxID_PREFERENCES); + 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); s->Add (panel, 1, wxEXPAND); SetSizer (s); - wxSizer* sizer = new wxBoxSizer (wxVERTICAL); - - JobManagerView* job_manager_view = new JobManagerView (panel, JobManagerView::PAUSE); - sizer->Add (job_manager_view, 1, wxALL | wxEXPAND, 6); + JobManagerView* job_manager_view = new JobManagerView (panel, true); + _sizer->Add (job_manager_view, 1, wxALL | wxEXPAND, 6); wxSizer* buttons = new wxBoxSizer (wxHORIZONTAL); - wxButton* add = new wxButton (panel, wxID_ANY, _("Add Film...")); - add->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (Frame::add_film)); + 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); + + setup_sensitivity (); + + _sizer->Add (buttons, 0, wxALL, 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()); + } + + void pause () + { + JobManager::instance()->pause (); + setup_sensitivity (); + } + + void resume () + { + JobManager::instance()->resume (); + setup_sensitivity (); + } + + void start_job (boost::filesystem::path path) + { + try { + shared_ptr film (new Film (path)); + film->read_metadata (); + + double total_required; + double available; + bool can_hard_link; + + film->should_be_enough_disk_space (total_required, available, can_hard_link); + + set > films; + + for (auto i: JobManager::instance()->get()) { + films.insert (i->film()); + } - sizer->Add (buttons, 0, wxALL, 6); + for (auto i: films) { + double progress = 0; + for (auto j: JobManager::instance()->get()) { + if (i == j->film() && dynamic_pointer_cast(j)) { + progress = j->progress().get_value_or(0); + } + } + + double required; + i->should_be_enough_disk_space (required, available, can_hard_link); + total_required += (1 - progress) * required; + } - panel->SetSizer (sizer); + if ((total_required - available) > 1) { + if (!confirm_dialog ( + this, + wxString::Format( + _("The DCPs for this film and the films already in the queue will take up about %.1f GB. The " + "disks that you are using only have %.1f GB available. Do you want to add this film to the queue anyway?"), + total_required, available))) { + return; + } + } - Connect (wxID_ANY, wxEVT_CLOSE_WINDOW, wxCloseEventHandler (Frame::close)); + film->make_dcp (); + } 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())); + } } private: + void sized (wxSizeEvent& ev) + { + _sizer->Layout (); + ev.Skip (); + } + bool should_close () { if (!JobManager::instance()->work_to_do ()) { @@ -106,7 +238,7 @@ private: d->Destroy (); return r; } - + void close (wxCloseEvent& ev) { if (!should_close ()) { @@ -117,55 +249,51 @@ private: ev.Skip (); } - void file_add_film (wxCommandEvent& ev) + void file_add_film () { - add_film (ev); + add_film (); } - - void file_quit (wxCommandEvent &) + + void file_quit () { if (should_close ()) { Close (true); } } - void help_about (wxCommandEvent &) + void edit_preferences () { - wxAboutDialogInfo info; - info.SetName (_("DCP-o-matic Batch Converter")); - if (strcmp (dcpomatic_git_commit, "release") == 0) { - info.SetVersion (std_to_wx (String::compose ("version %1", dcpomatic_version))); - } else { - info.SetVersion (std_to_wx (String::compose ("version %1 git %2", dcpomatic_version, dcpomatic_git_commit))); + if (!_config_dialog) { + _config_dialog = create_full_config_dialog (); } - info.SetDescription (_("Free, open-source DCP generation from almost anything.")); - info.SetCopyright (_("(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen")); - - wxArrayString authors; - authors.Add (wxT ("Carl Hetherington")); - authors.Add (wxT ("Terrence Meiczinger")); - authors.Add (wxT ("Paul Davis")); - authors.Add (wxT ("Ole Laursen")); - info.SetDevelopers (authors); - - wxArrayString translators; - translators.Add (wxT ("Olivier Perriere")); - translators.Add (wxT ("Lilian Lefranc")); - translators.Add (wxT ("Thierry Journet")); - translators.Add (wxT ("Massimiliano Broggi")); - translators.Add (wxT ("Manuel AC")); - translators.Add (wxT ("Adam Klotblixt")); - info.SetTranslators (translators); - - info.SetWebSite (wxT ("http://carlh.net/software/dcpomatic")); - wxAboutBox (info); - } - - void add_film (wxCommandEvent &) + _config_dialog->Show (this); + } + + void tools_encoding_servers () + { + if (!_servers_list_dialog) { + _servers_list_dialog = new ServersListDialog (this); + } + + _servers_list_dialog->Show (); + } + + void help_about () + { + AboutDialog* d = new AboutDialog (this); + 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); + if (_last_parent) { + c->SetPath (std_to_wx (_last_parent.get().string ())); + } + int r; - while (1) { + while (true) { r = c->ShowModal (); if (r == wxID_OK && c->GetPath() == wxStandardPaths::Get().GetDocumentsDir()) { error_dialog (this, _("You did not select a folder. Make sure that you select a folder before clicking Open.")); @@ -173,44 +301,118 @@ private: break; } } - + if (r == wxID_OK) { + start_job (wx_to_std (c->GetPath ())); + } + + _last_parent = boost::filesystem::path (wx_to_std (c->GetPath ())).parent_path (); + + c->Destroy (); + } + + void config_changed (Config::Property what) + { + /* Instantly save any config changes when using the DCP-o-matic GUI */ + if (what == Config::CINEMAS) { try { - shared_ptr film (new Film (wx_to_std (c->GetPath ()))); - film->read_metadata (); - film->make_dcp (); - } catch (std::exception& e) { - wxString p = c->GetPath (); - wxCharBuffer b = p.ToUTF8 (); - error_dialog (this, wxString::Format (_("Could not open film at %s (%s)"), p.data(), std_to_wx (e.what()).data())); + Config::instance()->write_cinemas(); + } catch (exception& e) { + error_dialog ( + this, + 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() + ) + ); + } + } else { + try { + Config::instance()->write_config(); + } catch (exception& e) { + error_dialog ( + this, + 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() + ) + ); } } + } - c->Destroy (); + boost::optional _last_parent; + wxSizer* _sizer; + wxPreferencesEditor* _config_dialog; + ServersListDialog* _servers_list_dialog; + wxButton* _pause; + wxButton* _resume; +}; + +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: + explicit JobServer (DOMFrame* frame) + : Server (BATCH_JOB_PORT) + , _frame (frame) + {} + + void handle (shared_ptr socket) + { + try { + int const length = socket->read_uint32 (); + 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); + } catch (...) { + + } } + +private: + DOMFrame* _frame; }; class App : public wxApp { bool OnInit () { + 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)); + + wxSplashScreen* splash = maybe_show_splash (); + if (!wxApp::OnInit()) { return false; } - -#ifdef DCPOMATIC_POSIX + +#ifdef DCPOMATIC_LINUX unsetenv ("UBUNTU_MENUPROXY"); -#endif +#endif + + dcpomatic_setup_path_encoding (); /* Enable i18n; this will create a Config object to look for a force-configured language. This Config object will be wrong, however, because dcpomatic_setup - hasn't yet been called and there aren't any scalers, filters etc. + hasn't yet been called and there aren't any filters etc. set up yet. */ dcpomatic_setup_i18n (); - /* Set things up, including scalers / filters etc. + /* Set things up, including filters etc. which will now be internationalised correctly. */ dcpomatic_setup (); @@ -220,21 +422,71 @@ class App : public wxApp */ Config::drop (); - Frame* f = new Frame (_("DCP-o-matic Batch Converter")); - SetTopWindow (f); - f->Maximize (); - f->Show (); + _frame = new DOMFrame (_("DCP-o-matic Batch Converter")); + SetTopWindow (_frame); + _frame->Maximize (); + if (splash) { + splash->Destroy (); + } + _frame->Show (); + + JobServer* server = new JobServer (_frame); + new thread (boost::bind (&JobServer::run, server)); + + signal_manager = new wxSignalManager (this); + this->Bind (wxEVT_IDLE, boost::bind (&App::idle, this)); + + shared_ptr film; + for (auto i: films_to_load) { + if (boost::filesystem::is_directory (i)) { + try { + film.reset (new Film (i)); + film->read_metadata (); + film->make_dcp (); + } catch (exception& e) { + error_dialog ( + 0, + std_to_wx (String::compose (wx_to_std (_("Could not load film %1")), i.string())), + std_to_wx(e.what()) + ); + } + } + } + + return true; + } + + void idle () + { + signal_manager->ui_idle (); + } - ui_signaller = new wxUISignaller (this); - this->Connect (-1, wxEVT_IDLE, wxIdleEventHandler (App::idle)); + void OnInitCmdLine (wxCmdLineParser& parser) + { + parser.SetDesc (command_line_description); + parser.SetSwitchChars (wxT ("-")); + } + + bool OnCmdLineParsed (wxCmdLineParser& parser) + { + for (size_t i = 0; i < parser.GetParamCount(); ++i) { + films_to_load.push_back (wx_to_std (parser.GetParam(i))); + } return true; } - void idle (wxIdleEvent &) + void config_failed_to_load () + { + message_dialog (_frame, _("The existing configuration failed to load. Default values will be used instead. These may take a short time to create.")); + } + + void config_warning (string m) { - ui_signaller->ui_idle (); + message_dialog (_frame, std_to_wx (m)); } + + DOMFrame* _frame; }; IMPLEMENT_APP (App)