2 Copyright (C) 2012-2022 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
23 #include "colour_conversion.h"
24 #include "compose.hpp"
26 #include "constants.h"
28 #include "dcp_content_type.h"
29 #include "dkdm_recipient.h"
30 #include "dkdm_wrapper.h"
37 #include <dcp/certificate_chain.h>
38 #include <dcp/name_format.h>
39 #include <dcp/raw_convert.h>
40 #include <libcxml/cxml.h>
42 #include <libxml++/libxml++.h>
43 #include <boost/filesystem.hpp>
44 #include <boost/algorithm/string.hpp>
45 #include <boost/thread.hpp>
54 using std::dynamic_pointer_cast;
57 using std::make_shared;
61 using std::shared_ptr;
64 using boost::algorithm::trim;
65 using boost::optional;
66 using dcp::raw_convert;
69 Config* Config::_instance = 0;
70 int const Config::_current_version = 3;
71 boost::signals2::signal<void (Config::LoadFailure)> Config::FailedToLoad;
72 boost::signals2::signal<void (string)> Config::Warning;
73 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
76 /** Construct default configuration */
78 /* DKDMs are not considered a thing to reset on set_defaults() */
79 : _dkdms (new DKDMGroup ("root"))
80 , _default_kdm_duration (1, RoughDuration::Unit::WEEKS)
87 Config::set_defaults ()
89 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
90 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
91 _server_port_base = 6192;
92 _use_any_servers = true;
94 _only_servers_encode = false;
95 _tms_protocol = FileTransferProtocol::SCP;
101 _allow_any_dcp_frame_rate = false;
102 _allow_any_container = false;
103 _allow_96khz_audio = false;
104 _use_all_audio_channels = false;
105 _show_experimental_audio_processors = false;
106 _language = optional<string> ();
107 _default_still_length = 10;
108 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
109 _default_dcp_audio_channels = 8;
110 _default_j2k_bandwidth = 150000000;
111 _default_audio_delay = 0;
112 _default_interop = false;
113 _default_metadata.clear ();
114 _upload_after_make_dcp = false;
117 _mail_protocol = EmailProtocol::AUTO;
123 _notification_from = "";
124 _notification_to = "";
125 _notification_cc.clear ();
126 _notification_bcc = "";
127 _check_for_updates = false;
128 _check_for_test_updates = false;
129 _maximum_j2k_bandwidth = 250000000;
130 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
131 _analyse_ebur128 = true;
132 _automatic_audio_analysis = false;
133 #ifdef DCPOMATIC_WINDOWS
134 _win32_console = false;
136 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
137 * ~/.config/dcpomatic2 (or equivalent) and written back there.
139 _cinemas_file = read_path ("cinemas.xml");
140 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
141 _show_hints_before_make_dcp = true;
142 _confirm_kdm_email = true;
143 _kdm_container_name_format = dcp::NameFormat("KDM_%f_%c");
144 _kdm_filename_format = dcp::NameFormat("KDM_%f_%c_%s");
145 _dkdm_filename_format = dcp::NameFormat("DKDM_%f_%c_%s");
146 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
147 _dcp_asset_filename_format = dcp::NameFormat ("%t");
148 _jump_to_selected = true;
149 for (int i = 0; i < NAG_COUNT; ++i) {
153 _sound_output = optional<string> ();
154 _last_kdm_write_type = KDM_WRITE_FLAT;
155 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
156 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
158 /* I think the scaling factor here should be the ratio of the longest frame
159 encode time to the shortest; if the thread count is T, longest time is L
160 and the shortest time S we could encode L/S frames per thread whilst waiting
161 for the L frame to encode so we might have to store LT/S frames.
163 However we don't want to use too much memory, so keep it a bit lower than we'd
164 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
165 use about 240Mb with 72 encoding threads.
167 _frames_in_memory_multiplier = 3;
168 _decode_reduction = optional<int>();
169 _default_notify = false;
170 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
171 _notification[i] = false;
173 _barco_username = optional<string>();
174 _barco_password = optional<string>();
175 _christie_username = optional<string>();
176 _christie_password = optional<string>();
177 _gdc_username = optional<string>();
178 _gdc_password = optional<string>();
179 _player_mode = PLAYER_MODE_WINDOW;
180 _player_restricted_menus = false;
181 _playlist_editor_restricted_menus = false;
183 _video_view_type = VIDEO_VIEW_SIMPLE;
184 _respect_kdm_validity_periods = true;
185 _player_debug_log_file = boost::none;
186 _player_content_directory = boost::none;
187 _player_playlist_directory = boost::none;
188 _player_kdm_directory = boost::none;
189 _audio_mapping = boost::none;
190 _custom_languages.clear ();
191 _initial_paths.clear();
192 _initial_paths["AddFilesPath"] = boost::none;
193 _initial_paths["AddKDMPath"] = boost::none;
194 _initial_paths["AddDKDMPath"] = boost::none;
195 _initial_paths["SelectCertificatePath"] = boost::none;
196 _initial_paths["AddCombinerInputPath"] = boost::none;
197 _initial_paths["ExportSubtitlesPath"] = boost::none;
198 _initial_paths["ExportVideoPath"] = boost::none;
199 _initial_paths["DebugLogPath"] = boost::none;
200 _initial_paths["CinemaDatabasePath"] = boost::none;
201 _initial_paths["ConfigFilePath"] = boost::none;
202 _initial_paths["Preferences"] = boost::none;
203 _use_isdcf_name_by_default = true;
204 _write_kdms_to_disk = true;
206 _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
207 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
208 _auto_crop_threshold = 0.1;
209 _last_release_notes_version = boost::none;
210 _allow_smpte_bv20 = false;
211 _isdcf_name_part_length = 14;
213 _allowed_dcp_frame_rates.clear ();
214 _allowed_dcp_frame_rates.push_back (24);
215 _allowed_dcp_frame_rates.push_back (25);
216 _allowed_dcp_frame_rates.push_back (30);
217 _allowed_dcp_frame_rates.push_back (48);
218 _allowed_dcp_frame_rates.push_back (50);
219 _allowed_dcp_frame_rates.push_back (60);
221 set_kdm_email_to_default ();
222 set_notification_email_to_default ();
223 set_cover_sheet_to_default ();
225 #ifdef DCPOMATIC_GROK
229 _main_divider_sash_position = {};
230 _main_content_divider_sash_position = {};
232 _export.set_defaults();
236 Config::restore_defaults ()
238 Config::instance()->set_defaults ();
239 Config::instance()->changed ();
242 shared_ptr<dcp::CertificateChain>
243 Config::create_certificate_chain ()
245 return make_shared<dcp::CertificateChain> (
247 CERTIFICATE_VALIDITY_PERIOD,
250 ".dcpomatic.smpte-430-2.ROOT",
251 ".dcpomatic.smpte-430-2.INTERMEDIATE",
252 "CS.dcpomatic.smpte-430-2.LEAF"
259 using namespace boost::filesystem;
261 auto copy_adding_number = [](path const& path_to_copy) {
263 auto add_number = [](path const& path, int number) {
264 return String::compose("%1.%2", path, number);
268 while (n < 100 && exists(add_number(path_to_copy, n))) {
271 boost::system::error_code ec;
272 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
275 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
276 * to write over. This is more intended for the situation where we have a corrupted config.xml,
277 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
278 * file). But we might as well back up the other files while we're about it.
281 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
282 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
284 copy_adding_number (config_write_file());
286 /* These do not use State::write_path, so whatever path is in the Config we will copy
289 copy_adding_number (_cinemas_file);
290 copy_adding_number (_dkdm_recipients_file);
298 read_dkdm_recipients();
303 Config::read_config()
306 cxml::Document f ("Config");
307 f.read_file(dcp::filesystem::fix_long_path(config_read_file()));
309 auto version = f.optional_number_child<int> ("Version");
310 if (version && *version < _current_version) {
311 /* Back up the old config before we re-write it in a back-incompatible way */
315 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
316 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
318 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
319 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
322 _default_directory = f.optional_string_child ("DefaultDirectory");
323 if (_default_directory && _default_directory->empty ()) {
324 /* We used to store an empty value for this to mean "none set" */
325 _default_directory = boost::optional<boost::filesystem::path> ();
328 auto b = f.optional_number_child<int> ("ServerPort");
330 b = f.optional_number_child<int> ("ServerPortBase");
332 _server_port_base = b.get ();
334 auto u = f.optional_bool_child ("UseAnyServers");
335 _use_any_servers = u.get_value_or (true);
337 for (auto i: f.node_children("Server")) {
338 if (i->node_children("HostName").size() == 1) {
339 _servers.push_back (i->string_child ("HostName"));
341 _servers.push_back (i->content ());
345 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
346 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
347 _tms_passive = f.optional_bool_child("TMSPassive").get_value_or(true);
348 _tms_ip = f.string_child ("TMSIP");
349 _tms_path = f.string_child ("TMSPath");
350 _tms_user = f.string_child ("TMSUser");
351 _tms_password = f.string_child ("TMSPassword");
353 _language = f.optional_string_child ("Language");
355 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
356 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
358 if (f.optional_string_child ("DCPMetadataIssuer")) {
359 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
360 } else if (f.optional_string_child ("DCPIssuer")) {
361 _dcp_issuer = f.string_child ("DCPIssuer");
364 auto up = f.optional_bool_child("UploadAfterMakeDCP");
366 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
368 _upload_after_make_dcp = up.get_value_or (false);
369 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
370 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
371 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
372 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
373 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
375 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
376 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
377 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
378 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
381 auto al = f.optional_string_child("DefaultAudioLanguage");
383 _default_audio_language = dcp::LanguageTag(*al);
385 } catch (std::runtime_error&) {}
388 auto te = f.optional_string_child("DefaultTerritory");
390 _default_territory = dcp::LanguageTag::RegionSubtag(*te);
392 } catch (std::runtime_error&) {}
394 for (auto const& i: f.node_children("DefaultMetadata")) {
395 _default_metadata[i->string_attribute("key")] = i->content();
398 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
400 /* Read any cinemas that are still lying around in the config file
401 * from an old version.
405 _mail_server = f.string_child ("MailServer");
406 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
409 /* Make sure this matches the code in write_config */
410 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
411 if (protocol == "Auto") {
412 _mail_protocol = EmailProtocol::AUTO;
413 } else if (protocol == "Plain") {
414 _mail_protocol = EmailProtocol::PLAIN;
415 } else if (protocol == "STARTTLS") {
416 _mail_protocol = EmailProtocol::STARTTLS;
417 } else if (protocol == "SSL") {
418 _mail_protocol = EmailProtocol::SSL;
422 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
423 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
425 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
426 _kdm_from = f.string_child ("KDMFrom");
427 for (auto i: f.node_children("KDMCC")) {
428 if (!i->content().empty()) {
429 _kdm_cc.push_back (i->content ());
432 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
433 _kdm_email = f.string_child ("KDMEmail");
435 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
436 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
437 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
438 for (auto i: f.node_children("NotificationCC")) {
439 if (!i->content().empty()) {
440 _notification_cc.push_back (i->content ());
443 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
444 if (f.optional_string_child("NotificationEmail")) {
445 _notification_email = f.string_child("NotificationEmail");
448 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
449 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
451 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
452 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
453 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
454 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
455 _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false);
456 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
458 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
459 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
460 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
461 #ifdef DCPOMATIC_WINDOWS
462 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
465 for (auto i: f.node_children("History")) {
466 _history.push_back (i->content ());
469 for (auto i: f.node_children("PlayerHistory")) {
470 _player_history.push_back (i->content ());
473 auto signer = f.optional_node_child ("Signer");
475 auto c = make_shared<dcp::CertificateChain>();
476 /* Read the signing certificates and private key in from the config file */
477 for (auto i: signer->node_children ("Certificate")) {
478 c->add (dcp::Certificate (i->content ()));
480 c->set_key (signer->string_child ("PrivateKey"));
483 /* Make a new set of signing certificates and key */
484 _signer_chain = create_certificate_chain ();
487 auto decryption = f.optional_node_child ("Decryption");
489 auto c = make_shared<dcp::CertificateChain>();
490 for (auto i: decryption->node_children ("Certificate")) {
491 c->add (dcp::Certificate (i->content ()));
493 c->set_key (decryption->string_child ("PrivateKey"));
494 _decryption_chain = c;
496 _decryption_chain = create_certificate_chain ();
499 /* These must be done before we call Bad as that might set one
502 for (auto i: f.node_children("Nagged")) {
503 auto const id = number_attribute<int>(i, "Id", "id");
504 if (id >= 0 && id < NAG_COUNT) {
505 _nagged[id] = raw_convert<int>(i->content());
509 auto bad = check_certificates ();
511 auto const remake = Bad(*bad);
512 if (remake && *remake) {
514 case BAD_SIGNER_UTF8_STRINGS:
515 case BAD_SIGNER_INCONSISTENT:
516 case BAD_SIGNER_VALIDITY_TOO_LONG:
517 case BAD_SIGNER_DN_QUALIFIER:
518 _signer_chain = create_certificate_chain ();
520 case BAD_DECRYPTION_INCONSISTENT:
521 _decryption_chain = create_certificate_chain ();
527 if (f.optional_node_child("DKDMGroup")) {
528 /* New-style: all DKDMs in a group */
529 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
531 /* Old-style: one or more DKDM nodes */
532 _dkdms = make_shared<DKDMGroup>("root");
533 for (auto i: f.node_children("DKDM")) {
534 _dkdms->add (DKDMBase::read (i));
537 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
538 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
539 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
540 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
541 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
542 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
543 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
544 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
545 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
546 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
547 /* The variable was renamed but not the XML tag */
548 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
549 _sound_output = f.optional_string_child("PreviewSoundOutput");
550 if (f.optional_string_child("CoverSheet")) {
551 _cover_sheet = f.optional_string_child("CoverSheet").get();
553 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
554 if (f.optional_string_child("LastKDMWriteType")) {
555 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
556 _last_kdm_write_type = KDM_WRITE_FLAT;
557 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
558 _last_kdm_write_type = KDM_WRITE_FOLDER;
559 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
560 _last_kdm_write_type = KDM_WRITE_ZIP;
563 if (f.optional_string_child("LastDKDMWriteType")) {
564 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
565 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
566 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
567 _last_dkdm_write_type = DKDM_WRITE_FILE;
570 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
571 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
572 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
574 for (auto i: f.node_children("Notification")) {
575 int const id = number_attribute<int>(i, "Id", "id");
576 if (id >= 0 && id < NOTIFICATION_COUNT) {
577 _notification[id] = raw_convert<int>(i->content());
581 _barco_username = f.optional_string_child("BarcoUsername");
582 _barco_password = f.optional_string_child("BarcoPassword");
583 _christie_username = f.optional_string_child("ChristieUsername");
584 _christie_password = f.optional_string_child("ChristiePassword");
585 _gdc_username = f.optional_string_child("GDCUsername");
586 _gdc_password = f.optional_string_child("GDCPassword");
588 auto pm = f.optional_string_child("PlayerMode");
589 if (pm && *pm == "window") {
590 _player_mode = PLAYER_MODE_WINDOW;
591 } else if (pm && *pm == "full") {
592 _player_mode = PLAYER_MODE_FULL;
593 } else if (pm && *pm == "dual") {
594 _player_mode = PLAYER_MODE_DUAL;
597 _player_restricted_menus = f.optional_bool_child("PlayerRestrictedMenus").get_value_or(false);
598 _playlist_editor_restricted_menus = f.optional_bool_child("PlaylistEditorRestrictedMenus").get_value_or(false);
600 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
601 auto vc = f.optional_string_child("VideoViewType");
602 if (vc && *vc == "opengl") {
603 _video_view_type = VIDEO_VIEW_OPENGL;
604 } else if (vc && *vc == "simple") {
605 _video_view_type = VIDEO_VIEW_SIMPLE;
607 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
608 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
609 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
610 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
611 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
613 if (f.optional_node_child("AudioMapping")) {
614 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
617 for (auto i: f.node_children("CustomLanguage")) {
619 /* This will fail if it's called before dcp::init() as it won't recognise the
620 * tag. That's OK because the Config will be reloaded again later.
622 _custom_languages.push_back (dcp::LanguageTag(i->content()));
623 } catch (std::runtime_error& e) {}
626 for (auto& initial: _initial_paths) {
627 initial.second = f.optional_string_child(initial.first);
629 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
630 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
631 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
632 _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
633 if (auto duration = f.optional_node_child("DefaultKDMDuration")) {
634 _default_kdm_duration = RoughDuration(duration);
636 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
638 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
639 _last_release_notes_version = f.optional_string_child("LastReleaseNotesVersion");
640 _main_divider_sash_position = f.optional_number_child<int>("MainDividerSashPosition");
641 _main_content_divider_sash_position = f.optional_number_child<int>("MainContentDividerSashPosition");
643 if (auto loc = f.optional_string_child("DefaultAddFileLocation")) {
644 if (*loc == "last") {
645 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
646 } else if (*loc == "project") {
647 _default_add_file_location = DefaultAddFileLocation::SAME_AS_PROJECT;
651 _allow_smpte_bv20 = f.optional_bool_child("AllowSMPTEBv20").get_value_or(false);
652 _isdcf_name_part_length = f.optional_number_child<int>("ISDCFNamePartLength").get_value_or(14);
654 #ifdef DCPOMATIC_GROK
655 if (auto grok = f.optional_node_child("Grok")) {
660 _export.read(f.optional_node_child("Export"));
663 if (have_existing("config.xml")) {
665 /* We have a config file but it didn't load */
666 FailedToLoad(LoadFailure::CONFIG);
669 /* Make a new set of signing certificates and key */
670 _signer_chain = create_certificate_chain ();
671 /* And similar for decryption of KDMs */
672 _decryption_chain = create_certificate_chain ();
678 Config::read_cinemas()
680 if (dcp::filesystem::exists(_cinemas_file)) {
682 cxml::Document f("Cinemas");
683 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
687 FailedToLoad(LoadFailure::CINEMAS);
695 Config::read_dkdm_recipients()
697 if (dcp::filesystem::exists(_dkdm_recipients_file)) {
699 cxml::Document f("DKDMRecipients");
700 f.read_file(dcp::filesystem::fix_long_path(_dkdm_recipients_file));
701 read_dkdm_recipients(f);
704 FailedToLoad(LoadFailure::DKDM_RECIPIENTS);
705 write_dkdm_recipients();
711 /** @return Singleton instance */
715 if (_instance == nullptr) {
716 _instance = new Config;
723 /** Write our configuration to disk */
725 Config::write () const
729 write_dkdm_recipients ();
733 Config::write_config () const
736 auto root = doc.create_root_node ("Config");
738 /* [XML] Version The version number of the configuration file format. */
739 cxml::add_text_child(root, "Version", raw_convert<string>(_current_version));
740 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
741 cxml::add_text_child(root, "MasterEncodingThreads", raw_convert<string>(_master_encoding_threads));
742 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
743 cxml::add_text_child(root, "ServerEncodingThreads", raw_convert<string>(_server_encoding_threads));
744 if (_default_directory) {
745 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
746 cxml::add_text_child(root, "DefaultDirectory", _default_directory->string());
748 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
749 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
750 by the batch converter to listen for job requests.
752 cxml::add_text_child(root, "ServerPortBase", raw_convert<string>(_server_port_base));
753 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
754 cxml::add_text_child(root, "UseAnyServers", _use_any_servers ? "1" : "0");
756 for (auto i: _servers) {
757 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
760 cxml::add_text_child(root, "Server", i);
763 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
764 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
766 cxml::add_text_child(root, "OnlyServersEncode", _only_servers_encode ? "1" : "0");
767 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
768 cxml::add_text_child(root, "TMSProtocol", raw_convert<string>(static_cast<int>(_tms_protocol)));
769 /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */
770 cxml::add_text_child(root, "TMSPassive", _tms_passive ? "1" : "0");
771 /* [XML] TMSIP IP address of TMS. */
772 cxml::add_text_child(root, "TMSIP", _tms_ip);
773 /* [XML] TMSPath Path on the TMS to copy files to. */
774 cxml::add_text_child(root, "TMSPath", _tms_path);
775 /* [XML] TMSUser Username to log into the TMS with. */
776 cxml::add_text_child(root, "TMSUser", _tms_user);
777 /* [XML] TMSPassword Password to log into the TMS with. */
778 cxml::add_text_child(root, "TMSPassword", _tms_password);
780 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
781 cxml::add_text_child(root, "Language", _language.get());
783 if (_default_dcp_content_type) {
784 /* [XML:opt] DefaultDCPContentType Default content type to use when creating new films (<code>FTR</code>, <code>SHR</code>,
785 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
786 <code>PSA</code> or <code>ADV</code>). */
787 cxml::add_text_child(root, "DefaultDCPContentType", _default_dcp_content_type->isdcf_name());
789 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
790 cxml::add_text_child(root, "DefaultDCPAudioChannels", raw_convert<string>(_default_dcp_audio_channels));
791 /* [XML] DCPIssuer Issuer text to write into CPL files. */
792 cxml::add_text_child(root, "DCPIssuer", _dcp_issuer);
793 /* [XML] DCPCreator Creator text to write into CPL files. */
794 cxml::add_text_child(root, "DCPCreator", _dcp_creator);
795 /* [XML] Company name to write into MXF files. */
796 cxml::add_text_child(root, "DCPCompanyName", _dcp_company_name);
797 /* [XML] Product name to write into MXF files. */
798 cxml::add_text_child(root, "DCPProductName", _dcp_product_name);
799 /* [XML] Product version to write into MXF files. */
800 cxml::add_text_child(root, "DCPProductVersion", _dcp_product_version);
801 /* [XML] Comment to write into JPEG2000 data. */
802 cxml::add_text_child(root, "DCPJ2KComment", _dcp_j2k_comment);
803 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
804 cxml::add_text_child(root, "UploadAfterMakeDCP", _upload_after_make_dcp ? "1" : "0");
806 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
807 cxml::add_text_child(root, "DefaultStillLength", raw_convert<string>(_default_still_length));
808 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
809 cxml::add_text_child(root, "DefaultJ2KBandwidth", raw_convert<string>(_default_j2k_bandwidth));
810 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
811 cxml::add_text_child(root, "DefaultAudioDelay", raw_convert<string>(_default_audio_delay));
812 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
813 cxml::add_text_child(root, "DefaultInterop", _default_interop ? "1" : "0");
814 if (_default_audio_language) {
815 /* [XML] DefaultAudioLanguage Default audio language to use for new films */
816 cxml::add_text_child(root, "DefaultAudioLanguage", _default_audio_language->to_string());
818 if (_default_territory) {
819 /* [XML] DefaultTerritory Default territory to use for new films */
820 cxml::add_text_child(root, "DefaultTerritory", _default_territory->subtag());
822 for (auto const& i: _default_metadata) {
823 auto c = cxml::add_child(root, "DefaultMetadata");
824 c->set_attribute("key", i.first);
825 c->add_child_text(i.second);
827 if (_default_kdm_directory) {
828 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
829 cxml::add_text_child(root, "DefaultKDMDirectory", _default_kdm_directory->string ());
831 _default_kdm_duration.as_xml(cxml::add_child(root, "DefaultKDMDuration"));
832 /* [XML] MailServer Hostname of SMTP server to use. */
833 cxml::add_text_child(root, "MailServer", _mail_server);
834 /* [XML] MailPort Port number to use on SMTP server. */
835 cxml::add_text_child(root, "MailPort", raw_convert<string>(_mail_port));
836 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
837 switch (_mail_protocol) {
838 case EmailProtocol::AUTO:
839 cxml::add_text_child(root, "MailProtocol", "Auto");
841 case EmailProtocol::PLAIN:
842 cxml::add_text_child(root, "MailProtocol", "Plain");
844 case EmailProtocol::STARTTLS:
845 cxml::add_text_child(root, "MailProtocol", "STARTTLS");
847 case EmailProtocol::SSL:
848 cxml::add_text_child(root, "MailProtocol", "SSL");
851 /* [XML] MailUser Username to use on SMTP server. */
852 cxml::add_text_child(root, "MailUser", _mail_user);
853 /* [XML] MailPassword Password to use on SMTP server. */
854 cxml::add_text_child(root, "MailPassword", _mail_password);
856 /* [XML] KDMSubject Subject to use for KDM emails. */
857 cxml::add_text_child(root, "KDMSubject", _kdm_subject);
858 /* [XML] KDMFrom From address to use for KDM emails. */
859 cxml::add_text_child(root, "KDMFrom", _kdm_from);
860 for (auto i: _kdm_cc) {
861 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
862 cxml::add_text_child(root, "KDMCC", i);
864 /* [XML] KDMBCC BCC address to use for KDM emails. */
865 cxml::add_text_child(root, "KDMBCC", _kdm_bcc);
866 /* [XML] KDMEmail Text of KDM email. */
867 cxml::add_text_child(root, "KDMEmail", _kdm_email);
869 /* [XML] NotificationSubject Subject to use for notification emails. */
870 cxml::add_text_child(root, "NotificationSubject", _notification_subject);
871 /* [XML] NotificationFrom From address to use for notification emails. */
872 cxml::add_text_child(root, "NotificationFrom", _notification_from);
873 /* [XML] NotificationFrom To address to use for notification emails. */
874 cxml::add_text_child(root, "NotificationTo", _notification_to);
875 for (auto i: _notification_cc) {
876 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
877 cxml::add_text_child(root, "NotificationCC", i);
879 /* [XML] NotificationBCC BCC address to use for notification emails. */
880 cxml::add_text_child(root, "NotificationBCC", _notification_bcc);
881 /* [XML] NotificationEmail Text of notification email. */
882 cxml::add_text_child(root, "NotificationEmail", _notification_email);
884 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
885 cxml::add_text_child(root, "CheckForUpdates", _check_for_updates ? "1" : "0");
886 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
887 cxml::add_text_child(root, "CheckForTestUpdates", _check_for_test_updates ? "1" : "0");
889 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
890 cxml::add_text_child(root, "MaximumJ2KBandwidth", raw_convert<string>(_maximum_j2k_bandwidth));
891 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
892 cxml::add_text_child(root, "AllowAnyDCPFrameRate", _allow_any_dcp_frame_rate ? "1" : "0");
893 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
894 cxml::add_text_child(root, "AllowAnyContainer", _allow_any_container ? "1" : "0");
895 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
896 cxml::add_text_child(root, "Allow96kHzAudio", _allow_96khz_audio ? "1" : "0");
897 /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
898 cxml::add_text_child(root, "UseAllAudioChannels", _use_all_audio_channels ? "1" : "0");
899 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
900 cxml::add_text_child(root, "ShowExperimentalAudioProcessors", _show_experimental_audio_processors ? "1" : "0");
901 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
902 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
903 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
904 related to the player, 1024 debug information related to audio analyses.
906 cxml::add_text_child(root, "LogTypes", raw_convert<string> (_log_types));
907 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
908 cxml::add_text_child(root, "AnalyseEBUR128", _analyse_ebur128 ? "1" : "0");
909 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
910 cxml::add_text_child(root, "AutomaticAudioAnalysis", _automatic_audio_analysis ? "1" : "0");
911 #ifdef DCPOMATIC_WINDOWS
912 if (_win32_console) {
913 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
914 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
915 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
917 cxml::add_text_child(root, "Win32Console", "1");
921 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
922 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
924 auto signer = cxml::add_child(root, "Signer");
925 DCPOMATIC_ASSERT (_signer_chain);
926 for (auto const& i: _signer_chain->unordered()) {
927 cxml::add_text_child(signer, "Certificate", i.certificate (true));
929 cxml::add_text_child(signer, "PrivateKey", _signer_chain->key().get ());
931 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
932 auto decryption = cxml::add_child(root, "Decryption");
933 DCPOMATIC_ASSERT (_decryption_chain);
934 for (auto const& i: _decryption_chain->unordered()) {
935 cxml::add_text_child(decryption, "Certificate", i.certificate (true));
937 cxml::add_text_child(decryption, "PrivateKey", _decryption_chain->key().get());
939 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
942 for (auto i: _history) {
943 cxml::add_text_child(root, "History", i.string());
946 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
949 for (auto i: _player_history) {
950 cxml::add_text_child(root, "PlayerHistory", i.string());
953 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
954 or <code><DKDM></code> tags.
956 /* [XML] DKDM A DKDM as XML */
957 _dkdms->as_xml (root);
959 /* [XML] CinemasFile Filename of cinemas list file. */
960 cxml::add_text_child(root, "CinemasFile", _cinemas_file.string());
961 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
962 cxml::add_text_child(root, "DKDMRecipientsFile", _dkdm_recipients_file.string());
963 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
964 cxml::add_text_child(root, "ShowHintsBeforeMakeDCP", _show_hints_before_make_dcp ? "1" : "0");
965 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
966 cxml::add_text_child(root, "ConfirmKDMEmail", _confirm_kdm_email ? "1" : "0");
967 /* [XML] KDMFilenameFormat Format for KDM filenames. */
968 cxml::add_text_child(root, "KDMFilenameFormat", _kdm_filename_format.specification());
969 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
970 cxml::add_text_child(root, "DKDMFilenameFormat", _dkdm_filename_format.specification());
971 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
972 cxml::add_text_child(root, "KDMContainerNameFormat", _kdm_container_name_format.specification());
973 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
974 cxml::add_text_child(root, "DCPMetadataFilenameFormat", _dcp_metadata_filename_format.specification());
975 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
976 cxml::add_text_child(root, "DCPAssetFilenameFormat", _dcp_asset_filename_format.specification());
977 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
978 cxml::add_text_child(root, "JumpToSelected", _jump_to_selected ? "1" : "0");
979 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
980 for (int i = 0; i < NAG_COUNT; ++i) {
981 auto e = cxml::add_child(root, "Nagged");
982 e->set_attribute("id", raw_convert<string>(i));
983 e->add_child_text (_nagged[i] ? "1" : "0");
985 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
986 cxml::add_text_child(root, "PreviewSound", _sound ? "1" : "0");
988 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
989 cxml::add_text_child(root, "PreviewSoundOutput", _sound_output.get());
991 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
992 cxml::add_text_child(root, "CoverSheet", _cover_sheet);
993 if (_last_player_load_directory) {
994 cxml::add_text_child(root, "LastPlayerLoadDirectory", _last_player_load_directory->string());
996 /* [XML] LastKDMWriteType Last type of KDM-write: <code>flat</code> for a flat file, <code>folder</code> for a folder or <code>zip</code> for a ZIP file. */
997 if (_last_kdm_write_type) {
998 switch (_last_kdm_write_type.get()) {
1000 cxml::add_text_child(root, "LastKDMWriteType", "flat");
1002 case KDM_WRITE_FOLDER:
1003 cxml::add_text_child(root, "LastKDMWriteType", "folder");
1006 cxml::add_text_child(root, "LastKDMWriteType", "zip");
1010 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
1011 if (_last_dkdm_write_type) {
1012 switch (_last_dkdm_write_type.get()) {
1013 case DKDM_WRITE_INTERNAL:
1014 cxml::add_text_child(root, "LastDKDMWriteType", "internal");
1016 case DKDM_WRITE_FILE:
1017 cxml::add_text_child(root, "LastDKDMWriteType", "file");
1021 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
1022 frames to be held in memory at once.
1024 cxml::add_text_child(root, "FramesInMemoryMultiplier", raw_convert<string>(_frames_in_memory_multiplier));
1026 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
1027 if (_decode_reduction) {
1028 cxml::add_text_child(root, "DecodeReduction", raw_convert<string>(_decode_reduction.get()));
1031 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
1032 cxml::add_text_child(root, "DefaultNotify", _default_notify ? "1" : "0");
1034 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
1035 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
1036 auto e = cxml::add_child(root, "Notification");
1037 e->set_attribute ("id", raw_convert<string>(i));
1038 e->add_child_text (_notification[i] ? "1" : "0");
1041 if (_barco_username) {
1042 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
1043 cxml::add_text_child(root, "BarcoUsername", *_barco_username);
1045 if (_barco_password) {
1046 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
1047 cxml::add_text_child(root, "BarcoPassword", *_barco_password);
1050 if (_christie_username) {
1051 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
1052 cxml::add_text_child(root, "ChristieUsername", *_christie_username);
1054 if (_christie_password) {
1055 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
1056 cxml::add_text_child(root, "ChristiePassword", *_christie_password);
1059 if (_gdc_username) {
1060 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
1061 cxml::add_text_child(root, "GDCUsername", *_gdc_username);
1063 if (_gdc_password) {
1064 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
1065 cxml::add_text_child(root, "GDCPassword", *_gdc_password);
1068 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
1069 with separate (advanced) controls.
1071 switch (_player_mode) {
1072 case PLAYER_MODE_WINDOW:
1073 cxml::add_text_child(root, "PlayerMode", "window");
1075 case PLAYER_MODE_FULL:
1076 cxml::add_text_child(root, "PlayerMode", "full");
1078 case PLAYER_MODE_DUAL:
1079 cxml::add_text_child(root, "PlayerMode", "dual");
1083 if (_player_restricted_menus) {
1084 cxml::add_text_child(root, "PlayerRestrictedMenus", "1");
1087 if (_playlist_editor_restricted_menus) {
1088 cxml::add_text_child(root, "PlaylistEditorRestrictedMenus", "1");
1091 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
1092 cxml::add_text_child(root, "ImageDisplay", raw_convert<string>(_image_display));
1093 switch (_video_view_type) {
1094 case VIDEO_VIEW_SIMPLE:
1095 cxml::add_text_child(root, "VideoViewType", "simple");
1097 case VIDEO_VIEW_OPENGL:
1098 cxml::add_text_child(root, "VideoViewType", "opengl");
1101 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1102 cxml::add_text_child(root, "RespectKDMValidityPeriods", _respect_kdm_validity_periods ? "1" : "0");
1103 if (_player_debug_log_file) {
1104 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1105 cxml::add_text_child(root, "PlayerDebugLogFile", _player_debug_log_file->string());
1107 if (_player_content_directory) {
1108 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1109 cxml::add_text_child(root, "PlayerContentDirectory", _player_content_directory->string());
1111 if (_player_playlist_directory) {
1112 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1113 cxml::add_text_child(root, "PlayerPlaylistDirectory", _player_playlist_directory->string());
1115 if (_player_kdm_directory) {
1116 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1117 cxml::add_text_child(root, "PlayerKDMDirectory", _player_kdm_directory->string());
1119 if (_audio_mapping) {
1120 _audio_mapping->as_xml(cxml::add_child(root, "AudioMapping"));
1122 for (auto const& i: _custom_languages) {
1123 cxml::add_text_child(root, "CustomLanguage", i.to_string());
1125 for (auto const& initial: _initial_paths) {
1126 if (initial.second) {
1127 cxml::add_text_child(root, initial.first, initial.second->string());
1130 cxml::add_text_child(root, "UseISDCFNameByDefault", _use_isdcf_name_by_default ? "1" : "0");
1131 cxml::add_text_child(root, "WriteKDMsToDisk", _write_kdms_to_disk ? "1" : "0");
1132 cxml::add_text_child(root, "EmailKDMs", _email_kdms ? "1" : "0");
1133 cxml::add_text_child(root, "DefaultKDMType", dcp::formulation_to_string(_default_kdm_type));
1134 cxml::add_text_child(root, "AutoCropThreshold", raw_convert<string>(_auto_crop_threshold));
1135 if (_last_release_notes_version) {
1136 cxml::add_text_child(root, "LastReleaseNotesVersion", *_last_release_notes_version);
1138 if (_main_divider_sash_position) {
1139 cxml::add_text_child(root, "MainDividerSashPosition", raw_convert<string>(*_main_divider_sash_position));
1141 if (_main_content_divider_sash_position) {
1142 cxml::add_text_child(root, "MainContentDividerSashPosition", raw_convert<string>(*_main_content_divider_sash_position));
1145 cxml::add_text_child(root, "DefaultAddFileLocation",
1146 _default_add_file_location == DefaultAddFileLocation::SAME_AS_LAST_TIME ? "last" : "project"
1149 /* [XML] AllowSMPTEBv20 1 to allow the user to choose SMPTE (Bv2.0 only) as a standard, otherwise 0 */
1150 cxml::add_text_child(root, "AllowSMPTEBv20", _allow_smpte_bv20 ? "1" : "0");
1151 /* [XML] ISDCFNamePartLength Maximum length of the "name" part of an ISDCF name, which should be 14 according to the standard */
1152 cxml::add_text_child(root, "ISDCFNamePartLength", raw_convert<string>(_isdcf_name_part_length));
1154 #ifdef DCPOMATIC_GROK
1156 _grok->as_xml(cxml::add_child(root, "Grok"));
1160 _export.write(cxml::add_child(root, "Export"));
1162 auto target = config_write_file();
1165 auto const s = doc.write_to_string_formatted ();
1166 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1167 dcp::File f(tmp, "w");
1169 throw FileError (_("Could not open file for writing"), tmp);
1171 f.checked_write(s.c_str(), s.bytes());
1173 dcp::filesystem::remove(target);
1174 dcp::filesystem::rename(tmp, target);
1175 } catch (xmlpp::exception& e) {
1176 string s = e.what ();
1178 throw FileError (s, target);
1185 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1187 xmlpp::Document doc;
1188 auto root = doc.create_root_node (root_node);
1189 cxml::add_text_child(root, "Version", version);
1191 for (auto i: things) {
1192 i->as_xml(cxml::add_child(root, node));
1196 doc.write_to_file_formatted (file.string() + ".tmp");
1197 dcp::filesystem::remove(file);
1198 dcp::filesystem::rename(file.string() + ".tmp", file);
1199 } catch (xmlpp::exception& e) {
1200 string s = e.what ();
1202 throw FileError (s, file);
1208 Config::write_cinemas () const
1210 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1215 Config::write_dkdm_recipients () const
1217 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1221 boost::filesystem::path
1222 Config::default_directory_or (boost::filesystem::path a) const
1224 return directory_or (_default_directory, a);
1227 boost::filesystem::path
1228 Config::default_kdm_directory_or (boost::filesystem::path a) const
1230 return directory_or (_default_kdm_directory, a);
1233 boost::filesystem::path
1234 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1240 boost::system::error_code ec;
1241 auto const e = dcp::filesystem::exists(*dir, ec);
1253 _instance = nullptr;
1257 Config::changed (Property what)
1263 Config::set_kdm_email_to_default ()
1265 _kdm_subject = _("KDM delivery: $CPL_NAME");
1268 "Dear Projectionist\n\n"
1269 "Please find attached KDMs for $CPL_NAME.\n\n"
1270 "Cinema: $CINEMA_NAME\n"
1271 "Screen(s): $SCREENS\n\n"
1272 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1273 "Best regards,\nDCP-o-matic"
1278 Config::set_notification_email_to_default ()
1280 _notification_subject = _("DCP-o-matic notification");
1282 _notification_email = _(
1283 "$JOB_NAME: $JOB_STATUS"
1288 Config::reset_kdm_email ()
1290 set_kdm_email_to_default ();
1295 Config::reset_notification_email ()
1297 set_notification_email_to_default ();
1302 Config::set_cover_sheet_to_default ()
1306 "CPL Filename: $CPL_FILENAME\n"
1308 "Format: $CONTAINER\n"
1310 "Audio Language: $AUDIO_LANGUAGE\n"
1311 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1318 Config::add_to_history (boost::filesystem::path p)
1320 add_to_history_internal (_history, p);
1323 /** Remove non-existent items from the history */
1325 Config::clean_history ()
1327 clean_history_internal (_history);
1331 Config::add_to_player_history (boost::filesystem::path p)
1333 add_to_history_internal (_player_history, p);
1336 /** Remove non-existent items from the player history */
1338 Config::clean_player_history ()
1340 clean_history_internal (_player_history);
1344 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1346 /* Remove existing instances of this path in the history */
1347 h.erase (remove (h.begin(), h.end(), p), h.end ());
1349 h.insert (h.begin (), p);
1350 if (h.size() > HISTORY_SIZE) {
1358 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1364 if (dcp::filesystem::is_directory(i)) {
1368 /* We couldn't find out if it's a directory for some reason; just ignore it */
1375 Config::have_existing (string file)
1377 return dcp::filesystem::exists(read_path(file));
1382 Config::read_cinemas (cxml::Document const & f)
1385 for (auto i: f.node_children("Cinema")) {
1386 /* Slightly grotty two-part construction of Cinema here so that we can use
1389 auto cinema = make_shared<Cinema>(i);
1390 cinema->read_screens (i);
1391 _cinemas.push_back (cinema);
1396 Config::set_cinemas_file (boost::filesystem::path file)
1398 if (file == _cinemas_file) {
1402 _cinemas_file = file;
1404 if (dcp::filesystem::exists(_cinemas_file)) {
1405 /* Existing file; read it in */
1406 cxml::Document f ("Cinemas");
1407 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
1417 Config::read_dkdm_recipients (cxml::Document const & f)
1419 _dkdm_recipients.clear ();
1420 for (auto i: f.node_children("DKDMRecipient")) {
1421 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1427 Config::save_template (shared_ptr<const Film> film, string name) const
1429 film->write_template (template_write_path(name));
1434 Config::templates () const
1436 if (!dcp::filesystem::exists(read_path("templates"))) {
1441 for (auto const& i: dcp::filesystem::directory_iterator(read_path("templates"))) {
1442 n.push_back (i.path().filename().string());
1448 Config::existing_template (string name) const
1450 return dcp::filesystem::exists(template_read_path(name));
1454 boost::filesystem::path
1455 Config::template_read_path (string name) const
1457 return read_path("templates") / tidy_for_filename (name);
1461 boost::filesystem::path
1462 Config::template_write_path (string name) const
1464 return write_path("templates") / tidy_for_filename (name);
1469 Config::rename_template (string old_name, string new_name) const
1471 dcp::filesystem::rename(template_read_path(old_name), template_write_path(new_name));
1475 Config::delete_template (string name) const
1477 dcp::filesystem::remove(template_write_path(name));
1480 /** @return Path to the config.xml containing the actual settings, following a link if required */
1481 boost::filesystem::path
1482 config_file (boost::filesystem::path main)
1484 cxml::Document f ("Config");
1485 if (!dcp::filesystem::exists(main)) {
1486 /* It doesn't exist, so there can't be any links; just return it */
1490 /* See if there's a link */
1492 f.read_file(dcp::filesystem::fix_long_path(main));
1493 auto link = f.optional_string_child("Link");
1497 } catch (xmlpp::exception& e) {
1498 /* There as a problem reading the main configuration file,
1499 so there can't be a link.
1507 boost::filesystem::path
1508 Config::config_read_file ()
1510 return config_file (read_path("config.xml"));
1514 boost::filesystem::path
1515 Config::config_write_file ()
1517 return config_file (write_path("config.xml"));
1522 Config::reset_cover_sheet ()
1524 set_cover_sheet_to_default ();
1529 Config::link (boost::filesystem::path new_file) const
1531 xmlpp::Document doc;
1532 cxml::add_text_child(doc.create_root_node("Config"), "Link", new_file.string());
1534 doc.write_to_file_formatted(write_path("config.xml").string());
1535 } catch (xmlpp::exception& e) {
1536 string s = e.what ();
1538 throw FileError (s, write_path("config.xml"));
1543 Config::copy_and_link (boost::filesystem::path new_file) const
1546 dcp::filesystem::copy_file(config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1551 Config::have_write_permission () const
1553 dcp::File f(config_write_file(), "r+");
1554 return static_cast<bool>(f);
1557 /** @param output_channels Number of output channels in use.
1558 * @return Audio mapping for this output channel count (may be a default).
1561 Config::audio_mapping (int output_channels)
1563 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1564 /* Set up a default */
1565 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1566 if (output_channels == 2) {
1567 /* Special case for stereo output.
1568 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1569 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1571 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1572 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1573 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1574 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1575 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1576 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1577 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1578 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1581 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1582 _audio_mapping->set (i, i, 1);
1587 return *_audio_mapping;
1591 Config::set_audio_mapping (AudioMapping m)
1594 changed (AUDIO_MAPPING);
1598 Config::set_audio_mapping_to_default ()
1600 DCPOMATIC_ASSERT (_audio_mapping);
1601 auto const ch = _audio_mapping->output_channels ();
1602 _audio_mapping = boost::none;
1603 _audio_mapping = audio_mapping (ch);
1604 changed (AUDIO_MAPPING);
1609 Config::add_custom_language (dcp::LanguageTag tag)
1611 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1612 _custom_languages.push_back (tag);
1618 optional<Config::BadReason>
1619 Config::check_certificates () const
1621 optional<BadReason> bad;
1623 for (auto const& i: _signer_chain->unordered()) {
1624 if (i.has_utf8_strings()) {
1625 bad = BAD_SIGNER_UTF8_STRINGS;
1627 if ((i.not_after().year() - i.not_before().year()) > 15) {
1628 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1630 if (dcp::escape_digest(i.subject_dn_qualifier()) != dcp::public_key_digest(i.public_key())) {
1631 bad = BAD_SIGNER_DN_QUALIFIER;
1635 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1636 bad = BAD_SIGNER_INCONSISTENT;
1639 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1640 bad = BAD_DECRYPTION_INCONSISTENT;
1648 save_all_config_as_zip (boost::filesystem::path zip_file)
1650 Zipper zipper (zip_file);
1652 auto config = Config::instance();
1653 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1654 if (dcp::filesystem::exists(config->cinemas_file())) {
1655 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1657 if (dcp::filesystem::exists(config->dkdm_recipients_file())) {
1658 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
1666 Config::load_from_zip(boost::filesystem::path zip_file)
1668 Unzipper unzipper(zip_file);
1669 dcp::write_string_to_file(unzipper.get("config.xml"), config_write_file());
1672 dcp::write_string_to_file(unzipper.get("cinemas.xml"), cinemas_file());
1673 dcp::write_string_to_file(unzipper.get("dkdm_recipient.xml"), dkdm_recipients_file());
1674 } catch (std::runtime_error&) {}
1678 changed(Property::USE_ANY_SERVERS);
1679 changed(Property::SERVERS);
1680 changed(Property::CINEMAS);
1681 changed(Property::DKDM_RECIPIENTS);
1682 changed(Property::SOUND);
1683 changed(Property::SOUND_OUTPUT);
1684 changed(Property::PLAYER_CONTENT_DIRECTORY);
1685 changed(Property::PLAYER_PLAYLIST_DIRECTORY);
1686 changed(Property::PLAYER_DEBUG_LOG);
1687 changed(Property::HISTORY);
1688 changed(Property::SHOW_EXPERIMENTAL_AUDIO_PROCESSORS);
1689 changed(Property::AUDIO_MAPPING);
1690 changed(Property::AUTO_CROP_THRESHOLD);
1691 changed(Property::ALLOW_SMPTE_BV20);
1692 changed(Property::ISDCF_NAME_PART_LENGTH);
1693 changed(Property::OTHER);
1698 Config::set_initial_path(string id, boost::filesystem::path path)
1700 auto iter = _initial_paths.find(id);
1701 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1702 iter->second = path;
1707 optional<boost::filesystem::path>
1708 Config::initial_path(string id) const
1710 auto iter = _initial_paths.find(id);
1711 if (iter == _initial_paths.end()) {
1714 return iter->second;
1718 #ifdef DCPOMATIC_GROK
1720 Config::Grok::Grok(cxml::ConstNodePtr node)
1721 : enable(node->bool_child("Enable"))
1722 , binary_location(node->string_child("BinaryLocation"))
1723 , selected(node->number_child<int>("Selected"))
1724 , licence_server(node->string_child("LicenceServer"))
1725 , licence_port(node->number_child<int>("LicencePort"))
1726 , licence(node->string_child("Licence"))
1733 Config::Grok::as_xml(xmlpp::Element* node) const
1735 node->add_child("BinaryLocation")->add_child_text(binary_location.string());
1736 node->add_child("Enable")->add_child_text((enable ? "1" : "0"));
1737 node->add_child("Selected")->add_child_text(raw_convert<string>(selected));
1738 node->add_child("LicenceServer")->add_child_text(licence_server);
1739 node->add_child("LicencePort")->add_child_text(raw_convert<string>(licence_port));
1740 node->add_child("Licence")->add_child_text(licence);
1745 Config::set_grok(Grok const& grok)