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"
28 #include "dcp_content_type.h"
29 #include "dkdm_recipient.h"
30 #include "dkdm_wrapper.h"
38 #include <dcp/certificate_chain.h>
39 #include <dcp/name_format.h>
40 #include <dcp/raw_convert.h>
41 #include <libcxml/cxml.h>
43 #include <libxml++/libxml++.h>
44 #include <boost/filesystem.hpp>
45 #include <boost/algorithm/string.hpp>
46 #include <boost/thread.hpp>
55 using std::dynamic_pointer_cast;
58 using std::make_shared;
62 using std::shared_ptr;
65 using boost::algorithm::trim;
66 using boost::optional;
67 using dcp::raw_convert;
70 Config* Config::_instance = 0;
71 int const Config::_current_version = 3;
72 boost::signals2::signal<void (Config::LoadFailure)> Config::FailedToLoad;
73 boost::signals2::signal<void (string)> Config::Warning;
74 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
77 /** Construct default configuration */
79 /* DKDMs are not considered a thing to reset on set_defaults() */
80 : _dkdms (new DKDMGroup ("root"))
81 , _default_kdm_duration (1, RoughDuration::Unit::WEEKS)
88 Config::set_defaults ()
90 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
91 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
92 _server_port_base = 6192;
93 _use_any_servers = true;
95 _only_servers_encode = false;
96 _tms_protocol = FileTransferProtocol::SCP;
102 _allow_any_dcp_frame_rate = false;
103 _allow_any_container = false;
104 _allow_96khz_audio = false;
105 _use_all_audio_channels = false;
106 _show_experimental_audio_processors = false;
107 _language = optional<string> ();
108 _default_still_length = 10;
109 _default_container = Ratio::from_id ("185");
110 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
111 _default_dcp_audio_channels = 6;
112 _default_j2k_bandwidth = 150000000;
113 _default_audio_delay = 0;
114 _default_interop = false;
115 _default_metadata.clear ();
116 _upload_after_make_dcp = false;
119 _mail_protocol = EmailProtocol::AUTO;
125 _notification_from = "";
126 _notification_to = "";
127 _notification_cc.clear ();
128 _notification_bcc = "";
129 _check_for_updates = false;
130 _check_for_test_updates = false;
131 _maximum_j2k_bandwidth = 250000000;
132 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
133 _analyse_ebur128 = true;
134 _automatic_audio_analysis = false;
135 #ifdef DCPOMATIC_WINDOWS
136 _win32_console = false;
138 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
139 * ~/.config/dcpomatic2 (or equivalent) and written back there.
141 _cinemas_file = read_path ("cinemas.xml");
142 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
143 _show_hints_before_make_dcp = true;
144 _confirm_kdm_email = true;
145 _kdm_container_name_format = dcp::NameFormat ("KDM %f %c");
146 _kdm_filename_format = dcp::NameFormat ("KDM %f %c %s");
147 _dkdm_filename_format = dcp::NameFormat ("DKDM %f %c %s");
148 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
149 _dcp_asset_filename_format = dcp::NameFormat ("%t");
150 _jump_to_selected = true;
151 for (int i = 0; i < NAG_COUNT; ++i) {
155 _sound_api = boost::none;
156 _sound_output = boost::none;
157 _last_kdm_write_type = KDM_WRITE_FLAT;
158 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
160 /* I think the scaling factor here should be the ratio of the longest frame
161 encode time to the shortest; if the thread count is T, longest time is L
162 and the shortest time S we could encode L/S frames per thread whilst waiting
163 for the L frame to encode so we might have to store LT/S frames.
165 However we don't want to use too much memory, so keep it a bit lower than we'd
166 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
167 use about 240Mb with 72 encoding threads.
169 _frames_in_memory_multiplier = 3;
170 _decode_reduction = optional<int>();
171 _default_notify = false;
172 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
173 _notification[i] = false;
175 _barco_username = optional<string>();
176 _barco_password = optional<string>();
177 _christie_username = optional<string>();
178 _christie_password = optional<string>();
179 _gdc_username = optional<string>();
180 _gdc_password = optional<string>();
181 _player_mode = PLAYER_MODE_WINDOW;
183 _video_view_type = VIDEO_VIEW_SIMPLE;
184 _respect_kdm_validity_periods = true;
185 _player_debug_log_file = boost::none;
186 _player_content_directory = boost::none;
187 _player_playlist_directory = boost::none;
188 _player_kdm_directory = boost::none;
189 _audio_mapping = boost::none;
190 _custom_languages.clear ();
191 _add_files_path = boost::none;
192 _use_isdcf_name_by_default = true;
193 _write_kdms_to_disk = true;
195 _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
196 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
197 _auto_crop_threshold = 0.1;
198 _last_release_notes_version = boost::none;
200 _allowed_dcp_frame_rates.clear ();
201 _allowed_dcp_frame_rates.push_back (24);
202 _allowed_dcp_frame_rates.push_back (25);
203 _allowed_dcp_frame_rates.push_back (30);
204 _allowed_dcp_frame_rates.push_back (48);
205 _allowed_dcp_frame_rates.push_back (50);
206 _allowed_dcp_frame_rates.push_back (60);
208 set_kdm_email_to_default ();
209 set_notification_email_to_default ();
210 set_cover_sheet_to_default ();
212 _main_divider_sash_position = {};
213 _main_content_divider_sash_position = {};
215 _export.set_defaults();
219 Config::restore_defaults ()
221 Config::instance()->set_defaults ();
222 Config::instance()->changed ();
225 shared_ptr<dcp::CertificateChain>
226 Config::create_certificate_chain ()
228 return make_shared<dcp::CertificateChain> (
230 CERTIFICATE_VALIDITY_PERIOD,
233 ".dcpomatic.smpte-430-2.ROOT",
234 ".dcpomatic.smpte-430-2.INTERMEDIATE",
235 "CS.dcpomatic.smpte-430-2.LEAF"
242 using namespace boost::filesystem;
244 auto copy_adding_number = [](path const& path_to_copy) {
246 auto add_number = [](path const& path, int number) {
247 return String::compose("%1.%2", path, number);
251 while (n < 100 && exists(add_number(path_to_copy, n))) {
254 boost::system::error_code ec;
255 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
258 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
259 * to write over. This is more intended for the situation where we have a corrupted config.xml,
260 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
261 * file). But we might as well back up the other files while we're about it.
264 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
265 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
267 copy_adding_number (config_write_file());
269 /* These do not use State::write_path, so whatever path is in the Config we will copy
272 copy_adding_number (_cinemas_file);
273 copy_adding_number (_dkdm_recipients_file);
281 read_dkdm_recipients();
286 Config::read_config()
289 cxml::Document f ("Config");
290 f.read_file (config_read_file());
292 auto version = f.optional_number_child<int> ("Version");
293 if (version && *version < _current_version) {
294 /* Back up the old config before we re-write it in a back-incompatible way */
298 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
299 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
301 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
302 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
305 _default_directory = f.optional_string_child ("DefaultDirectory");
306 if (_default_directory && _default_directory->empty ()) {
307 /* We used to store an empty value for this to mean "none set" */
308 _default_directory = boost::optional<boost::filesystem::path> ();
311 auto b = f.optional_number_child<int> ("ServerPort");
313 b = f.optional_number_child<int> ("ServerPortBase");
315 _server_port_base = b.get ();
317 auto u = f.optional_bool_child ("UseAnyServers");
318 _use_any_servers = u.get_value_or (true);
320 for (auto i: f.node_children("Server")) {
321 if (i->node_children("HostName").size() == 1) {
322 _servers.push_back (i->string_child ("HostName"));
324 _servers.push_back (i->content ());
328 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
329 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
330 _tms_passive = f.optional_bool_child("TMSPassive").get_value_or(true);
331 _tms_ip = f.string_child ("TMSIP");
332 _tms_path = f.string_child ("TMSPath");
333 _tms_user = f.string_child ("TMSUser");
334 _tms_password = f.string_child ("TMSPassword");
336 _language = f.optional_string_child ("Language");
338 auto c = f.optional_string_child ("DefaultContainer");
340 _default_container = Ratio::from_id (c.get ());
343 if (_default_container && !_default_container->used_for_container()) {
344 Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)"));
345 _default_container = Ratio::from_id ("185");
348 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
349 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
351 if (f.optional_string_child ("DCPMetadataIssuer")) {
352 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
353 } else if (f.optional_string_child ("DCPIssuer")) {
354 _dcp_issuer = f.string_child ("DCPIssuer");
357 auto up = f.optional_bool_child("UploadAfterMakeDCP");
359 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
361 _upload_after_make_dcp = up.get_value_or (false);
362 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
363 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
364 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
365 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
366 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
368 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
369 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
370 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
371 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
373 for (auto const& i: f.node_children("DefaultMetadata")) {
374 _default_metadata[i->string_attribute("key")] = i->content();
377 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
379 /* Read any cinemas that are still lying around in the config file
380 * from an old version.
384 _mail_server = f.string_child ("MailServer");
385 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
388 /* Make sure this matches the code in write_config */
389 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
390 if (protocol == "Auto") {
391 _mail_protocol = EmailProtocol::AUTO;
392 } else if (protocol == "Plain") {
393 _mail_protocol = EmailProtocol::PLAIN;
394 } else if (protocol == "STARTTLS") {
395 _mail_protocol = EmailProtocol::STARTTLS;
396 } else if (protocol == "SSL") {
397 _mail_protocol = EmailProtocol::SSL;
401 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
402 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
404 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
405 _kdm_from = f.string_child ("KDMFrom");
406 for (auto i: f.node_children("KDMCC")) {
407 if (!i->content().empty()) {
408 _kdm_cc.push_back (i->content ());
411 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
412 _kdm_email = f.string_child ("KDMEmail");
414 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
415 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
416 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
417 for (auto i: f.node_children("NotificationCC")) {
418 if (!i->content().empty()) {
419 _notification_cc.push_back (i->content ());
422 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
423 if (f.optional_string_child("NotificationEmail")) {
424 _notification_email = f.string_child("NotificationEmail");
427 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
428 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
430 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
431 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
432 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
433 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
434 _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false);
435 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
437 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
438 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
439 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
440 #ifdef DCPOMATIC_WINDOWS
441 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
444 for (auto i: f.node_children("History")) {
445 _history.push_back (i->content ());
448 for (auto i: f.node_children("PlayerHistory")) {
449 _player_history.push_back (i->content ());
452 auto signer = f.optional_node_child ("Signer");
454 auto c = make_shared<dcp::CertificateChain>();
455 /* Read the signing certificates and private key in from the config file */
456 for (auto i: signer->node_children ("Certificate")) {
457 c->add (dcp::Certificate (i->content ()));
459 c->set_key (signer->string_child ("PrivateKey"));
462 /* Make a new set of signing certificates and key */
463 _signer_chain = create_certificate_chain ();
466 auto decryption = f.optional_node_child ("Decryption");
468 auto c = make_shared<dcp::CertificateChain>();
469 for (auto i: decryption->node_children ("Certificate")) {
470 c->add (dcp::Certificate (i->content ()));
472 c->set_key (decryption->string_child ("PrivateKey"));
473 _decryption_chain = c;
475 _decryption_chain = create_certificate_chain ();
478 /* These must be done before we call Bad as that might set one
481 for (auto i: f.node_children("Nagged")) {
482 auto const id = i->number_attribute<int>("Id");
483 if (id >= 0 && id < NAG_COUNT) {
484 _nagged[id] = raw_convert<int>(i->content());
488 auto bad = check_certificates ();
490 auto const remake = Bad(*bad);
491 if (remake && *remake) {
493 case BAD_SIGNER_UTF8_STRINGS:
494 case BAD_SIGNER_INCONSISTENT:
495 case BAD_SIGNER_VALIDITY_TOO_LONG:
496 _signer_chain = create_certificate_chain ();
498 case BAD_DECRYPTION_INCONSISTENT:
499 _decryption_chain = create_certificate_chain ();
505 if (f.optional_node_child("DKDMGroup")) {
506 /* New-style: all DKDMs in a group */
507 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
509 /* Old-style: one or more DKDM nodes */
510 _dkdms = make_shared<DKDMGroup>("root");
511 for (auto i: f.node_children("DKDM")) {
512 _dkdms->add (DKDMBase::read (i));
515 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
516 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
517 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
518 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
519 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
520 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
521 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
522 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
523 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
524 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
525 /* The variable was renamed but not the XML tag */
526 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
527 _sound_api = f.optional_string_child("PreviewSoundAPI");
528 _sound_output = f.optional_string_child("PreviewSoundOutput");
529 if (f.optional_string_child("CoverSheet")) {
530 _cover_sheet = f.optional_string_child("CoverSheet").get();
532 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
533 if (f.optional_string_child("LastKDMWriteType")) {
534 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
535 _last_kdm_write_type = KDM_WRITE_FLAT;
536 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
537 _last_kdm_write_type = KDM_WRITE_FOLDER;
538 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
539 _last_kdm_write_type = KDM_WRITE_ZIP;
542 if (f.optional_string_child("LastDKDMWriteType")) {
543 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
544 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
545 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
546 _last_dkdm_write_type = DKDM_WRITE_FILE;
549 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
550 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
551 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
553 for (auto i: f.node_children("Notification")) {
554 int const id = i->number_attribute<int>("Id");
555 if (id >= 0 && id < NOTIFICATION_COUNT) {
556 _notification[id] = raw_convert<int>(i->content());
560 _barco_username = f.optional_string_child("BarcoUsername");
561 _barco_password = f.optional_string_child("BarcoPassword");
562 _christie_username = f.optional_string_child("ChristieUsername");
563 _christie_password = f.optional_string_child("ChristiePassword");
564 _gdc_username = f.optional_string_child("GDCUsername");
565 _gdc_password = f.optional_string_child("GDCPassword");
567 auto pm = f.optional_string_child("PlayerMode");
568 if (pm && *pm == "window") {
569 _player_mode = PLAYER_MODE_WINDOW;
570 } else if (pm && *pm == "full") {
571 _player_mode = PLAYER_MODE_FULL;
572 } else if (pm && *pm == "dual") {
573 _player_mode = PLAYER_MODE_DUAL;
576 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
577 auto vc = f.optional_string_child("VideoViewType");
578 if (vc && *vc == "opengl") {
579 _video_view_type = VIDEO_VIEW_OPENGL;
580 } else if (vc && *vc == "simple") {
581 _video_view_type = VIDEO_VIEW_SIMPLE;
583 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
584 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
585 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
586 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
587 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
589 if (f.optional_node_child("AudioMapping")) {
590 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
593 for (auto i: f.node_children("CustomLanguage")) {
595 /* This will fail if it's called before dcp::init() as it won't recognise the
596 * tag. That's OK because the Config will be reloaded again later.
598 _custom_languages.push_back (dcp::LanguageTag(i->content()));
599 } catch (std::runtime_error& e) {}
602 _add_files_path = f.optional_string_child("AddFilesPath");
603 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
604 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
605 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
606 _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
607 if (auto duration = f.optional_node_child("DefaultKDMDuration")) {
608 _default_kdm_duration = RoughDuration(duration);
610 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
612 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
613 _last_release_notes_version = f.optional_string_child("LastReleaseNotesVersion");
614 _main_divider_sash_position = f.optional_number_child<int>("MainDividerSashPosition");
615 _main_content_divider_sash_position = f.optional_number_child<int>("MainContentDividerSashPosition");
617 _export.read(f.optional_node_child("Export"));
620 if (have_existing("config.xml")) {
622 /* We have a config file but it didn't load */
623 FailedToLoad(LoadFailure::CONFIG);
626 /* Make a new set of signing certificates and key */
627 _signer_chain = create_certificate_chain ();
628 /* And similar for decryption of KDMs */
629 _decryption_chain = create_certificate_chain ();
635 Config::read_cinemas()
637 if (boost::filesystem::exists (_cinemas_file)) {
639 cxml::Document f("Cinemas");
640 f.read_file(_cinemas_file);
644 FailedToLoad(LoadFailure::CINEMAS);
652 Config::read_dkdm_recipients()
654 if (boost::filesystem::exists (_dkdm_recipients_file)) {
656 cxml::Document f("DKDMRecipients");
657 f.read_file(_dkdm_recipients_file);
658 read_dkdm_recipients(f);
661 FailedToLoad(LoadFailure::DKDM_RECIPIENTS);
662 write_dkdm_recipients();
668 /** @return Singleton instance */
672 if (_instance == nullptr) {
673 _instance = new Config;
680 /** Write our configuration to disk */
682 Config::write () const
686 write_dkdm_recipients ();
690 Config::write_config () const
693 auto root = doc.create_root_node ("Config");
695 /* [XML] Version The version number of the configuration file format. */
696 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
697 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
698 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
699 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
700 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
701 if (_default_directory) {
702 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
703 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
705 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
706 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
707 by the batch converter to listen for job requests.
709 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
710 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
711 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
713 for (auto i: _servers) {
714 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
717 root->add_child("Server")->add_child_text (i);
720 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
721 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
723 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
724 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
725 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
726 /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */
727 root->add_child("TMSPassive")->add_child_text(_tms_passive ? "1" : "0");
728 /* [XML] TMSIP IP address of TMS. */
729 root->add_child("TMSIP")->add_child_text (_tms_ip);
730 /* [XML] TMSPath Path on the TMS to copy files to. */
731 root->add_child("TMSPath")->add_child_text (_tms_path);
732 /* [XML] TMSUser Username to log into the TMS with. */
733 root->add_child("TMSUser")->add_child_text (_tms_user);
734 /* [XML] TMSPassword Password to log into the TMS with. */
735 root->add_child("TMSPassword")->add_child_text (_tms_password);
737 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
738 root->add_child("Language")->add_child_text (_language.get());
740 if (_default_container) {
741 /* [XML:opt] DefaultContainer ID of default container
742 to use when creating new films (<code>185</code>,<code>239</code> or
745 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
747 if (_default_dcp_content_type) {
748 /* [XML:opt] DefaultDCPContentType Default content type to use when creating new films (<code>FTR</code>, <code>SHR</code>,
749 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
750 <code>PSA</code> or <code>ADV</code>). */
751 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
753 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
754 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
755 /* [XML] DCPIssuer Issuer text to write into CPL files. */
756 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
757 /* [XML] DCPCreator Creator text to write into CPL files. */
758 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
759 /* [XML] Company name to write into MXF files. */
760 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
761 /* [XML] Product name to write into MXF files. */
762 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
763 /* [XML] Product version to write into MXF files. */
764 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
765 /* [XML] Comment to write into JPEG2000 data. */
766 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
767 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
768 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
770 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
771 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
772 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
773 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
774 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
775 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
776 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
777 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
778 for (auto const& i: _default_metadata) {
779 auto c = root->add_child("DefaultMetadata");
780 c->set_attribute("key", i.first);
781 c->add_child_text(i.second);
783 if (_default_kdm_directory) {
784 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
785 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
787 _default_kdm_duration.as_xml(root->add_child("DefaultKDMDuration"));
788 /* [XML] MailServer Hostname of SMTP server to use. */
789 root->add_child("MailServer")->add_child_text (_mail_server);
790 /* [XML] MailPort Port number to use on SMTP server. */
791 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
792 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
793 switch (_mail_protocol) {
794 case EmailProtocol::AUTO:
795 root->add_child("MailProtocol")->add_child_text("Auto");
797 case EmailProtocol::PLAIN:
798 root->add_child("MailProtocol")->add_child_text("Plain");
800 case EmailProtocol::STARTTLS:
801 root->add_child("MailProtocol")->add_child_text("STARTTLS");
803 case EmailProtocol::SSL:
804 root->add_child("MailProtocol")->add_child_text("SSL");
807 /* [XML] MailUser Username to use on SMTP server. */
808 root->add_child("MailUser")->add_child_text (_mail_user);
809 /* [XML] MailPassword Password to use on SMTP server. */
810 root->add_child("MailPassword")->add_child_text (_mail_password);
812 /* [XML] KDMSubject Subject to use for KDM emails. */
813 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
814 /* [XML] KDMFrom From address to use for KDM emails. */
815 root->add_child("KDMFrom")->add_child_text (_kdm_from);
816 for (auto i: _kdm_cc) {
817 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
818 root->add_child("KDMCC")->add_child_text (i);
820 /* [XML] KDMBCC BCC address to use for KDM emails. */
821 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
822 /* [XML] KDMEmail Text of KDM email. */
823 root->add_child("KDMEmail")->add_child_text (_kdm_email);
825 /* [XML] NotificationSubject Subject to use for notification emails. */
826 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
827 /* [XML] NotificationFrom From address to use for notification emails. */
828 root->add_child("NotificationFrom")->add_child_text (_notification_from);
829 /* [XML] NotificationFrom To address to use for notification emails. */
830 root->add_child("NotificationTo")->add_child_text (_notification_to);
831 for (auto i: _notification_cc) {
832 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
833 root->add_child("NotificationCC")->add_child_text (i);
835 /* [XML] NotificationBCC BCC address to use for notification emails. */
836 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
837 /* [XML] NotificationEmail Text of notification email. */
838 root->add_child("NotificationEmail")->add_child_text (_notification_email);
840 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
841 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
842 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
843 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
845 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
846 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
847 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
848 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
849 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
850 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
851 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
852 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
853 /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
854 root->add_child("UseAllAudioChannels")->add_child_text(_use_all_audio_channels ? "1" : "0");
855 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
856 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
857 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
858 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
859 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
860 related to the player, 1024 debug information related to audio analyses.
862 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
863 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
864 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
865 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
866 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
867 #ifdef DCPOMATIC_WINDOWS
868 if (_win32_console) {
869 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
870 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
871 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
873 root->add_child("Win32Console")->add_child_text ("1");
877 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
878 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
880 auto signer = root->add_child ("Signer");
881 DCPOMATIC_ASSERT (_signer_chain);
882 for (auto const& i: _signer_chain->unordered()) {
883 signer->add_child("Certificate")->add_child_text (i.certificate (true));
885 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
887 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
888 auto decryption = root->add_child ("Decryption");
889 DCPOMATIC_ASSERT (_decryption_chain);
890 for (auto const& i: _decryption_chain->unordered()) {
891 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
893 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
895 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
898 for (auto i: _history) {
899 root->add_child("History")->add_child_text (i.string ());
902 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
905 for (auto i: _player_history) {
906 root->add_child("PlayerHistory")->add_child_text (i.string ());
909 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
910 or <code><DKDM></code> tags.
912 /* [XML] DKDM A DKDM as XML */
913 _dkdms->as_xml (root);
915 /* [XML] CinemasFile Filename of cinemas list file. */
916 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
917 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
918 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
919 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
920 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
921 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
922 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
923 /* [XML] KDMFilenameFormat Format for KDM filenames. */
924 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
925 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
926 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
927 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
928 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
929 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
930 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
931 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
932 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
933 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
934 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
935 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
936 for (int i = 0; i < NAG_COUNT; ++i) {
937 xmlpp::Element* e = root->add_child ("Nagged");
938 e->set_attribute ("Id", raw_convert<string>(i));
939 e->add_child_text (_nagged[i] ? "1" : "0");
941 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
942 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
944 /* [XML:opt] PreviewSoundAPI ID of the audio API to use. */
945 root->add_child("PreviewSoundAPI")->add_child_text(_sound_api.get());
948 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
949 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
951 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
952 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
953 if (_last_player_load_directory) {
954 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
956 /* [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. */
957 if (_last_kdm_write_type) {
958 switch (_last_kdm_write_type.get()) {
960 root->add_child("LastKDMWriteType")->add_child_text("flat");
962 case KDM_WRITE_FOLDER:
963 root->add_child("LastKDMWriteType")->add_child_text("folder");
966 root->add_child("LastKDMWriteType")->add_child_text("zip");
970 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
971 if (_last_dkdm_write_type) {
972 switch (_last_dkdm_write_type.get()) {
973 case DKDM_WRITE_INTERNAL:
974 root->add_child("LastDKDMWriteType")->add_child_text("internal");
976 case DKDM_WRITE_FILE:
977 root->add_child("LastDKDMWriteType")->add_child_text("file");
981 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
982 frames to be held in memory at once.
984 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
986 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
987 if (_decode_reduction) {
988 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
991 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
992 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
994 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
995 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
996 xmlpp::Element* e = root->add_child ("Notification");
997 e->set_attribute ("Id", raw_convert<string>(i));
998 e->add_child_text (_notification[i] ? "1" : "0");
1001 if (_barco_username) {
1002 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
1003 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
1005 if (_barco_password) {
1006 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
1007 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
1010 if (_christie_username) {
1011 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
1012 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
1014 if (_christie_password) {
1015 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
1016 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
1019 if (_gdc_username) {
1020 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
1021 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
1023 if (_gdc_password) {
1024 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
1025 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
1028 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
1029 with controls on another monitor.
1031 switch (_player_mode) {
1032 case PLAYER_MODE_WINDOW:
1033 root->add_child("PlayerMode")->add_child_text("window");
1035 case PLAYER_MODE_FULL:
1036 root->add_child("PlayerMode")->add_child_text("full");
1038 case PLAYER_MODE_DUAL:
1039 root->add_child("PlayerMode")->add_child_text("dual");
1043 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
1044 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
1045 switch (_video_view_type) {
1046 case VIDEO_VIEW_SIMPLE:
1047 root->add_child("VideoViewType")->add_child_text("simple");
1049 case VIDEO_VIEW_OPENGL:
1050 root->add_child("VideoViewType")->add_child_text("opengl");
1053 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1054 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
1055 if (_player_debug_log_file) {
1056 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1057 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
1059 if (_player_content_directory) {
1060 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1061 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
1063 if (_player_playlist_directory) {
1064 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1065 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1067 if (_player_kdm_directory) {
1068 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1069 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1071 if (_audio_mapping) {
1072 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1074 for (auto const& i: _custom_languages) {
1075 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1077 if (_add_files_path) {
1078 /* [XML] AddFilesPath The default path that will be offered in the picker when adding files to a film. */
1079 root->add_child("AddFilesPath")->add_child_text(_add_files_path->string());
1081 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1082 root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0");
1083 root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0");
1084 root->add_child("DefaultKDMType")->add_child_text(dcp::formulation_to_string(_default_kdm_type));
1085 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1086 if (_last_release_notes_version) {
1087 root->add_child("LastReleaseNotesVersion")->add_child_text(*_last_release_notes_version);
1089 if (_main_divider_sash_position) {
1090 root->add_child("MainDividerSashPosition")->add_child_text(raw_convert<string>(*_main_divider_sash_position));
1092 if (_main_content_divider_sash_position) {
1093 root->add_child("MainContentDividerSashPosition")->add_child_text(raw_convert<string>(*_main_content_divider_sash_position));
1096 _export.write(root->add_child("Export"));
1098 auto target = config_write_file();
1101 auto const s = doc.write_to_string_formatted ();
1102 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1103 dcp::File f(tmp, "w");
1105 throw FileError (_("Could not open file for writing"), tmp);
1107 f.checked_write(s.c_str(), s.bytes());
1109 boost::filesystem::remove (target);
1110 boost::filesystem::rename (tmp, target);
1111 } catch (xmlpp::exception& e) {
1112 string s = e.what ();
1114 throw FileError (s, target);
1121 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1123 xmlpp::Document doc;
1124 auto root = doc.create_root_node (root_node);
1125 root->add_child("Version")->add_child_text(version);
1127 for (auto i: things) {
1128 i->as_xml (root->add_child(node));
1132 doc.write_to_file_formatted (file.string() + ".tmp");
1133 boost::filesystem::remove (file);
1134 boost::filesystem::rename (file.string() + ".tmp", file);
1135 } catch (xmlpp::exception& e) {
1136 string s = e.what ();
1138 throw FileError (s, file);
1144 Config::write_cinemas () const
1146 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1151 Config::write_dkdm_recipients () const
1153 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1157 boost::filesystem::path
1158 Config::default_directory_or (boost::filesystem::path a) const
1160 return directory_or (_default_directory, a);
1163 boost::filesystem::path
1164 Config::default_kdm_directory_or (boost::filesystem::path a) const
1166 return directory_or (_default_kdm_directory, a);
1169 boost::filesystem::path
1170 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1176 boost::system::error_code ec;
1177 auto const e = boost::filesystem::exists (*dir, ec);
1193 Config::changed (Property what)
1199 Config::set_kdm_email_to_default ()
1201 _kdm_subject = _("KDM delivery: $CPL_NAME");
1204 "Dear Projectionist\n\n"
1205 "Please find attached KDMs for $CPL_NAME.\n\n"
1206 "Cinema: $CINEMA_NAME\n"
1207 "Screen(s): $SCREENS\n\n"
1208 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1209 "Best regards,\nDCP-o-matic"
1214 Config::set_notification_email_to_default ()
1216 _notification_subject = _("DCP-o-matic notification");
1218 _notification_email = _(
1219 "$JOB_NAME: $JOB_STATUS"
1224 Config::reset_kdm_email ()
1226 set_kdm_email_to_default ();
1231 Config::reset_notification_email ()
1233 set_notification_email_to_default ();
1238 Config::set_cover_sheet_to_default ()
1242 "CPL Filename: $CPL_FILENAME\n"
1244 "Format: $CONTAINER\n"
1246 "Audio Language: $AUDIO_LANGUAGE\n"
1247 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1254 Config::add_to_history (boost::filesystem::path p)
1256 add_to_history_internal (_history, p);
1259 /** Remove non-existent items from the history */
1261 Config::clean_history ()
1263 clean_history_internal (_history);
1267 Config::add_to_player_history (boost::filesystem::path p)
1269 add_to_history_internal (_player_history, p);
1272 /** Remove non-existant items from the player history */
1274 Config::clean_player_history ()
1276 clean_history_internal (_player_history);
1280 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1282 /* Remove existing instances of this path in the history */
1283 h.erase (remove (h.begin(), h.end(), p), h.end ());
1285 h.insert (h.begin (), p);
1286 if (h.size() > HISTORY_SIZE) {
1294 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1300 if (boost::filesystem::is_directory(i)) {
1304 /* We couldn't find out if it's a directory for some reason; just ignore it */
1311 Config::have_existing (string file)
1313 return boost::filesystem::exists (read_path(file));
1318 Config::read_cinemas (cxml::Document const & f)
1321 for (auto i: f.node_children("Cinema")) {
1322 /* Slightly grotty two-part construction of Cinema here so that we can use
1325 auto cinema = make_shared<Cinema>(i);
1326 cinema->read_screens (i);
1327 _cinemas.push_back (cinema);
1332 Config::set_cinemas_file (boost::filesystem::path file)
1334 if (file == _cinemas_file) {
1338 _cinemas_file = file;
1340 if (boost::filesystem::exists (_cinemas_file)) {
1341 /* Existing file; read it in */
1342 cxml::Document f ("Cinemas");
1343 f.read_file (_cinemas_file);
1353 Config::read_dkdm_recipients (cxml::Document const & f)
1355 _dkdm_recipients.clear ();
1356 for (auto i: f.node_children("DKDMRecipient")) {
1357 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1363 Config::save_template (shared_ptr<const Film> film, string name) const
1365 film->write_template (template_write_path(name));
1370 Config::templates () const
1372 if (!boost::filesystem::exists(read_path("templates"))) {
1377 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1378 n.push_back (i.path().filename().string());
1384 Config::existing_template (string name) const
1386 return boost::filesystem::exists (template_read_path(name));
1390 boost::filesystem::path
1391 Config::template_read_path (string name) const
1393 return read_path("templates") / tidy_for_filename (name);
1397 boost::filesystem::path
1398 Config::template_write_path (string name) const
1400 return write_path("templates") / tidy_for_filename (name);
1405 Config::rename_template (string old_name, string new_name) const
1407 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1411 Config::delete_template (string name) const
1413 boost::filesystem::remove (template_write_path(name));
1416 /** @return Path to the config.xml containing the actual settings, following a link if required */
1417 boost::filesystem::path
1418 config_file (boost::filesystem::path main)
1420 cxml::Document f ("Config");
1421 if (!boost::filesystem::exists (main)) {
1422 /* It doesn't exist, so there can't be any links; just return it */
1426 /* See if there's a link */
1429 auto link = f.optional_string_child("Link");
1433 } catch (xmlpp::exception& e) {
1434 /* There as a problem reading the main configuration file,
1435 so there can't be a link.
1443 boost::filesystem::path
1444 Config::config_read_file ()
1446 return config_file (read_path("config.xml"));
1450 boost::filesystem::path
1451 Config::config_write_file ()
1453 return config_file (write_path("config.xml"));
1458 Config::reset_cover_sheet ()
1460 set_cover_sheet_to_default ();
1465 Config::link (boost::filesystem::path new_file) const
1467 xmlpp::Document doc;
1468 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1470 doc.write_to_file_formatted(write_path("config.xml").string());
1471 } catch (xmlpp::exception& e) {
1472 string s = e.what ();
1474 throw FileError (s, write_path("config.xml"));
1479 Config::copy_and_link (boost::filesystem::path new_file) const
1482 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1487 Config::have_write_permission () const
1489 dcp::File f(config_write_file(), "r+");
1490 return static_cast<bool>(f);
1493 /** @param output_channels Number of output channels in use.
1494 * @return Audio mapping for this output channel count (may be a default).
1497 Config::audio_mapping (int output_channels)
1499 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1500 /* Set up a default */
1501 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1502 if (output_channels == 2) {
1503 /* Special case for stereo output.
1504 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1505 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1507 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1508 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1509 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1510 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1511 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1512 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1513 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1514 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1517 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1518 _audio_mapping->set (i, i, 1);
1523 return *_audio_mapping;
1527 Config::set_audio_mapping (AudioMapping m)
1530 changed (AUDIO_MAPPING);
1534 Config::set_audio_mapping_to_default ()
1536 DCPOMATIC_ASSERT (_audio_mapping);
1537 auto const ch = _audio_mapping->output_channels ();
1538 _audio_mapping = boost::none;
1539 _audio_mapping = audio_mapping (ch);
1540 changed (AUDIO_MAPPING);
1545 Config::add_custom_language (dcp::LanguageTag tag)
1547 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1548 _custom_languages.push_back (tag);
1554 optional<Config::BadReason>
1555 Config::check_certificates () const
1557 optional<BadReason> bad;
1559 for (auto const& i: _signer_chain->unordered()) {
1560 if (i.has_utf8_strings()) {
1561 bad = BAD_SIGNER_UTF8_STRINGS;
1563 if ((i.not_after().year() - i.not_before().year()) > 15) {
1564 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1568 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1569 bad = BAD_SIGNER_INCONSISTENT;
1572 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1573 bad = BAD_DECRYPTION_INCONSISTENT;
1581 save_all_config_as_zip (boost::filesystem::path zip_file)
1583 Zipper zipper (zip_file);
1585 auto config = Config::instance();
1586 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1587 if (boost::filesystem::exists(config->cinemas_file())) {
1588 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1590 if (boost::filesystem::exists(config->dkdm_recipients_file())) {
1591 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));