2 Copyright (C) 2012-2022 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
23 #include "colour_conversion.h"
24 #include "compose.hpp"
26 #include "constants.h"
29 #include "dcp_content_type.h"
30 #include "dkdm_recipient.h"
31 #include "dkdm_wrapper.h"
37 #include <dcp/certificate_chain.h>
38 #include <dcp/name_format.h>
39 #include <dcp/raw_convert.h>
40 #include <libcxml/cxml.h>
42 #include <libxml++/libxml++.h>
43 #include <boost/filesystem.hpp>
44 #include <boost/algorithm/string.hpp>
45 #include <boost/thread.hpp>
54 using std::dynamic_pointer_cast;
57 using std::make_shared;
61 using std::shared_ptr;
64 using boost::algorithm::trim;
65 using boost::optional;
66 using dcp::raw_convert;
69 Config* Config::_instance = 0;
70 int const Config::_current_version = 3;
71 boost::signals2::signal<void (Config::LoadFailure)> Config::FailedToLoad;
72 boost::signals2::signal<void (string)> Config::Warning;
73 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
76 /** Construct default configuration */
78 /* DKDMs are not considered a thing to reset on set_defaults() */
79 : _dkdms (new DKDMGroup ("root"))
80 , _default_kdm_duration (1, RoughDuration::Unit::WEEKS)
87 Config::set_defaults ()
89 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
90 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
91 _server_port_base = 6192;
92 _use_any_servers = true;
94 _only_servers_encode = false;
95 _tms_protocol = FileTransferProtocol::SCP;
101 _allow_any_dcp_frame_rate = false;
102 _allow_any_container = false;
103 _allow_96khz_audio = false;
104 _use_all_audio_channels = false;
105 _show_experimental_audio_processors = false;
106 _language = optional<string> ();
107 _default_still_length = 10;
108 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
109 _default_dcp_audio_channels = 6;
110 _default_j2k_bandwidth = 150000000;
111 _default_audio_delay = 0;
112 _default_interop = false;
113 _default_metadata.clear ();
114 _upload_after_make_dcp = false;
117 _mail_protocol = EmailProtocol::AUTO;
123 _notification_from = "";
124 _notification_to = "";
125 _notification_cc.clear ();
126 _notification_bcc = "";
127 _check_for_updates = false;
128 _check_for_test_updates = false;
129 _maximum_j2k_bandwidth = 250000000;
130 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
131 _analyse_ebur128 = true;
132 _automatic_audio_analysis = false;
133 #ifdef DCPOMATIC_WINDOWS
134 _win32_console = false;
136 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
137 * ~/.config/dcpomatic2 (or equivalent) and written back there.
139 _cinemas_file = read_path ("cinemas.xml");
140 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
141 _show_hints_before_make_dcp = true;
142 _confirm_kdm_email = true;
143 _kdm_container_name_format = dcp::NameFormat ("KDM %f %c");
144 _kdm_filename_format = dcp::NameFormat ("KDM %f %c %s");
145 _dkdm_filename_format = dcp::NameFormat ("DKDM %f %c %s");
146 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
147 _dcp_asset_filename_format = dcp::NameFormat ("%t");
148 _jump_to_selected = true;
149 for (int i = 0; i < NAG_COUNT; ++i) {
153 _sound_output = optional<string> ();
154 _last_kdm_write_type = KDM_WRITE_FLAT;
155 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
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 _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 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
339 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
341 if (f.optional_string_child ("DCPMetadataIssuer")) {
342 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
343 } else if (f.optional_string_child ("DCPIssuer")) {
344 _dcp_issuer = f.string_child ("DCPIssuer");
347 auto up = f.optional_bool_child("UploadAfterMakeDCP");
349 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
351 _upload_after_make_dcp = up.get_value_or (false);
352 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
353 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
354 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
355 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
356 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
358 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
359 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
360 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
361 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
364 auto al = f.optional_string_child("DefaultAudioLanguage");
366 _default_audio_language = dcp::LanguageTag(*al);
368 } catch (std::runtime_error&) {}
371 auto te = f.optional_string_child("DefaultTerritory");
373 _default_territory = dcp::LanguageTag::RegionSubtag(*te);
375 } catch (std::runtime_error&) {}
377 for (auto const& i: f.node_children("DefaultMetadata")) {
378 _default_metadata[i->string_attribute("key")] = i->content();
381 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
383 /* Read any cinemas that are still lying around in the config file
384 * from an old version.
388 _mail_server = f.string_child ("MailServer");
389 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
392 /* Make sure this matches the code in write_config */
393 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
394 if (protocol == "Auto") {
395 _mail_protocol = EmailProtocol::AUTO;
396 } else if (protocol == "Plain") {
397 _mail_protocol = EmailProtocol::PLAIN;
398 } else if (protocol == "STARTTLS") {
399 _mail_protocol = EmailProtocol::STARTTLS;
400 } else if (protocol == "SSL") {
401 _mail_protocol = EmailProtocol::SSL;
405 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
406 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
408 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
409 _kdm_from = f.string_child ("KDMFrom");
410 for (auto i: f.node_children("KDMCC")) {
411 if (!i->content().empty()) {
412 _kdm_cc.push_back (i->content ());
415 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
416 _kdm_email = f.string_child ("KDMEmail");
418 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
419 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
420 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
421 for (auto i: f.node_children("NotificationCC")) {
422 if (!i->content().empty()) {
423 _notification_cc.push_back (i->content ());
426 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
427 if (f.optional_string_child("NotificationEmail")) {
428 _notification_email = f.string_child("NotificationEmail");
431 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
432 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
434 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
435 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
436 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
437 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
438 _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false);
439 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
441 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
442 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
443 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
444 #ifdef DCPOMATIC_WINDOWS
445 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
448 for (auto i: f.node_children("History")) {
449 _history.push_back (i->content ());
452 for (auto i: f.node_children("PlayerHistory")) {
453 _player_history.push_back (i->content ());
456 auto signer = f.optional_node_child ("Signer");
458 auto c = make_shared<dcp::CertificateChain>();
459 /* Read the signing certificates and private key in from the config file */
460 for (auto i: signer->node_children ("Certificate")) {
461 c->add (dcp::Certificate (i->content ()));
463 c->set_key (signer->string_child ("PrivateKey"));
466 /* Make a new set of signing certificates and key */
467 _signer_chain = create_certificate_chain ();
470 auto decryption = f.optional_node_child ("Decryption");
472 auto c = make_shared<dcp::CertificateChain>();
473 for (auto i: decryption->node_children ("Certificate")) {
474 c->add (dcp::Certificate (i->content ()));
476 c->set_key (decryption->string_child ("PrivateKey"));
477 _decryption_chain = c;
479 _decryption_chain = create_certificate_chain ();
482 /* These must be done before we call Bad as that might set one
485 for (auto i: f.node_children("Nagged")) {
486 auto const id = i->number_attribute<int>("Id");
487 if (id >= 0 && id < NAG_COUNT) {
488 _nagged[id] = raw_convert<int>(i->content());
492 auto bad = check_certificates ();
494 auto const remake = Bad(*bad);
495 if (remake && *remake) {
497 case BAD_SIGNER_UTF8_STRINGS:
498 case BAD_SIGNER_INCONSISTENT:
499 case BAD_SIGNER_VALIDITY_TOO_LONG:
500 _signer_chain = create_certificate_chain ();
502 case BAD_DECRYPTION_INCONSISTENT:
503 _decryption_chain = create_certificate_chain ();
509 if (f.optional_node_child("DKDMGroup")) {
510 /* New-style: all DKDMs in a group */
511 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
513 /* Old-style: one or more DKDM nodes */
514 _dkdms = make_shared<DKDMGroup>("root");
515 for (auto i: f.node_children("DKDM")) {
516 _dkdms->add (DKDMBase::read (i));
519 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
520 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
521 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
522 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
523 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
524 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
525 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
526 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
527 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
528 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
529 /* The variable was renamed but not the XML tag */
530 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
531 _sound_output = f.optional_string_child("PreviewSoundOutput");
532 if (f.optional_string_child("CoverSheet")) {
533 _cover_sheet = f.optional_string_child("CoverSheet").get();
535 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
536 if (f.optional_string_child("LastKDMWriteType")) {
537 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
538 _last_kdm_write_type = KDM_WRITE_FLAT;
539 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
540 _last_kdm_write_type = KDM_WRITE_FOLDER;
541 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
542 _last_kdm_write_type = KDM_WRITE_ZIP;
545 if (f.optional_string_child("LastDKDMWriteType")) {
546 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
547 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
548 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
549 _last_dkdm_write_type = DKDM_WRITE_FILE;
552 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
553 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
554 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
556 for (auto i: f.node_children("Notification")) {
557 int const id = i->number_attribute<int>("Id");
558 if (id >= 0 && id < NOTIFICATION_COUNT) {
559 _notification[id] = raw_convert<int>(i->content());
563 _barco_username = f.optional_string_child("BarcoUsername");
564 _barco_password = f.optional_string_child("BarcoPassword");
565 _christie_username = f.optional_string_child("ChristieUsername");
566 _christie_password = f.optional_string_child("ChristiePassword");
567 _gdc_username = f.optional_string_child("GDCUsername");
568 _gdc_password = f.optional_string_child("GDCPassword");
570 auto pm = f.optional_string_child("PlayerMode");
571 if (pm && *pm == "window") {
572 _player_mode = PLAYER_MODE_WINDOW;
573 } else if (pm && *pm == "full") {
574 _player_mode = PLAYER_MODE_FULL;
575 } else if (pm && *pm == "dual") {
576 _player_mode = PLAYER_MODE_DUAL;
579 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
580 auto vc = f.optional_string_child("VideoViewType");
581 if (vc && *vc == "opengl") {
582 _video_view_type = VIDEO_VIEW_OPENGL;
583 } else if (vc && *vc == "simple") {
584 _video_view_type = VIDEO_VIEW_SIMPLE;
586 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
587 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
588 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
589 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
590 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
592 if (f.optional_node_child("AudioMapping")) {
593 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
596 for (auto i: f.node_children("CustomLanguage")) {
598 /* This will fail if it's called before dcp::init() as it won't recognise the
599 * tag. That's OK because the Config will be reloaded again later.
601 _custom_languages.push_back (dcp::LanguageTag(i->content()));
602 } catch (std::runtime_error& e) {}
605 for (auto& initial: _initial_paths) {
606 initial.second = f.optional_string_child(initial.first);
608 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
609 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
610 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
611 _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
612 if (auto duration = f.optional_node_child("DefaultKDMDuration")) {
613 _default_kdm_duration = RoughDuration(duration);
615 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
617 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
618 _last_release_notes_version = f.optional_string_child("LastReleaseNotesVersion");
619 _main_divider_sash_position = f.optional_number_child<int>("MainDividerSashPosition");
620 _main_content_divider_sash_position = f.optional_number_child<int>("MainContentDividerSashPosition");
622 _export.read(f.optional_node_child("Export"));
625 if (have_existing("config.xml")) {
627 /* We have a config file but it didn't load */
628 FailedToLoad(LoadFailure::CONFIG);
631 /* Make a new set of signing certificates and key */
632 _signer_chain = create_certificate_chain ();
633 /* And similar for decryption of KDMs */
634 _decryption_chain = create_certificate_chain ();
640 Config::read_cinemas()
642 if (boost::filesystem::exists (_cinemas_file)) {
644 cxml::Document f("Cinemas");
645 f.read_file(_cinemas_file);
649 FailedToLoad(LoadFailure::CINEMAS);
657 Config::read_dkdm_recipients()
659 if (boost::filesystem::exists (_dkdm_recipients_file)) {
661 cxml::Document f("DKDMRecipients");
662 f.read_file(_dkdm_recipients_file);
663 read_dkdm_recipients(f);
666 FailedToLoad(LoadFailure::DKDM_RECIPIENTS);
667 write_dkdm_recipients();
673 /** @return Singleton instance */
677 if (_instance == nullptr) {
678 _instance = new Config;
685 /** Write our configuration to disk */
687 Config::write () const
691 write_dkdm_recipients ();
695 Config::write_config () const
698 auto root = doc.create_root_node ("Config");
700 /* [XML] Version The version number of the configuration file format. */
701 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
702 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
703 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
704 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
705 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
706 if (_default_directory) {
707 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
708 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
710 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
711 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
712 by the batch converter to listen for job requests.
714 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
715 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
716 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
718 for (auto i: _servers) {
719 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
722 root->add_child("Server")->add_child_text (i);
725 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
726 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
728 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
729 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
730 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
731 /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */
732 root->add_child("TMSPassive")->add_child_text(_tms_passive ? "1" : "0");
733 /* [XML] TMSIP IP address of TMS. */
734 root->add_child("TMSIP")->add_child_text (_tms_ip);
735 /* [XML] TMSPath Path on the TMS to copy files to. */
736 root->add_child("TMSPath")->add_child_text (_tms_path);
737 /* [XML] TMSUser Username to log into the TMS with. */
738 root->add_child("TMSUser")->add_child_text (_tms_user);
739 /* [XML] TMSPassword Password to log into the TMS with. */
740 root->add_child("TMSPassword")->add_child_text (_tms_password);
742 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
743 root->add_child("Language")->add_child_text (_language.get());
745 if (_default_dcp_content_type) {
746 /* [XML:opt] DefaultDCPContentType Default content type to use when creating new films (<code>FTR</code>, <code>SHR</code>,
747 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
748 <code>PSA</code> or <code>ADV</code>). */
749 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
751 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
752 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
753 /* [XML] DCPIssuer Issuer text to write into CPL files. */
754 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
755 /* [XML] DCPCreator Creator text to write into CPL files. */
756 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
757 /* [XML] Company name to write into MXF files. */
758 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
759 /* [XML] Product name to write into MXF files. */
760 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
761 /* [XML] Product version to write into MXF files. */
762 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
763 /* [XML] Comment to write into JPEG2000 data. */
764 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
765 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
766 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
768 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
769 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
770 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
771 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
772 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
773 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
774 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
775 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
776 if (_default_audio_language) {
777 /* [XML] DefaultAudioLanguage Default audio language to use for new films */
778 root->add_child("DefaultAudioLanguage")->add_child_text(_default_audio_language->to_string());
780 if (_default_territory) {
781 /* [XML] DefaultTerritory Default territory to use for new films */
782 root->add_child("DefaultTerritory")->add_child_text(_default_territory->subtag());
784 for (auto const& i: _default_metadata) {
785 auto c = root->add_child("DefaultMetadata");
786 c->set_attribute("key", i.first);
787 c->add_child_text(i.second);
789 if (_default_kdm_directory) {
790 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
791 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
793 _default_kdm_duration.as_xml(root->add_child("DefaultKDMDuration"));
794 /* [XML] MailServer Hostname of SMTP server to use. */
795 root->add_child("MailServer")->add_child_text (_mail_server);
796 /* [XML] MailPort Port number to use on SMTP server. */
797 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
798 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
799 switch (_mail_protocol) {
800 case EmailProtocol::AUTO:
801 root->add_child("MailProtocol")->add_child_text("Auto");
803 case EmailProtocol::PLAIN:
804 root->add_child("MailProtocol")->add_child_text("Plain");
806 case EmailProtocol::STARTTLS:
807 root->add_child("MailProtocol")->add_child_text("STARTTLS");
809 case EmailProtocol::SSL:
810 root->add_child("MailProtocol")->add_child_text("SSL");
813 /* [XML] MailUser Username to use on SMTP server. */
814 root->add_child("MailUser")->add_child_text (_mail_user);
815 /* [XML] MailPassword Password to use on SMTP server. */
816 root->add_child("MailPassword")->add_child_text (_mail_password);
818 /* [XML] KDMSubject Subject to use for KDM emails. */
819 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
820 /* [XML] KDMFrom From address to use for KDM emails. */
821 root->add_child("KDMFrom")->add_child_text (_kdm_from);
822 for (auto i: _kdm_cc) {
823 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
824 root->add_child("KDMCC")->add_child_text (i);
826 /* [XML] KDMBCC BCC address to use for KDM emails. */
827 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
828 /* [XML] KDMEmail Text of KDM email. */
829 root->add_child("KDMEmail")->add_child_text (_kdm_email);
831 /* [XML] NotificationSubject Subject to use for notification emails. */
832 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
833 /* [XML] NotificationFrom From address to use for notification emails. */
834 root->add_child("NotificationFrom")->add_child_text (_notification_from);
835 /* [XML] NotificationFrom To address to use for notification emails. */
836 root->add_child("NotificationTo")->add_child_text (_notification_to);
837 for (auto i: _notification_cc) {
838 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
839 root->add_child("NotificationCC")->add_child_text (i);
841 /* [XML] NotificationBCC BCC address to use for notification emails. */
842 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
843 /* [XML] NotificationEmail Text of notification email. */
844 root->add_child("NotificationEmail")->add_child_text (_notification_email);
846 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
847 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
848 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
849 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
851 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
852 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
853 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
854 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
855 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
856 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
857 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
858 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
859 /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
860 root->add_child("UseAllAudioChannels")->add_child_text(_use_all_audio_channels ? "1" : "0");
861 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
862 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
863 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
864 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
865 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
866 related to the player, 1024 debug information related to audio analyses.
868 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
869 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
870 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
871 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
872 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
873 #ifdef DCPOMATIC_WINDOWS
874 if (_win32_console) {
875 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
876 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
877 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
879 root->add_child("Win32Console")->add_child_text ("1");
883 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
884 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
886 auto signer = root->add_child ("Signer");
887 DCPOMATIC_ASSERT (_signer_chain);
888 for (auto const& i: _signer_chain->unordered()) {
889 signer->add_child("Certificate")->add_child_text (i.certificate (true));
891 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
893 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
894 auto decryption = root->add_child ("Decryption");
895 DCPOMATIC_ASSERT (_decryption_chain);
896 for (auto const& i: _decryption_chain->unordered()) {
897 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
899 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
901 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
904 for (auto i: _history) {
905 root->add_child("History")->add_child_text (i.string ());
908 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
911 for (auto i: _player_history) {
912 root->add_child("PlayerHistory")->add_child_text (i.string ());
915 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
916 or <code><DKDM></code> tags.
918 /* [XML] DKDM A DKDM as XML */
919 _dkdms->as_xml (root);
921 /* [XML] CinemasFile Filename of cinemas list file. */
922 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
923 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
924 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
925 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
926 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
927 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
928 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
929 /* [XML] KDMFilenameFormat Format for KDM filenames. */
930 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
931 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
932 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
933 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
934 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
935 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
936 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
937 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
938 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
939 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
940 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
941 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
942 for (int i = 0; i < NAG_COUNT; ++i) {
943 xmlpp::Element* e = root->add_child ("Nagged");
944 e->set_attribute ("Id", raw_convert<string>(i));
945 e->add_child_text (_nagged[i] ? "1" : "0");
947 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
948 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
950 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
951 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
953 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
954 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
955 if (_last_player_load_directory) {
956 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
958 /* [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. */
959 if (_last_kdm_write_type) {
960 switch (_last_kdm_write_type.get()) {
962 root->add_child("LastKDMWriteType")->add_child_text("flat");
964 case KDM_WRITE_FOLDER:
965 root->add_child("LastKDMWriteType")->add_child_text("folder");
968 root->add_child("LastKDMWriteType")->add_child_text("zip");
972 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
973 if (_last_dkdm_write_type) {
974 switch (_last_dkdm_write_type.get()) {
975 case DKDM_WRITE_INTERNAL:
976 root->add_child("LastDKDMWriteType")->add_child_text("internal");
978 case DKDM_WRITE_FILE:
979 root->add_child("LastDKDMWriteType")->add_child_text("file");
983 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
984 frames to be held in memory at once.
986 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
988 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
989 if (_decode_reduction) {
990 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
993 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
994 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
996 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
997 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
998 xmlpp::Element* e = root->add_child ("Notification");
999 e->set_attribute ("Id", raw_convert<string>(i));
1000 e->add_child_text (_notification[i] ? "1" : "0");
1003 if (_barco_username) {
1004 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
1005 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
1007 if (_barco_password) {
1008 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
1009 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
1012 if (_christie_username) {
1013 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
1014 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
1016 if (_christie_password) {
1017 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
1018 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
1021 if (_gdc_username) {
1022 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
1023 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
1025 if (_gdc_password) {
1026 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
1027 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
1030 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
1031 with controls on another monitor.
1033 switch (_player_mode) {
1034 case PLAYER_MODE_WINDOW:
1035 root->add_child("PlayerMode")->add_child_text("window");
1037 case PLAYER_MODE_FULL:
1038 root->add_child("PlayerMode")->add_child_text("full");
1040 case PLAYER_MODE_DUAL:
1041 root->add_child("PlayerMode")->add_child_text("dual");
1045 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
1046 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
1047 switch (_video_view_type) {
1048 case VIDEO_VIEW_SIMPLE:
1049 root->add_child("VideoViewType")->add_child_text("simple");
1051 case VIDEO_VIEW_OPENGL:
1052 root->add_child("VideoViewType")->add_child_text("opengl");
1055 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1056 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
1057 if (_player_debug_log_file) {
1058 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1059 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
1061 if (_player_content_directory) {
1062 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1063 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
1065 if (_player_playlist_directory) {
1066 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1067 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1069 if (_player_kdm_directory) {
1070 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1071 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1073 if (_audio_mapping) {
1074 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1076 for (auto const& i: _custom_languages) {
1077 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1079 for (auto const& initial: _initial_paths) {
1080 if (initial.second) {
1081 root->add_child(initial.first)->add_child_text(initial.second->string());
1084 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1085 root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0");
1086 root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0");
1087 root->add_child("DefaultKDMType")->add_child_text(dcp::formulation_to_string(_default_kdm_type));
1088 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1089 if (_last_release_notes_version) {
1090 root->add_child("LastReleaseNotesVersion")->add_child_text(*_last_release_notes_version);
1092 if (_main_divider_sash_position) {
1093 root->add_child("MainDividerSashPosition")->add_child_text(raw_convert<string>(*_main_divider_sash_position));
1095 if (_main_content_divider_sash_position) {
1096 root->add_child("MainContentDividerSashPosition")->add_child_text(raw_convert<string>(*_main_content_divider_sash_position));
1099 _export.write(root->add_child("Export"));
1101 auto target = config_write_file();
1104 auto const s = doc.write_to_string_formatted ();
1105 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1106 dcp::File f(tmp, "w");
1108 throw FileError (_("Could not open file for writing"), tmp);
1110 f.checked_write(s.c_str(), s.bytes());
1112 boost::filesystem::remove (target);
1113 boost::filesystem::rename (tmp, target);
1114 } catch (xmlpp::exception& e) {
1115 string s = e.what ();
1117 throw FileError (s, target);
1124 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1126 xmlpp::Document doc;
1127 auto root = doc.create_root_node (root_node);
1128 root->add_child("Version")->add_child_text(version);
1130 for (auto i: things) {
1131 i->as_xml (root->add_child(node));
1135 doc.write_to_file_formatted (file.string() + ".tmp");
1136 boost::filesystem::remove (file);
1137 boost::filesystem::rename (file.string() + ".tmp", file);
1138 } catch (xmlpp::exception& e) {
1139 string s = e.what ();
1141 throw FileError (s, file);
1147 Config::write_cinemas () const
1149 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1154 Config::write_dkdm_recipients () const
1156 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1160 boost::filesystem::path
1161 Config::default_directory_or (boost::filesystem::path a) const
1163 return directory_or (_default_directory, a);
1166 boost::filesystem::path
1167 Config::default_kdm_directory_or (boost::filesystem::path a) const
1169 return directory_or (_default_kdm_directory, a);
1172 boost::filesystem::path
1173 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1179 boost::system::error_code ec;
1180 auto const e = boost::filesystem::exists (*dir, ec);
1196 Config::changed (Property what)
1202 Config::set_kdm_email_to_default ()
1204 _kdm_subject = _("KDM delivery: $CPL_NAME");
1207 "Dear Projectionist\n\n"
1208 "Please find attached KDMs for $CPL_NAME.\n\n"
1209 "Cinema: $CINEMA_NAME\n"
1210 "Screen(s): $SCREENS\n\n"
1211 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1212 "Best regards,\nDCP-o-matic"
1217 Config::set_notification_email_to_default ()
1219 _notification_subject = _("DCP-o-matic notification");
1221 _notification_email = _(
1222 "$JOB_NAME: $JOB_STATUS"
1227 Config::reset_kdm_email ()
1229 set_kdm_email_to_default ();
1234 Config::reset_notification_email ()
1236 set_notification_email_to_default ();
1241 Config::set_cover_sheet_to_default ()
1245 "CPL Filename: $CPL_FILENAME\n"
1247 "Format: $CONTAINER\n"
1249 "Audio Language: $AUDIO_LANGUAGE\n"
1250 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1257 Config::add_to_history (boost::filesystem::path p)
1259 add_to_history_internal (_history, p);
1262 /** Remove non-existent items from the history */
1264 Config::clean_history ()
1266 clean_history_internal (_history);
1270 Config::add_to_player_history (boost::filesystem::path p)
1272 add_to_history_internal (_player_history, p);
1275 /** Remove non-existant items from the player history */
1277 Config::clean_player_history ()
1279 clean_history_internal (_player_history);
1283 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1285 /* Remove existing instances of this path in the history */
1286 h.erase (remove (h.begin(), h.end(), p), h.end ());
1288 h.insert (h.begin (), p);
1289 if (h.size() > HISTORY_SIZE) {
1297 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1303 if (boost::filesystem::is_directory(i)) {
1307 /* We couldn't find out if it's a directory for some reason; just ignore it */
1314 Config::have_existing (string file)
1316 return boost::filesystem::exists (read_path(file));
1321 Config::read_cinemas (cxml::Document const & f)
1324 for (auto i: f.node_children("Cinema")) {
1325 /* Slightly grotty two-part construction of Cinema here so that we can use
1328 auto cinema = make_shared<Cinema>(i);
1329 cinema->read_screens (i);
1330 _cinemas.push_back (cinema);
1335 Config::set_cinemas_file (boost::filesystem::path file)
1337 if (file == _cinemas_file) {
1341 _cinemas_file = file;
1343 if (boost::filesystem::exists (_cinemas_file)) {
1344 /* Existing file; read it in */
1345 cxml::Document f ("Cinemas");
1346 f.read_file (_cinemas_file);
1356 Config::read_dkdm_recipients (cxml::Document const & f)
1358 _dkdm_recipients.clear ();
1359 for (auto i: f.node_children("DKDMRecipient")) {
1360 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1366 Config::save_template (shared_ptr<const Film> film, string name) const
1368 film->write_template (template_write_path(name));
1373 Config::templates () const
1375 if (!boost::filesystem::exists(read_path("templates"))) {
1380 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1381 n.push_back (i.path().filename().string());
1387 Config::existing_template (string name) const
1389 return boost::filesystem::exists (template_read_path(name));
1393 boost::filesystem::path
1394 Config::template_read_path (string name) const
1396 return read_path("templates") / tidy_for_filename (name);
1400 boost::filesystem::path
1401 Config::template_write_path (string name) const
1403 return write_path("templates") / tidy_for_filename (name);
1408 Config::rename_template (string old_name, string new_name) const
1410 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1414 Config::delete_template (string name) const
1416 boost::filesystem::remove (template_write_path(name));
1419 /** @return Path to the config.xml containing the actual settings, following a link if required */
1420 boost::filesystem::path
1421 config_file (boost::filesystem::path main)
1423 cxml::Document f ("Config");
1424 if (!boost::filesystem::exists (main)) {
1425 /* It doesn't exist, so there can't be any links; just return it */
1429 /* See if there's a link */
1432 auto link = f.optional_string_child("Link");
1436 } catch (xmlpp::exception& e) {
1437 /* There as a problem reading the main configuration file,
1438 so there can't be a link.
1446 boost::filesystem::path
1447 Config::config_read_file ()
1449 return config_file (read_path("config.xml"));
1453 boost::filesystem::path
1454 Config::config_write_file ()
1456 return config_file (write_path("config.xml"));
1461 Config::reset_cover_sheet ()
1463 set_cover_sheet_to_default ();
1468 Config::link (boost::filesystem::path new_file) const
1470 xmlpp::Document doc;
1471 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1473 doc.write_to_file_formatted(write_path("config.xml").string());
1474 } catch (xmlpp::exception& e) {
1475 string s = e.what ();
1477 throw FileError (s, write_path("config.xml"));
1482 Config::copy_and_link (boost::filesystem::path new_file) const
1485 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1490 Config::have_write_permission () const
1492 dcp::File f(config_write_file(), "r+");
1493 return static_cast<bool>(f);
1496 /** @param output_channels Number of output channels in use.
1497 * @return Audio mapping for this output channel count (may be a default).
1500 Config::audio_mapping (int output_channels)
1502 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1503 /* Set up a default */
1504 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1505 if (output_channels == 2) {
1506 /* Special case for stereo output.
1507 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1508 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1510 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1511 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1512 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1513 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1514 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1515 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1516 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1517 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1520 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1521 _audio_mapping->set (i, i, 1);
1526 return *_audio_mapping;
1530 Config::set_audio_mapping (AudioMapping m)
1533 changed (AUDIO_MAPPING);
1537 Config::set_audio_mapping_to_default ()
1539 DCPOMATIC_ASSERT (_audio_mapping);
1540 auto const ch = _audio_mapping->output_channels ();
1541 _audio_mapping = boost::none;
1542 _audio_mapping = audio_mapping (ch);
1543 changed (AUDIO_MAPPING);
1548 Config::add_custom_language (dcp::LanguageTag tag)
1550 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1551 _custom_languages.push_back (tag);
1557 optional<Config::BadReason>
1558 Config::check_certificates () const
1560 optional<BadReason> bad;
1562 for (auto const& i: _signer_chain->unordered()) {
1563 if (i.has_utf8_strings()) {
1564 bad = BAD_SIGNER_UTF8_STRINGS;
1566 if ((i.not_after().year() - i.not_before().year()) > 15) {
1567 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1571 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1572 bad = BAD_SIGNER_INCONSISTENT;
1575 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1576 bad = BAD_DECRYPTION_INCONSISTENT;
1584 save_all_config_as_zip (boost::filesystem::path zip_file)
1586 Zipper zipper (zip_file);
1588 auto config = Config::instance();
1589 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1590 if (boost::filesystem::exists(config->cinemas_file())) {
1591 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1593 if (boost::filesystem::exists(config->dkdm_recipients_file())) {
1594 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
1602 Config::set_initial_path(string id, boost::filesystem::path path)
1604 auto iter = _initial_paths.find(id);
1605 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1606 iter->second = path;
1611 optional<boost::filesystem::path>
1612 Config::initial_path(string id) const
1614 auto iter = _initial_paths.find(id);
1615 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1616 return iter->second;