2 Copyright (C) 2012-2022 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
23 #include "colour_conversion.h"
24 #include "compose.hpp"
26 #include "constants.h"
28 #include "dcp_content_type.h"
29 #include "dkdm_recipient.h"
30 #include "dkdm_wrapper.h"
36 #include <dcp/certificate_chain.h>
37 #include <dcp/name_format.h>
38 #include <dcp/raw_convert.h>
39 #include <libcxml/cxml.h>
41 #include <libxml++/libxml++.h>
42 #include <boost/filesystem.hpp>
43 #include <boost/algorithm/string.hpp>
44 #include <boost/thread.hpp>
53 using std::dynamic_pointer_cast;
56 using std::make_shared;
60 using std::shared_ptr;
63 using boost::algorithm::trim;
64 using boost::optional;
65 using dcp::raw_convert;
68 Config* Config::_instance = 0;
69 int const Config::_current_version = 3;
70 boost::signals2::signal<void (Config::LoadFailure)> Config::FailedToLoad;
71 boost::signals2::signal<void (string)> Config::Warning;
72 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
75 /** Construct default configuration */
77 /* DKDMs are not considered a thing to reset on set_defaults() */
78 : _dkdms (new DKDMGroup ("root"))
79 , _default_kdm_duration (1, RoughDuration::Unit::WEEKS)
86 Config::set_defaults ()
88 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
89 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
90 _server_port_base = 6192;
91 _use_any_servers = true;
93 _only_servers_encode = false;
94 _tms_protocol = FileTransferProtocol::SCP;
100 _allow_any_dcp_frame_rate = false;
101 _allow_any_container = false;
102 _allow_96khz_audio = false;
103 _use_all_audio_channels = false;
104 _show_experimental_audio_processors = false;
105 _language = optional<string> ();
106 _default_still_length = 10;
107 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
108 _default_dcp_audio_channels = 8;
109 _default_j2k_bandwidth = 150000000;
110 _default_audio_delay = 0;
111 _default_interop = false;
112 _default_metadata.clear ();
113 _upload_after_make_dcp = false;
116 _mail_protocol = EmailProtocol::AUTO;
122 _notification_from = "";
123 _notification_to = "";
124 _notification_cc.clear ();
125 _notification_bcc = "";
126 _check_for_updates = false;
127 _check_for_test_updates = false;
128 _maximum_j2k_bandwidth = 250000000;
129 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
130 _analyse_ebur128 = true;
131 _automatic_audio_analysis = false;
132 #ifdef DCPOMATIC_WINDOWS
133 _win32_console = false;
135 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
136 * ~/.config/dcpomatic2 (or equivalent) and written back there.
138 _cinemas_file = read_path ("cinemas.xml");
139 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
140 _show_hints_before_make_dcp = true;
141 _confirm_kdm_email = true;
142 _kdm_container_name_format = dcp::NameFormat("KDM_%f_%c");
143 _kdm_filename_format = dcp::NameFormat("KDM_%f_%c_%s");
144 _dkdm_filename_format = dcp::NameFormat("DKDM_%f_%c_%s");
145 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
146 _dcp_asset_filename_format = dcp::NameFormat ("%t");
147 _jump_to_selected = true;
148 for (int i = 0; i < NAG_COUNT; ++i) {
152 _sound_output = optional<string> ();
153 _last_kdm_write_type = KDM_WRITE_FLAT;
154 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
155 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
157 /* I think the scaling factor here should be the ratio of the longest frame
158 encode time to the shortest; if the thread count is T, longest time is L
159 and the shortest time S we could encode L/S frames per thread whilst waiting
160 for the L frame to encode so we might have to store LT/S frames.
162 However we don't want to use too much memory, so keep it a bit lower than we'd
163 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
164 use about 240Mb with 72 encoding threads.
166 _frames_in_memory_multiplier = 3;
167 _decode_reduction = optional<int>();
168 _default_notify = false;
169 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
170 _notification[i] = false;
172 _barco_username = optional<string>();
173 _barco_password = optional<string>();
174 _christie_username = optional<string>();
175 _christie_password = optional<string>();
176 _gdc_username = optional<string>();
177 _gdc_password = optional<string>();
178 _player_mode = PLAYER_MODE_WINDOW;
180 _video_view_type = VIDEO_VIEW_SIMPLE;
181 _respect_kdm_validity_periods = true;
182 _player_debug_log_file = boost::none;
183 _player_content_directory = boost::none;
184 _player_playlist_directory = boost::none;
185 _player_kdm_directory = boost::none;
186 _audio_mapping = boost::none;
187 _custom_languages.clear ();
188 _initial_paths.clear();
189 _initial_paths["AddFilesPath"] = boost::none;
190 _initial_paths["AddKDMPath"] = boost::none;
191 _initial_paths["AddDKDMPath"] = boost::none;
192 _initial_paths["SelectCertificatePath"] = boost::none;
193 _initial_paths["AddCombinerInputPath"] = boost::none;
194 _use_isdcf_name_by_default = true;
195 _write_kdms_to_disk = true;
197 _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
198 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
199 _auto_crop_threshold = 0.1;
200 _last_release_notes_version = boost::none;
201 _allow_smpte_bv20 = false;
202 _isdcf_name_part_length = 14;
204 _allowed_dcp_frame_rates.clear ();
205 _allowed_dcp_frame_rates.push_back (24);
206 _allowed_dcp_frame_rates.push_back (25);
207 _allowed_dcp_frame_rates.push_back (30);
208 _allowed_dcp_frame_rates.push_back (48);
209 _allowed_dcp_frame_rates.push_back (50);
210 _allowed_dcp_frame_rates.push_back (60);
212 set_kdm_email_to_default ();
213 set_notification_email_to_default ();
214 set_cover_sheet_to_default ();
216 _gpu_binary_location = "";
219 _gpu_license_server = "";
220 _gpu_license_port = 5000;
223 _main_divider_sash_position = {};
224 _main_content_divider_sash_position = {};
226 _export.set_defaults();
230 Config::restore_defaults ()
232 Config::instance()->set_defaults ();
233 Config::instance()->changed ();
236 shared_ptr<dcp::CertificateChain>
237 Config::create_certificate_chain ()
239 return make_shared<dcp::CertificateChain> (
241 CERTIFICATE_VALIDITY_PERIOD,
244 ".dcpomatic.smpte-430-2.ROOT",
245 ".dcpomatic.smpte-430-2.INTERMEDIATE",
246 "CS.dcpomatic.smpte-430-2.LEAF"
253 using namespace boost::filesystem;
255 auto copy_adding_number = [](path const& path_to_copy) {
257 auto add_number = [](path const& path, int number) {
258 return String::compose("%1.%2", path, number);
262 while (n < 100 && exists(add_number(path_to_copy, n))) {
265 boost::system::error_code ec;
266 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
269 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
270 * to write over. This is more intended for the situation where we have a corrupted config.xml,
271 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
272 * file). But we might as well back up the other files while we're about it.
275 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
276 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
278 copy_adding_number (config_write_file());
280 /* These do not use State::write_path, so whatever path is in the Config we will copy
283 copy_adding_number (_cinemas_file);
284 copy_adding_number (_dkdm_recipients_file);
292 read_dkdm_recipients();
297 Config::read_config()
300 cxml::Document f ("Config");
301 f.read_file(dcp::filesystem::fix_long_path(config_read_file()));
303 auto version = f.optional_number_child<int> ("Version");
304 if (version && *version < _current_version) {
305 /* Back up the old config before we re-write it in a back-incompatible way */
309 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
310 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
312 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
313 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
316 _default_directory = f.optional_string_child ("DefaultDirectory");
317 if (_default_directory && _default_directory->empty ()) {
318 /* We used to store an empty value for this to mean "none set" */
319 _default_directory = boost::optional<boost::filesystem::path> ();
322 auto b = f.optional_number_child<int> ("ServerPort");
324 b = f.optional_number_child<int> ("ServerPortBase");
326 _server_port_base = b.get ();
328 auto u = f.optional_bool_child ("UseAnyServers");
329 _use_any_servers = u.get_value_or (true);
331 for (auto i: f.node_children("Server")) {
332 if (i->node_children("HostName").size() == 1) {
333 _servers.push_back (i->string_child ("HostName"));
335 _servers.push_back (i->content ());
339 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
340 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
341 _tms_passive = f.optional_bool_child("TMSPassive").get_value_or(true);
342 _tms_ip = f.string_child ("TMSIP");
343 _tms_path = f.string_child ("TMSPath");
344 _tms_user = f.string_child ("TMSUser");
345 _tms_password = f.string_child ("TMSPassword");
347 _language = f.optional_string_child ("Language");
349 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
350 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
352 if (f.optional_string_child ("DCPMetadataIssuer")) {
353 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
354 } else if (f.optional_string_child ("DCPIssuer")) {
355 _dcp_issuer = f.string_child ("DCPIssuer");
358 auto up = f.optional_bool_child("UploadAfterMakeDCP");
360 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
362 _upload_after_make_dcp = up.get_value_or (false);
363 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
364 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
365 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
366 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
367 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
369 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
370 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
371 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
372 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
375 auto al = f.optional_string_child("DefaultAudioLanguage");
377 _default_audio_language = dcp::LanguageTag(*al);
379 } catch (std::runtime_error&) {}
382 auto te = f.optional_string_child("DefaultTerritory");
384 _default_territory = dcp::LanguageTag::RegionSubtag(*te);
386 } catch (std::runtime_error&) {}
388 for (auto const& i: f.node_children("DefaultMetadata")) {
389 _default_metadata[i->string_attribute("key")] = i->content();
392 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
394 /* Read any cinemas that are still lying around in the config file
395 * from an old version.
399 _mail_server = f.string_child ("MailServer");
400 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
403 /* Make sure this matches the code in write_config */
404 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
405 if (protocol == "Auto") {
406 _mail_protocol = EmailProtocol::AUTO;
407 } else if (protocol == "Plain") {
408 _mail_protocol = EmailProtocol::PLAIN;
409 } else if (protocol == "STARTTLS") {
410 _mail_protocol = EmailProtocol::STARTTLS;
411 } else if (protocol == "SSL") {
412 _mail_protocol = EmailProtocol::SSL;
416 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
417 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
419 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
420 _kdm_from = f.string_child ("KDMFrom");
421 for (auto i: f.node_children("KDMCC")) {
422 if (!i->content().empty()) {
423 _kdm_cc.push_back (i->content ());
426 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
427 _kdm_email = f.string_child ("KDMEmail");
429 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
430 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
431 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
432 for (auto i: f.node_children("NotificationCC")) {
433 if (!i->content().empty()) {
434 _notification_cc.push_back (i->content ());
437 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
438 if (f.optional_string_child("NotificationEmail")) {
439 _notification_email = f.string_child("NotificationEmail");
442 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
443 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
445 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
446 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
447 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
448 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
449 _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false);
450 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
452 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
453 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
454 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
455 #ifdef DCPOMATIC_WINDOWS
456 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
459 for (auto i: f.node_children("History")) {
460 _history.push_back (i->content ());
463 for (auto i: f.node_children("PlayerHistory")) {
464 _player_history.push_back (i->content ());
467 auto signer = f.optional_node_child ("Signer");
469 auto c = make_shared<dcp::CertificateChain>();
470 /* Read the signing certificates and private key in from the config file */
471 for (auto i: signer->node_children ("Certificate")) {
472 c->add (dcp::Certificate (i->content ()));
474 c->set_key (signer->string_child ("PrivateKey"));
477 /* Make a new set of signing certificates and key */
478 _signer_chain = create_certificate_chain ();
481 auto decryption = f.optional_node_child ("Decryption");
483 auto c = make_shared<dcp::CertificateChain>();
484 for (auto i: decryption->node_children ("Certificate")) {
485 c->add (dcp::Certificate (i->content ()));
487 c->set_key (decryption->string_child ("PrivateKey"));
488 _decryption_chain = c;
490 _decryption_chain = create_certificate_chain ();
493 /* These must be done before we call Bad as that might set one
496 for (auto i: f.node_children("Nagged")) {
497 auto const id = number_attribute<int>(i, "Id", "id");
498 if (id >= 0 && id < NAG_COUNT) {
499 _nagged[id] = raw_convert<int>(i->content());
503 auto bad = check_certificates ();
505 auto const remake = Bad(*bad);
506 if (remake && *remake) {
508 case BAD_SIGNER_UTF8_STRINGS:
509 case BAD_SIGNER_INCONSISTENT:
510 case BAD_SIGNER_VALIDITY_TOO_LONG:
511 _signer_chain = create_certificate_chain ();
513 case BAD_DECRYPTION_INCONSISTENT:
514 _decryption_chain = create_certificate_chain ();
520 if (f.optional_node_child("DKDMGroup")) {
521 /* New-style: all DKDMs in a group */
522 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
524 /* Old-style: one or more DKDM nodes */
525 _dkdms = make_shared<DKDMGroup>("root");
526 for (auto i: f.node_children("DKDM")) {
527 _dkdms->add (DKDMBase::read (i));
530 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
531 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
532 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
533 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
534 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
535 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
536 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
537 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
538 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
539 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
540 /* The variable was renamed but not the XML tag */
541 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
542 _sound_output = f.optional_string_child("PreviewSoundOutput");
543 if (f.optional_string_child("CoverSheet")) {
544 _cover_sheet = f.optional_string_child("CoverSheet").get();
546 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
547 if (f.optional_string_child("LastKDMWriteType")) {
548 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
549 _last_kdm_write_type = KDM_WRITE_FLAT;
550 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
551 _last_kdm_write_type = KDM_WRITE_FOLDER;
552 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
553 _last_kdm_write_type = KDM_WRITE_ZIP;
556 if (f.optional_string_child("LastDKDMWriteType")) {
557 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
558 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
559 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
560 _last_dkdm_write_type = DKDM_WRITE_FILE;
563 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
564 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
565 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
567 for (auto i: f.node_children("Notification")) {
568 int const id = number_attribute<int>(i, "Id", "id");
569 if (id >= 0 && id < NOTIFICATION_COUNT) {
570 _notification[id] = raw_convert<int>(i->content());
574 _barco_username = f.optional_string_child("BarcoUsername");
575 _barco_password = f.optional_string_child("BarcoPassword");
576 _christie_username = f.optional_string_child("ChristieUsername");
577 _christie_password = f.optional_string_child("ChristiePassword");
578 _gdc_username = f.optional_string_child("GDCUsername");
579 _gdc_password = f.optional_string_child("GDCPassword");
581 auto pm = f.optional_string_child("PlayerMode");
582 if (pm && *pm == "window") {
583 _player_mode = PLAYER_MODE_WINDOW;
584 } else if (pm && *pm == "full") {
585 _player_mode = PLAYER_MODE_FULL;
586 } else if (pm && *pm == "dual") {
587 _player_mode = PLAYER_MODE_DUAL;
590 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
591 auto vc = f.optional_string_child("VideoViewType");
592 if (vc && *vc == "opengl") {
593 _video_view_type = VIDEO_VIEW_OPENGL;
594 } else if (vc && *vc == "simple") {
595 _video_view_type = VIDEO_VIEW_SIMPLE;
597 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
598 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
599 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
600 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
601 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
603 if (f.optional_node_child("AudioMapping")) {
604 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
607 for (auto i: f.node_children("CustomLanguage")) {
609 /* This will fail if it's called before dcp::init() as it won't recognise the
610 * tag. That's OK because the Config will be reloaded again later.
612 _custom_languages.push_back (dcp::LanguageTag(i->content()));
613 } catch (std::runtime_error& e) {}
616 for (auto& initial: _initial_paths) {
617 initial.second = f.optional_string_child(initial.first);
619 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
620 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
621 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
622 _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
623 if (auto duration = f.optional_node_child("DefaultKDMDuration")) {
624 _default_kdm_duration = RoughDuration(duration);
626 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
628 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
629 _last_release_notes_version = f.optional_string_child("LastReleaseNotesVersion");
630 _main_divider_sash_position = f.optional_number_child<int>("MainDividerSashPosition");
631 _main_content_divider_sash_position = f.optional_number_child<int>("MainContentDividerSashPosition");
633 if (auto loc = f.optional_string_child("DefaultAddFileLocation")) {
634 if (*loc == "last") {
635 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
636 } else if (*loc == "project") {
637 _default_add_file_location = DefaultAddFileLocation::SAME_AS_PROJECT;
641 _allow_smpte_bv20 = f.optional_bool_child("AllowSMPTEBv20").get_value_or(false);
642 _isdcf_name_part_length = f.optional_number_child<int>("ISDCFNamePartLength").get_value_or(14);
644 _enable_gpu = f.optional_bool_child("EnableGpu").get_value_or(false);
645 _gpu_binary_location = f.string_child("GpuBinaryLocation");
646 _selected_gpu = f.number_child<int>("SelectedGpu");
647 _gpu_license_server = f.string_child ("GpuLicenseServer");
648 _gpu_license_port = f.number_child<int> ("GpuLicensePort");
649 _gpu_license = f.string_child("GpuLicense");
651 _export.read(f.optional_node_child("Export"));
654 if (have_existing("config.xml")) {
656 /* We have a config file but it didn't load */
657 FailedToLoad(LoadFailure::CONFIG);
660 /* Make a new set of signing certificates and key */
661 _signer_chain = create_certificate_chain ();
662 /* And similar for decryption of KDMs */
663 _decryption_chain = create_certificate_chain ();
669 Config::read_cinemas()
671 if (dcp::filesystem::exists(_cinemas_file)) {
673 cxml::Document f("Cinemas");
674 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
678 FailedToLoad(LoadFailure::CINEMAS);
686 Config::read_dkdm_recipients()
688 if (dcp::filesystem::exists(_dkdm_recipients_file)) {
690 cxml::Document f("DKDMRecipients");
691 f.read_file(dcp::filesystem::fix_long_path(_dkdm_recipients_file));
692 read_dkdm_recipients(f);
695 FailedToLoad(LoadFailure::DKDM_RECIPIENTS);
696 write_dkdm_recipients();
702 /** @return Singleton instance */
706 if (_instance == nullptr) {
707 _instance = new Config;
714 /** Write our configuration to disk */
716 Config::write () const
720 write_dkdm_recipients ();
724 Config::write_config () const
727 auto root = doc.create_root_node ("Config");
729 /* [XML] Version The version number of the configuration file format. */
730 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
731 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
732 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
733 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
734 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
735 if (_default_directory) {
736 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
737 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
739 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
740 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
741 by the batch converter to listen for job requests.
743 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
744 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
745 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
747 for (auto i: _servers) {
748 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
751 root->add_child("Server")->add_child_text (i);
754 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
755 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
757 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
758 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
759 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
760 /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */
761 root->add_child("TMSPassive")->add_child_text(_tms_passive ? "1" : "0");
762 /* [XML] TMSIP IP address of TMS. */
763 root->add_child("TMSIP")->add_child_text (_tms_ip);
764 /* [XML] TMSPath Path on the TMS to copy files to. */
765 root->add_child("TMSPath")->add_child_text (_tms_path);
766 /* [XML] TMSUser Username to log into the TMS with. */
767 root->add_child("TMSUser")->add_child_text (_tms_user);
768 /* [XML] TMSPassword Password to log into the TMS with. */
769 root->add_child("TMSPassword")->add_child_text (_tms_password);
771 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
772 root->add_child("Language")->add_child_text (_language.get());
774 if (_default_dcp_content_type) {
775 /* [XML:opt] DefaultDCPContentType Default content type to use when creating new films (<code>FTR</code>, <code>SHR</code>,
776 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
777 <code>PSA</code> or <code>ADV</code>). */
778 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
780 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
781 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
782 /* [XML] DCPIssuer Issuer text to write into CPL files. */
783 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
784 /* [XML] DCPCreator Creator text to write into CPL files. */
785 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
786 /* [XML] Company name to write into MXF files. */
787 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
788 /* [XML] Product name to write into MXF files. */
789 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
790 /* [XML] Product version to write into MXF files. */
791 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
792 /* [XML] Comment to write into JPEG2000 data. */
793 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
794 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
795 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
797 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
798 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
799 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
800 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
801 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
802 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
803 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
804 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
805 if (_default_audio_language) {
806 /* [XML] DefaultAudioLanguage Default audio language to use for new films */
807 root->add_child("DefaultAudioLanguage")->add_child_text(_default_audio_language->to_string());
809 if (_default_territory) {
810 /* [XML] DefaultTerritory Default territory to use for new films */
811 root->add_child("DefaultTerritory")->add_child_text(_default_territory->subtag());
813 for (auto const& i: _default_metadata) {
814 auto c = root->add_child("DefaultMetadata");
815 c->set_attribute("key", i.first);
816 c->add_child_text(i.second);
818 if (_default_kdm_directory) {
819 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
820 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
822 _default_kdm_duration.as_xml(root->add_child("DefaultKDMDuration"));
823 /* [XML] MailServer Hostname of SMTP server to use. */
824 root->add_child("MailServer")->add_child_text (_mail_server);
825 /* [XML] MailPort Port number to use on SMTP server. */
826 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
827 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
828 switch (_mail_protocol) {
829 case EmailProtocol::AUTO:
830 root->add_child("MailProtocol")->add_child_text("Auto");
832 case EmailProtocol::PLAIN:
833 root->add_child("MailProtocol")->add_child_text("Plain");
835 case EmailProtocol::STARTTLS:
836 root->add_child("MailProtocol")->add_child_text("STARTTLS");
838 case EmailProtocol::SSL:
839 root->add_child("MailProtocol")->add_child_text("SSL");
842 /* [XML] MailUser Username to use on SMTP server. */
843 root->add_child("MailUser")->add_child_text (_mail_user);
844 /* [XML] MailPassword Password to use on SMTP server. */
845 root->add_child("MailPassword")->add_child_text (_mail_password);
847 /* [XML] KDMSubject Subject to use for KDM emails. */
848 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
849 /* [XML] KDMFrom From address to use for KDM emails. */
850 root->add_child("KDMFrom")->add_child_text (_kdm_from);
851 for (auto i: _kdm_cc) {
852 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
853 root->add_child("KDMCC")->add_child_text (i);
855 /* [XML] KDMBCC BCC address to use for KDM emails. */
856 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
857 /* [XML] KDMEmail Text of KDM email. */
858 root->add_child("KDMEmail")->add_child_text (_kdm_email);
860 /* [XML] NotificationSubject Subject to use for notification emails. */
861 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
862 /* [XML] NotificationFrom From address to use for notification emails. */
863 root->add_child("NotificationFrom")->add_child_text (_notification_from);
864 /* [XML] NotificationFrom To address to use for notification emails. */
865 root->add_child("NotificationTo")->add_child_text (_notification_to);
866 for (auto i: _notification_cc) {
867 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
868 root->add_child("NotificationCC")->add_child_text (i);
870 /* [XML] NotificationBCC BCC address to use for notification emails. */
871 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
872 /* [XML] NotificationEmail Text of notification email. */
873 root->add_child("NotificationEmail")->add_child_text (_notification_email);
875 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
876 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
877 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
878 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
880 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
881 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
882 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
883 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
884 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
885 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
886 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
887 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
888 /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
889 root->add_child("UseAllAudioChannels")->add_child_text(_use_all_audio_channels ? "1" : "0");
890 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
891 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
892 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
893 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
894 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
895 related to the player, 1024 debug information related to audio analyses.
897 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
898 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
899 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
900 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
901 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
902 #ifdef DCPOMATIC_WINDOWS
903 if (_win32_console) {
904 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
905 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
906 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
908 root->add_child("Win32Console")->add_child_text ("1");
912 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
913 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
915 auto signer = root->add_child ("Signer");
916 DCPOMATIC_ASSERT (_signer_chain);
917 for (auto const& i: _signer_chain->unordered()) {
918 signer->add_child("Certificate")->add_child_text (i.certificate (true));
920 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
922 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
923 auto decryption = root->add_child ("Decryption");
924 DCPOMATIC_ASSERT (_decryption_chain);
925 for (auto const& i: _decryption_chain->unordered()) {
926 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
928 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
930 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
933 for (auto i: _history) {
934 root->add_child("History")->add_child_text (i.string ());
937 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
940 for (auto i: _player_history) {
941 root->add_child("PlayerHistory")->add_child_text (i.string ());
944 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
945 or <code><DKDM></code> tags.
947 /* [XML] DKDM A DKDM as XML */
948 _dkdms->as_xml (root);
950 /* [XML] CinemasFile Filename of cinemas list file. */
951 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
952 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
953 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
954 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
955 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
956 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
957 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
958 /* [XML] KDMFilenameFormat Format for KDM filenames. */
959 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
960 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
961 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
962 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
963 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
964 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
965 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
966 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
967 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
968 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
969 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
970 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
971 for (int i = 0; i < NAG_COUNT; ++i) {
972 xmlpp::Element* e = root->add_child ("Nagged");
973 e->set_attribute("id", raw_convert<string>(i));
974 e->add_child_text (_nagged[i] ? "1" : "0");
976 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
977 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
979 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
980 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
982 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
983 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
984 if (_last_player_load_directory) {
985 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
987 /* [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. */
988 if (_last_kdm_write_type) {
989 switch (_last_kdm_write_type.get()) {
991 root->add_child("LastKDMWriteType")->add_child_text("flat");
993 case KDM_WRITE_FOLDER:
994 root->add_child("LastKDMWriteType")->add_child_text("folder");
997 root->add_child("LastKDMWriteType")->add_child_text("zip");
1001 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
1002 if (_last_dkdm_write_type) {
1003 switch (_last_dkdm_write_type.get()) {
1004 case DKDM_WRITE_INTERNAL:
1005 root->add_child("LastDKDMWriteType")->add_child_text("internal");
1007 case DKDM_WRITE_FILE:
1008 root->add_child("LastDKDMWriteType")->add_child_text("file");
1012 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
1013 frames to be held in memory at once.
1015 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
1017 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
1018 if (_decode_reduction) {
1019 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
1022 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
1023 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
1025 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
1026 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
1027 xmlpp::Element* e = root->add_child ("Notification");
1028 e->set_attribute("id", raw_convert<string>(i));
1029 e->add_child_text (_notification[i] ? "1" : "0");
1032 if (_barco_username) {
1033 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
1034 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
1036 if (_barco_password) {
1037 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
1038 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
1041 if (_christie_username) {
1042 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
1043 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
1045 if (_christie_password) {
1046 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
1047 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
1050 if (_gdc_username) {
1051 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
1052 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
1054 if (_gdc_password) {
1055 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
1056 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
1059 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
1060 with controls on another monitor.
1062 switch (_player_mode) {
1063 case PLAYER_MODE_WINDOW:
1064 root->add_child("PlayerMode")->add_child_text("window");
1066 case PLAYER_MODE_FULL:
1067 root->add_child("PlayerMode")->add_child_text("full");
1069 case PLAYER_MODE_DUAL:
1070 root->add_child("PlayerMode")->add_child_text("dual");
1074 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
1075 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
1076 switch (_video_view_type) {
1077 case VIDEO_VIEW_SIMPLE:
1078 root->add_child("VideoViewType")->add_child_text("simple");
1080 case VIDEO_VIEW_OPENGL:
1081 root->add_child("VideoViewType")->add_child_text("opengl");
1084 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1085 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
1086 if (_player_debug_log_file) {
1087 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1088 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
1090 if (_player_content_directory) {
1091 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1092 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
1094 if (_player_playlist_directory) {
1095 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1096 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1098 if (_player_kdm_directory) {
1099 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1100 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1102 if (_audio_mapping) {
1103 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1105 for (auto const& i: _custom_languages) {
1106 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1108 for (auto const& initial: _initial_paths) {
1109 if (initial.second) {
1110 root->add_child(initial.first)->add_child_text(initial.second->string());
1113 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1114 root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0");
1115 root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0");
1116 root->add_child("DefaultKDMType")->add_child_text(dcp::formulation_to_string(_default_kdm_type));
1117 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1118 if (_last_release_notes_version) {
1119 root->add_child("LastReleaseNotesVersion")->add_child_text(*_last_release_notes_version);
1121 if (_main_divider_sash_position) {
1122 root->add_child("MainDividerSashPosition")->add_child_text(raw_convert<string>(*_main_divider_sash_position));
1124 if (_main_content_divider_sash_position) {
1125 root->add_child("MainContentDividerSashPosition")->add_child_text(raw_convert<string>(*_main_content_divider_sash_position));
1128 root->add_child("DefaultAddFileLocation")->add_child_text(
1129 _default_add_file_location == DefaultAddFileLocation::SAME_AS_LAST_TIME ? "last" : "project"
1132 /* [XML] AllowSMPTEBv20 1 to allow the user to choose SMPTE (Bv2.0 only) as a standard, otherwise 0 */
1133 root->add_child("AllowSMPTEBv20")->add_child_text(_allow_smpte_bv20 ? "1" : "0");
1134 /* [XML] ISDCFNamePartLength Maximum length of the "name" part of an ISDCF name, which should be 14 according to the standard */
1135 root->add_child("ISDCFNamePartLength")->add_child_text(raw_convert<string>(_isdcf_name_part_length));
1137 root->add_child("GpuBinaryLocation")->add_child_text (_gpu_binary_location.string());
1138 root->add_child("EnableGpu")->add_child_text ((_enable_gpu ? "1" : "0"));
1139 root->add_child("SelectedGpu")->add_child_text (raw_convert<string> (_selected_gpu));
1140 root->add_child("GpuLicenseServer")->add_child_text (_gpu_license_server);
1141 root->add_child("GpuLicensePort")->add_child_text (raw_convert<string> (_gpu_license_port));
1142 root->add_child("GpuLicense")->add_child_text (_gpu_license);
1144 _export.write(root->add_child("Export"));
1146 auto target = config_write_file();
1149 auto const s = doc.write_to_string_formatted ();
1150 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1151 dcp::File f(tmp, "w");
1153 throw FileError (_("Could not open file for writing"), tmp);
1155 f.checked_write(s.c_str(), s.bytes());
1157 dcp::filesystem::remove(target);
1158 dcp::filesystem::rename(tmp, target);
1159 } catch (xmlpp::exception& e) {
1160 string s = e.what ();
1162 throw FileError (s, target);
1169 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1171 xmlpp::Document doc;
1172 auto root = doc.create_root_node (root_node);
1173 root->add_child("Version")->add_child_text(version);
1175 for (auto i: things) {
1176 i->as_xml (root->add_child(node));
1180 doc.write_to_file_formatted (file.string() + ".tmp");
1181 dcp::filesystem::remove(file);
1182 dcp::filesystem::rename(file.string() + ".tmp", file);
1183 } catch (xmlpp::exception& e) {
1184 string s = e.what ();
1186 throw FileError (s, file);
1192 Config::write_cinemas () const
1194 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1199 Config::write_dkdm_recipients () const
1201 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1205 boost::filesystem::path
1206 Config::default_directory_or (boost::filesystem::path a) const
1208 return directory_or (_default_directory, a);
1211 boost::filesystem::path
1212 Config::default_kdm_directory_or (boost::filesystem::path a) const
1214 return directory_or (_default_kdm_directory, a);
1217 boost::filesystem::path
1218 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1224 boost::system::error_code ec;
1225 auto const e = dcp::filesystem::exists(*dir, ec);
1241 Config::changed (Property what)
1247 Config::set_kdm_email_to_default ()
1249 _kdm_subject = _("KDM delivery: $CPL_NAME");
1252 "Dear Projectionist\n\n"
1253 "Please find attached KDMs for $CPL_NAME.\n\n"
1254 "Cinema: $CINEMA_NAME\n"
1255 "Screen(s): $SCREENS\n\n"
1256 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1257 "Best regards,\nDCP-o-matic"
1262 Config::set_notification_email_to_default ()
1264 _notification_subject = _("DCP-o-matic notification");
1266 _notification_email = _(
1267 "$JOB_NAME: $JOB_STATUS"
1272 Config::reset_kdm_email ()
1274 set_kdm_email_to_default ();
1279 Config::reset_notification_email ()
1281 set_notification_email_to_default ();
1286 Config::set_cover_sheet_to_default ()
1290 "CPL Filename: $CPL_FILENAME\n"
1292 "Format: $CONTAINER\n"
1294 "Audio Language: $AUDIO_LANGUAGE\n"
1295 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1302 Config::add_to_history (boost::filesystem::path p)
1304 add_to_history_internal (_history, p);
1307 /** Remove non-existent items from the history */
1309 Config::clean_history ()
1311 clean_history_internal (_history);
1315 Config::add_to_player_history (boost::filesystem::path p)
1317 add_to_history_internal (_player_history, p);
1320 /** Remove non-existent items from the player history */
1322 Config::clean_player_history ()
1324 clean_history_internal (_player_history);
1328 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1330 /* Remove existing instances of this path in the history */
1331 h.erase (remove (h.begin(), h.end(), p), h.end ());
1333 h.insert (h.begin (), p);
1334 if (h.size() > HISTORY_SIZE) {
1342 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1348 if (dcp::filesystem::is_directory(i)) {
1352 /* We couldn't find out if it's a directory for some reason; just ignore it */
1359 Config::have_existing (string file)
1361 return dcp::filesystem::exists(read_path(file));
1366 Config::read_cinemas (cxml::Document const & f)
1369 for (auto i: f.node_children("Cinema")) {
1370 /* Slightly grotty two-part construction of Cinema here so that we can use
1373 auto cinema = make_shared<Cinema>(i);
1374 cinema->read_screens (i);
1375 _cinemas.push_back (cinema);
1380 Config::set_cinemas_file (boost::filesystem::path file)
1382 if (file == _cinemas_file) {
1386 _cinemas_file = file;
1388 if (dcp::filesystem::exists(_cinemas_file)) {
1389 /* Existing file; read it in */
1390 cxml::Document f ("Cinemas");
1391 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
1401 Config::read_dkdm_recipients (cxml::Document const & f)
1403 _dkdm_recipients.clear ();
1404 for (auto i: f.node_children("DKDMRecipient")) {
1405 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1411 Config::save_template (shared_ptr<const Film> film, string name) const
1413 film->write_template (template_write_path(name));
1418 Config::templates () const
1420 if (!dcp::filesystem::exists(read_path("templates"))) {
1425 for (auto const& i: dcp::filesystem::directory_iterator(read_path("templates"))) {
1426 n.push_back (i.path().filename().string());
1432 Config::existing_template (string name) const
1434 return dcp::filesystem::exists(template_read_path(name));
1438 boost::filesystem::path
1439 Config::template_read_path (string name) const
1441 return read_path("templates") / tidy_for_filename (name);
1445 boost::filesystem::path
1446 Config::template_write_path (string name) const
1448 return write_path("templates") / tidy_for_filename (name);
1453 Config::rename_template (string old_name, string new_name) const
1455 dcp::filesystem::rename(template_read_path(old_name), template_write_path(new_name));
1459 Config::delete_template (string name) const
1461 dcp::filesystem::remove(template_write_path(name));
1464 /** @return Path to the config.xml containing the actual settings, following a link if required */
1465 boost::filesystem::path
1466 config_file (boost::filesystem::path main)
1468 cxml::Document f ("Config");
1469 if (!dcp::filesystem::exists(main)) {
1470 /* It doesn't exist, so there can't be any links; just return it */
1474 /* See if there's a link */
1476 f.read_file(dcp::filesystem::fix_long_path(main));
1477 auto link = f.optional_string_child("Link");
1481 } catch (xmlpp::exception& e) {
1482 /* There as a problem reading the main configuration file,
1483 so there can't be a link.
1491 boost::filesystem::path
1492 Config::config_read_file ()
1494 return config_file (read_path("config.xml"));
1498 boost::filesystem::path
1499 Config::config_write_file ()
1501 return config_file (write_path("config.xml"));
1506 Config::reset_cover_sheet ()
1508 set_cover_sheet_to_default ();
1513 Config::link (boost::filesystem::path new_file) const
1515 xmlpp::Document doc;
1516 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1518 doc.write_to_file_formatted(write_path("config.xml").string());
1519 } catch (xmlpp::exception& e) {
1520 string s = e.what ();
1522 throw FileError (s, write_path("config.xml"));
1527 Config::copy_and_link (boost::filesystem::path new_file) const
1530 dcp::filesystem::copy_file(config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1535 Config::have_write_permission () const
1537 dcp::File f(config_write_file(), "r+");
1538 return static_cast<bool>(f);
1541 /** @param output_channels Number of output channels in use.
1542 * @return Audio mapping for this output channel count (may be a default).
1545 Config::audio_mapping (int output_channels)
1547 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1548 /* Set up a default */
1549 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1550 if (output_channels == 2) {
1551 /* Special case for stereo output.
1552 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1553 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1555 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1556 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1557 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1558 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1559 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1560 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1561 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1562 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1565 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1566 _audio_mapping->set (i, i, 1);
1571 return *_audio_mapping;
1575 Config::set_audio_mapping (AudioMapping m)
1578 changed (AUDIO_MAPPING);
1582 Config::set_audio_mapping_to_default ()
1584 DCPOMATIC_ASSERT (_audio_mapping);
1585 auto const ch = _audio_mapping->output_channels ();
1586 _audio_mapping = boost::none;
1587 _audio_mapping = audio_mapping (ch);
1588 changed (AUDIO_MAPPING);
1593 Config::add_custom_language (dcp::LanguageTag tag)
1595 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1596 _custom_languages.push_back (tag);
1602 optional<Config::BadReason>
1603 Config::check_certificates () const
1605 optional<BadReason> bad;
1607 for (auto const& i: _signer_chain->unordered()) {
1608 if (i.has_utf8_strings()) {
1609 bad = BAD_SIGNER_UTF8_STRINGS;
1611 if ((i.not_after().year() - i.not_before().year()) > 15) {
1612 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1616 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1617 bad = BAD_SIGNER_INCONSISTENT;
1620 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1621 bad = BAD_DECRYPTION_INCONSISTENT;
1629 save_all_config_as_zip (boost::filesystem::path zip_file)
1631 Zipper zipper (zip_file);
1633 auto config = Config::instance();
1634 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1635 if (dcp::filesystem::exists(config->cinemas_file())) {
1636 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1638 if (dcp::filesystem::exists(config->dkdm_recipients_file())) {
1639 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
1647 Config::set_initial_path(string id, boost::filesystem::path path)
1649 auto iter = _initial_paths.find(id);
1650 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1651 iter->second = path;
1656 optional<boost::filesystem::path>
1657 Config::initial_path(string id) const
1659 auto iter = _initial_paths.find(id);
1660 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1661 return iter->second;