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"
36 #include <dcp/certificate_chain.h>
37 #include <dcp/name_format.h>
38 #include <dcp/raw_convert.h>
39 #include <libcxml/cxml.h>
41 #include <libxml++/libxml++.h>
42 #include <boost/filesystem.hpp>
43 #include <boost/algorithm/string.hpp>
44 #include <boost/thread.hpp>
53 using std::dynamic_pointer_cast;
56 using std::make_shared;
60 using std::shared_ptr;
63 using boost::algorithm::trim;
64 using boost::optional;
65 using dcp::raw_convert;
68 Config* Config::_instance = 0;
69 int const Config::_current_version = 3;
70 boost::signals2::signal<void (Config::LoadFailure)> Config::FailedToLoad;
71 boost::signals2::signal<void (string)> Config::Warning;
72 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
75 /** Construct default configuration */
77 /* DKDMs are not considered a thing to reset on set_defaults() */
78 : _dkdms (new DKDMGroup ("root"))
79 , _default_kdm_duration (1, RoughDuration::Unit::WEEKS)
86 Config::set_defaults ()
88 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
89 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
90 _server_port_base = 6192;
91 _use_any_servers = true;
93 _only_servers_encode = false;
94 _tms_protocol = FileTransferProtocol::SCP;
100 _allow_any_dcp_frame_rate = false;
101 _allow_any_container = false;
102 _allow_96khz_audio = false;
103 _use_all_audio_channels = false;
104 _show_experimental_audio_processors = false;
105 _language = optional<string> ();
106 _default_still_length = 10;
107 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
108 _default_dcp_audio_channels = 8;
109 _default_j2k_bandwidth = 150000000;
110 _default_audio_delay = 0;
111 _default_interop = false;
112 _default_metadata.clear ();
113 _upload_after_make_dcp = false;
116 _mail_protocol = EmailProtocol::AUTO;
122 _notification_from = "";
123 _notification_to = "";
124 _notification_cc.clear ();
125 _notification_bcc = "";
126 _check_for_updates = false;
127 _check_for_test_updates = false;
128 _maximum_j2k_bandwidth = 250000000;
129 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
130 _analyse_ebur128 = true;
131 _automatic_audio_analysis = false;
132 #ifdef DCPOMATIC_WINDOWS
133 _win32_console = false;
135 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
136 * ~/.config/dcpomatic2 (or equivalent) and written back there.
138 _cinemas_file = read_path ("cinemas.xml");
139 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
140 _show_hints_before_make_dcp = true;
141 _confirm_kdm_email = true;
142 _kdm_container_name_format = dcp::NameFormat("KDM_%f_%c");
143 _kdm_filename_format = dcp::NameFormat("KDM_%f_%c_%s");
144 _dkdm_filename_format = dcp::NameFormat("DKDM_%f_%c_%s");
145 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
146 _dcp_asset_filename_format = dcp::NameFormat ("%t");
147 _jump_to_selected = true;
148 for (int i = 0; i < NAG_COUNT; ++i) {
152 _sound_output = optional<string> ();
153 _last_kdm_write_type = KDM_WRITE_FLAT;
154 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
155 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
157 /* I think the scaling factor here should be the ratio of the longest frame
158 encode time to the shortest; if the thread count is T, longest time is L
159 and the shortest time S we could encode L/S frames per thread whilst waiting
160 for the L frame to encode so we might have to store LT/S frames.
162 However we don't want to use too much memory, so keep it a bit lower than we'd
163 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
164 use about 240Mb with 72 encoding threads.
166 _frames_in_memory_multiplier = 3;
167 _decode_reduction = optional<int>();
168 _default_notify = false;
169 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
170 _notification[i] = false;
172 _barco_username = optional<string>();
173 _barco_password = optional<string>();
174 _christie_username = optional<string>();
175 _christie_password = optional<string>();
176 _gdc_username = optional<string>();
177 _gdc_password = optional<string>();
178 _player_mode = PLAYER_MODE_WINDOW;
180 _video_view_type = VIDEO_VIEW_SIMPLE;
181 _respect_kdm_validity_periods = true;
182 _player_debug_log_file = boost::none;
183 _player_content_directory = boost::none;
184 _player_playlist_directory = boost::none;
185 _player_kdm_directory = boost::none;
186 _audio_mapping = boost::none;
187 _custom_languages.clear ();
188 _initial_paths.clear();
189 _initial_paths["AddFilesPath"] = boost::none;
190 _initial_paths["AddDKDMPath"] = boost::none;
191 _initial_paths["SelectCertificatePath"] = boost::none;
192 _initial_paths["AddCombinerInputPath"] = boost::none;
193 _use_isdcf_name_by_default = true;
194 _write_kdms_to_disk = true;
196 _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
197 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
198 _auto_crop_threshold = 0.1;
199 _last_release_notes_version = boost::none;
200 _allow_smpte_bv20 = false;
201 _isdcf_name_part_length = 14;
203 _allowed_dcp_frame_rates.clear ();
204 _allowed_dcp_frame_rates.push_back (24);
205 _allowed_dcp_frame_rates.push_back (25);
206 _allowed_dcp_frame_rates.push_back (30);
207 _allowed_dcp_frame_rates.push_back (48);
208 _allowed_dcp_frame_rates.push_back (50);
209 _allowed_dcp_frame_rates.push_back (60);
211 set_kdm_email_to_default ();
212 set_notification_email_to_default ();
213 set_cover_sheet_to_default ();
215 _main_divider_sash_position = {};
216 _main_content_divider_sash_position = {};
218 _export.set_defaults();
222 Config::restore_defaults ()
224 Config::instance()->set_defaults ();
225 Config::instance()->changed ();
228 shared_ptr<dcp::CertificateChain>
229 Config::create_certificate_chain ()
231 return make_shared<dcp::CertificateChain> (
233 CERTIFICATE_VALIDITY_PERIOD,
236 ".dcpomatic.smpte-430-2.ROOT",
237 ".dcpomatic.smpte-430-2.INTERMEDIATE",
238 "CS.dcpomatic.smpte-430-2.LEAF"
245 using namespace boost::filesystem;
247 auto copy_adding_number = [](path const& path_to_copy) {
249 auto add_number = [](path const& path, int number) {
250 return String::compose("%1.%2", path, number);
254 while (n < 100 && exists(add_number(path_to_copy, n))) {
257 boost::system::error_code ec;
258 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
261 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
262 * to write over. This is more intended for the situation where we have a corrupted config.xml,
263 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
264 * file). But we might as well back up the other files while we're about it.
267 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
268 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
270 copy_adding_number (config_write_file());
272 /* These do not use State::write_path, so whatever path is in the Config we will copy
275 copy_adding_number (_cinemas_file);
276 copy_adding_number (_dkdm_recipients_file);
284 read_dkdm_recipients();
289 Config::read_config()
292 cxml::Document f ("Config");
293 f.read_file(dcp::filesystem::fix_long_path(config_read_file()));
295 auto version = f.optional_number_child<int> ("Version");
296 if (version && *version < _current_version) {
297 /* Back up the old config before we re-write it in a back-incompatible way */
301 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
302 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
304 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
305 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
308 _default_directory = f.optional_string_child ("DefaultDirectory");
309 if (_default_directory && _default_directory->empty ()) {
310 /* We used to store an empty value for this to mean "none set" */
311 _default_directory = boost::optional<boost::filesystem::path> ();
314 auto b = f.optional_number_child<int> ("ServerPort");
316 b = f.optional_number_child<int> ("ServerPortBase");
318 _server_port_base = b.get ();
320 auto u = f.optional_bool_child ("UseAnyServers");
321 _use_any_servers = u.get_value_or (true);
323 for (auto i: f.node_children("Server")) {
324 if (i->node_children("HostName").size() == 1) {
325 _servers.push_back (i->string_child ("HostName"));
327 _servers.push_back (i->content ());
331 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
332 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
333 _tms_passive = f.optional_bool_child("TMSPassive").get_value_or(true);
334 _tms_ip = f.string_child ("TMSIP");
335 _tms_path = f.string_child ("TMSPath");
336 _tms_user = f.string_child ("TMSUser");
337 _tms_password = f.string_child ("TMSPassword");
339 _language = f.optional_string_child ("Language");
341 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
342 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
344 if (f.optional_string_child ("DCPMetadataIssuer")) {
345 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
346 } else if (f.optional_string_child ("DCPIssuer")) {
347 _dcp_issuer = f.string_child ("DCPIssuer");
350 auto up = f.optional_bool_child("UploadAfterMakeDCP");
352 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
354 _upload_after_make_dcp = up.get_value_or (false);
355 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
356 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
357 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
358 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
359 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
361 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
362 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
363 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
364 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
367 auto al = f.optional_string_child("DefaultAudioLanguage");
369 _default_audio_language = dcp::LanguageTag(*al);
371 } catch (std::runtime_error&) {}
374 auto te = f.optional_string_child("DefaultTerritory");
376 _default_territory = dcp::LanguageTag::RegionSubtag(*te);
378 } catch (std::runtime_error&) {}
380 for (auto const& i: f.node_children("DefaultMetadata")) {
381 _default_metadata[i->string_attribute("key")] = i->content();
384 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
386 /* Read any cinemas that are still lying around in the config file
387 * from an old version.
391 _mail_server = f.string_child ("MailServer");
392 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
395 /* Make sure this matches the code in write_config */
396 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
397 if (protocol == "Auto") {
398 _mail_protocol = EmailProtocol::AUTO;
399 } else if (protocol == "Plain") {
400 _mail_protocol = EmailProtocol::PLAIN;
401 } else if (protocol == "STARTTLS") {
402 _mail_protocol = EmailProtocol::STARTTLS;
403 } else if (protocol == "SSL") {
404 _mail_protocol = EmailProtocol::SSL;
408 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
409 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
411 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
412 _kdm_from = f.string_child ("KDMFrom");
413 for (auto i: f.node_children("KDMCC")) {
414 if (!i->content().empty()) {
415 _kdm_cc.push_back (i->content ());
418 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
419 _kdm_email = f.string_child ("KDMEmail");
421 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
422 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
423 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
424 for (auto i: f.node_children("NotificationCC")) {
425 if (!i->content().empty()) {
426 _notification_cc.push_back (i->content ());
429 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
430 if (f.optional_string_child("NotificationEmail")) {
431 _notification_email = f.string_child("NotificationEmail");
434 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
435 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
437 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
438 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
439 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
440 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
441 _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false);
442 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
444 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
445 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
446 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
447 #ifdef DCPOMATIC_WINDOWS
448 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
451 for (auto i: f.node_children("History")) {
452 _history.push_back (i->content ());
455 for (auto i: f.node_children("PlayerHistory")) {
456 _player_history.push_back (i->content ());
459 auto signer = f.optional_node_child ("Signer");
461 auto c = make_shared<dcp::CertificateChain>();
462 /* Read the signing certificates and private key in from the config file */
463 for (auto i: signer->node_children ("Certificate")) {
464 c->add (dcp::Certificate (i->content ()));
466 c->set_key (signer->string_child ("PrivateKey"));
469 /* Make a new set of signing certificates and key */
470 _signer_chain = create_certificate_chain ();
473 auto decryption = f.optional_node_child ("Decryption");
475 auto c = make_shared<dcp::CertificateChain>();
476 for (auto i: decryption->node_children ("Certificate")) {
477 c->add (dcp::Certificate (i->content ()));
479 c->set_key (decryption->string_child ("PrivateKey"));
480 _decryption_chain = c;
482 _decryption_chain = create_certificate_chain ();
485 /* These must be done before we call Bad as that might set one
488 for (auto i: f.node_children("Nagged")) {
489 auto const id = number_attribute<int>(i, "Id", "id");
490 if (id >= 0 && id < NAG_COUNT) {
491 _nagged[id] = raw_convert<int>(i->content());
495 auto bad = check_certificates ();
497 auto const remake = Bad(*bad);
498 if (remake && *remake) {
500 case BAD_SIGNER_UTF8_STRINGS:
501 case BAD_SIGNER_INCONSISTENT:
502 case BAD_SIGNER_VALIDITY_TOO_LONG:
503 _signer_chain = create_certificate_chain ();
505 case BAD_DECRYPTION_INCONSISTENT:
506 _decryption_chain = create_certificate_chain ();
512 if (f.optional_node_child("DKDMGroup")) {
513 /* New-style: all DKDMs in a group */
514 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
516 /* Old-style: one or more DKDM nodes */
517 _dkdms = make_shared<DKDMGroup>("root");
518 for (auto i: f.node_children("DKDM")) {
519 _dkdms->add (DKDMBase::read (i));
522 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
523 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
524 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
525 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
526 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
527 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
528 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
529 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
530 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
531 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
532 /* The variable was renamed but not the XML tag */
533 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
534 _sound_output = f.optional_string_child("PreviewSoundOutput");
535 if (f.optional_string_child("CoverSheet")) {
536 _cover_sheet = f.optional_string_child("CoverSheet").get();
538 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
539 if (f.optional_string_child("LastKDMWriteType")) {
540 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
541 _last_kdm_write_type = KDM_WRITE_FLAT;
542 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
543 _last_kdm_write_type = KDM_WRITE_FOLDER;
544 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
545 _last_kdm_write_type = KDM_WRITE_ZIP;
548 if (f.optional_string_child("LastDKDMWriteType")) {
549 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
550 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
551 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
552 _last_dkdm_write_type = DKDM_WRITE_FILE;
555 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
556 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
557 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
559 for (auto i: f.node_children("Notification")) {
560 int const id = number_attribute<int>(i, "Id", "id");
561 if (id >= 0 && id < NOTIFICATION_COUNT) {
562 _notification[id] = raw_convert<int>(i->content());
566 _barco_username = f.optional_string_child("BarcoUsername");
567 _barco_password = f.optional_string_child("BarcoPassword");
568 _christie_username = f.optional_string_child("ChristieUsername");
569 _christie_password = f.optional_string_child("ChristiePassword");
570 _gdc_username = f.optional_string_child("GDCUsername");
571 _gdc_password = f.optional_string_child("GDCPassword");
573 auto pm = f.optional_string_child("PlayerMode");
574 if (pm && *pm == "window") {
575 _player_mode = PLAYER_MODE_WINDOW;
576 } else if (pm && *pm == "full") {
577 _player_mode = PLAYER_MODE_FULL;
578 } else if (pm && *pm == "dual") {
579 _player_mode = PLAYER_MODE_DUAL;
582 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
583 auto vc = f.optional_string_child("VideoViewType");
584 if (vc && *vc == "opengl") {
585 _video_view_type = VIDEO_VIEW_OPENGL;
586 } else if (vc && *vc == "simple") {
587 _video_view_type = VIDEO_VIEW_SIMPLE;
589 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
590 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
591 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
592 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
593 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
595 if (f.optional_node_child("AudioMapping")) {
596 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
599 for (auto i: f.node_children("CustomLanguage")) {
601 /* This will fail if it's called before dcp::init() as it won't recognise the
602 * tag. That's OK because the Config will be reloaded again later.
604 _custom_languages.push_back (dcp::LanguageTag(i->content()));
605 } catch (std::runtime_error& e) {}
608 for (auto& initial: _initial_paths) {
609 initial.second = f.optional_string_child(initial.first);
611 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
612 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
613 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
614 _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
615 if (auto duration = f.optional_node_child("DefaultKDMDuration")) {
616 _default_kdm_duration = RoughDuration(duration);
618 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
620 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
621 _last_release_notes_version = f.optional_string_child("LastReleaseNotesVersion");
622 _main_divider_sash_position = f.optional_number_child<int>("MainDividerSashPosition");
623 _main_content_divider_sash_position = f.optional_number_child<int>("MainContentDividerSashPosition");
625 if (auto loc = f.optional_string_child("DefaultAddFileLocation")) {
626 if (*loc == "last") {
627 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
628 } else if (*loc == "project") {
629 _default_add_file_location = DefaultAddFileLocation::SAME_AS_PROJECT;
633 _allow_smpte_bv20 = f.optional_bool_child("AllowSMPTEBv20").get_value_or(false);
634 _isdcf_name_part_length = f.optional_number_child<int>("ISDCFNamePartLength").get_value_or(14);
636 _export.read(f.optional_node_child("Export"));
639 if (have_existing("config.xml")) {
641 /* We have a config file but it didn't load */
642 FailedToLoad(LoadFailure::CONFIG);
645 /* Make a new set of signing certificates and key */
646 _signer_chain = create_certificate_chain ();
647 /* And similar for decryption of KDMs */
648 _decryption_chain = create_certificate_chain ();
654 Config::read_cinemas()
656 if (dcp::filesystem::exists(_cinemas_file)) {
658 cxml::Document f("Cinemas");
659 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
663 FailedToLoad(LoadFailure::CINEMAS);
671 Config::read_dkdm_recipients()
673 if (dcp::filesystem::exists(_dkdm_recipients_file)) {
675 cxml::Document f("DKDMRecipients");
676 f.read_file(dcp::filesystem::fix_long_path(_dkdm_recipients_file));
677 read_dkdm_recipients(f);
680 FailedToLoad(LoadFailure::DKDM_RECIPIENTS);
681 write_dkdm_recipients();
687 /** @return Singleton instance */
691 if (_instance == nullptr) {
692 _instance = new Config;
699 /** Write our configuration to disk */
701 Config::write () const
705 write_dkdm_recipients ();
709 Config::write_config () const
712 auto root = doc.create_root_node ("Config");
714 /* [XML] Version The version number of the configuration file format. */
715 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
716 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
717 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
718 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
719 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
720 if (_default_directory) {
721 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
722 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
724 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
725 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
726 by the batch converter to listen for job requests.
728 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
729 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
730 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
732 for (auto i: _servers) {
733 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
736 root->add_child("Server")->add_child_text (i);
739 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
740 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
742 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
743 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
744 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
745 /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */
746 root->add_child("TMSPassive")->add_child_text(_tms_passive ? "1" : "0");
747 /* [XML] TMSIP IP address of TMS. */
748 root->add_child("TMSIP")->add_child_text (_tms_ip);
749 /* [XML] TMSPath Path on the TMS to copy files to. */
750 root->add_child("TMSPath")->add_child_text (_tms_path);
751 /* [XML] TMSUser Username to log into the TMS with. */
752 root->add_child("TMSUser")->add_child_text (_tms_user);
753 /* [XML] TMSPassword Password to log into the TMS with. */
754 root->add_child("TMSPassword")->add_child_text (_tms_password);
756 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
757 root->add_child("Language")->add_child_text (_language.get());
759 if (_default_dcp_content_type) {
760 /* [XML:opt] DefaultDCPContentType Default content type to use when creating new films (<code>FTR</code>, <code>SHR</code>,
761 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
762 <code>PSA</code> or <code>ADV</code>). */
763 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
765 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
766 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
767 /* [XML] DCPIssuer Issuer text to write into CPL files. */
768 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
769 /* [XML] DCPCreator Creator text to write into CPL files. */
770 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
771 /* [XML] Company name to write into MXF files. */
772 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
773 /* [XML] Product name to write into MXF files. */
774 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
775 /* [XML] Product version to write into MXF files. */
776 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
777 /* [XML] Comment to write into JPEG2000 data. */
778 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
779 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
780 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
782 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
783 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
784 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
785 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
786 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
787 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
788 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
789 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
790 if (_default_audio_language) {
791 /* [XML] DefaultAudioLanguage Default audio language to use for new films */
792 root->add_child("DefaultAudioLanguage")->add_child_text(_default_audio_language->to_string());
794 if (_default_territory) {
795 /* [XML] DefaultTerritory Default territory to use for new films */
796 root->add_child("DefaultTerritory")->add_child_text(_default_territory->subtag());
798 for (auto const& i: _default_metadata) {
799 auto c = root->add_child("DefaultMetadata");
800 c->set_attribute("key", i.first);
801 c->add_child_text(i.second);
803 if (_default_kdm_directory) {
804 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
805 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
807 _default_kdm_duration.as_xml(root->add_child("DefaultKDMDuration"));
808 /* [XML] MailServer Hostname of SMTP server to use. */
809 root->add_child("MailServer")->add_child_text (_mail_server);
810 /* [XML] MailPort Port number to use on SMTP server. */
811 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
812 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
813 switch (_mail_protocol) {
814 case EmailProtocol::AUTO:
815 root->add_child("MailProtocol")->add_child_text("Auto");
817 case EmailProtocol::PLAIN:
818 root->add_child("MailProtocol")->add_child_text("Plain");
820 case EmailProtocol::STARTTLS:
821 root->add_child("MailProtocol")->add_child_text("STARTTLS");
823 case EmailProtocol::SSL:
824 root->add_child("MailProtocol")->add_child_text("SSL");
827 /* [XML] MailUser Username to use on SMTP server. */
828 root->add_child("MailUser")->add_child_text (_mail_user);
829 /* [XML] MailPassword Password to use on SMTP server. */
830 root->add_child("MailPassword")->add_child_text (_mail_password);
832 /* [XML] KDMSubject Subject to use for KDM emails. */
833 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
834 /* [XML] KDMFrom From address to use for KDM emails. */
835 root->add_child("KDMFrom")->add_child_text (_kdm_from);
836 for (auto i: _kdm_cc) {
837 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
838 root->add_child("KDMCC")->add_child_text (i);
840 /* [XML] KDMBCC BCC address to use for KDM emails. */
841 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
842 /* [XML] KDMEmail Text of KDM email. */
843 root->add_child("KDMEmail")->add_child_text (_kdm_email);
845 /* [XML] NotificationSubject Subject to use for notification emails. */
846 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
847 /* [XML] NotificationFrom From address to use for notification emails. */
848 root->add_child("NotificationFrom")->add_child_text (_notification_from);
849 /* [XML] NotificationFrom To address to use for notification emails. */
850 root->add_child("NotificationTo")->add_child_text (_notification_to);
851 for (auto i: _notification_cc) {
852 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
853 root->add_child("NotificationCC")->add_child_text (i);
855 /* [XML] NotificationBCC BCC address to use for notification emails. */
856 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
857 /* [XML] NotificationEmail Text of notification email. */
858 root->add_child("NotificationEmail")->add_child_text (_notification_email);
860 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
861 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
862 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
863 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
865 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
866 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
867 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
868 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
869 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
870 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
871 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
872 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
873 /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
874 root->add_child("UseAllAudioChannels")->add_child_text(_use_all_audio_channels ? "1" : "0");
875 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
876 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
877 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
878 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
879 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
880 related to the player, 1024 debug information related to audio analyses.
882 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
883 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
884 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
885 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
886 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
887 #ifdef DCPOMATIC_WINDOWS
888 if (_win32_console) {
889 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
890 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
891 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
893 root->add_child("Win32Console")->add_child_text ("1");
897 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
898 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
900 auto signer = root->add_child ("Signer");
901 DCPOMATIC_ASSERT (_signer_chain);
902 for (auto const& i: _signer_chain->unordered()) {
903 signer->add_child("Certificate")->add_child_text (i.certificate (true));
905 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
907 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
908 auto decryption = root->add_child ("Decryption");
909 DCPOMATIC_ASSERT (_decryption_chain);
910 for (auto const& i: _decryption_chain->unordered()) {
911 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
913 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
915 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
918 for (auto i: _history) {
919 root->add_child("History")->add_child_text (i.string ());
922 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
925 for (auto i: _player_history) {
926 root->add_child("PlayerHistory")->add_child_text (i.string ());
929 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
930 or <code><DKDM></code> tags.
932 /* [XML] DKDM A DKDM as XML */
933 _dkdms->as_xml (root);
935 /* [XML] CinemasFile Filename of cinemas list file. */
936 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
937 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
938 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
939 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
940 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
941 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
942 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
943 /* [XML] KDMFilenameFormat Format for KDM filenames. */
944 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
945 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
946 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
947 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
948 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
949 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
950 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
951 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
952 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
953 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
954 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
955 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
956 for (int i = 0; i < NAG_COUNT; ++i) {
957 xmlpp::Element* e = root->add_child ("Nagged");
958 e->set_attribute("id", raw_convert<string>(i));
959 e->add_child_text (_nagged[i] ? "1" : "0");
961 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
962 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
964 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
965 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
967 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
968 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
969 if (_last_player_load_directory) {
970 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
972 /* [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. */
973 if (_last_kdm_write_type) {
974 switch (_last_kdm_write_type.get()) {
976 root->add_child("LastKDMWriteType")->add_child_text("flat");
978 case KDM_WRITE_FOLDER:
979 root->add_child("LastKDMWriteType")->add_child_text("folder");
982 root->add_child("LastKDMWriteType")->add_child_text("zip");
986 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
987 if (_last_dkdm_write_type) {
988 switch (_last_dkdm_write_type.get()) {
989 case DKDM_WRITE_INTERNAL:
990 root->add_child("LastDKDMWriteType")->add_child_text("internal");
992 case DKDM_WRITE_FILE:
993 root->add_child("LastDKDMWriteType")->add_child_text("file");
997 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
998 frames to be held in memory at once.
1000 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
1002 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
1003 if (_decode_reduction) {
1004 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
1007 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
1008 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
1010 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
1011 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
1012 xmlpp::Element* e = root->add_child ("Notification");
1013 e->set_attribute("id", raw_convert<string>(i));
1014 e->add_child_text (_notification[i] ? "1" : "0");
1017 if (_barco_username) {
1018 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
1019 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
1021 if (_barco_password) {
1022 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
1023 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
1026 if (_christie_username) {
1027 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
1028 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
1030 if (_christie_password) {
1031 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
1032 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
1035 if (_gdc_username) {
1036 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
1037 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
1039 if (_gdc_password) {
1040 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
1041 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
1044 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
1045 with controls on another monitor.
1047 switch (_player_mode) {
1048 case PLAYER_MODE_WINDOW:
1049 root->add_child("PlayerMode")->add_child_text("window");
1051 case PLAYER_MODE_FULL:
1052 root->add_child("PlayerMode")->add_child_text("full");
1054 case PLAYER_MODE_DUAL:
1055 root->add_child("PlayerMode")->add_child_text("dual");
1059 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
1060 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
1061 switch (_video_view_type) {
1062 case VIDEO_VIEW_SIMPLE:
1063 root->add_child("VideoViewType")->add_child_text("simple");
1065 case VIDEO_VIEW_OPENGL:
1066 root->add_child("VideoViewType")->add_child_text("opengl");
1069 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1070 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
1071 if (_player_debug_log_file) {
1072 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1073 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
1075 if (_player_content_directory) {
1076 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1077 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
1079 if (_player_playlist_directory) {
1080 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1081 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1083 if (_player_kdm_directory) {
1084 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1085 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1087 if (_audio_mapping) {
1088 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1090 for (auto const& i: _custom_languages) {
1091 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1093 for (auto const& initial: _initial_paths) {
1094 if (initial.second) {
1095 root->add_child(initial.first)->add_child_text(initial.second->string());
1098 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1099 root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0");
1100 root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0");
1101 root->add_child("DefaultKDMType")->add_child_text(dcp::formulation_to_string(_default_kdm_type));
1102 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1103 if (_last_release_notes_version) {
1104 root->add_child("LastReleaseNotesVersion")->add_child_text(*_last_release_notes_version);
1106 if (_main_divider_sash_position) {
1107 root->add_child("MainDividerSashPosition")->add_child_text(raw_convert<string>(*_main_divider_sash_position));
1109 if (_main_content_divider_sash_position) {
1110 root->add_child("MainContentDividerSashPosition")->add_child_text(raw_convert<string>(*_main_content_divider_sash_position));
1113 root->add_child("DefaultAddFileLocation")->add_child_text(
1114 _default_add_file_location == DefaultAddFileLocation::SAME_AS_LAST_TIME ? "last" : "project"
1117 /* [XML] AllowSMPTEBv20 1 to allow the user to choose SMPTE (Bv2.0 only) as a standard, otherwise 0 */
1118 root->add_child("AllowSMPTEBv20")->add_child_text(_allow_smpte_bv20 ? "1" : "0");
1119 /* [XML] ISDCFNamePartLength Maximum length of the "name" part of an ISDCF name, which should be 14 according to the standard */
1120 root->add_child("ISDCFNamePartLength")->add_child_text(raw_convert<string>(_isdcf_name_part_length));
1122 _export.write(root->add_child("Export"));
1124 auto target = config_write_file();
1127 auto const s = doc.write_to_string_formatted ();
1128 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1129 dcp::File f(tmp, "w");
1131 throw FileError (_("Could not open file for writing"), tmp);
1133 f.checked_write(s.c_str(), s.bytes());
1135 dcp::filesystem::remove(target);
1136 dcp::filesystem::rename(tmp, target);
1137 } catch (xmlpp::exception& e) {
1138 string s = e.what ();
1140 throw FileError (s, target);
1147 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1149 xmlpp::Document doc;
1150 auto root = doc.create_root_node (root_node);
1151 root->add_child("Version")->add_child_text(version);
1153 for (auto i: things) {
1154 i->as_xml (root->add_child(node));
1158 doc.write_to_file_formatted (file.string() + ".tmp");
1159 dcp::filesystem::remove(file);
1160 dcp::filesystem::rename(file.string() + ".tmp", file);
1161 } catch (xmlpp::exception& e) {
1162 string s = e.what ();
1164 throw FileError (s, file);
1170 Config::write_cinemas () const
1172 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1177 Config::write_dkdm_recipients () const
1179 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1183 boost::filesystem::path
1184 Config::default_directory_or (boost::filesystem::path a) const
1186 return directory_or (_default_directory, a);
1189 boost::filesystem::path
1190 Config::default_kdm_directory_or (boost::filesystem::path a) const
1192 return directory_or (_default_kdm_directory, a);
1195 boost::filesystem::path
1196 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1202 boost::system::error_code ec;
1203 auto const e = dcp::filesystem::exists(*dir, ec);
1219 Config::changed (Property what)
1225 Config::set_kdm_email_to_default ()
1227 _kdm_subject = _("KDM delivery: $CPL_NAME");
1230 "Dear Projectionist\n\n"
1231 "Please find attached KDMs for $CPL_NAME.\n\n"
1232 "Cinema: $CINEMA_NAME\n"
1233 "Screen(s): $SCREENS\n\n"
1234 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1235 "Best regards,\nDCP-o-matic"
1240 Config::set_notification_email_to_default ()
1242 _notification_subject = _("DCP-o-matic notification");
1244 _notification_email = _(
1245 "$JOB_NAME: $JOB_STATUS"
1250 Config::reset_kdm_email ()
1252 set_kdm_email_to_default ();
1257 Config::reset_notification_email ()
1259 set_notification_email_to_default ();
1264 Config::set_cover_sheet_to_default ()
1268 "CPL Filename: $CPL_FILENAME\n"
1270 "Format: $CONTAINER\n"
1272 "Audio Language: $AUDIO_LANGUAGE\n"
1273 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1280 Config::add_to_history (boost::filesystem::path p)
1282 add_to_history_internal (_history, p);
1285 /** Remove non-existent items from the history */
1287 Config::clean_history ()
1289 clean_history_internal (_history);
1293 Config::add_to_player_history (boost::filesystem::path p)
1295 add_to_history_internal (_player_history, p);
1298 /** Remove non-existent items from the player history */
1300 Config::clean_player_history ()
1302 clean_history_internal (_player_history);
1306 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1308 /* Remove existing instances of this path in the history */
1309 h.erase (remove (h.begin(), h.end(), p), h.end ());
1311 h.insert (h.begin (), p);
1312 if (h.size() > HISTORY_SIZE) {
1320 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1326 if (dcp::filesystem::is_directory(i)) {
1330 /* We couldn't find out if it's a directory for some reason; just ignore it */
1337 Config::have_existing (string file)
1339 return dcp::filesystem::exists(read_path(file));
1344 Config::read_cinemas (cxml::Document const & f)
1347 for (auto i: f.node_children("Cinema")) {
1348 /* Slightly grotty two-part construction of Cinema here so that we can use
1351 auto cinema = make_shared<Cinema>(i);
1352 cinema->read_screens (i);
1353 _cinemas.push_back (cinema);
1358 Config::set_cinemas_file (boost::filesystem::path file)
1360 if (file == _cinemas_file) {
1364 _cinemas_file = file;
1366 if (dcp::filesystem::exists(_cinemas_file)) {
1367 /* Existing file; read it in */
1368 cxml::Document f ("Cinemas");
1369 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
1379 Config::read_dkdm_recipients (cxml::Document const & f)
1381 _dkdm_recipients.clear ();
1382 for (auto i: f.node_children("DKDMRecipient")) {
1383 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1389 Config::save_template (shared_ptr<const Film> film, string name) const
1391 film->write_template (template_write_path(name));
1396 Config::templates () const
1398 if (!dcp::filesystem::exists(read_path("templates"))) {
1403 for (auto const& i: dcp::filesystem::directory_iterator(read_path("templates"))) {
1404 n.push_back (i.path().filename().string());
1410 Config::existing_template (string name) const
1412 return dcp::filesystem::exists(template_read_path(name));
1416 boost::filesystem::path
1417 Config::template_read_path (string name) const
1419 return read_path("templates") / tidy_for_filename (name);
1423 boost::filesystem::path
1424 Config::template_write_path (string name) const
1426 return write_path("templates") / tidy_for_filename (name);
1431 Config::rename_template (string old_name, string new_name) const
1433 dcp::filesystem::rename(template_read_path(old_name), template_write_path(new_name));
1437 Config::delete_template (string name) const
1439 dcp::filesystem::remove(template_write_path(name));
1442 /** @return Path to the config.xml containing the actual settings, following a link if required */
1443 boost::filesystem::path
1444 config_file (boost::filesystem::path main)
1446 cxml::Document f ("Config");
1447 if (!dcp::filesystem::exists(main)) {
1448 /* It doesn't exist, so there can't be any links; just return it */
1452 /* See if there's a link */
1454 f.read_file(dcp::filesystem::fix_long_path(main));
1455 auto link = f.optional_string_child("Link");
1459 } catch (xmlpp::exception& e) {
1460 /* There as a problem reading the main configuration file,
1461 so there can't be a link.
1469 boost::filesystem::path
1470 Config::config_read_file ()
1472 return config_file (read_path("config.xml"));
1476 boost::filesystem::path
1477 Config::config_write_file ()
1479 return config_file (write_path("config.xml"));
1484 Config::reset_cover_sheet ()
1486 set_cover_sheet_to_default ();
1491 Config::link (boost::filesystem::path new_file) const
1493 xmlpp::Document doc;
1494 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1496 doc.write_to_file_formatted(write_path("config.xml").string());
1497 } catch (xmlpp::exception& e) {
1498 string s = e.what ();
1500 throw FileError (s, write_path("config.xml"));
1505 Config::copy_and_link (boost::filesystem::path new_file) const
1508 dcp::filesystem::copy_file(config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1513 Config::have_write_permission () const
1515 dcp::File f(config_write_file(), "r+");
1516 return static_cast<bool>(f);
1519 /** @param output_channels Number of output channels in use.
1520 * @return Audio mapping for this output channel count (may be a default).
1523 Config::audio_mapping (int output_channels)
1525 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1526 /* Set up a default */
1527 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1528 if (output_channels == 2) {
1529 /* Special case for stereo output.
1530 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1531 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1533 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1534 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1535 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1536 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1537 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1538 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1539 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1540 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1543 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1544 _audio_mapping->set (i, i, 1);
1549 return *_audio_mapping;
1553 Config::set_audio_mapping (AudioMapping m)
1556 changed (AUDIO_MAPPING);
1560 Config::set_audio_mapping_to_default ()
1562 DCPOMATIC_ASSERT (_audio_mapping);
1563 auto const ch = _audio_mapping->output_channels ();
1564 _audio_mapping = boost::none;
1565 _audio_mapping = audio_mapping (ch);
1566 changed (AUDIO_MAPPING);
1571 Config::add_custom_language (dcp::LanguageTag tag)
1573 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1574 _custom_languages.push_back (tag);
1580 optional<Config::BadReason>
1581 Config::check_certificates () const
1583 optional<BadReason> bad;
1585 for (auto const& i: _signer_chain->unordered()) {
1586 if (i.has_utf8_strings()) {
1587 bad = BAD_SIGNER_UTF8_STRINGS;
1589 if ((i.not_after().year() - i.not_before().year()) > 15) {
1590 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1594 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1595 bad = BAD_SIGNER_INCONSISTENT;
1598 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1599 bad = BAD_DECRYPTION_INCONSISTENT;
1607 save_all_config_as_zip (boost::filesystem::path zip_file)
1609 Zipper zipper (zip_file);
1611 auto config = Config::instance();
1612 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1613 if (dcp::filesystem::exists(config->cinemas_file())) {
1614 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1616 if (dcp::filesystem::exists(config->dkdm_recipients_file())) {
1617 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
1625 Config::set_initial_path(string id, boost::filesystem::path path)
1627 auto iter = _initial_paths.find(id);
1628 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1629 iter->second = path;
1634 optional<boost::filesystem::path>
1635 Config::initial_path(string id) const
1637 auto iter = _initial_paths.find(id);
1638 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1639 return iter->second;