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"
29 #include "dcp_content_type.h"
30 #include "dkdm_recipient.h"
31 #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 = 6;
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;
181 _video_view_type = VIDEO_VIEW_SIMPLE;
182 _respect_kdm_validity_periods = true;
183 _player_debug_log_file = boost::none;
184 _player_content_directory = boost::none;
185 _player_playlist_directory = boost::none;
186 _player_kdm_directory = boost::none;
187 _audio_mapping = boost::none;
188 _custom_languages.clear ();
189 _initial_paths.clear();
190 _initial_paths["AddFilesPath"] = boost::none;
191 _initial_paths["AddDKDMPath"] = boost::none;
192 _initial_paths["SelectCertificatePath"] = boost::none;
193 _initial_paths["AddCombinerInputPath"] = boost::none;
194 _use_isdcf_name_by_default = true;
195 _write_kdms_to_disk = true;
197 _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
198 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
199 _auto_crop_threshold = 0.1;
200 _last_release_notes_version = boost::none;
201 _allow_smpte_bv20 = false;
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 (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 = i->number_attribute<int>("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 = i->number_attribute<int>("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);
635 _export.read(f.optional_node_child("Export"));
638 if (have_existing("config.xml")) {
640 /* We have a config file but it didn't load */
641 FailedToLoad(LoadFailure::CONFIG);
644 /* Make a new set of signing certificates and key */
645 _signer_chain = create_certificate_chain ();
646 /* And similar for decryption of KDMs */
647 _decryption_chain = create_certificate_chain ();
653 Config::read_cinemas()
655 if (boost::filesystem::exists (_cinemas_file)) {
657 cxml::Document f("Cinemas");
658 f.read_file(_cinemas_file);
662 FailedToLoad(LoadFailure::CINEMAS);
670 Config::read_dkdm_recipients()
672 if (boost::filesystem::exists (_dkdm_recipients_file)) {
674 cxml::Document f("DKDMRecipients");
675 f.read_file(_dkdm_recipients_file);
676 read_dkdm_recipients(f);
679 FailedToLoad(LoadFailure::DKDM_RECIPIENTS);
680 write_dkdm_recipients();
686 /** @return Singleton instance */
690 if (_instance == nullptr) {
691 _instance = new Config;
698 /** Write our configuration to disk */
700 Config::write () const
704 write_dkdm_recipients ();
708 Config::write_config () const
711 auto root = doc.create_root_node ("Config");
713 /* [XML] Version The version number of the configuration file format. */
714 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
715 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
716 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
717 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
718 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
719 if (_default_directory) {
720 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
721 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
723 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
724 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
725 by the batch converter to listen for job requests.
727 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
728 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
729 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
731 for (auto i: _servers) {
732 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
735 root->add_child("Server")->add_child_text (i);
738 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
739 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
741 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
742 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
743 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
744 /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */
745 root->add_child("TMSPassive")->add_child_text(_tms_passive ? "1" : "0");
746 /* [XML] TMSIP IP address of TMS. */
747 root->add_child("TMSIP")->add_child_text (_tms_ip);
748 /* [XML] TMSPath Path on the TMS to copy files to. */
749 root->add_child("TMSPath")->add_child_text (_tms_path);
750 /* [XML] TMSUser Username to log into the TMS with. */
751 root->add_child("TMSUser")->add_child_text (_tms_user);
752 /* [XML] TMSPassword Password to log into the TMS with. */
753 root->add_child("TMSPassword")->add_child_text (_tms_password);
755 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
756 root->add_child("Language")->add_child_text (_language.get());
758 if (_default_dcp_content_type) {
759 /* [XML:opt] DefaultDCPContentType Default content type to use when creating new films (<code>FTR</code>, <code>SHR</code>,
760 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
761 <code>PSA</code> or <code>ADV</code>). */
762 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
764 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
765 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
766 /* [XML] DCPIssuer Issuer text to write into CPL files. */
767 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
768 /* [XML] DCPCreator Creator text to write into CPL files. */
769 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
770 /* [XML] Company name to write into MXF files. */
771 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
772 /* [XML] Product name to write into MXF files. */
773 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
774 /* [XML] Product version to write into MXF files. */
775 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
776 /* [XML] Comment to write into JPEG2000 data. */
777 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
778 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
779 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
781 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
782 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
783 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
784 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
785 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
786 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
787 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
788 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
789 if (_default_audio_language) {
790 /* [XML] DefaultAudioLanguage Default audio language to use for new films */
791 root->add_child("DefaultAudioLanguage")->add_child_text(_default_audio_language->to_string());
793 if (_default_territory) {
794 /* [XML] DefaultTerritory Default territory to use for new films */
795 root->add_child("DefaultTerritory")->add_child_text(_default_territory->subtag());
797 for (auto const& i: _default_metadata) {
798 auto c = root->add_child("DefaultMetadata");
799 c->set_attribute("key", i.first);
800 c->add_child_text(i.second);
802 if (_default_kdm_directory) {
803 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
804 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
806 _default_kdm_duration.as_xml(root->add_child("DefaultKDMDuration"));
807 /* [XML] MailServer Hostname of SMTP server to use. */
808 root->add_child("MailServer")->add_child_text (_mail_server);
809 /* [XML] MailPort Port number to use on SMTP server. */
810 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
811 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
812 switch (_mail_protocol) {
813 case EmailProtocol::AUTO:
814 root->add_child("MailProtocol")->add_child_text("Auto");
816 case EmailProtocol::PLAIN:
817 root->add_child("MailProtocol")->add_child_text("Plain");
819 case EmailProtocol::STARTTLS:
820 root->add_child("MailProtocol")->add_child_text("STARTTLS");
822 case EmailProtocol::SSL:
823 root->add_child("MailProtocol")->add_child_text("SSL");
826 /* [XML] MailUser Username to use on SMTP server. */
827 root->add_child("MailUser")->add_child_text (_mail_user);
828 /* [XML] MailPassword Password to use on SMTP server. */
829 root->add_child("MailPassword")->add_child_text (_mail_password);
831 /* [XML] KDMSubject Subject to use for KDM emails. */
832 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
833 /* [XML] KDMFrom From address to use for KDM emails. */
834 root->add_child("KDMFrom")->add_child_text (_kdm_from);
835 for (auto i: _kdm_cc) {
836 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
837 root->add_child("KDMCC")->add_child_text (i);
839 /* [XML] KDMBCC BCC address to use for KDM emails. */
840 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
841 /* [XML] KDMEmail Text of KDM email. */
842 root->add_child("KDMEmail")->add_child_text (_kdm_email);
844 /* [XML] NotificationSubject Subject to use for notification emails. */
845 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
846 /* [XML] NotificationFrom From address to use for notification emails. */
847 root->add_child("NotificationFrom")->add_child_text (_notification_from);
848 /* [XML] NotificationFrom To address to use for notification emails. */
849 root->add_child("NotificationTo")->add_child_text (_notification_to);
850 for (auto i: _notification_cc) {
851 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
852 root->add_child("NotificationCC")->add_child_text (i);
854 /* [XML] NotificationBCC BCC address to use for notification emails. */
855 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
856 /* [XML] NotificationEmail Text of notification email. */
857 root->add_child("NotificationEmail")->add_child_text (_notification_email);
859 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
860 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
861 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
862 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
864 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
865 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
866 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
867 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
868 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
869 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
870 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
871 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
872 /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
873 root->add_child("UseAllAudioChannels")->add_child_text(_use_all_audio_channels ? "1" : "0");
874 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
875 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
876 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
877 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
878 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
879 related to the player, 1024 debug information related to audio analyses.
881 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
882 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
883 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
884 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
885 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
886 #ifdef DCPOMATIC_WINDOWS
887 if (_win32_console) {
888 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
889 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
890 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
892 root->add_child("Win32Console")->add_child_text ("1");
896 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
897 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
899 auto signer = root->add_child ("Signer");
900 DCPOMATIC_ASSERT (_signer_chain);
901 for (auto const& i: _signer_chain->unordered()) {
902 signer->add_child("Certificate")->add_child_text (i.certificate (true));
904 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
906 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
907 auto decryption = root->add_child ("Decryption");
908 DCPOMATIC_ASSERT (_decryption_chain);
909 for (auto const& i: _decryption_chain->unordered()) {
910 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
912 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
914 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
917 for (auto i: _history) {
918 root->add_child("History")->add_child_text (i.string ());
921 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
924 for (auto i: _player_history) {
925 root->add_child("PlayerHistory")->add_child_text (i.string ());
928 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
929 or <code><DKDM></code> tags.
931 /* [XML] DKDM A DKDM as XML */
932 _dkdms->as_xml (root);
934 /* [XML] CinemasFile Filename of cinemas list file. */
935 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
936 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
937 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
938 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
939 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
940 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
941 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
942 /* [XML] KDMFilenameFormat Format for KDM filenames. */
943 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
944 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
945 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
946 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
947 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
948 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
949 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
950 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
951 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
952 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
953 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
954 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
955 for (int i = 0; i < NAG_COUNT; ++i) {
956 xmlpp::Element* e = root->add_child ("Nagged");
957 e->set_attribute ("Id", raw_convert<string>(i));
958 e->add_child_text (_nagged[i] ? "1" : "0");
960 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
961 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
963 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
964 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
966 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
967 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
968 if (_last_player_load_directory) {
969 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
971 /* [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. */
972 if (_last_kdm_write_type) {
973 switch (_last_kdm_write_type.get()) {
975 root->add_child("LastKDMWriteType")->add_child_text("flat");
977 case KDM_WRITE_FOLDER:
978 root->add_child("LastKDMWriteType")->add_child_text("folder");
981 root->add_child("LastKDMWriteType")->add_child_text("zip");
985 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
986 if (_last_dkdm_write_type) {
987 switch (_last_dkdm_write_type.get()) {
988 case DKDM_WRITE_INTERNAL:
989 root->add_child("LastDKDMWriteType")->add_child_text("internal");
991 case DKDM_WRITE_FILE:
992 root->add_child("LastDKDMWriteType")->add_child_text("file");
996 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
997 frames to be held in memory at once.
999 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
1001 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
1002 if (_decode_reduction) {
1003 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
1006 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
1007 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
1009 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
1010 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
1011 xmlpp::Element* e = root->add_child ("Notification");
1012 e->set_attribute ("Id", raw_convert<string>(i));
1013 e->add_child_text (_notification[i] ? "1" : "0");
1016 if (_barco_username) {
1017 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
1018 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
1020 if (_barco_password) {
1021 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
1022 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
1025 if (_christie_username) {
1026 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
1027 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
1029 if (_christie_password) {
1030 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
1031 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
1034 if (_gdc_username) {
1035 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
1036 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
1038 if (_gdc_password) {
1039 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
1040 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
1043 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
1044 with controls on another monitor.
1046 switch (_player_mode) {
1047 case PLAYER_MODE_WINDOW:
1048 root->add_child("PlayerMode")->add_child_text("window");
1050 case PLAYER_MODE_FULL:
1051 root->add_child("PlayerMode")->add_child_text("full");
1053 case PLAYER_MODE_DUAL:
1054 root->add_child("PlayerMode")->add_child_text("dual");
1058 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
1059 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
1060 switch (_video_view_type) {
1061 case VIDEO_VIEW_SIMPLE:
1062 root->add_child("VideoViewType")->add_child_text("simple");
1064 case VIDEO_VIEW_OPENGL:
1065 root->add_child("VideoViewType")->add_child_text("opengl");
1068 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1069 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
1070 if (_player_debug_log_file) {
1071 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1072 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
1074 if (_player_content_directory) {
1075 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1076 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
1078 if (_player_playlist_directory) {
1079 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1080 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1082 if (_player_kdm_directory) {
1083 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1084 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1086 if (_audio_mapping) {
1087 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1089 for (auto const& i: _custom_languages) {
1090 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1092 for (auto const& initial: _initial_paths) {
1093 if (initial.second) {
1094 root->add_child(initial.first)->add_child_text(initial.second->string());
1097 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1098 root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0");
1099 root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0");
1100 root->add_child("DefaultKDMType")->add_child_text(dcp::formulation_to_string(_default_kdm_type));
1101 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1102 if (_last_release_notes_version) {
1103 root->add_child("LastReleaseNotesVersion")->add_child_text(*_last_release_notes_version);
1105 if (_main_divider_sash_position) {
1106 root->add_child("MainDividerSashPosition")->add_child_text(raw_convert<string>(*_main_divider_sash_position));
1108 if (_main_content_divider_sash_position) {
1109 root->add_child("MainContentDividerSashPosition")->add_child_text(raw_convert<string>(*_main_content_divider_sash_position));
1112 root->add_child("DefaultAddFileLocation")->add_child_text(
1113 _default_add_file_location == DefaultAddFileLocation::SAME_AS_LAST_TIME ? "last" : "project"
1116 /* [XML] AllowSMPTEBv20 1 to allow the user to choose SMPTE (Bv2.0 only) as a standard, otherwise 0 */
1117 root->add_child("AllowSMPTEBv20")->add_child_text(_allow_smpte_bv20 ? "1" : "0");
1119 _export.write(root->add_child("Export"));
1121 auto target = config_write_file();
1124 auto const s = doc.write_to_string_formatted ();
1125 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1126 dcp::File f(tmp, "w");
1128 throw FileError (_("Could not open file for writing"), tmp);
1130 f.checked_write(s.c_str(), s.bytes());
1132 boost::filesystem::remove (target);
1133 boost::filesystem::rename (tmp, target);
1134 } catch (xmlpp::exception& e) {
1135 string s = e.what ();
1137 throw FileError (s, target);
1144 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1146 xmlpp::Document doc;
1147 auto root = doc.create_root_node (root_node);
1148 root->add_child("Version")->add_child_text(version);
1150 for (auto i: things) {
1151 i->as_xml (root->add_child(node));
1155 doc.write_to_file_formatted (file.string() + ".tmp");
1156 boost::filesystem::remove (file);
1157 boost::filesystem::rename (file.string() + ".tmp", file);
1158 } catch (xmlpp::exception& e) {
1159 string s = e.what ();
1161 throw FileError (s, file);
1167 Config::write_cinemas () const
1169 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1174 Config::write_dkdm_recipients () const
1176 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1180 boost::filesystem::path
1181 Config::default_directory_or (boost::filesystem::path a) const
1183 return directory_or (_default_directory, a);
1186 boost::filesystem::path
1187 Config::default_kdm_directory_or (boost::filesystem::path a) const
1189 return directory_or (_default_kdm_directory, a);
1192 boost::filesystem::path
1193 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1199 boost::system::error_code ec;
1200 auto const e = boost::filesystem::exists (*dir, ec);
1216 Config::changed (Property what)
1222 Config::set_kdm_email_to_default ()
1224 _kdm_subject = _("KDM delivery: $CPL_NAME");
1227 "Dear Projectionist\n\n"
1228 "Please find attached KDMs for $CPL_NAME.\n\n"
1229 "Cinema: $CINEMA_NAME\n"
1230 "Screen(s): $SCREENS\n\n"
1231 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1232 "Best regards,\nDCP-o-matic"
1237 Config::set_notification_email_to_default ()
1239 _notification_subject = _("DCP-o-matic notification");
1241 _notification_email = _(
1242 "$JOB_NAME: $JOB_STATUS"
1247 Config::reset_kdm_email ()
1249 set_kdm_email_to_default ();
1254 Config::reset_notification_email ()
1256 set_notification_email_to_default ();
1261 Config::set_cover_sheet_to_default ()
1265 "CPL Filename: $CPL_FILENAME\n"
1267 "Format: $CONTAINER\n"
1269 "Audio Language: $AUDIO_LANGUAGE\n"
1270 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1277 Config::add_to_history (boost::filesystem::path p)
1279 add_to_history_internal (_history, p);
1282 /** Remove non-existent items from the history */
1284 Config::clean_history ()
1286 clean_history_internal (_history);
1290 Config::add_to_player_history (boost::filesystem::path p)
1292 add_to_history_internal (_player_history, p);
1295 /** Remove non-existent items from the player history */
1297 Config::clean_player_history ()
1299 clean_history_internal (_player_history);
1303 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1305 /* Remove existing instances of this path in the history */
1306 h.erase (remove (h.begin(), h.end(), p), h.end ());
1308 h.insert (h.begin (), p);
1309 if (h.size() > HISTORY_SIZE) {
1317 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1323 if (boost::filesystem::is_directory(i)) {
1327 /* We couldn't find out if it's a directory for some reason; just ignore it */
1334 Config::have_existing (string file)
1336 return boost::filesystem::exists (read_path(file));
1341 Config::read_cinemas (cxml::Document const & f)
1344 for (auto i: f.node_children("Cinema")) {
1345 /* Slightly grotty two-part construction of Cinema here so that we can use
1348 auto cinema = make_shared<Cinema>(i);
1349 cinema->read_screens (i);
1350 _cinemas.push_back (cinema);
1355 Config::set_cinemas_file (boost::filesystem::path file)
1357 if (file == _cinemas_file) {
1361 _cinemas_file = file;
1363 if (boost::filesystem::exists (_cinemas_file)) {
1364 /* Existing file; read it in */
1365 cxml::Document f ("Cinemas");
1366 f.read_file (_cinemas_file);
1376 Config::read_dkdm_recipients (cxml::Document const & f)
1378 _dkdm_recipients.clear ();
1379 for (auto i: f.node_children("DKDMRecipient")) {
1380 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1386 Config::save_template (shared_ptr<const Film> film, string name) const
1388 film->write_template (template_write_path(name));
1393 Config::templates () const
1395 if (!boost::filesystem::exists(read_path("templates"))) {
1400 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1401 n.push_back (i.path().filename().string());
1407 Config::existing_template (string name) const
1409 return boost::filesystem::exists (template_read_path(name));
1413 boost::filesystem::path
1414 Config::template_read_path (string name) const
1416 return read_path("templates") / tidy_for_filename (name);
1420 boost::filesystem::path
1421 Config::template_write_path (string name) const
1423 return write_path("templates") / tidy_for_filename (name);
1428 Config::rename_template (string old_name, string new_name) const
1430 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1434 Config::delete_template (string name) const
1436 boost::filesystem::remove (template_write_path(name));
1439 /** @return Path to the config.xml containing the actual settings, following a link if required */
1440 boost::filesystem::path
1441 config_file (boost::filesystem::path main)
1443 cxml::Document f ("Config");
1444 if (!boost::filesystem::exists (main)) {
1445 /* It doesn't exist, so there can't be any links; just return it */
1449 /* See if there's a link */
1452 auto link = f.optional_string_child("Link");
1456 } catch (xmlpp::exception& e) {
1457 /* There as a problem reading the main configuration file,
1458 so there can't be a link.
1466 boost::filesystem::path
1467 Config::config_read_file ()
1469 return config_file (read_path("config.xml"));
1473 boost::filesystem::path
1474 Config::config_write_file ()
1476 return config_file (write_path("config.xml"));
1481 Config::reset_cover_sheet ()
1483 set_cover_sheet_to_default ();
1488 Config::link (boost::filesystem::path new_file) const
1490 xmlpp::Document doc;
1491 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1493 doc.write_to_file_formatted(write_path("config.xml").string());
1494 } catch (xmlpp::exception& e) {
1495 string s = e.what ();
1497 throw FileError (s, write_path("config.xml"));
1502 Config::copy_and_link (boost::filesystem::path new_file) const
1505 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1510 Config::have_write_permission () const
1512 dcp::File f(config_write_file(), "r+");
1513 return static_cast<bool>(f);
1516 /** @param output_channels Number of output channels in use.
1517 * @return Audio mapping for this output channel count (may be a default).
1520 Config::audio_mapping (int output_channels)
1522 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1523 /* Set up a default */
1524 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1525 if (output_channels == 2) {
1526 /* Special case for stereo output.
1527 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1528 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1530 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1531 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1532 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1533 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1534 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1535 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1536 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1537 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1540 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1541 _audio_mapping->set (i, i, 1);
1546 return *_audio_mapping;
1550 Config::set_audio_mapping (AudioMapping m)
1553 changed (AUDIO_MAPPING);
1557 Config::set_audio_mapping_to_default ()
1559 DCPOMATIC_ASSERT (_audio_mapping);
1560 auto const ch = _audio_mapping->output_channels ();
1561 _audio_mapping = boost::none;
1562 _audio_mapping = audio_mapping (ch);
1563 changed (AUDIO_MAPPING);
1568 Config::add_custom_language (dcp::LanguageTag tag)
1570 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1571 _custom_languages.push_back (tag);
1577 optional<Config::BadReason>
1578 Config::check_certificates () const
1580 optional<BadReason> bad;
1582 for (auto const& i: _signer_chain->unordered()) {
1583 if (i.has_utf8_strings()) {
1584 bad = BAD_SIGNER_UTF8_STRINGS;
1586 if ((i.not_after().year() - i.not_before().year()) > 15) {
1587 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1591 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1592 bad = BAD_SIGNER_INCONSISTENT;
1595 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1596 bad = BAD_DECRYPTION_INCONSISTENT;
1604 save_all_config_as_zip (boost::filesystem::path zip_file)
1606 Zipper zipper (zip_file);
1608 auto config = Config::instance();
1609 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1610 if (boost::filesystem::exists(config->cinemas_file())) {
1611 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1613 if (boost::filesystem::exists(config->dkdm_recipients_file())) {
1614 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
1622 Config::set_initial_path(string id, boost::filesystem::path path)
1624 auto iter = _initial_paths.find(id);
1625 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1626 iter->second = path;
1631 optional<boost::filesystem::path>
1632 Config::initial_path(string id) const
1634 auto iter = _initial_paths.find(id);
1635 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1636 return iter->second;