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_audio_channels = 8;
109 _default_j2k_bandwidth = 150000000;
110 _default_audio_delay = 0;
111 _default_interop = false;
112 _default_metadata.clear ();
113 _upload_after_make_dcp = false;
116 _mail_protocol = EmailProtocol::AUTO;
122 _notification_from = "";
123 _notification_to = "";
124 _notification_cc.clear ();
125 _notification_bcc = "";
126 _check_for_updates = false;
127 _check_for_test_updates = false;
128 _maximum_j2k_bandwidth = 250000000;
129 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
130 _analyse_ebur128 = true;
131 _automatic_audio_analysis = false;
132 #ifdef DCPOMATIC_WINDOWS
133 _win32_console = false;
135 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
136 * ~/.config/dcpomatic2 (or equivalent) and written back there.
138 _cinemas_file = read_path ("cinemas.xml");
139 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
140 _show_hints_before_make_dcp = true;
141 _confirm_kdm_email = true;
142 _kdm_container_name_format = dcp::NameFormat("KDM_%f_%c");
143 _kdm_filename_format = dcp::NameFormat("KDM_%f_%c_%s");
144 _dkdm_filename_format = dcp::NameFormat("DKDM_%f_%c_%s");
145 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
146 _dcp_asset_filename_format = dcp::NameFormat ("%t");
147 _jump_to_selected = true;
148 for (int i = 0; i < NAG_COUNT; ++i) {
152 _sound_output = optional<string> ();
153 _last_kdm_write_type = KDM_WRITE_FLAT;
154 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
155 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
157 /* I think the scaling factor here should be the ratio of the longest frame
158 encode time to the shortest; if the thread count is T, longest time is L
159 and the shortest time S we could encode L/S frames per thread whilst waiting
160 for the L frame to encode so we might have to store LT/S frames.
162 However we don't want to use too much memory, so keep it a bit lower than we'd
163 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
164 use about 240Mb with 72 encoding threads.
166 _frames_in_memory_multiplier = 3;
167 _decode_reduction = optional<int>();
168 _default_notify = false;
169 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
170 _notification[i] = false;
172 _barco_username = optional<string>();
173 _barco_password = optional<string>();
174 _christie_username = optional<string>();
175 _christie_password = optional<string>();
176 _gdc_username = optional<string>();
177 _gdc_password = optional<string>();
178 _player_mode = PLAYER_MODE_WINDOW;
180 _video_view_type = VIDEO_VIEW_SIMPLE;
181 _respect_kdm_validity_periods = true;
182 _player_debug_log_file = boost::none;
183 _player_content_directory = boost::none;
184 _player_playlist_directory = boost::none;
185 _player_kdm_directory = boost::none;
186 _audio_mapping = boost::none;
187 _custom_languages.clear ();
188 _initial_paths.clear();
189 _initial_paths["AddFilesPath"] = boost::none;
190 _initial_paths["AddDKDMPath"] = boost::none;
191 _initial_paths["SelectCertificatePath"] = boost::none;
192 _initial_paths["AddCombinerInputPath"] = boost::none;
193 _use_isdcf_name_by_default = true;
194 _write_kdms_to_disk = true;
196 _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
197 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
198 _auto_crop_threshold = 0.1;
199 _last_release_notes_version = boost::none;
200 _allow_smpte_bv20 = false;
201 _isdcf_name_part_length = 14;
203 _allowed_dcp_frame_rates.clear ();
204 _allowed_dcp_frame_rates.push_back (24);
205 _allowed_dcp_frame_rates.push_back (25);
206 _allowed_dcp_frame_rates.push_back (30);
207 _allowed_dcp_frame_rates.push_back (48);
208 _allowed_dcp_frame_rates.push_back (50);
209 _allowed_dcp_frame_rates.push_back (60);
211 set_kdm_email_to_default ();
212 set_notification_email_to_default ();
213 set_cover_sheet_to_default ();
215 _main_divider_sash_position = {};
216 _main_content_divider_sash_position = {};
218 _export.set_defaults();
222 Config::restore_defaults ()
224 Config::instance()->set_defaults ();
225 Config::instance()->changed ();
228 shared_ptr<dcp::CertificateChain>
229 Config::create_certificate_chain ()
231 return make_shared<dcp::CertificateChain> (
233 CERTIFICATE_VALIDITY_PERIOD,
236 ".dcpomatic.smpte-430-2.ROOT",
237 ".dcpomatic.smpte-430-2.INTERMEDIATE",
238 "CS.dcpomatic.smpte-430-2.LEAF"
245 using namespace boost::filesystem;
247 auto copy_adding_number = [](path const& path_to_copy) {
249 auto add_number = [](path const& path, int number) {
250 return String::compose("%1.%2", path, number);
254 while (n < 100 && exists(add_number(path_to_copy, n))) {
257 boost::system::error_code ec;
258 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
261 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
262 * to write over. This is more intended for the situation where we have a corrupted config.xml,
263 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
264 * file). But we might as well back up the other files while we're about it.
267 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
268 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
270 copy_adding_number (config_write_file());
272 /* These do not use State::write_path, so whatever path is in the Config we will copy
275 copy_adding_number (_cinemas_file);
276 copy_adding_number (_dkdm_recipients_file);
284 read_dkdm_recipients();
289 Config::read_config()
292 cxml::Document f ("Config");
293 f.read_file (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_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
343 if (f.optional_string_child ("DCPMetadataIssuer")) {
344 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
345 } else if (f.optional_string_child ("DCPIssuer")) {
346 _dcp_issuer = f.string_child ("DCPIssuer");
349 auto up = f.optional_bool_child("UploadAfterMakeDCP");
351 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
353 _upload_after_make_dcp = up.get_value_or (false);
354 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
355 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
356 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
357 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
358 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
360 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
361 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
362 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
363 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
366 auto al = f.optional_string_child("DefaultAudioLanguage");
368 _default_audio_language = dcp::LanguageTag(*al);
370 } catch (std::runtime_error&) {}
373 auto te = f.optional_string_child("DefaultTerritory");
375 _default_territory = dcp::LanguageTag::RegionSubtag(*te);
377 } catch (std::runtime_error&) {}
379 for (auto const& i: f.node_children("DefaultMetadata")) {
380 _default_metadata[i->string_attribute("key")] = i->content();
383 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
385 /* Read any cinemas that are still lying around in the config file
386 * from an old version.
390 _mail_server = f.string_child ("MailServer");
391 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
394 /* Make sure this matches the code in write_config */
395 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
396 if (protocol == "Auto") {
397 _mail_protocol = EmailProtocol::AUTO;
398 } else if (protocol == "Plain") {
399 _mail_protocol = EmailProtocol::PLAIN;
400 } else if (protocol == "STARTTLS") {
401 _mail_protocol = EmailProtocol::STARTTLS;
402 } else if (protocol == "SSL") {
403 _mail_protocol = EmailProtocol::SSL;
407 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
408 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
410 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
411 _kdm_from = f.string_child ("KDMFrom");
412 for (auto i: f.node_children("KDMCC")) {
413 if (!i->content().empty()) {
414 _kdm_cc.push_back (i->content ());
417 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
418 _kdm_email = f.string_child ("KDMEmail");
420 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
421 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
422 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
423 for (auto i: f.node_children("NotificationCC")) {
424 if (!i->content().empty()) {
425 _notification_cc.push_back (i->content ());
428 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
429 if (f.optional_string_child("NotificationEmail")) {
430 _notification_email = f.string_child("NotificationEmail");
433 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
434 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
436 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
437 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
438 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
439 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
440 _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false);
441 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
443 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
444 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
445 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
446 #ifdef DCPOMATIC_WINDOWS
447 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
450 for (auto i: f.node_children("History")) {
451 _history.push_back (i->content ());
454 for (auto i: f.node_children("PlayerHistory")) {
455 _player_history.push_back (i->content ());
458 auto signer = f.optional_node_child ("Signer");
460 auto c = make_shared<dcp::CertificateChain>();
461 /* Read the signing certificates and private key in from the config file */
462 for (auto i: signer->node_children ("Certificate")) {
463 c->add (dcp::Certificate (i->content ()));
465 c->set_key (signer->string_child ("PrivateKey"));
468 /* Make a new set of signing certificates and key */
469 _signer_chain = create_certificate_chain ();
472 auto decryption = f.optional_node_child ("Decryption");
474 auto c = make_shared<dcp::CertificateChain>();
475 for (auto i: decryption->node_children ("Certificate")) {
476 c->add (dcp::Certificate (i->content ()));
478 c->set_key (decryption->string_child ("PrivateKey"));
479 _decryption_chain = c;
481 _decryption_chain = create_certificate_chain ();
484 /* These must be done before we call Bad as that might set one
487 for (auto i: f.node_children("Nagged")) {
488 auto const id = number_attribute<int>(i, "Id", "id");
489 if (id >= 0 && id < NAG_COUNT) {
490 _nagged[id] = raw_convert<int>(i->content());
494 auto bad = check_certificates ();
496 auto const remake = Bad(*bad);
497 if (remake && *remake) {
499 case BAD_SIGNER_UTF8_STRINGS:
500 case BAD_SIGNER_INCONSISTENT:
501 case BAD_SIGNER_VALIDITY_TOO_LONG:
502 _signer_chain = create_certificate_chain ();
504 case BAD_DECRYPTION_INCONSISTENT:
505 _decryption_chain = create_certificate_chain ();
511 if (f.optional_node_child("DKDMGroup")) {
512 /* New-style: all DKDMs in a group */
513 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
515 /* Old-style: one or more DKDM nodes */
516 _dkdms = make_shared<DKDMGroup>("root");
517 for (auto i: f.node_children("DKDM")) {
518 _dkdms->add (DKDMBase::read (i));
521 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
522 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
523 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
524 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
525 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
526 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
527 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
528 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
529 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
530 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
531 /* The variable was renamed but not the XML tag */
532 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
533 _sound_output = f.optional_string_child("PreviewSoundOutput");
534 if (f.optional_string_child("CoverSheet")) {
535 _cover_sheet = f.optional_string_child("CoverSheet").get();
537 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
538 if (f.optional_string_child("LastKDMWriteType")) {
539 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
540 _last_kdm_write_type = KDM_WRITE_FLAT;
541 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
542 _last_kdm_write_type = KDM_WRITE_FOLDER;
543 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
544 _last_kdm_write_type = KDM_WRITE_ZIP;
547 if (f.optional_string_child("LastDKDMWriteType")) {
548 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
549 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
550 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
551 _last_dkdm_write_type = DKDM_WRITE_FILE;
554 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
555 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
556 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
558 for (auto i: f.node_children("Notification")) {
559 int const id = number_attribute<int>(i, "Id", "id");
560 if (id >= 0 && id < NOTIFICATION_COUNT) {
561 _notification[id] = raw_convert<int>(i->content());
565 _barco_username = f.optional_string_child("BarcoUsername");
566 _barco_password = f.optional_string_child("BarcoPassword");
567 _christie_username = f.optional_string_child("ChristieUsername");
568 _christie_password = f.optional_string_child("ChristiePassword");
569 _gdc_username = f.optional_string_child("GDCUsername");
570 _gdc_password = f.optional_string_child("GDCPassword");
572 auto pm = f.optional_string_child("PlayerMode");
573 if (pm && *pm == "window") {
574 _player_mode = PLAYER_MODE_WINDOW;
575 } else if (pm && *pm == "full") {
576 _player_mode = PLAYER_MODE_FULL;
577 } else if (pm && *pm == "dual") {
578 _player_mode = PLAYER_MODE_DUAL;
581 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
582 auto vc = f.optional_string_child("VideoViewType");
583 if (vc && *vc == "opengl") {
584 _video_view_type = VIDEO_VIEW_OPENGL;
585 } else if (vc && *vc == "simple") {
586 _video_view_type = VIDEO_VIEW_SIMPLE;
588 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
589 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
590 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
591 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
592 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
594 if (f.optional_node_child("AudioMapping")) {
595 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
598 for (auto i: f.node_children("CustomLanguage")) {
600 /* This will fail if it's called before dcp::init() as it won't recognise the
601 * tag. That's OK because the Config will be reloaded again later.
603 _custom_languages.push_back (dcp::LanguageTag(i->content()));
604 } catch (std::runtime_error& e) {}
607 for (auto& initial: _initial_paths) {
608 initial.second = f.optional_string_child(initial.first);
610 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
611 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
612 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
613 _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
614 if (auto duration = f.optional_node_child("DefaultKDMDuration")) {
615 _default_kdm_duration = RoughDuration(duration);
617 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
619 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
620 _last_release_notes_version = f.optional_string_child("LastReleaseNotesVersion");
621 _main_divider_sash_position = f.optional_number_child<int>("MainDividerSashPosition");
622 _main_content_divider_sash_position = f.optional_number_child<int>("MainContentDividerSashPosition");
624 if (auto loc = f.optional_string_child("DefaultAddFileLocation")) {
625 if (*loc == "last") {
626 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
627 } else if (*loc == "project") {
628 _default_add_file_location = DefaultAddFileLocation::SAME_AS_PROJECT;
632 _allow_smpte_bv20 = f.optional_bool_child("AllowSMPTEBv20").get_value_or(false);
633 _isdcf_name_part_length = f.optional_number_child<int>("ISDCFNamePartLength").get_value_or(14);
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 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
759 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
760 /* [XML] DCPIssuer Issuer text to write into CPL files. */
761 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
762 /* [XML] DCPCreator Creator text to write into CPL files. */
763 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
764 /* [XML] Company name to write into MXF files. */
765 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
766 /* [XML] Product name to write into MXF files. */
767 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
768 /* [XML] Product version to write into MXF files. */
769 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
770 /* [XML] Comment to write into JPEG2000 data. */
771 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
772 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
773 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
775 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
776 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
777 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
778 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
779 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
780 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
781 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
782 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
783 if (_default_audio_language) {
784 /* [XML] DefaultAudioLanguage Default audio language to use for new films */
785 root->add_child("DefaultAudioLanguage")->add_child_text(_default_audio_language->to_string());
787 if (_default_territory) {
788 /* [XML] DefaultTerritory Default territory to use for new films */
789 root->add_child("DefaultTerritory")->add_child_text(_default_territory->subtag());
791 for (auto const& i: _default_metadata) {
792 auto c = root->add_child("DefaultMetadata");
793 c->set_attribute("key", i.first);
794 c->add_child_text(i.second);
796 if (_default_kdm_directory) {
797 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
798 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
800 _default_kdm_duration.as_xml(root->add_child("DefaultKDMDuration"));
801 /* [XML] MailServer Hostname of SMTP server to use. */
802 root->add_child("MailServer")->add_child_text (_mail_server);
803 /* [XML] MailPort Port number to use on SMTP server. */
804 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
805 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
806 switch (_mail_protocol) {
807 case EmailProtocol::AUTO:
808 root->add_child("MailProtocol")->add_child_text("Auto");
810 case EmailProtocol::PLAIN:
811 root->add_child("MailProtocol")->add_child_text("Plain");
813 case EmailProtocol::STARTTLS:
814 root->add_child("MailProtocol")->add_child_text("STARTTLS");
816 case EmailProtocol::SSL:
817 root->add_child("MailProtocol")->add_child_text("SSL");
820 /* [XML] MailUser Username to use on SMTP server. */
821 root->add_child("MailUser")->add_child_text (_mail_user);
822 /* [XML] MailPassword Password to use on SMTP server. */
823 root->add_child("MailPassword")->add_child_text (_mail_password);
825 /* [XML] KDMSubject Subject to use for KDM emails. */
826 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
827 /* [XML] KDMFrom From address to use for KDM emails. */
828 root->add_child("KDMFrom")->add_child_text (_kdm_from);
829 for (auto i: _kdm_cc) {
830 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
831 root->add_child("KDMCC")->add_child_text (i);
833 /* [XML] KDMBCC BCC address to use for KDM emails. */
834 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
835 /* [XML] KDMEmail Text of KDM email. */
836 root->add_child("KDMEmail")->add_child_text (_kdm_email);
838 /* [XML] NotificationSubject Subject to use for notification emails. */
839 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
840 /* [XML] NotificationFrom From address to use for notification emails. */
841 root->add_child("NotificationFrom")->add_child_text (_notification_from);
842 /* [XML] NotificationFrom To address to use for notification emails. */
843 root->add_child("NotificationTo")->add_child_text (_notification_to);
844 for (auto i: _notification_cc) {
845 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
846 root->add_child("NotificationCC")->add_child_text (i);
848 /* [XML] NotificationBCC BCC address to use for notification emails. */
849 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
850 /* [XML] NotificationEmail Text of notification email. */
851 root->add_child("NotificationEmail")->add_child_text (_notification_email);
853 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
854 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
855 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
856 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
858 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
859 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
860 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
861 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
862 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
863 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
864 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
865 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
866 /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
867 root->add_child("UseAllAudioChannels")->add_child_text(_use_all_audio_channels ? "1" : "0");
868 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
869 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
870 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
871 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
872 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
873 related to the player, 1024 debug information related to audio analyses.
875 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
876 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
877 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
878 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
879 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
880 #ifdef DCPOMATIC_WINDOWS
881 if (_win32_console) {
882 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
883 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
884 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
886 root->add_child("Win32Console")->add_child_text ("1");
890 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
891 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
893 auto signer = root->add_child ("Signer");
894 DCPOMATIC_ASSERT (_signer_chain);
895 for (auto const& i: _signer_chain->unordered()) {
896 signer->add_child("Certificate")->add_child_text (i.certificate (true));
898 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
900 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
901 auto decryption = root->add_child ("Decryption");
902 DCPOMATIC_ASSERT (_decryption_chain);
903 for (auto const& i: _decryption_chain->unordered()) {
904 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
906 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
908 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
911 for (auto i: _history) {
912 root->add_child("History")->add_child_text (i.string ());
915 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
918 for (auto i: _player_history) {
919 root->add_child("PlayerHistory")->add_child_text (i.string ());
922 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
923 or <code><DKDM></code> tags.
925 /* [XML] DKDM A DKDM as XML */
926 _dkdms->as_xml (root);
928 /* [XML] CinemasFile Filename of cinemas list file. */
929 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
930 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
931 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
932 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
933 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
934 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
935 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
936 /* [XML] KDMFilenameFormat Format for KDM filenames. */
937 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
938 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
939 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
940 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
941 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
942 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
943 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
944 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
945 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
946 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
947 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
948 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
949 for (int i = 0; i < NAG_COUNT; ++i) {
950 xmlpp::Element* e = root->add_child ("Nagged");
951 e->set_attribute("id", raw_convert<string>(i));
952 e->add_child_text (_nagged[i] ? "1" : "0");
954 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
955 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
957 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
958 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
960 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
961 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
962 if (_last_player_load_directory) {
963 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
965 /* [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. */
966 if (_last_kdm_write_type) {
967 switch (_last_kdm_write_type.get()) {
969 root->add_child("LastKDMWriteType")->add_child_text("flat");
971 case KDM_WRITE_FOLDER:
972 root->add_child("LastKDMWriteType")->add_child_text("folder");
975 root->add_child("LastKDMWriteType")->add_child_text("zip");
979 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
980 if (_last_dkdm_write_type) {
981 switch (_last_dkdm_write_type.get()) {
982 case DKDM_WRITE_INTERNAL:
983 root->add_child("LastDKDMWriteType")->add_child_text("internal");
985 case DKDM_WRITE_FILE:
986 root->add_child("LastDKDMWriteType")->add_child_text("file");
990 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
991 frames to be held in memory at once.
993 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
995 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
996 if (_decode_reduction) {
997 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
1000 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
1001 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
1003 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
1004 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
1005 xmlpp::Element* e = root->add_child ("Notification");
1006 e->set_attribute("id", raw_convert<string>(i));
1007 e->add_child_text (_notification[i] ? "1" : "0");
1010 if (_barco_username) {
1011 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
1012 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
1014 if (_barco_password) {
1015 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
1016 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
1019 if (_christie_username) {
1020 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
1021 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
1023 if (_christie_password) {
1024 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
1025 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
1028 if (_gdc_username) {
1029 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
1030 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
1032 if (_gdc_password) {
1033 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
1034 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
1037 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
1038 with controls on another monitor.
1040 switch (_player_mode) {
1041 case PLAYER_MODE_WINDOW:
1042 root->add_child("PlayerMode")->add_child_text("window");
1044 case PLAYER_MODE_FULL:
1045 root->add_child("PlayerMode")->add_child_text("full");
1047 case PLAYER_MODE_DUAL:
1048 root->add_child("PlayerMode")->add_child_text("dual");
1052 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
1053 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
1054 switch (_video_view_type) {
1055 case VIDEO_VIEW_SIMPLE:
1056 root->add_child("VideoViewType")->add_child_text("simple");
1058 case VIDEO_VIEW_OPENGL:
1059 root->add_child("VideoViewType")->add_child_text("opengl");
1062 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1063 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
1064 if (_player_debug_log_file) {
1065 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1066 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
1068 if (_player_content_directory) {
1069 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1070 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
1072 if (_player_playlist_directory) {
1073 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1074 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1076 if (_player_kdm_directory) {
1077 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1078 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1080 if (_audio_mapping) {
1081 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1083 for (auto const& i: _custom_languages) {
1084 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1086 for (auto const& initial: _initial_paths) {
1087 if (initial.second) {
1088 root->add_child(initial.first)->add_child_text(initial.second->string());
1091 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1092 root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0");
1093 root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0");
1094 root->add_child("DefaultKDMType")->add_child_text(dcp::formulation_to_string(_default_kdm_type));
1095 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1096 if (_last_release_notes_version) {
1097 root->add_child("LastReleaseNotesVersion")->add_child_text(*_last_release_notes_version);
1099 if (_main_divider_sash_position) {
1100 root->add_child("MainDividerSashPosition")->add_child_text(raw_convert<string>(*_main_divider_sash_position));
1102 if (_main_content_divider_sash_position) {
1103 root->add_child("MainContentDividerSashPosition")->add_child_text(raw_convert<string>(*_main_content_divider_sash_position));
1106 root->add_child("DefaultAddFileLocation")->add_child_text(
1107 _default_add_file_location == DefaultAddFileLocation::SAME_AS_LAST_TIME ? "last" : "project"
1110 /* [XML] AllowSMPTEBv20 1 to allow the user to choose SMPTE (Bv2.0 only) as a standard, otherwise 0 */
1111 root->add_child("AllowSMPTEBv20")->add_child_text(_allow_smpte_bv20 ? "1" : "0");
1112 /* [XML] ISDCFNamePartLength Maximum length of the "name" part of an ISDCF name, which should be 14 according to the standard */
1113 root->add_child("ISDCFNamePartLength")->add_child_text(raw_convert<string>(_isdcf_name_part_length));
1115 _export.write(root->add_child("Export"));
1117 auto target = config_write_file();
1120 auto const s = doc.write_to_string_formatted ();
1121 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1122 dcp::File f(tmp, "w");
1124 throw FileError (_("Could not open file for writing"), tmp);
1126 f.checked_write(s.c_str(), s.bytes());
1128 boost::filesystem::remove (target);
1129 boost::filesystem::rename (tmp, target);
1130 } catch (xmlpp::exception& e) {
1131 string s = e.what ();
1133 throw FileError (s, target);
1140 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1142 xmlpp::Document doc;
1143 auto root = doc.create_root_node (root_node);
1144 root->add_child("Version")->add_child_text(version);
1146 for (auto i: things) {
1147 i->as_xml (root->add_child(node));
1151 doc.write_to_file_formatted (file.string() + ".tmp");
1152 boost::filesystem::remove (file);
1153 boost::filesystem::rename (file.string() + ".tmp", file);
1154 } catch (xmlpp::exception& e) {
1155 string s = e.what ();
1157 throw FileError (s, file);
1163 Config::write_cinemas () const
1165 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1170 Config::write_dkdm_recipients () const
1172 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1176 boost::filesystem::path
1177 Config::default_directory_or (boost::filesystem::path a) const
1179 return directory_or (_default_directory, a);
1182 boost::filesystem::path
1183 Config::default_kdm_directory_or (boost::filesystem::path a) const
1185 return directory_or (_default_kdm_directory, a);
1188 boost::filesystem::path
1189 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1195 boost::system::error_code ec;
1196 auto const e = boost::filesystem::exists (*dir, ec);
1212 Config::changed (Property what)
1218 Config::set_kdm_email_to_default ()
1220 _kdm_subject = _("KDM delivery: $CPL_NAME");
1223 "Dear Projectionist\n\n"
1224 "Please find attached KDMs for $CPL_NAME.\n\n"
1225 "Cinema: $CINEMA_NAME\n"
1226 "Screen(s): $SCREENS\n\n"
1227 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1228 "Best regards,\nDCP-o-matic"
1233 Config::set_notification_email_to_default ()
1235 _notification_subject = _("DCP-o-matic notification");
1237 _notification_email = _(
1238 "$JOB_NAME: $JOB_STATUS"
1243 Config::reset_kdm_email ()
1245 set_kdm_email_to_default ();
1250 Config::reset_notification_email ()
1252 set_notification_email_to_default ();
1257 Config::set_cover_sheet_to_default ()
1261 "CPL Filename: $CPL_FILENAME\n"
1263 "Format: $CONTAINER\n"
1265 "Audio Language: $AUDIO_LANGUAGE\n"
1266 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1273 Config::add_to_history (boost::filesystem::path p)
1275 add_to_history_internal (_history, p);
1278 /** Remove non-existent items from the history */
1280 Config::clean_history ()
1282 clean_history_internal (_history);
1286 Config::add_to_player_history (boost::filesystem::path p)
1288 add_to_history_internal (_player_history, p);
1291 /** Remove non-existent items from the player history */
1293 Config::clean_player_history ()
1295 clean_history_internal (_player_history);
1299 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1301 /* Remove existing instances of this path in the history */
1302 h.erase (remove (h.begin(), h.end(), p), h.end ());
1304 h.insert (h.begin (), p);
1305 if (h.size() > HISTORY_SIZE) {
1313 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1319 if (boost::filesystem::is_directory(i)) {
1323 /* We couldn't find out if it's a directory for some reason; just ignore it */
1330 Config::have_existing (string file)
1332 return boost::filesystem::exists (read_path(file));
1337 Config::read_cinemas (cxml::Document const & f)
1340 for (auto i: f.node_children("Cinema")) {
1341 /* Slightly grotty two-part construction of Cinema here so that we can use
1344 auto cinema = make_shared<Cinema>(i);
1345 cinema->read_screens (i);
1346 _cinemas.push_back (cinema);
1351 Config::set_cinemas_file (boost::filesystem::path file)
1353 if (file == _cinemas_file) {
1357 _cinemas_file = file;
1359 if (boost::filesystem::exists (_cinemas_file)) {
1360 /* Existing file; read it in */
1361 cxml::Document f ("Cinemas");
1362 f.read_file (_cinemas_file);
1372 Config::read_dkdm_recipients (cxml::Document const & f)
1374 _dkdm_recipients.clear ();
1375 for (auto i: f.node_children("DKDMRecipient")) {
1376 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1382 Config::save_template (shared_ptr<const Film> film, string name) const
1384 film->write_template (template_write_path(name));
1389 Config::templates () const
1391 if (!boost::filesystem::exists(read_path("templates"))) {
1396 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1397 n.push_back (i.path().filename().string());
1400 /* Make sure that the default exists, and is the first thing on the list */
1401 n.erase(std::remove(n.begin(), n.end(), default_template_name()), n.end());
1402 n.insert(n.begin(), default_template_name());
1409 Config::existing_template (string name) const
1411 return name == default_template_name() || template_read_path(name);
1415 optional<boost::filesystem::path>
1416 Config::template_read_path (string name) const
1418 auto const path = read_path("templates") / tidy_for_filename(name);
1419 if (!boost::filesystem::exists(path)) {
1427 boost::filesystem::path
1428 Config::template_write_path (string name) const
1430 return write_path("templates") / tidy_for_filename (name);
1435 Config::rename_template (string old_name, string new_name) const
1437 if (auto read_path = template_read_path(old_name)) {
1438 boost::filesystem::rename(*read_path, template_write_path(new_name));
1443 Config::delete_template (string name) const
1445 boost::filesystem::remove (template_write_path(name));
1448 /** @return Path to the config.xml containing the actual settings, following a link if required */
1449 boost::filesystem::path
1450 config_file (boost::filesystem::path main)
1452 cxml::Document f ("Config");
1453 if (!boost::filesystem::exists (main)) {
1454 /* It doesn't exist, so there can't be any links; just return it */
1458 /* See if there's a link */
1461 auto link = f.optional_string_child("Link");
1465 } catch (xmlpp::exception& e) {
1466 /* There as a problem reading the main configuration file,
1467 so there can't be a link.
1475 boost::filesystem::path
1476 Config::config_read_file ()
1478 return config_file (read_path("config.xml"));
1482 boost::filesystem::path
1483 Config::config_write_file ()
1485 return config_file (write_path("config.xml"));
1490 Config::reset_cover_sheet ()
1492 set_cover_sheet_to_default ();
1497 Config::link (boost::filesystem::path new_file) const
1499 xmlpp::Document doc;
1500 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1502 doc.write_to_file_formatted(write_path("config.xml").string());
1503 } catch (xmlpp::exception& e) {
1504 string s = e.what ();
1506 throw FileError (s, write_path("config.xml"));
1511 Config::copy_and_link (boost::filesystem::path new_file) const
1514 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1519 Config::have_write_permission () const
1521 dcp::File f(config_write_file(), "r+");
1522 return static_cast<bool>(f);
1525 /** @param output_channels Number of output channels in use.
1526 * @return Audio mapping for this output channel count (may be a default).
1529 Config::audio_mapping (int output_channels)
1531 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1532 /* Set up a default */
1533 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1534 if (output_channels == 2) {
1535 /* Special case for stereo output.
1536 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1537 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1539 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1540 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1541 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1542 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1543 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1544 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1545 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1546 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1549 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1550 _audio_mapping->set (i, i, 1);
1555 return *_audio_mapping;
1559 Config::set_audio_mapping (AudioMapping m)
1562 changed (AUDIO_MAPPING);
1566 Config::set_audio_mapping_to_default ()
1568 DCPOMATIC_ASSERT (_audio_mapping);
1569 auto const ch = _audio_mapping->output_channels ();
1570 _audio_mapping = boost::none;
1571 _audio_mapping = audio_mapping (ch);
1572 changed (AUDIO_MAPPING);
1577 Config::add_custom_language (dcp::LanguageTag tag)
1579 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1580 _custom_languages.push_back (tag);
1586 optional<Config::BadReason>
1587 Config::check_certificates () const
1589 optional<BadReason> bad;
1591 for (auto const& i: _signer_chain->unordered()) {
1592 if (i.has_utf8_strings()) {
1593 bad = BAD_SIGNER_UTF8_STRINGS;
1595 if ((i.not_after().year() - i.not_before().year()) > 15) {
1596 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1600 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1601 bad = BAD_SIGNER_INCONSISTENT;
1604 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1605 bad = BAD_DECRYPTION_INCONSISTENT;
1613 save_all_config_as_zip (boost::filesystem::path zip_file)
1615 Zipper zipper (zip_file);
1617 auto config = Config::instance();
1618 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1619 if (boost::filesystem::exists(config->cinemas_file())) {
1620 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1622 if (boost::filesystem::exists(config->dkdm_recipients_file())) {
1623 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
1631 Config::set_initial_path(string id, boost::filesystem::path path)
1633 auto iter = _initial_paths.find(id);
1634 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1635 iter->second = path;
1640 optional<boost::filesystem::path>
1641 Config::initial_path(string id) const
1643 auto iter = _initial_paths.find(id);
1644 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1645 return iter->second;