summaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/dcpomatic.cc272
-rw-r--r--src/tools/dcpomatic_batch.cc67
-rw-r--r--src/tools/dcpomatic_cli.cc21
-rw-r--r--src/tools/dcpomatic_combiner.cc25
-rw-r--r--src/tools/dcpomatic_disk.cc60
-rw-r--r--src/tools/dcpomatic_editor.cc27
-rw-r--r--src/tools/dcpomatic_kdm.cc57
-rw-r--r--src/tools/dcpomatic_player.cc186
-rw-r--r--src/tools/dcpomatic_playlist.cc67
-rw-r--r--src/tools/dcpomatic_server.cc15
-rw-r--r--src/tools/dcpomatic_server_cli.cc29
-rw-r--r--src/tools/dcpomatic_verifier.cc251
-rw-r--r--src/tools/wscript89
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)