diff options
Diffstat (limited to 'src/tools')
| -rw-r--r-- | src/tools/dcpomatic.cc | 272 | ||||
| -rw-r--r-- | src/tools/dcpomatic_batch.cc | 67 | ||||
| -rw-r--r-- | src/tools/dcpomatic_cli.cc | 21 | ||||
| -rw-r--r-- | src/tools/dcpomatic_combiner.cc | 25 | ||||
| -rw-r--r-- | src/tools/dcpomatic_disk.cc | 60 | ||||
| -rw-r--r-- | src/tools/dcpomatic_editor.cc | 27 | ||||
| -rw-r--r-- | src/tools/dcpomatic_kdm.cc | 57 | ||||
| -rw-r--r-- | src/tools/dcpomatic_player.cc | 186 | ||||
| -rw-r--r-- | src/tools/dcpomatic_playlist.cc | 67 | ||||
| -rw-r--r-- | src/tools/dcpomatic_server.cc | 15 | ||||
| -rw-r--r-- | src/tools/dcpomatic_server_cli.cc | 29 | ||||
| -rw-r--r-- | src/tools/dcpomatic_verifier.cc | 251 | ||||
| -rw-r--r-- | src/tools/wscript | 89 |
13 files changed, 856 insertions, 310 deletions
diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index 584ebd27d..0a6728731 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -26,6 +26,7 @@ #include "wx/about_dialog.h" #include "wx/content_panel.h" +#include "wx/dcp_referencing_dialog.h" #include "wx/dkdm_dialog.h" #include "wx/export_subtitles_dialog.h" #include "wx/export_video_file_dialog.h" @@ -41,6 +42,7 @@ #include "wx/id.h" #include "wx/job_manager_view.h" #include "wx/kdm_dialog.h" +#include "wx/load_config_from_zip_dialog.h" #include "wx/nag_dialog.h" #include "wx/paste_dialog.h" #include "wx/recreate_chain_dialog.h" @@ -55,6 +57,7 @@ #include "wx/video_waveform_dialog.h" #include "wx/wx_signal_manager.h" #include "wx/wx_util.h" +#include "wx/wx_variant.h" #include "lib/analytics.h" #include "lib/audio_content.h" #include "lib/check_content_job.h" @@ -73,9 +76,12 @@ #include "lib/email.h" #include "lib/encode_server_finder.h" #include "lib/exceptions.h" -#include "lib/ffmpeg_encoder.h" +#include "lib/ffmpeg_film_encoder.h" #include "lib/film.h" #include "lib/font_config.h" +#ifdef DCPOMATIC_GROK +#include "lib/grok/context.h" +#endif #include "lib/hints.h" #include "lib/job_manager.h" #include "lib/kdm_with_metadata.h" @@ -85,15 +91,18 @@ #include "lib/screen.h" #include "lib/send_kdm_email_job.h" #include "lib/signal_manager.h" -#include "lib/subtitle_encoder.h" +#include "lib/subtitle_film_encoder.h" #include "lib/text_content.h" #include "lib/transcode_job.h" +#include "lib/unzipper.h" #include "lib/update_checker.h" +#include "lib/variant.h" #include "lib/version.h" #include "lib/video_content.h" #include <dcp/exceptions.h> #include <dcp/filesystem.h> #include <dcp/raw_convert.h> +#include <dcp/scope_guard.h> #include <dcp/warnings.h> LIBDCP_DISABLE_WARNINGS #include <wx/cmdline.h> @@ -207,6 +216,7 @@ private: #define NEEDS_SELECTED_VIDEO_CONTENT 0x20 #define NEEDS_CLIPBOARD 0x40 #define NEEDS_ENCRYPTION 0x80 +#define NEEDS_DCP_CONTENT 0x100 map<wxMenuItem*, int> menu_items; @@ -236,6 +246,7 @@ enum { ID_jobs_open_dcp_in_player, ID_view_closed_captions, ID_view_video_waveform, + ID_tools_version_file, ID_tools_hints, ID_tools_encoding_servers, ID_tools_manage_templates, @@ -318,7 +329,7 @@ public: #endif _config_changed_connection = Config::instance()->Changed.connect(boost::bind(&DOMFrame::config_changed, this, _1)); - config_changed (Config::OTHER); + config_changed(Config::OTHER); _analytics_message_connection = Analytics::instance()->Message.connect(boost::bind(&DOMFrame::analytics_message, this, _1, _2)); @@ -347,6 +358,7 @@ public: Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_open_dcp_in_player, this), ID_jobs_open_dcp_in_player); Bind (wxEVT_MENU, boost::bind (&DOMFrame::view_closed_captions, this), ID_view_closed_captions); Bind (wxEVT_MENU, boost::bind (&DOMFrame::view_video_waveform, this), ID_view_video_waveform); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_version_file, this), ID_tools_version_file); Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_hints, this), ID_tools_hints); Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_encoding_servers, this), ID_tools_encoding_servers); Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_manage_templates, this), ID_tools_manage_templates); @@ -442,12 +454,13 @@ public: } } + /** Make a new film in the given path, using template_name as a template + * (or the default template if it's empty). + */ void new_film (boost::filesystem::path path, optional<string> template_name) { auto film = make_shared<Film>(path); - if (template_name) { - film->use_template (template_name.get()); - } + film->use_template(template_name); film->set_name (path.filename().generic_string()); film->write_metadata (); set_film (film); @@ -458,6 +471,7 @@ public: { auto film = make_shared<Film>(file); auto const notes = film->read_metadata (); + film->read_ui_state(); if (film->state_version() == 4) { error_dialog ( @@ -479,8 +493,10 @@ public: auto const dir = e.file().parent_path(); if (dcp::filesystem::exists(dir / "ASSETMAP") || dcp::filesystem::exists(dir / "ASSETMAP.xml")) { error_dialog ( - this, _("Could not open this folder as a DCP-o-matic project."), - _("It looks like you are trying to open a DCP. File -> Open is for loading DCP-o-matic projects, not DCPs. To import a DCP, create a new project with File -> New and then click the \"Add DCP...\" button.") + this, variant::wx::insert_dcpomatic(_("Could not open this folder as a %s project.")), + variant::wx::insert_dcpomatic( + _("It looks like you are trying to open a DCP. File -> Open is for loading %s projects, not DCPs. " + "To import a DCP, create a new project with File -> New and then click the \"Add DCP...\" button.")) ); } else { auto const p = std_to_wx(file.string ()); @@ -564,7 +580,7 @@ private: if (!found_bad_chars.empty()) { message += wxString::Format (_("Try removing the %s characters from your folder name."), std_to_wx(found_bad_chars).data()); } else { - message += _("Please check that you do not have Windows controlled folder access enabled for DCP-o-matic."); + message += variant::wx::insert_dcpomatic(_("Please check that you do not have Windows controlled folder access enabled for %s.")); } error_dialog (this, message, std_to_wx(e.what())); #else @@ -611,7 +627,11 @@ private: SaveTemplateDialog dialog(this); if (dialog.ShowModal() == wxID_OK) { try { - Config::instance()->save_template(_film, dialog.name()); + if (dialog.name()) { + Config::instance()->save_template(_film, *dialog.name()); + } else { + Config::instance()->save_default_template(_film); + } } catch (exception& e) { error_dialog(this, _("Could not save template."), std_to_wx(e.what())); } @@ -780,24 +800,31 @@ private: { FileDialog dialog(this, _("Specify ZIP file"), wxT("ZIP files (*.zip)|*.zip"), wxFD_OPEN, "Preferences"); - if (dialog.show()) { - Config::instance()->load_from_zip(dialog.path()); + if (!dialog.show()) { + return; } + + auto action = Config::CinemasAction::WRITE_TO_CURRENT_PATH; + + if (Config::zip_contains_cinemas(dialog.path()) && Config::cinemas_file_from_zip(dialog.path()) != Config::instance()->cinemas_file()) { + LoadConfigFromZIPDialog how(this, dialog.path()); + if (how.ShowModal() == wxID_CANCEL) { + return; + } + + action = how.action(); + } + + Config::instance()->load_from_zip(dialog.path(), action); } void jobs_make_dcp () { double required; double available; - bool can_hard_link; - if (!_film->should_be_enough_disk_space (required, available, can_hard_link)) { - wxString message; - if (can_hard_link) { - message = wxString::Format (_("The DCP for this film will take up about %.1f GB, and the disk that you are using only has %.1f GB available. Do you want to continue anyway?"), required, available); - } else { - message = wxString::Format (_("The DCP and intermediate files for this film will take up about %.1f GB, and the disk that you are using only has %.1f GB available. You would need half as much space if the filesystem supported hard links, but it does not. Do you want to continue anyway?"), required, available); - } + if (!_film->should_be_enough_disk_space(required, available)) { + auto const message = wxString::Format(_("The DCP for this film will take up about %.1f GB, and the disk that you are using only has %.1f GB available. Do you want to continue anyway?"), required, available); if (!confirm_dialog (this, message)) { return; } @@ -827,6 +854,8 @@ private: if (!confirm_dialog (this, wxString::Format (_("Do you want to overwrite the existing DCP %s?"), std_to_wx(dcp_dir.string()).data()))) { return; } + + preserve_assets(dcp_dir, _film->assets_path()); dcp::filesystem::remove_all(dcp_dir); } @@ -1014,7 +1043,7 @@ private: auto job = make_shared<TranscodeJob>(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP); job->set_encoder ( - make_shared<FFmpegEncoder> ( + make_shared<FFmpegFilmEncoder>( _film, job, dialog.path(), dialog.format(), dialog.mixdown_to_stereo(), dialog.split_reels(), dialog.split_streams(), dialog.x264_crf()) ); JobManager::instance()->add (job); @@ -1029,7 +1058,7 @@ private: } auto job = make_shared<TranscodeJob>(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP); job->set_encoder( - make_shared<SubtitleEncoder>(_film, job, dialog.path(), _film->isdcf_name(true), dialog.split_reels(), dialog.include_font()) + make_shared<SubtitleFilmEncoder>(_film, job, dialog.path(), _film->isdcf_name(true), dialog.split_reels(), dialog.include_font()) ); JobManager::instance()->add(job); } @@ -1071,6 +1100,17 @@ private: _system_information_dialog->Show (); } + void tools_version_file() + { + if (_dcp_referencing_dialog) { + _dcp_referencing_dialog->Destroy(); + _dcp_referencing_dialog = nullptr; + } + + _dcp_referencing_dialog = new DCPReferencingDialog(this, _film); + _dcp_referencing_dialog->Show(); + } + void tools_hints () { if (!_hints_dialog) { @@ -1170,6 +1210,7 @@ private: FontConfig::drop(); ev.Skip (); + JobManager::drop (); } void active_jobs_changed() @@ -1193,6 +1234,13 @@ private: bool const have_single_selected_content = _film_editor->content_panel()->selected().size() == 1; bool const have_selected_content = !_film_editor->content_panel()->selected().empty(); bool const have_selected_video_content = !_film_editor->content_panel()->selected_video().empty(); + vector<shared_ptr<Content>> content; + if (_film) { + content = _film->content(); + } + bool const have_dcp_content = std::find_if(content.begin(), content.end(), [](shared_ptr<const Content> content) { + return static_cast<bool>(dynamic_pointer_cast<const DCPContent>(content)); + }) != content.end(); for (auto j: menu_items) { @@ -1230,6 +1278,10 @@ private: enabled = false; } + if ((j.second & NEEDS_DCP_CONTENT) && !have_dcp_content) { + enabled = false; + } + j.first->Enable (enabled); } } @@ -1340,7 +1392,7 @@ private: add_item (jobs_menu, _("Make &KDMs...\tCtrl-K"), ID_jobs_make_kdms, NEEDS_FILM); /* [Shortcut] Ctrl+D:Make DKDMs */ add_item (jobs_menu, _("Make &DKDMs...\tCtrl-D"), ID_jobs_make_dkdms, NEEDS_FILM); - add_item (jobs_menu, _("Make DKDM for DCP-o-matic..."), ID_jobs_make_self_dkdm, NEEDS_FILM | NEEDS_ENCRYPTION); + add_item(jobs_menu, variant::wx::insert_dcpomatic(_("Make DKDM for %s...")), ID_jobs_make_self_dkdm, NEEDS_FILM | NEEDS_ENCRYPTION); jobs_menu->AppendSeparator (); /* [Shortcut] Ctrl+E:Export video file */ add_item (jobs_menu, _("Export video file...\tCtrl-E"), ID_jobs_export_video_file, NEEDS_FILM); @@ -1363,6 +1415,7 @@ private: add_item (view, _("Video waveform..."), ID_view_video_waveform, NEEDS_FILM); auto tools = new wxMenu; + add_item (tools, _("Version File (VF)..."), ID_tools_version_file, NEEDS_FILM | NEEDS_DCP_CONTENT); add_item (tools, _("Hints..."), ID_tools_hints, NEEDS_FILM); add_item (tools, _("Encoding servers..."), ID_tools_encoding_servers, 0); add_item (tools, _("Manage templates..."), ID_tools_manage_templates, 0); @@ -1376,11 +1429,13 @@ private: wxMenu* help = new wxMenu; #ifdef __WXOSX__ - add_item (help, _("About DCP-o-matic"), wxID_ABOUT, ALWAYS); + add_item(help, variant::wx::insert_dcpomatic(_("About %s")), wxID_ABOUT, ALWAYS); #else add_item (help, _("About"), wxID_ABOUT, ALWAYS); #endif - add_item (help, _("Report a problem..."), ID_help_report_a_problem, NEEDS_FILM); + if (variant::show_report_a_problem()) { + add_item(help, _("Report a problem..."), ID_help_report_a_problem, NEEDS_FILM); + } m->Append (_file_menu, _("&File")); m->Append (edit, _("&Edit")); @@ -1390,48 +1445,19 @@ private: m->Append (help, _("&Help")); } - void config_changed (Config::Property what) + void config_changed(Config::Property what) { /* Instantly save any config changes when using the DCP-o-matic GUI */ - switch (what) { - case Config::CINEMAS: - try { - 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() - ) - ); - } - break; - case Config::DKDM_RECIPIENTS: - try { - Config::instance()->write_dkdm_recipients(); - } catch (exception& e) { - error_dialog ( - this, - wxString::Format ( - _("Could not write to DKDM recipients file at %s. Your changes have not been saved."), - std_to_wx(Config::instance()->dkdm_recipients_file().string()).data() - ) - ); - } - break; - default: - 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() - ) - ); - } + 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() + ) + ); } for (int i = 0; i < _history_items; ++i) { @@ -1467,6 +1493,14 @@ private: _history_items = history.size (); dcpomatic_log->set_types (Config::instance()->log_types()); + +#ifdef DCPOMATIC_GROK + if (what == Config::GROK) { + setup_grok_library_path(); + } +#else + LIBDCP_UNUSED(what); +#endif } void update_checker_state_changed () @@ -1488,9 +1522,9 @@ private: UpdateDialog dialog(this, uc->stable(), uc->test()); dialog.ShowModal(); } else if (uc->state() == UpdateChecker::State::FAILED) { - error_dialog (this, _("The DCP-o-matic download server could not be contacted.")); + error_dialog(this, variant::wx::insert_dcpomatic(_("The %s download server could not be contacted."))); } else { - error_dialog (this, _("There are no new versions of DCP-o-matic available.")); + error_dialog(this, variant::wx::insert_dcpomatic(_("There are no new versions of %s available."))); } _update_news_requested = false; @@ -1528,7 +1562,7 @@ private: void set_title () { - auto s = wx_to_std(_("DCP-o-matic")); + auto s = variant::dcpomatic(); if (_film) { if (_film->directory()) { s += " - " + _film->directory()->string(); @@ -1548,6 +1582,7 @@ private: StandardControls* _controls; wx_ptr<VideoWaveformDialog> _video_waveform_dialog; SystemInformationDialog* _system_information_dialog = nullptr; + DCPReferencingDialog* _dcp_referencing_dialog = nullptr; HintsDialog* _hints_dialog = nullptr; ServersListDialog* _servers_list_dialog = nullptr; wxPreferencesEditor* _config_dialog = nullptr; @@ -1571,7 +1606,7 @@ static const wxCmdLineEntryDesc command_line_description[] = { { wxCMD_LINE_SWITCH, "n", "new", "create new film", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL }, { wxCMD_LINE_OPTION, "c", "content", "add content file / directory", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, { wxCMD_LINE_OPTION, "d", "dcp", "add content DCP", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, - { wxCMD_LINE_SWITCH, "v", "version", "show DCP-o-matic version", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL }, + { wxCMD_LINE_SWITCH, "v", "version", "show version", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL }, { wxCMD_LINE_OPTION, "", "config", "directory containing config.xml and cinemas.xml", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, { wxCMD_LINE_PARAM, 0, 0, "film to load or create", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 } @@ -1615,7 +1650,7 @@ private: setvbuf(hf_in, NULL, _IONBF, 128); *stdin = *hf_in; - cout << "DCP-o-matic is starting." << "\n"; + cout << variant::insert_dcpomatic("%1 is starting.") << "\n"; } #endif wxInitAllImageHandlers (); @@ -1625,7 +1660,7 @@ private: _splash = maybe_show_splash (); - SetAppName (_("DCP-o-matic")); + SetAppName(variant::wx::dcpomatic()); if (!wxApp::OnInit()) { return false; @@ -1664,7 +1699,7 @@ private: */ Config::Bad.connect (boost::bind(&App::config_bad, this, _1)); - _frame = new DOMFrame (_("DCP-o-matic")); + _frame = new DOMFrame(variant::wx::dcpomatic()); SetTopWindow (_frame); _frame->Maximize (); close_splash (); @@ -1672,7 +1707,14 @@ private: if (running_32_on_64 ()) { NagDialog::maybe_nag ( _frame, Config::NAG_32_ON_64, - _("You are running the 32-bit version of DCP-o-matic on a 64-bit version of Windows. This will limit the memory available to DCP-o-matic and may cause errors. You are strongly advised to install the 64-bit version of DCP-o-matic."), + wxString::Format( + _("You are running the 32-bit version of %s on a 64-bit version of Windows. " + "This will limit the memory available to %s and may cause errors. You are " + "strongly advised to install the 64-bit version of %s."), + variant::wx::dcpomatic(), + variant::wx::dcpomatic(), + variant::wx::dcpomatic() + ), false); } @@ -1715,11 +1757,16 @@ private: notes.Centre(); notes.ShowModal(); } + +#ifdef DCPOMATIC_GROK + grk_plugin::setMessengerLogger(new grk_plugin::GrokLogger("[GROK] ")); + setup_grok_library_path(); +#endif } catch (exception& e) { close_splash(); - error_dialog (nullptr, wxString::Format ("DCP-o-matic could not start."), std_to_wx(e.what())); + error_dialog(nullptr, variant::wx::insert_dcpomatic(_("%s could not start.")), std_to_wx(e.what())); } return true; @@ -1772,31 +1819,34 @@ private: error_dialog ( nullptr, wxString::Format( - _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM, - std_to_wx (e.what()), - std_to_wx (e.file().string().c_str()) + _("An exception occurred: %s (%s)\n\n%s"), + std_to_wx(e.what()), + std_to_wx(e.file().string().c_str()), + wx::report_problem() ) ); } catch (boost::filesystem::filesystem_error& e) { error_dialog ( nullptr, wxString::Format( - _("An exception occurred: %s (%s) (%s)\n\n") + REPORT_PROBLEM, - std_to_wx (e.what()), - std_to_wx (e.path1().string()), - std_to_wx (e.path2().string()) + _("An exception occurred: %s (%s) (%s)\n\n%s"), + std_to_wx(e.what()), + std_to_wx(e.path1().string()), + std_to_wx(e.path2().string()), + wx::report_problem() ) ); } catch (exception& e) { error_dialog ( nullptr, wxString::Format( - _("An exception occurred: %s.\n\n") + REPORT_PROBLEM, - std_to_wx (e.what ()) + _("An exception occurred: %s.\n\n%s"), + std_to_wx(e.what()), + wx::report_problem() ) ); } catch (...) { - error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + error_dialog(nullptr, _("An unknown exception occurred.") + " " + wx::report_problem()); } } @@ -1862,9 +1912,11 @@ private: } RecreateChainDialog dialog( _frame, _("Recreate signing certificates"), - _("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs contains a small error\n" - "which will prevent DCPs from being validated correctly on some systems. Do you want to re-create\n" - "the certificate chain for signing DCPs and KDMs?"), + variant::wx::insert_dcpomatic( + _("The certificate chain that %s uses for signing DCPs and KDMs contains a small error\n" + "which will prevent DCPs from being validated correctly on some systems. Do you want to re-create\n" + "the certificate chain for signing DCPs and KDMs?") + ), _("Do nothing"), Config::NAG_BAD_SIGNER_CHAIN_UTF8 ); @@ -1877,9 +1929,11 @@ private: } RecreateChainDialog dialog( _frame, _("Recreate signing certificates"), - _("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs has a validity period\n" - "that is too long. This will cause problems playing back DCPs on some systems.\n" - "Do you want to re-create the certificate chain for signing DCPs and KDMs?"), + variant::wx::insert_dcpomatic( + _("The certificate chain that %s uses for signing DCPs and KDMs has a validity period\n" + "that is too long. This will cause problems playing back DCPs on some systems.\n" + "Do you want to re-create the certificate chain for signing DCPs and KDMs?") + ), _("Do nothing"), Config::NAG_BAD_SIGNER_CHAIN_VALIDITY ); @@ -1889,10 +1943,14 @@ private: { RecreateChainDialog dialog( _frame, _("Recreate signing certificates"), - _("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs is inconsistent and\n" - "cannot be used. DCP-o-matic cannot start unless you re-create it. Do you want to re-create\n" - "the certificate chain for signing DCPs and KDMs?"), - _("Close DCP-o-matic") + wxString::Format( + _("The certificate chain that %s uses for signing DCPs and KDMs is inconsistent and\n" + "cannot be used. %s cannot start unless you re-create it. Do you want to re-create\n" + "the certificate chain for signing DCPs and KDMs?"), + variant::wx::dcpomatic(), + variant::wx::dcpomatic() + ), + variant::wx::insert_dcpomatic(_("Close %s")) ); if (dialog.ShowModal() != wxID_OK) { exit (EXIT_FAILURE); @@ -1903,11 +1961,15 @@ private: { RecreateChainDialog dialog( _frame, _("Recreate KDM decryption chain"), - _("The certificate chain that DCP-o-matic uses for decrypting KDMs is inconsistent and\n" - "cannot be used. DCP-o-matic cannot start unless you re-create it. Do you want to re-create\n" - "the certificate chain for decrypting KDMs? You may want to say \"No\" here and back up your\n" - "configuration before continuing."), - _("Close DCP-o-matic") + wxString::Format( + _("The certificate chain that %s uses for decrypting KDMs is inconsistent and\n" + "cannot be used. %s cannot start unless you re-create it. Do you want to re-create\n" + "the certificate chain for decrypting KDMs? You may want to say \"No\" here and back up your\n" + "configuration before continuing."), + variant::wx::dcpomatic(), + variant::wx::dcpomatic() + ), + variant::wx::insert_dcpomatic(_("Close %s")) ); if (dialog.ShowModal() != wxID_OK) { exit (EXIT_FAILURE); @@ -1918,10 +1980,14 @@ private: { RecreateChainDialog dialog( _frame, _("Recreate signing certificates"), - _("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs contains a small error\n" - "which will prevent DCPs from being validated correctly on some systems. This error was caused\n" - "by a bug in DCP-o-matic which has now been fixed. Do you want to re-create the certificate chain\n" - "for signing DCPs and KDMs?"), + wxString::Format( + _("The certificate chain that %s uses for signing DCPs and KDMs contains a small error\n" + "which will prevent DCPs from being validated correctly on some systems. This error was caused\n" + "by a bug in %s which has now been fixed. Do you want to re-create the certificate chain\n" + "for signing DCPs and KDMs?"), + variant::wx::dcpomatic(), + variant::wx::dcpomatic() + ), _("Do nothing"), Config::NAG_BAD_SIGNER_DN_QUALIFIER ); diff --git a/src/tools/dcpomatic_batch.cc b/src/tools/dcpomatic_batch.cc index e476c2163..51d700d20 100644 --- a/src/tools/dcpomatic_batch.cc +++ b/src/tools/dcpomatic_batch.cc @@ -28,10 +28,14 @@ #include "wx/wx_ptr.h" #include "wx/wx_signal_manager.h" #include "wx/wx_util.h" +#include "wx/wx_variant.h" #include "lib/compose.hpp" #include "lib/config.h" #include "lib/dcpomatic_socket.h" #include "lib/film.h" +#ifdef DCPOMATIC_GROK +#include "lib/grok/context.h" +#endif #include "lib/job.h" #include "lib/job_manager.h" #include "lib/make_dcp.h" @@ -216,9 +220,8 @@ public: double total_required; double available; - bool can_hard_link; - film->should_be_enough_disk_space (total_required, available, can_hard_link); + film->should_be_enough_disk_space(total_required, available); set<shared_ptr<const Film>> films; @@ -235,7 +238,7 @@ public: } double required; - i->should_be_enough_disk_space (required, available, can_hard_link); + i->should_be_enough_disk_space(required, available); total_required += (1 - progress) * required; } @@ -289,6 +292,7 @@ private: } ev.Skip (); + JobManager::drop (); } void file_add_film () @@ -353,31 +357,25 @@ private: void config_changed (Config::Property what) { /* Instantly save any config changes when using the DCP-o-matic GUI */ - if (what == Config::CINEMAS) { - try { - 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() - ) - ); - } + 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() + ) + ); + } + +#ifdef DCPOMATIC_GROK + if (what == Config::GROK) { + setup_grok_library_path(); } +#else + LIBDCP_UNUSED(what); +#endif } boost::optional<boost::filesystem::path> _last_parent; @@ -426,7 +424,7 @@ class App : public wxApp { wxInitAllImageHandlers (); - SetAppName (_("DCP-o-matic Batch Converter")); + SetAppName(variant::wx::dcpomatic_batch_converter()); is_batch_converter = true; Config::FailedToLoad.connect(boost::bind(&App::config_failed_to_load, this, _1)); @@ -462,7 +460,7 @@ class App : public wxApp */ Config::drop (); - _frame = new DOMFrame (_("DCP-o-matic Batch Converter")); + _frame = new DOMFrame(variant::wx::dcpomatic_batch_converter()); SetTopWindow (_frame); _frame->Maximize (); if (splash) { @@ -475,7 +473,11 @@ class App : public wxApp server->StartJob.connect(bind(&DOMFrame::start_job, _frame, _1)); 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.")); + error_dialog( + _frame, + variant::wx::insert_dcpomatic_batch_converter( + _("Could not listen for new batch jobs. Perhaps another instance of the %s is running.") + )); } signal_manager = new wxSignalManager (this); @@ -498,6 +500,11 @@ class App : public wxApp } } +#ifdef DCPOMATIC_GROK + grk_plugin::setMessengerLogger(new grk_plugin::GrokLogger("[GROK] ")); + setup_grok_library_path(); +#endif + return true; } diff --git a/src/tools/dcpomatic_cli.cc b/src/tools/dcpomatic_cli.cc index 5a7ec3c72..8b55d6205 100644 --- a/src/tools/dcpomatic_cli.cc +++ b/src/tools/dcpomatic_cli.cc @@ -25,9 +25,12 @@ #include "lib/cross.h" #include "lib/dcpomatic_log.h" #include "lib/encode_server_finder.h" -#include "lib/ffmpeg_encoder.h" +#include "lib/ffmpeg_film_encoder.h" #include "lib/film.h" #include "lib/filter.h" +#ifdef DCPOMATIC_GROK +#include "lib/grok/context.h" +#endif #include "lib/hints.h" #include "lib/job_manager.h" #include "lib/json_server.h" @@ -37,6 +40,7 @@ #include "lib/signal_manager.h" #include "lib/transcode_job.h" #include "lib/util.h" +#include "lib/variant.h" #include "lib/version.h" #include "lib/video_content.h" #include <dcp/filesystem.h> @@ -63,7 +67,7 @@ static void help (string n) { cerr << "Syntax: " << n << " [OPTION] [<FILM>]\n" - << " -v, --version show DCP-o-matic version\n" + << variant::insert_dcpomatic(" -v, --version show %1 version\n") << " -h, --help show this help\n" << " -f, --flags show flags passed to C++ compiler on build\n" << " -n, --no-progress do not print progress to stdout\n" @@ -72,7 +76,7 @@ help (string n) << " -j, --json <port> run a JSON server on the specified port\n" << " -k, --keep-going keep running even when the job is complete\n" << " -s, --servers <file> specify servers to use in a text file\n" - << " -l, --list-servers just display a list of encoding servers that DCP-o-matic is configured to use; don't encode\n" + << variant::insert_dcpomatic(" -l, --list-servers just display a list of encoding servers that %1 is configured to use; don't encode\n") << " -d, --dcp-path echo DCP's path to stdout on successful completion (implies -n)\n" << " -c, --config <dir> directory containing config.xml and cinemas.xml\n" << " --dump just dump a summary of the film's settings; don't encode\n" @@ -90,7 +94,7 @@ print_dump (shared_ptr<Film> film) { cout << film->dcp_name (true) << "\n" << film->container()->container_nickname() << " at " << ((film->resolution() == Resolution::TWO_K) ? "2K" : "4K") << "\n" - << (film->j2k_bandwidth() / 1000000) << "Mbit/s" << "\n" + << (film->video_bit_rate(film->video_encoding()) / 1000000) << "Mbit/s" << "\n" << "Duration " << (film->length().timecode(film->video_frame_rate())) << "\n" << "Output " << film->video_frame_rate() << "fps " << (film->three_d() ? "3D" : "2D") << " " << (film->audio_frame_rate() / 1000) << "kHz\n" << (film->interop() ? "Inter-Op" : "SMPTE") << " " << (film->encrypted() ? "encrypted" : "unencrypted") << "\n"; @@ -417,7 +421,7 @@ main (int argc, char* argv[]) signal_manager = new SignalManager (); if (no_remote || export_format) { - EncodeServerFinder::instance()->stop (); + EncodeServerFinder::drop(); } if (json_port) { @@ -500,6 +504,11 @@ main (int argc, char* argv[]) } } +#ifdef DCPOMATIC_GROK + grk_plugin::setMessengerLogger(new grk_plugin::GrokLogger("[GROK] ")); + setup_grok_library_path(); +#endif + if (progress) { if (export_format) { cout << "\nExporting " << film->name() << "\n"; @@ -513,7 +522,7 @@ main (int argc, char* argv[]) if (export_format) { auto job = std::make_shared<TranscodeJob>(film, behaviour); job->set_encoder ( - std::make_shared<FFmpegEncoder> ( + std::make_shared<FFmpegFilmEncoder>( film, job, *export_filename, *export_format == "mp4" ? ExportFormat::H264_AAC : ExportFormat::PRORES_HQ, false, false, false, 23 ) ); diff --git a/src/tools/dcpomatic_combiner.cc b/src/tools/dcpomatic_combiner.cc index c135184e6..26ca0022b 100644 --- a/src/tools/dcpomatic_combiner.cc +++ b/src/tools/dcpomatic_combiner.cc @@ -23,11 +23,14 @@ #include "wx/dir_picker_ctrl.h" #include "wx/editable_list.h" #include "wx/wx_signal_manager.h" +#include "wx/wx_util.h" +#include "wx/wx_variant.h" #include "lib/combine_dcp_job.h" #include "lib/config.h" #include "lib/constants.h" #include "lib/cross.h" #include "lib/job_manager.h" +#include "lib/util.h" #include <dcp/combine.h> LIBDCP_DISABLE_WARNINGS #include <wx/filepicker.h> @@ -175,7 +178,7 @@ private: auto jm = JobManager::instance (); jm->add (make_shared<CombineDCPJob>(_inputs, output, wx_to_std(_annotation_text->GetValue()))); - bool const ok = display_progress(_("DCP-o-matic Combiner"), _("Combining DCPs")); + bool const ok = display_progress(variant::wx::dcpomatic_combiner(), _("Combining DCPs")); if (!ok) { return; } @@ -219,7 +222,7 @@ public: Config::FailedToLoad.connect(boost::bind(&App::config_failed_to_load, this, _1)); Config::Warning.connect (boost::bind (&App::config_warning, this, _1)); - SetAppName (_("DCP-o-matic Combiner")); + SetAppName(variant::wx::dcpomatic_combiner()); if (!wxApp::OnInit()) { return false; @@ -253,7 +256,7 @@ public: */ Config::drop (); - _frame = new DOMFrame(_("DCP-o-matic Combiner")); + _frame = new DOMFrame(variant::wx::dcpomatic_combiner()); SetTopWindow (_frame); _frame->Show (); @@ -263,7 +266,7 @@ public: } catch (exception& e) { - error_dialog(nullptr, wxString::Format("DCP-o-matic Combiner could not start."), std_to_wx(e.what())); + error_dialog(nullptr, wxString::Format(_("%s could not start (%s)"), variant::wx::dcpomatic_combiner()), std_to_wx(e.what())); return false; } @@ -294,21 +297,23 @@ public: error_dialog ( 0, wxString::Format( - _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM, - std_to_wx (e.what()), - std_to_wx (e.file().string().c_str ()) + _("An exception occurred: %s (%s)\n\n%s"), + std_to_wx(e.what()), + std_to_wx(e.file().string().c_str()), + wx::report_problem() ) ); } catch (exception& e) { error_dialog ( 0, wxString::Format( - _("An exception occurred: %s.\n\n") + REPORT_PROBLEM, - std_to_wx (e.what ()) + _("An exception occurred: %s\n\n%s"), + std_to_wx(e.what()), + wx::report_problem() ) ); } catch (...) { - error_dialog (nullptr, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + error_dialog(nullptr, _("An unknown exception occurred.") + " " + wx::report_problem()); } } diff --git a/src/tools/dcpomatic_disk.cc b/src/tools/dcpomatic_disk.cc index 395b60a88..a20a062e3 100644 --- a/src/tools/dcpomatic_disk.cc +++ b/src/tools/dcpomatic_disk.cc @@ -29,6 +29,7 @@ #include "wx/wx_util.h" #include "wx/wx_signal_manager.h" #include "wx/wx_util.h" +#include "wx/wx_variant.h" #include "lib/config.h" #include "lib/constants.h" #include "lib/copy_to_drive_job.h" @@ -43,6 +44,7 @@ #include <dcp/filesystem.h> #include <dcp/warnings.h> #include <wx/cmdline.h> +#include <wx/progdlg.h> #include <wx/wx.h> LIBDCP_DISABLE_WARNINGS #include <boost/process.hpp> @@ -130,7 +132,7 @@ public: auto grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); int r = 0; - add_label_to_sizer (grid, overall_panel, _("DCP"), true, wxGBPosition(r, 0)); + add_label_to_sizer(grid, overall_panel, _("DCPs"), true, wxGBPosition(r, 0)); auto dcp_sizer = new wxBoxSizer (wxHORIZONTAL); auto dcps = new EditableList<boost::filesystem::path, DirDialogWrapper>( overall_panel, @@ -149,9 +151,9 @@ public: add_label_to_sizer (grid, overall_panel, _("Drive"), true, wxGBPosition(r, 0)); auto drive_sizer = new wxBoxSizer (wxHORIZONTAL); _drive = new wxChoice (overall_panel, wxID_ANY); - drive_sizer->Add (_drive, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, DCPOMATIC_SIZER_X_GAP); + drive_sizer->Add(_drive, 1, wxTOP, 2); _drive_refresh = new wxButton (overall_panel, wxID_ANY, _("Refresh")); - drive_sizer->Add (_drive_refresh, 0); + drive_sizer->Add(_drive_refresh, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP); grid->Add (drive_sizer, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND); ++r; @@ -159,7 +161,7 @@ public: grid->Add (_jobs, wxGBPosition(r, 0), wxGBSpan(6, 2), wxEXPAND); r += 6; - _copy = new wxButton (overall_panel, wxID_ANY, _("Copy DCP")); + _copy = new wxButton(overall_panel, wxID_ANY, _("Copy DCPs")); grid->Add (_copy, wxGBPosition(r, 0), wxGBSpan(1, 2), wxEXPAND); ++r; @@ -172,7 +174,7 @@ public: _sizer->Add (grid, 1, wxALL | wxEXPAND, DCPOMATIC_DIALOG_BORDER); overall_panel->SetSizer (_sizer); Fit (); - SetSize (1024, GetSize().GetHeight() + 32); + SetSize(768, GetSize().GetHeight() + 32); /* XXX: this is a hack, but I expect we'll need logs and I'm not sure if there's * a better place to put them. @@ -181,7 +183,17 @@ public: dcpomatic_log->set_types (dcpomatic_log->types() | LogEntry::TYPE_DISK); LOG_DISK("dcpomatic_disk %1 started", dcpomatic_git_commit); - drive_refresh (); + { + int constexpr seconds_to_look = 3; + wxProgressDialog find_drives_progress(_("Disk Writer"), _("Finding disks"), seconds_to_look * 4, this); + for (auto i = 0; i < seconds_to_look * 4; ++i) { + if (!find_drives_progress.Update(i)) { + break; + } + drive_refresh(); + dcpomatic_sleep_milliseconds(250); + } + } Bind (wxEVT_SIZE, boost::bind(&DOMFrame::sized, this, _1)); Bind (wxEVT_CLOSE_WINDOW, boost::bind(&DOMFrame::close, this, _1)); @@ -270,6 +282,7 @@ private: } ev.Skip (); + JobManager::drop (); } void copy () @@ -313,7 +326,7 @@ private: #if defined(DCPOMATIC_WINDOWS) auto m = make_wx<MessageDialog>( this, - _("DCP-o-matic Disk Writer"), + variant::wx::dcpomatic_disk_writer(), _("Do you see a 'User Account Control' dialogue asking about dcpomatic2_disk_writer.exe? If so, click 'Yes', then try again.") ); m->ShowModal (); @@ -321,8 +334,8 @@ private: #elif defined(DCPOMATIC_OSX) auto m = make_wx<MessageDialog>( this, - _("DCP-o-matic Disk Writer"), - _("Did you install the DCP-o-matic Disk Writer.pkg from the .dmg? Please check and try again.") + variant::wx::dcpomatic_disk_writer(), + variant::wx::insert_dcpomatic(_("Did you install the %s Disk Writer.pkg from the .dmg? Please check and try again.")) ); m->ShowModal (); return; @@ -354,7 +367,7 @@ private: if (!reply || reply->type() != DiskWriterBackEndResponse::Type::OK) { auto m = make_wx<MessageDialog>( this, - _("DCP-o-matic Disk Writer"), + variant::wx::dcpomatic_disk_writer(), wxString::Format( _("The drive %s could not be unmounted.\nClose any application that is using it, then try again. (%s)"), std_to_wx(drive.description()), @@ -442,7 +455,7 @@ public: Config::FailedToLoad.connect (boost::bind (&App::config_failed_to_load, this)); Config::Warning.connect (boost::bind (&App::config_warning, this, _1)); - SetAppName (_("DCP-o-matic Disk Writer")); + SetAppName(variant::wx::dcpomatic_disk_writer()); if (!wxApp::OnInit()) { return false; @@ -483,12 +496,17 @@ public: return false; } if (!warning->confirmed()) { - message_dialog(nullptr, _("You did not correctly confirm that you read the warning that was just shown. DCP-o-matic Disk Writer will close now. Please try again.")); + message_dialog( + nullptr, + variant::wx::insert_dcpomatic_disk_writer( + _("You did not correctly confirm that you read the warning that was just shown. %s will close now. Please try again.") + ) + ); return false; } } - _frame = new DOMFrame (_("DCP-o-matic Disk Writer")); + _frame = new DOMFrame(variant::wx::dcpomatic_disk_writer()); SetTopWindow (_frame); _frame->Show (); @@ -502,7 +520,7 @@ public: } catch (exception& e) { - error_dialog (0, wxString::Format ("DCP-o-matic could not start."), std_to_wx(e.what())); + error_dialog(nullptr, wxString::Format(_("%s could not start"), variant::wx::dcpomatic_disk_writer()), std_to_wx(e.what())); return false; } @@ -551,21 +569,23 @@ public: error_dialog ( 0, wxString::Format ( - _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM, - std_to_wx (e.what()), - std_to_wx (e.file().string().c_str ()) + _("An exception occurred: %s (%s)\n\n%s"), + std_to_wx(e.what()), + std_to_wx(e.file().string().c_str()), + wx::report_problem() ) ); } catch (exception& e) { error_dialog ( 0, wxString::Format ( - _("An exception occurred: %s.\n\n") + REPORT_PROBLEM, - std_to_wx (e.what ()) + _("An exception occurred: %s.\n\n%s"), + std_to_wx(e.what()), + wx::report_problem() ) ); } catch (...) { - error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + error_dialog(nullptr, _("An unknown exception occurred.") + " " + wx::report_problem()); } } diff --git a/src/tools/dcpomatic_editor.cc b/src/tools/dcpomatic_editor.cc index 14ff6da7f..47c7bac14 100644 --- a/src/tools/dcpomatic_editor.cc +++ b/src/tools/dcpomatic_editor.cc @@ -24,10 +24,13 @@ #include "wx/id.h" #include "wx/wx_signal_manager.h" #include "wx/wx_util.h" +#include "wx/wx_variant.h" #include "lib/constants.h" #include "lib/cross.h" #include "lib/dcpomatic_log.h" #include "lib/null_log.h" +#include "lib/util.h" +#include "lib/variant.h" #include <dcp/cpl.h> #include <dcp/dcp.h> #include <dcp/reel.h> @@ -290,14 +293,14 @@ class DOMFrame : public wxFrame { public: DOMFrame () - : wxFrame(nullptr, -1, _("DCP-o-matic Editor")) + : wxFrame(nullptr, -1, variant::wx::dcpomatic_editor()) , _main_sizer(new wxBoxSizer(wxVERTICAL)) { dcpomatic_log = make_shared<NullLog>(); #if defined(DCPOMATIC_WINDOWS) maybe_open_console(); - std::cout << "DCP-o-matic Editor is starting." << "\n"; + std::cout << variant::dcpomatic_editor() << " is starting." << "\n"; #endif auto bar = new wxMenuBar; @@ -360,7 +363,7 @@ private: auto help = new wxMenu; #ifdef __WXOSX__ - help->Append (wxID_ABOUT, _("About DCP-o-matic")); + help->Append(wxID_ABOUT, variant::wx::insert_dcpomatic_editor(_("About %s"))); #else help->Append (wxID_ABOUT, _("About")); #endif @@ -444,7 +447,7 @@ private: splash = maybe_show_splash (); - SetAppName (_("DCP-o-matic Editor")); + SetAppName(variant::wx::dcpomatic_editor()); if (!wxApp::OnInit()) { return false; @@ -495,7 +498,7 @@ private: if (splash) { splash->Destroy (); } - error_dialog (0, _("DCP-o-matic Editor could not start."), std_to_wx(e.what())); + error_dialog(nullptr, variant::wx::insert_dcpomatic_editor(_("%s could not start.")), std_to_wx(e.what())); } return true; @@ -524,21 +527,23 @@ private: error_dialog ( 0, wxString::Format ( - _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM, - std_to_wx (e.what()), - std_to_wx (e.file().string().c_str ()) + _("An exception occurred: %s (%s)\n\n%s"), + std_to_wx(e.what()), + std_to_wx(e.file().string().c_str()), + wx::report_problem() ) ); } catch (exception& e) { error_dialog ( 0, wxString::Format ( - _("An exception occurred: %s.\n\n") + REPORT_PROBLEM, - std_to_wx (e.what ()) + _("An exception occurred: %s\n\n%s"), + std_to_wx(e.what()), + wx::report_problem() ) ); } catch (...) { - error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + error_dialog(nullptr, _("An unknown exception occurred.") + " " + wx::report_problem()); } } diff --git a/src/tools/dcpomatic_kdm.cc b/src/tools/dcpomatic_kdm.cc index 9adef0ab0..af16f57aa 100644 --- a/src/tools/dcpomatic_kdm.cc +++ b/src/tools/dcpomatic_kdm.cc @@ -37,6 +37,7 @@ #include "wx/static_text.h" #include "wx/wx_signal_manager.h" #include "wx/wx_util.h" +#include "wx/wx_variant.h" #include "lib/cinema.h" #include "lib/collator.h" #include "lib/compose.hpp" @@ -52,6 +53,8 @@ #include "lib/kdm_with_metadata.h" #include "lib/screen.h" #include "lib/send_kdm_email_job.h" +#include "lib/util.h" +#include "lib/variant.h" #include <dcp/encrypted_kdm.h> #include <dcp/decrypted_kdm.h> #include <dcp/exceptions.h> @@ -125,7 +128,7 @@ public: setvbuf(hf_in, NULL, _IONBF, 128); *stdin = *hf_in; - std::cout << "DCP-o-matic KDM creator is starting." << "\n"; + std::cout << variant::insert_dcpomatic_kdm_creator("%1 is starting.\n"); } #endif @@ -234,7 +237,7 @@ public: /* Instantly save any config changes when using a DCP-o-matic GUI */ Config::instance()->Changed.connect (boost::bind (&Config::write, Config::instance ())); - _screens->ScreensChanged.connect (boost::bind (&DOMFrame::setup_sensitivity, this)); + _screens->ScreensChanged.connect(boost::bind(&DOMFrame::screens_changed, this)); _create->Bind (wxEVT_BUTTON, bind (&DOMFrame::create_kdms, this)); _dkdm->Bind(wxEVT_TREE_SEL_CHANGED, boost::bind(&DOMFrame::dkdm_selection_changed, this)); _dkdm->Bind (wxEVT_TREE_BEGIN_DRAG, boost::bind (&DOMFrame::dkdm_begin_drag, this, _1)); @@ -302,11 +305,13 @@ private: wxMenu* help = new wxMenu; #ifdef __WXOSX__ - help->Append (wxID_ABOUT, _("About DCP-o-matic")); + help->Append(wxID_ABOUT, variant::wx::insert_dcpomatic_kdm_creator(_("About %s"))); #else help->Append (wxID_ABOUT, _("About")); #endif - help->Append (ID_help_report_a_problem, _("Report a problem...")); + if (variant::show_report_a_problem()) { + help->Append(ID_help_report_a_problem, _("Report a problem...")); + } m->Append (file, _("&File")); #ifndef __WXOSX__ @@ -400,11 +405,15 @@ private: return kdm; }; + CinemaList cinemas; + for (auto i: _screens->screens()) { auto kdm = kdm_for_screen( make_kdm, - i, + i.first, + *cinemas.cinema(i.first), + *cinemas.screen(i.second), _timing->from(), _timing->until(), _output->formulation(), @@ -460,7 +469,13 @@ private: if (e.starts_too_early()) { error_dialog(this, _("The KDM start period is before (or close to) the start of the signing certificate's validity period. Use a later start time for this KDM.")); } else { - error_dialog(this, _("The KDM end period is after (or close to) the end of the signing certificates' validity period. Either use an earlier end time for this KDM or re-create your signing certificates in the DCP-o-matic preferences window.")); + error_dialog( + this, + variant::wx::insert_dcpomatic_kdm_creator( + _("The KDM end period is after (or close to) the end of the signing certificates' validity period. " + "Either use an earlier end time for this KDM or re-create your signing certificates in the %s preferences window.") + ) + ); } return; } catch (dcp::NotEncryptedError& e) { @@ -788,6 +803,12 @@ private: update_dkdm_view(); } + void screens_changed() + { + _timing->suggest_utc_offset(_screens->best_utc_offset()); + setup_sensitivity(); + } + wxPreferencesEditor* _config_dialog; ScreensPanel* _screens; KDMTimingPanel* _timing; @@ -841,7 +862,7 @@ private: splash = maybe_show_splash (); - SetAppName (_("DCP-o-matic KDM Creator")); + SetAppName(variant::wx::dcpomatic_kdm_creator()); if (!wxApp::OnInit()) { return false; @@ -875,7 +896,7 @@ private: */ Config::drop (); - _frame = new DOMFrame (_("DCP-o-matic KDM Creator")); + _frame = new DOMFrame(variant::wx::dcpomatic_kdm_creator()); SetTopWindow (_frame); _frame->Maximize (); if (splash) { @@ -892,7 +913,7 @@ private: if (splash) { splash->Destroy (); } - error_dialog (0, _("DCP-o-matic could not start"), std_to_wx(e.what())); + error_dialog(nullptr, variant::wx::insert_dcpomatic_kdm_creator(_("%s could not start")), std_to_wx(e.what())); } return true; @@ -905,23 +926,25 @@ private: throw; } catch (FileError& e) { error_dialog ( - 0, + nullptr, wxString::Format ( - _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM, - std_to_wx (e.what()), - std_to_wx (e.file().string().c_str ()) + _("An exception occurred: %s (%s)\n\n%s"), + std_to_wx(e.what()), + std_to_wx(e.file().string().c_str()), + wx::report_problem() ) ); } catch (exception& e) { error_dialog ( nullptr, wxString::Format ( - _("An exception occurred: %s.\n\n") + " " + REPORT_PROBLEM, - std_to_wx(e.what()) + _("An exception occurred: %s.\n\n%s"), + std_to_wx(e.what()), + wx::report_problem() ) ); } catch (...) { - error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + error_dialog(nullptr, _("An unknown exception occurred.") + " " + wx::report_problem()); } /* This will terminate the program */ @@ -930,7 +953,7 @@ private: void OnUnhandledException () override { - error_dialog (nullptr, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + error_dialog(nullptr, _("An unknown exception occurred.") + " " + wx::report_problem()); } void idle () diff --git a/src/tools/dcpomatic_player.cc b/src/tools/dcpomatic_player.cc index e65839521..55cd00027 100644 --- a/src/tools/dcpomatic_player.cc +++ b/src/tools/dcpomatic_player.cc @@ -32,11 +32,12 @@ #include "wx/system_information_dialog.h" #include "wx/timer_display.h" #include "wx/update_dialog.h" -#include "wx/verify_dcp_dialog.h" #include "wx/verify_dcp_progress_dialog.h" +#include "wx/verify_dcp_result_dialog.h" #include "wx/wx_ptr.h" #include "wx/wx_signal_manager.h" #include "wx/wx_util.h" +#include "wx/wx_variant.h" #include "lib/compose.hpp" #include "lib/config.h" #include "lib/constants.h" @@ -50,9 +51,11 @@ #include "lib/file_log.h" #include "lib/film.h" #include "lib/font_config.h" +#include "lib/http_server.h" #include "lib/image.h" #include "lib/image_jpeg.h" #include "lib/image_png.h" +#include "lib/internal_player_server.h" #include "lib/internet.h" #include "lib/job.h" #include "lib/job_manager.h" @@ -64,6 +67,7 @@ #include "lib/server.h" #include "lib/text_content.h" #include "lib/update_checker.h" +#include "lib/variant.h" #include "lib/verify_dcp_job.h" #include "lib/video_content.h" #include <dcp/cpl.h> @@ -198,7 +202,7 @@ public: DOMFrame () - : wxFrame (nullptr, -1, _("DCP-o-matic Player")) + : wxFrame(nullptr, -1, variant::wx::dcpomatic_player()) , _mode (Config::instance()->player_mode()) /* Use a panel as the only child of the Frame so that we avoid the dark-grey background on Windows. @@ -211,7 +215,7 @@ public: #if defined(DCPOMATIC_WINDOWS) maybe_open_console (); - cout << "DCP-o-matic Player is starting." << "\n"; + cout << variant::dcpomatic_player() << " is starting." << "\n"; #endif auto bar = new wxMenuBar; @@ -259,7 +263,6 @@ public: } _controls->set_film(_viewer.film()); _viewer.set_dcp_decode_reduction(Config::instance()->decode_reduction()); - _viewer.set_optimise_for_j2k(true); _viewer.PlaybackPermitted.connect(bind(&DOMFrame::playback_permitted, this)); _viewer.TooManyDropped.connect(bind(&DOMFrame::too_many_frames_dropped, this)); _info = new PlayerInformation (_overall_panel, _viewer); @@ -309,11 +312,15 @@ public: _stress.LoadDCP.connect (boost::bind(&DOMFrame::load_dcp, this, _1)); + setup_internal_player_server(); + setup_http_server(); + SetDropTarget(new DCPDropTarget(this)); } ~DOMFrame () { + stop_http_server(); /* It's important that this is stopped before our frame starts destroying its children, * otherwise UI elements that it depends on will disappear from under it. */ @@ -398,7 +405,7 @@ public: auto job = make_shared<ExamineContentJob>(_film, dcp); _examine_job_connection = job->Finished.connect(bind(&DOMFrame::add_dcp_to_film, this, weak_ptr<Job>(job), weak_ptr<Content>(dcp))); JobManager::instance()->add (job); - bool const ok = display_progress (_("DCP-o-matic Player"), _("Loading content")); + bool const ok = display_progress(variant::wx::dcpomatic_player(), _("Loading content")); if (!ok || !report_errors_from_last_job(this)) { return; } @@ -406,14 +413,26 @@ public: if (dcp->video_frame_rate()) { _film->set_video_frame_rate(dcp->video_frame_rate().get(), true); } + switch (dcp->video_encoding()) { + case VideoEncoding::JPEG2000: + _viewer.set_optimisation(Optimisation::JPEG2000); + break; + case VideoEncoding::MPEG2: + _viewer.set_optimisation(Optimisation::MPEG2); + break; + case VideoEncoding::COUNT: + DCPOMATIC_ASSERT(false); + } } catch (ProjectFolderError &) { error_dialog ( this, wxString::Format(_("Could not load a DCP from %s"), std_to_wx(dir.string())), - _( - "This looks like a DCP-o-matic project folder, which cannot be loaded into the player. " - "Choose the DCP folder inside the DCP-o-matic project folder if that's what you want to play." - ) + wxString::Format( + _("This looks like a %s project folder, which cannot be loaded into the player. " + "Choose the DCP folder inside the %s project folder if that's what you want to play."), + variant::wx::dcpomatic(), + variant::wx::dcpomatic() + ) ); } catch (dcp::ReadError& e) { error_dialog (this, wxString::Format(_("Could not load a DCP from %s"), std_to_wx(dir.string())), std_to_wx(e.what())); @@ -523,6 +542,25 @@ public: _stress.load_script (path); } + void idle() + { + if (_http_server) { + struct timeval now; + gettimeofday(&now, 0); + auto time_since_last_update = (now.tv_sec + now.tv_usec / 1e6) - (_last_http_server_update.tv_sec + _last_http_server_update.tv_usec / 1e6); + if (time_since_last_update > 0.25) { + _http_server->set_playing(_viewer.playing()); + if (auto dcp = _viewer.dcp()) { + _http_server->set_dcp_name(dcp->name()); + } else { + _http_server->set_dcp_name(""); + } + _http_server->set_position(_viewer.position()); + _last_http_server_update = now; + } + } + } + private: void examine_content () @@ -609,19 +647,23 @@ private: auto help = new wxMenu; #ifdef __WXOSX__ - help->Append (wxID_ABOUT, _("About DCP-o-matic")); + help->Append(wxID_ABOUT, variant::wx::insert_dcpomatic_player(_("About %s"))); #else help->Append (wxID_ABOUT, _("About")); #endif - help->Append (ID_help_report_a_problem, _("Report a problem...")); + if (variant::show_report_a_problem()) { + help->Append(ID_help_report_a_problem, _("Report a problem...")); + } m->Append (_file_menu, _("&File")); + if (!Config::instance()->player_restricted_menus()) { #ifndef __WXOSX__ - m->Append (edit, _("&Edit")); + m->Append (edit, _("&Edit")); #endif - m->Append (view, _("&View")); - m->Append (tools, _("&Tools")); - m->Append (help, _("&Help")); + m->Append (view, _("&View")); + m->Append (tools, _("&Tools")); + m->Append (help, _("&Help")); + } } void file_open () @@ -680,7 +722,7 @@ private: DCPOMATIC_ASSERT (dcp); dcp->add_ov (wx_to_std(c->GetPath())); JobManager::instance()->add(make_shared<ExamineContentJob>(_film, dcp)); - bool const ok = display_progress (_("DCP-o-matic Player"), _("Loading content")); + bool const ok = display_progress(variant::wx::dcpomatic_player(), _("Loading content")); if (!ok || !report_errors_from_last_job(this)) { return; } @@ -925,12 +967,12 @@ private: DCPOMATIC_ASSERT (dcp); auto job = make_shared<VerifyDCPJob>(dcp->directories(), _kdms); - VerifyDCPProgressDialog progress(this, _("DCP-o-matic Player")); + VerifyDCPProgressDialog progress(this, variant::wx::dcpomatic_player()); bool const completed = progress.run(job); progress.Close(); if (completed) { - VerifyDCPDialog dialog(this, job); + VerifyDCPResultDialog dialog(this, job); dialog.ShowModal(); } } @@ -989,9 +1031,9 @@ private: auto dialog = make_wx<UpdateDialog>(this, uc->stable (), uc->test ()); dialog->ShowModal (); } else if (uc->state() == UpdateChecker::State::FAILED) { - error_dialog (this, _("The DCP-o-matic download server could not be contacted.")); + error_dialog(this, variant::wx::insert_dcpomatic(_("The %s download server could not be contacted."))); } else { - error_dialog (this, _("There are no new versions of DCP-o-matic available.")); + error_dialog(this, variant::wx::insert_dcpomatic(_("There are no new versions of %s available."))); } _update_news_requested = false; @@ -1020,6 +1062,48 @@ private: } update_from_config (prop); + + setup_http_server(); + } + + void stop_http_server() + { + if (_http_server) { + _http_server->stop(); + _http_server_thread.join(); + _http_server.reset(); + } + } + + void setup_http_server() + { + stop_http_server(); + + auto config = Config::instance(); + try { + if (config->enable_player_http_server()) { + _http_server.reset(new HTTPServer(config->player_http_server_port())); + _http_server->Play.connect(boost::bind(&FilmViewer::start, &_viewer)); + _http_server->Stop.connect(boost::bind(&FilmViewer::stop, &_viewer)); + _http_server_thread = boost::thread(boost::bind(&HTTPServer::run, _http_server.get())); + } + } catch (std::exception& e) { + LOG_DEBUG_PLAYER("Failed to start player HTTP server (%1)", e.what()); + } + } + + void setup_internal_player_server() + { + try { + auto server = new InternalPlayerServer(); + server->LoadDCP.connect(boost::bind(&DOMFrame::load_dcp, this, _1)); + new thread(boost::bind(&InternalPlayerServer::run, server)); + } catch (std::exception& e) { + /* This is not the end of the world; probably a failure to bind the server socket + * because there's already another player running. + */ + LOG_DEBUG_PLAYER("Failed to start internal player server (%1)", e.what()); + } } void update_from_config (Config::Property prop) @@ -1138,6 +1222,9 @@ private: PlayerStressTester _stress; /** KDMs that have been loaded, so that we can pass them to the verifier */ std::vector<boost::filesystem::path> _kdms; + boost::thread _http_server_thread; + std::unique_ptr<HTTPServer> _http_server; + struct timeval _last_http_server_update = { 0, 0 }; }; static const wxCmdLineEntryDesc command_line_description[] = { @@ -1147,34 +1234,6 @@ static const wxCmdLineEntryDesc command_line_description[] = { { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 } }; -class PlayServer : public Server -{ -public: - explicit PlayServer (DOMFrame* frame) - : Server (PLAYER_PLAY_PORT) - , _frame (frame) - {} - - void handle (shared_ptr<Socket> socket) override - { - try { - uint32_t const length = socket->read_uint32 (); - if (length > 65536) { - return; - } - scoped_array<char> buffer (new char[length]); - socket->read (reinterpret_cast<uint8_t*> (buffer.get()), length); - string s (buffer.get()); - signal_manager->when_idle (bind (&DOMFrame::load_dcp, _frame, s)); - socket->write (reinterpret_cast<uint8_t const *> ("OK"), 3); - } catch (...) { - - } - } - -private: - DOMFrame* _frame; -}; /** @class App * @brief The magic App class for wxWidgets. @@ -1203,7 +1262,7 @@ private: splash = maybe_show_splash (); - SetAppName (_("DCP-o-matic Player")); + SetAppName(variant::wx::dcpomatic_player()); if (!wxApp::OnInit()) { return false; @@ -1248,16 +1307,6 @@ private: } _frame->Show (); - try { - auto server = new PlayServer (_frame); - new thread (boost::bind (&PlayServer::run, server)); - } catch (std::exception& e) { - /* This is not the end of the world; probably a failure to bind the server socket - * because there's already another player running. - */ - LOG_DEBUG_PLAYER ("Failed to start play server (%1)", e.what()); - } - if (!_dcp_to_load.empty() && dcp::filesystem::is_directory(_dcp_to_load)) { try { _frame->load_dcp (_dcp_to_load); @@ -1285,7 +1334,7 @@ private: if (splash) { splash->Destroy (); } - error_dialog (0, _("DCP-o-matic Player could not start."), std_to_wx(e.what())); + error_dialog(nullptr, variant::wx::insert_dcpomatic_player(_("%s could not start")), std_to_wx(e.what())); } return true; @@ -1323,21 +1372,23 @@ private: error_dialog ( 0, wxString::Format ( - _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM, - std_to_wx (e.what()), - std_to_wx (e.file().string().c_str ()) + _("An exception occurred: %s (%s)\n\n%s"), + std_to_wx(e.what()), + std_to_wx(e.file().string().c_str()), + wx::report_problem() ) ); } catch (exception& e) { error_dialog ( 0, wxString::Format ( - _("An exception occurred: %s.\n\n") + REPORT_PROBLEM, - std_to_wx (e.what ()) + _("An exception occurred: %s\n\n%s"), + std_to_wx(e.what()), + wx::report_problem() ) ); } catch (...) { - error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + error_dialog(nullptr, _("An unknown exception occurred.") + " " + wx::report_problem()); } } @@ -1357,6 +1408,9 @@ private: void idle () { signal_manager->ui_idle (); + if (_frame) { + _frame->idle(); + } } void config_failed_to_load () diff --git a/src/tools/dcpomatic_playlist.cc b/src/tools/dcpomatic_playlist.cc index a373d81e6..a50fab37d 100644 --- a/src/tools/dcpomatic_playlist.cc +++ b/src/tools/dcpomatic_playlist.cc @@ -25,6 +25,7 @@ #include "wx/playlist_editor_config_dialog.h" #include "wx/wx_signal_manager.h" #include "wx/wx_util.h" +#include "wx/wx_variant.h" #include "lib/config.h" #include "lib/constants.h" #include "lib/cross.h" @@ -567,34 +568,36 @@ private: void setup_menu (wxMenuBar* m) { - auto file = new wxMenu; -#ifdef __WXOSX__ - file->Append(wxID_PREFERENCES, _("&Preferences...\tCtrl-,")); - file->Append (wxID_EXIT, _("&Exit")); +#ifdef DCPOMATIC_OSX + auto help = new wxMenu; + /* These just need to be appended somewhere, it seems - they magically + * get moved to the right place. + */ + if (!Config::instance()->playlist_editor_restricted_menus()) { + help->Append(wxID_PREFERENCES, _("&Preferences...\tCtrl-,")); + } + help->Append(wxID_EXIT, _("&Exit")); + help->Append(wxID_ABOUT, variant::wx::insert_dcpomatic_playlist_editor(_("About %s"))); + + m->Append(help, _("&Help")); #else - file->Append (wxID_EXIT, _("&Quit")); -#endif + auto file = new wxMenu; + file->Append(wxID_EXIT, _("&Quit")); -#ifndef __WXOSX__ auto edit = new wxMenu; - edit->Append (wxID_PREFERENCES, _("&Preferences...\tCtrl-P")); -#endif + edit->Append(wxID_PREFERENCES, _("&Preferences...\tCtrl-P")); auto help = new wxMenu; -#ifdef __WXOSX__ - help->Append (wxID_ABOUT, _("About DCP-o-matic")); -#else - help->Append (wxID_ABOUT, _("About")); -#endif + help->Append(wxID_ABOUT, _("About")); - m->Append (file, _("&File")); -#ifndef __WXOSX__ - m->Append (edit, _("&Edit")); + m->Append(file, _("&File")); + if (!Config::instance()->playlist_editor_restricted_menus()) { + m->Append(edit, _("&Edit")); + m->Append(help, _("&Help")); + } #endif - m->Append (help, _("&Help")); } - void config_changed () { try { @@ -635,7 +638,7 @@ private: try { wxInitAllImageHandlers (); - SetAppName (_("DCP-o-matic Playlist Editor")); + SetAppName(variant::wx::dcpomatic_playlist_editor()); if (!wxApp::OnInit()) { return false; @@ -669,7 +672,7 @@ private: */ Config::drop (); - _frame = new DOMFrame (_("DCP-o-matic Playlist Editor")); + _frame = new DOMFrame(variant::wx::dcpomatic_playlist_editor()); SetTopWindow (_frame); _frame->Maximize (); _frame->Show (); @@ -681,7 +684,7 @@ private: } catch (exception& e) { - error_dialog (0, _("DCP-o-matic could not start"), std_to_wx(e.what())); + error_dialog(nullptr, variant::wx::insert_dcpomatic_playlist_editor(_("%s could not start %s")), std_to_wx(e.what())); return true; } @@ -694,21 +697,23 @@ private: error_dialog ( 0, wxString::Format ( - _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM, - std_to_wx (e.what()), - std_to_wx (e.file().string().c_str ()) + _("An exception occurred: %s (%s)\n\n%s") + wx::report_problem(), + std_to_wx(e.what()), + std_to_wx(e.file().string().c_str()), + wx::report_problem() ) ); } catch (exception& e) { error_dialog ( - 0, - wxString::Format ( - _("An exception occurred: %s.\n\n") + " " + REPORT_PROBLEM, - std_to_wx (e.what ()) + nullptr, + wxString::Format( + _("An exception occurred: %s\n\n%s"), + std_to_wx(e.what()), + wx::report_problem() ) ); } catch (...) { - error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + error_dialog(nullptr, _("An unknown exception occurred.") + " " + wx::report_problem()); } /* This will terminate the program */ @@ -717,7 +722,7 @@ private: void OnUnhandledException () override { - error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + error_dialog(nullptr, _("An unknown exception occurred.") + " " + wx::report_problem()); } void idle () diff --git a/src/tools/dcpomatic_server.cc b/src/tools/dcpomatic_server.cc index 9bdc688c8..5aad282fd 100644 --- a/src/tools/dcpomatic_server.cc +++ b/src/tools/dcpomatic_server.cc @@ -22,7 +22,13 @@ #include "wx/static_text.h" #include "wx/wx_signal_manager.h" #include "wx/wx_util.h" +#include "wx/wx_variant.h" #include "lib/config.h" +#ifdef DCPOMATIC_GROK +#include "lib/grok/context.h" +#endif +#include "lib/log.h" +#include "lib/signaller.h" #include "lib/cross.h" #include "lib/dcpomatic_log.h" #include "lib/encode_server.h" @@ -149,7 +155,7 @@ class StatusDialog : public wxDialog public: StatusDialog () : wxDialog ( - nullptr, wxID_ANY, _("DCP-o-matic Encode Server"), + nullptr, wxID_ANY, variant::wx::dcpomatic_encode_server(), wxDefaultPosition, wxDefaultSize, #ifdef DCPOMATIC_OSX wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxSTAY_ON_TOP @@ -327,6 +333,11 @@ private: SetExitOnFrameDelete (false); +#ifdef DCPOMATIC_GROK + grk_plugin::setMessengerLogger(new grk_plugin::GrokLogger("[GROK] ")); + setup_grok_library_path(); +#endif + return true; } @@ -352,7 +363,7 @@ private: error_dialog (nullptr, std_to_wx(e.what())); wxTheApp->ExitMainLoop (); } catch (...) { - error_dialog (nullptr, _("An unknown error has occurred with the DCP-o-matic server.")); + error_dialog(nullptr, variant::wx::insert_dcpomatic_encode_server(_("An unknown error has occurred with the %s."))); wxTheApp->ExitMainLoop (); } } diff --git a/src/tools/dcpomatic_server_cli.cc b/src/tools/dcpomatic_server_cli.cc index 023099034..ea78d41a5 100644 --- a/src/tools/dcpomatic_server_cli.cc +++ b/src/tools/dcpomatic_server_cli.cc @@ -19,22 +19,26 @@ */ #include "lib/config.h" +#include "lib/config.h" #include "lib/dcp_video.h" +#include "lib/dcpomatic_log.h" +#include "lib/encode_server.h" #include "lib/exceptions.h" -#include "lib/util.h" -#include "lib/config.h" -#include "lib/image.h" #include "lib/file_log.h" +#ifdef DCPOMATIC_GROK +#include "lib/grok/context.h" +#endif +#include "lib/image.h" #include "lib/null_log.h" +#include "lib/util.h" +#include "lib/variant.h" #include "lib/version.h" -#include "lib/encode_server.h" -#include "lib/dcpomatic_log.h" +#include <boost/algorithm/string.hpp> #include <boost/array.hpp> #include <boost/asio.hpp> -#include <boost/algorithm/string.hpp> #include <boost/thread.hpp> -#include <boost/thread/mutex.hpp> #include <boost/thread/condition.hpp> +#include <boost/thread/mutex.hpp> #include <unistd.h> #include <errno.h> #include <getopt.h> @@ -44,15 +48,15 @@ #include <vector> using std::cerr; -using std::string; using std::cout; using std::shared_ptr; +using std::string; static void help (string n) { cerr << "Syntax: " << n << " [OPTION]\n" - << " -v, --version show DCP-o-matic version\n" + << variant::insert_dcpomatic(" -v, --version show %1 version\n") << " -h, --help show this help\n" << " -t, --threads number of parallel encoding threads to use\n" << " --verbose be verbose to stdout\n" @@ -112,13 +116,18 @@ main (int argc, char* argv[]) dcpomatic_log.reset (new FileLog("dcpomatic_server_cli.log")); } +#ifdef DCPOMATIC_GROK + setMessengerLogger(new grk_plugin::GrokLogger("[GROK] ")); + setup_grok_library_path(); +#endif + EncodeServer server (verbose, num_threads); try { server.run (); } catch (boost::system::system_error& e) { if (e.code() == boost::system::errc::address_in_use) { - cerr << program_name << ": address already in use. Is another DCP-o-matic server instance already running?\n"; + cerr << program_name << variant::insert_dcpomatic(": address already in use. Is another %1 server instance already running?\n"); exit (EXIT_FAILURE); } cerr << program_name << ": " << e.what() << "\n"; diff --git a/src/tools/dcpomatic_verifier.cc b/src/tools/dcpomatic_verifier.cc new file mode 100644 index 000000000..834ebefa1 --- /dev/null +++ b/src/tools/dcpomatic_verifier.cc @@ -0,0 +1,251 @@ +/* + Copyright (C) 2024 Carl Hetherington <cth@carlh.net> + + 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. + + 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +/** @file src/tools/dcpomatic_verify.cc + * @brief A DCP verify GUI. + */ + + +#include "wx/check_box.h" +#include "wx/dcpomatic_button.h" +#include "wx/dir_picker_ctrl.h" +#include "wx/verify_dcp_progress_panel.h" +#include "wx/verify_dcp_result_panel.h" +#include "wx/wx_util.h" +#include "wx/wx_variant.h" +#include "lib/constants.h" +#include "lib/cross.h" +#include "lib/job_manager.h" +#include "lib/verify_dcp_job.h" +#include "lib/util.h" +#include <dcp/verify_report.h> +LIBDCP_DISABLE_WARNINGS +#include <wx/evtloop.h> +#include <wx/wx.h> +LIBDCP_ENABLE_WARNINGS +#ifdef __WXGTK__ +#include <X11/Xlib.h> +#endif + + +using std::exception; +using std::make_shared; + + +class DOMFrame : public wxFrame +{ +public: + explicit DOMFrame(wxString const& title) + : wxFrame(nullptr, -1, title) + { +#ifdef DCPOMATIC_WINDOWS + SetIcon(wxIcon(std_to_wx("id"))); +#endif + auto overall_sizer = new wxBoxSizer(wxVERTICAL); + + auto dcp_sizer = new wxBoxSizer(wxHORIZONTAL); + add_label_to_sizer(dcp_sizer, this, _("DCP"), true, 0, wxALIGN_CENTER_VERTICAL); + _dcp = new DirPickerCtrl(this, true); + dcp_sizer->Add(_dcp, 1, wxEXPAND); + overall_sizer->Add(dcp_sizer, 0, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); + + auto options_sizer = new wxBoxSizer(wxVERTICAL); + _write_log = new CheckBox(this, _("Write log to DCP folder")); + options_sizer->Add(_write_log, 0, wxBOTTOM, DCPOMATIC_SIZER_GAP); + overall_sizer->Add(options_sizer, 0, wxLEFT, DCPOMATIC_DIALOG_BORDER); + + _verify = new Button(this, _("Verify")); + overall_sizer->Add(_verify, 0, wxEXPAND | wxLEFT | wxRIGHT, DCPOMATIC_DIALOG_BORDER); + + _progress_panel = new VerifyDCPProgressPanel(this); + overall_sizer->Add(_progress_panel, 0, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); + + _result_panel = new VerifyDCPResultPanel(this); + overall_sizer->Add(_result_panel, 0, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); + + SetSizerAndFit(overall_sizer); + + _dcp->Changed.connect(boost::bind(&DOMFrame::setup_sensitivity, this)); + _verify->bind(&DOMFrame::verify_clicked, this); + + setup_sensitivity(); + } + +private: + void setup_sensitivity() + { + _verify->Enable(!_dcp->GetPath().IsEmpty()); + } + + void verify_clicked() + { + auto dcp = boost::filesystem::path(wx_to_std(_dcp->GetPath())); + if (dcp.empty()) { + return; + } + + auto job_manager = JobManager::instance(); + auto job = make_shared<VerifyDCPJob>(std::vector<boost::filesystem::path>{dcp}, std::vector<boost::filesystem::path>()); + job_manager->add(job); + + while (job_manager->work_to_do()) { + wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT); + dcpomatic_sleep_seconds(1); + + _progress_panel->update(job); + } + + _result_panel->fill(job); + if (_write_log->get()) { + dcp::TextFormatter formatter(dcp / "REPORT.txt"); + dcp::verify_report(job->result(), formatter); + } + } + + DirPickerCtrl* _dcp; + CheckBox* _write_log; + Button* _verify; + VerifyDCPProgressPanel* _progress_panel; + VerifyDCPResultPanel* _result_panel; +}; + + +/** @class App + * @brief The magic App class for wxWidgets. + */ +class App : public wxApp +{ +public: + App() + : wxApp() + { + dcpomatic_setup_path_encoding(); +#ifdef DCPOMATIC_LINUX + XInitThreads(); +#endif + } + +private: + bool OnInit() override + { + try { + SetAppName(variant::wx::dcpomatic_verifier()); + + if (!wxApp::OnInit()) { + return false; + } + +#ifdef DCPOMATIC_LINUX + unsetenv("UBUNTU_MENUPROXY"); +#endif + +#ifdef DCPOMATIC_OSX + dcpomatic_sleep_seconds(1); + make_foreground_application(); +#endif + + /* 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 filters etc. + set up yet. + */ + dcpomatic_setup_i18n(); + + /* Set things up, including filters etc. + which will now be internationalised correctly. + */ + dcpomatic_setup(); + + /* Force the configuration to be re-loaded correctly next + time it is needed. + */ + Config::drop(); + + _frame = new DOMFrame(variant::wx::dcpomatic_verifier()); + SetTopWindow(_frame); + _frame->SetSize({480, 640}); + _frame->Show(); + } + catch (exception& e) + { + error_dialog(nullptr, variant::wx::insert_dcpomatic_verifier("%s could not start."), std_to_wx(e.what())); + } + + return true; + } + + void report_exception() + { + try { + throw; + } catch (FileError& e) { + error_dialog( + nullptr, + wxString::Format( + _("An exception occurred: %s (%s)\n\n%s"), + std_to_wx(e.what()), + std_to_wx(e.file().string().c_str()), + wx::report_problem() + ) + ); + } catch (boost::filesystem::filesystem_error& e) { + error_dialog( + nullptr, + wxString::Format( + _("An exception occurred: %s (%s) (%s)\n\n%s"), + std_to_wx(e.what()), + std_to_wx(e.path1().string()), + std_to_wx(e.path2().string()), + wx::report_problem() + ) + ); + } catch (exception& e) { + error_dialog( + nullptr, + wxString::Format( + _("An exception occurred: %s.\n\n%s"), + std_to_wx(e.what()), + wx::report_problem() + ) + ); + } catch (...) { + error_dialog(nullptr, _("An unknown exception occurred.") + " " + wx::report_problem()); + } + } + + /* An unhandled exception has occurred inside the main event loop */ + bool OnExceptionInMainLoop() override + { + report_exception(); + return false; + } + + void OnUnhandledException() override + { + report_exception(); + } + + DOMFrame* _frame = nullptr; +}; + + +IMPLEMENT_APP(App) diff --git a/src/tools/wscript b/src/tools/wscript index c3b2b5fe0..e6d7c4be1 100644 --- a/src/tools/wscript +++ b/src/tools/wscript @@ -17,11 +17,79 @@ # along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. # +from __future__ import print_function import os import glob from waflib import Logs import i18n + +def description(tool, variant): + descriptions = { + 'dcpomatic': 'DCP-o-matic', + 'dcpomatic_batch': 'DCP-o-matic Batch Converter', + 'dcpomatic_server': 'DCP-o-matic Encode Server', + 'dcpomatic_kdm': 'DCP-o-matic KDM Creator', + 'dcpomatic_player': 'DCP-o-matic Player', + 'dcpomatic_playlist': 'DCP-o-matic Playlist Editor', + 'dcpomatic_combiner': 'DCP-o-matic Combiner', + 'dcpomatic_verifier': 'DCP-o-matic Verifier', + } + return descriptions[tool] if tool in descriptions else tool + + +def make_rc(tool, icon, variant): + filename = 'build/src/tools/%s.rc' % tool + with open(filename, 'w') as rc: + if tool == 'dcpomatic_disk_writer': + print('#include "winuser.h"', file=rc) + print('1 RT_MANIFEST "dcpomatic2_disk_writer.exe.manifest"', file=rc) + with open("build/src/tools/dcpomatic2_disk_writer.exe.manifest", "w") as manifest: + print(""" +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="dcpomatic2_disk_writer" type="win32"/> + <description>DCP-o-matic Disk Writer</description> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/> + </requestedPrivileges> + </security> + </trustInfo> +</assembly>""", file=manifest) + else: + print(""" +id ICON "../../graphics/windows/%s" +#include "wx-3.1/wx/msw/wx.rc" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,0,0,2 + PRODUCTVERSION 0,0,0,2 + FILEFLAGSMASK 0x3fL + #ifdef _DEBUG + FILEFLAGS 0x1L + #else + FILEFLAGS 0x0L + #endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L + { + BLOCK "StringFileInfo" + { + BLOCK "040904b0" + { + VALUE "FileDescription", "%s\\0" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1200 + } + } + """ % (icon, description(tool, variant)), file=rc) + return filename + def configure(conf): if conf.env.TARGET_WINDOWS_64 or conf.env.TARGET_WINDOWS_32: conf.env.append_value('CXXFLAGS', ['-mconsole']) @@ -30,7 +98,7 @@ def configure(conf): def build(bld): uselib = 'BOOST_THREAD BOOST_DATETIME DCP XMLSEC CXML XMLPP AVFORMAT AVFILTER AVCODEC ' uselib += 'AVUTIL SWSCALE SWRESAMPLE POSTPROC CURL BOOST_FILESYSTEM SSH ZIP CAIROMM FONTCONFIG PANGOMM SUB ' - uselib += 'SNDFILE SAMPLERATE BOOST_REGEX ICU NETTLE RTAUDIO PNG JPEG LEQM_NRT ' + uselib += 'SNDFILE SAMPLERATE BOOST_REGEX ICU NETTLE RTAUDIO PNG JPEG LEQM_NRT SQLITE3 ' if bld.env.ENABLE_DISK: if bld.env.TARGET_LINUX: @@ -46,6 +114,11 @@ def build(bld): if bld.env.ENABLE_DISK and not bld.env.DISABLE_GUI: cli_tools.append('dcpomatic_disk_writer') + try: + os.makedirs('build/src/tools') + except: + pass + for t in cli_tools: obj = bld(features='cxx cxxprogram') obj.uselib = uselib @@ -53,7 +126,7 @@ def build(bld): obj.use = ['libdcpomatic2'] obj.source = '%s.cc' % t if (bld.env.TARGET_WINDOWS_64 or bld.env.TARGET_WINDOWS_32) and t == 'dcpomatic_disk_writer': - obj.source += ' ../../platform/windows/%s.rc' % t + obj.source += ' ../../%s' % (make_rc(t, None, bld.env.VARIANT)) # Prevent a console window opening when we start dcpomatic2_disk_writer obj.env.append_value('LINKFLAGS', '-Wl,-subsystem,windows') obj.target = t.replace('dcpomatic', 'dcpomatic2') @@ -62,7 +135,15 @@ def build(bld): gui_tools = [] if not bld.env.DISABLE_GUI: - gui_tools = ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player', 'dcpomatic_playlist', 'dcpomatic_combiner', 'dcpomatic_editor'] + gui_tools = ['dcpomatic', + 'dcpomatic_batch', + 'dcpomatic_server', + 'dcpomatic_kdm', + 'dcpomatic_player', + 'dcpomatic_playlist', + 'dcpomatic_combiner', + 'dcpomatic_editor', + 'dcpomatic_verifier'] if bld.env.ENABLE_DISK: gui_tools.append('dcpomatic_disk') @@ -82,7 +163,7 @@ def build(bld): obj.use = ['libdcpomatic2', 'libdcpomatic2-wx'] obj.source = '%s.cc' % t if bld.env.TARGET_WINDOWS_64 or bld.env.TARGET_WINDOWS_32: - obj.source += ' ../../platform/windows/%s.rc' % t + obj.source += ' ../../%s' % make_rc(t, t.replace("dcpomatic", "dcpomatic2") + ".ico", bld.env.VARIANT) obj.target = t.replace('dcpomatic', 'dcpomatic2') i18n.po_to_mo(os.path.join('src', 'tools'), 'dcpomatic2', bld) |
