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"
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 = 8;
110 _default_j2k_bandwidth = 150000000;
111 _default_audio_delay = 0;
112 _default_interop = false;
113 _default_metadata.clear ();
114 _upload_after_make_dcp = false;
117 _mail_protocol = EmailProtocol::AUTO;
123 _notification_from = "";
124 _notification_to = "";
125 _notification_cc.clear ();
126 _notification_bcc = "";
127 _check_for_updates = false;
128 _check_for_test_updates = false;
129 _maximum_j2k_bandwidth = 250000000;
130 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
131 _analyse_ebur128 = true;
132 _automatic_audio_analysis = false;
133 #ifdef DCPOMATIC_WINDOWS
134 _win32_console = false;
136 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
137 * ~/.config/dcpomatic2 (or equivalent) and written back there.
139 _cinemas_file = read_path ("cinemas.xml");
140 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
141 _show_hints_before_make_dcp = true;
142 _confirm_kdm_email = true;
143 _kdm_container_name_format = dcp::NameFormat("KDM_%f_%c");
144 _kdm_filename_format = dcp::NameFormat("KDM_%f_%c_%s");
145 _dkdm_filename_format = dcp::NameFormat("DKDM_%f_%c_%s");
146 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
147 _dcp_asset_filename_format = dcp::NameFormat ("%t");
148 _jump_to_selected = true;
149 for (int i = 0; i < NAG_COUNT; ++i) {
153 _sound_output = optional<string> ();
154 _last_kdm_write_type = KDM_WRITE_FLAT;
155 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
156 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
158 /* I think the scaling factor here should be the ratio of the longest frame
159 encode time to the shortest; if the thread count is T, longest time is L
160 and the shortest time S we could encode L/S frames per thread whilst waiting
161 for the L frame to encode so we might have to store LT/S frames.
163 However we don't want to use too much memory, so keep it a bit lower than we'd
164 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
165 use about 240Mb with 72 encoding threads.
167 _frames_in_memory_multiplier = 3;
168 _decode_reduction = optional<int>();
169 _default_notify = false;
170 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
171 _notification[i] = false;
173 _barco_username = optional<string>();
174 _barco_password = optional<string>();
175 _christie_username = optional<string>();
176 _christie_password = optional<string>();
177 _gdc_username = optional<string>();
178 _gdc_password = optional<string>();
179 _player_mode = PLAYER_MODE_WINDOW;
180 _player_restricted_menus = false;
182 _video_view_type = VIDEO_VIEW_SIMPLE;
183 _respect_kdm_validity_periods = true;
184 _player_debug_log_file = boost::none;
185 _player_content_directory = boost::none;
186 _player_playlist_directory = boost::none;
187 _player_kdm_directory = boost::none;
188 _audio_mapping = boost::none;
189 _custom_languages.clear ();
190 _initial_paths.clear();
191 _initial_paths["AddFilesPath"] = boost::none;
192 _initial_paths["AddKDMPath"] = boost::none;
193 _initial_paths["AddDKDMPath"] = boost::none;
194 _initial_paths["SelectCertificatePath"] = boost::none;
195 _initial_paths["AddCombinerInputPath"] = boost::none;
196 _initial_paths["ExportSubtitlesPath"] = boost::none;
197 _initial_paths["ExportVideoPath"] = boost::none;
198 _initial_paths["DebugLogPath"] = boost::none;
199 _initial_paths["CinemaDatabasePath"] = boost::none;
200 _initial_paths["ConfigFilePath"] = boost::none;
201 _initial_paths["Preferences"] = boost::none;
202 _use_isdcf_name_by_default = true;
203 _write_kdms_to_disk = true;
205 _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
206 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
207 _auto_crop_threshold = 0.1;
208 _last_release_notes_version = boost::none;
209 _allow_smpte_bv20 = false;
210 _isdcf_name_part_length = 14;
212 _allowed_dcp_frame_rates.clear ();
213 _allowed_dcp_frame_rates.push_back (24);
214 _allowed_dcp_frame_rates.push_back (25);
215 _allowed_dcp_frame_rates.push_back (30);
216 _allowed_dcp_frame_rates.push_back (48);
217 _allowed_dcp_frame_rates.push_back (50);
218 _allowed_dcp_frame_rates.push_back (60);
220 set_kdm_email_to_default ();
221 set_notification_email_to_default ();
222 set_cover_sheet_to_default ();
224 #ifdef DCPOMATIC_GROK
228 _main_divider_sash_position = {};
229 _main_content_divider_sash_position = {};
231 _export.set_defaults();
235 Config::restore_defaults ()
237 Config::instance()->set_defaults ();
238 Config::instance()->changed ();
241 shared_ptr<dcp::CertificateChain>
242 Config::create_certificate_chain ()
244 return make_shared<dcp::CertificateChain> (
246 CERTIFICATE_VALIDITY_PERIOD,
249 ".dcpomatic.smpte-430-2.ROOT",
250 ".dcpomatic.smpte-430-2.INTERMEDIATE",
251 "CS.dcpomatic.smpte-430-2.LEAF"
258 using namespace boost::filesystem;
260 auto copy_adding_number = [](path const& path_to_copy) {
262 auto add_number = [](path const& path, int number) {
263 return String::compose("%1.%2", path, number);
267 while (n < 100 && exists(add_number(path_to_copy, n))) {
270 boost::system::error_code ec;
271 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
274 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
275 * to write over. This is more intended for the situation where we have a corrupted config.xml,
276 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
277 * file). But we might as well back up the other files while we're about it.
280 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
281 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
283 copy_adding_number (config_write_file());
285 /* These do not use State::write_path, so whatever path is in the Config we will copy
288 copy_adding_number (_cinemas_file);
289 copy_adding_number (_dkdm_recipients_file);
297 read_dkdm_recipients();
302 Config::read_config()
305 cxml::Document f ("Config");
306 f.read_file(dcp::filesystem::fix_long_path(config_read_file()));
308 auto version = f.optional_number_child<int> ("Version");
309 if (version && *version < _current_version) {
310 /* Back up the old config before we re-write it in a back-incompatible way */
314 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
315 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
317 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
318 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
321 _default_directory = f.optional_string_child ("DefaultDirectory");
322 if (_default_directory && _default_directory->empty ()) {
323 /* We used to store an empty value for this to mean "none set" */
324 _default_directory = boost::optional<boost::filesystem::path> ();
327 auto b = f.optional_number_child<int> ("ServerPort");
329 b = f.optional_number_child<int> ("ServerPortBase");
331 _server_port_base = b.get ();
333 auto u = f.optional_bool_child ("UseAnyServers");
334 _use_any_servers = u.get_value_or (true);
336 for (auto i: f.node_children("Server")) {
337 if (i->node_children("HostName").size() == 1) {
338 _servers.push_back (i->string_child ("HostName"));
340 _servers.push_back (i->content ());
344 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
345 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
346 _tms_passive = f.optional_bool_child("TMSPassive").get_value_or(true);
347 _tms_ip = f.string_child ("TMSIP");
348 _tms_path = f.string_child ("TMSPath");
349 _tms_user = f.string_child ("TMSUser");
350 _tms_password = f.string_child ("TMSPassword");
352 _language = f.optional_string_child ("Language");
354 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
355 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
357 if (f.optional_string_child ("DCPMetadataIssuer")) {
358 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
359 } else if (f.optional_string_child ("DCPIssuer")) {
360 _dcp_issuer = f.string_child ("DCPIssuer");
363 auto up = f.optional_bool_child("UploadAfterMakeDCP");
365 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
367 _upload_after_make_dcp = up.get_value_or (false);
368 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
369 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
370 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
371 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
372 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
374 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
375 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
376 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
377 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
380 auto al = f.optional_string_child("DefaultAudioLanguage");
382 _default_audio_language = dcp::LanguageTag(*al);
384 } catch (std::runtime_error&) {}
387 auto te = f.optional_string_child("DefaultTerritory");
389 _default_territory = dcp::LanguageTag::RegionSubtag(*te);
391 } catch (std::runtime_error&) {}
393 for (auto const& i: f.node_children("DefaultMetadata")) {
394 _default_metadata[i->string_attribute("key")] = i->content();
397 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
399 /* Read any cinemas that are still lying around in the config file
400 * from an old version.
404 _mail_server = f.string_child ("MailServer");
405 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
408 /* Make sure this matches the code in write_config */
409 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
410 if (protocol == "Auto") {
411 _mail_protocol = EmailProtocol::AUTO;
412 } else if (protocol == "Plain") {
413 _mail_protocol = EmailProtocol::PLAIN;
414 } else if (protocol == "STARTTLS") {
415 _mail_protocol = EmailProtocol::STARTTLS;
416 } else if (protocol == "SSL") {
417 _mail_protocol = EmailProtocol::SSL;
421 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
422 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
424 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
425 _kdm_from = f.string_child ("KDMFrom");
426 for (auto i: f.node_children("KDMCC")) {
427 if (!i->content().empty()) {
428 _kdm_cc.push_back (i->content ());
431 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
432 _kdm_email = f.string_child ("KDMEmail");
434 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
435 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
436 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
437 for (auto i: f.node_children("NotificationCC")) {
438 if (!i->content().empty()) {
439 _notification_cc.push_back (i->content ());
442 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
443 if (f.optional_string_child("NotificationEmail")) {
444 _notification_email = f.string_child("NotificationEmail");
447 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
448 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
450 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
451 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
452 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
453 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
454 _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false);
455 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
457 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
458 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
459 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
460 #ifdef DCPOMATIC_WINDOWS
461 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
464 for (auto i: f.node_children("History")) {
465 _history.push_back (i->content ());
468 for (auto i: f.node_children("PlayerHistory")) {
469 _player_history.push_back (i->content ());
472 auto signer = f.optional_node_child ("Signer");
474 auto c = make_shared<dcp::CertificateChain>();
475 /* Read the signing certificates and private key in from the config file */
476 for (auto i: signer->node_children ("Certificate")) {
477 c->add (dcp::Certificate (i->content ()));
479 c->set_key (signer->string_child ("PrivateKey"));
482 /* Make a new set of signing certificates and key */
483 _signer_chain = create_certificate_chain ();
486 auto decryption = f.optional_node_child ("Decryption");
488 auto c = make_shared<dcp::CertificateChain>();
489 for (auto i: decryption->node_children ("Certificate")) {
490 c->add (dcp::Certificate (i->content ()));
492 c->set_key (decryption->string_child ("PrivateKey"));
493 _decryption_chain = c;
495 _decryption_chain = create_certificate_chain ();
498 /* These must be done before we call Bad as that might set one
501 for (auto i: f.node_children("Nagged")) {
502 auto const id = number_attribute<int>(i, "Id", "id");
503 if (id >= 0 && id < NAG_COUNT) {
504 _nagged[id] = raw_convert<int>(i->content());
508 auto bad = check_certificates ();
510 auto const remake = Bad(*bad);
511 if (remake && *remake) {
513 case BAD_SIGNER_UTF8_STRINGS:
514 case BAD_SIGNER_INCONSISTENT:
515 case BAD_SIGNER_VALIDITY_TOO_LONG:
516 case BAD_SIGNER_DN_QUALIFIER:
517 _signer_chain = create_certificate_chain ();
519 case BAD_DECRYPTION_INCONSISTENT:
520 _decryption_chain = create_certificate_chain ();
526 if (f.optional_node_child("DKDMGroup")) {
527 /* New-style: all DKDMs in a group */
528 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
530 /* Old-style: one or more DKDM nodes */
531 _dkdms = make_shared<DKDMGroup>("root");
532 for (auto i: f.node_children("DKDM")) {
533 _dkdms->add (DKDMBase::read (i));
536 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
537 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
538 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
539 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
540 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
541 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
542 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
543 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
544 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
545 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
546 /* The variable was renamed but not the XML tag */
547 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
548 _sound_output = f.optional_string_child("PreviewSoundOutput");
549 if (f.optional_string_child("CoverSheet")) {
550 _cover_sheet = f.optional_string_child("CoverSheet").get();
552 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
553 if (f.optional_string_child("LastKDMWriteType")) {
554 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
555 _last_kdm_write_type = KDM_WRITE_FLAT;
556 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
557 _last_kdm_write_type = KDM_WRITE_FOLDER;
558 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
559 _last_kdm_write_type = KDM_WRITE_ZIP;
562 if (f.optional_string_child("LastDKDMWriteType")) {
563 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
564 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
565 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
566 _last_dkdm_write_type = DKDM_WRITE_FILE;
569 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
570 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
571 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
573 for (auto i: f.node_children("Notification")) {
574 int const id = number_attribute<int>(i, "Id", "id");
575 if (id >= 0 && id < NOTIFICATION_COUNT) {
576 _notification[id] = raw_convert<int>(i->content());
580 _barco_username = f.optional_string_child("BarcoUsername");
581 _barco_password = f.optional_string_child("BarcoPassword");
582 _christie_username = f.optional_string_child("ChristieUsername");
583 _christie_password = f.optional_string_child("ChristiePassword");
584 _gdc_username = f.optional_string_child("GDCUsername");
585 _gdc_password = f.optional_string_child("GDCPassword");
587 auto pm = f.optional_string_child("PlayerMode");
588 if (pm && *pm == "window") {
589 _player_mode = PLAYER_MODE_WINDOW;
590 } else if (pm && *pm == "full") {
591 _player_mode = PLAYER_MODE_FULL;
592 } else if (pm && *pm == "dual") {
593 _player_mode = PLAYER_MODE_DUAL;
596 _player_restricted_menus = f.optional_bool_child("PlayerRestrictedMenus").get_value_or(false);
598 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
599 auto vc = f.optional_string_child("VideoViewType");
600 if (vc && *vc == "opengl") {
601 _video_view_type = VIDEO_VIEW_OPENGL;
602 } else if (vc && *vc == "simple") {
603 _video_view_type = VIDEO_VIEW_SIMPLE;
605 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
606 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
607 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
608 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
609 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
611 if (f.optional_node_child("AudioMapping")) {
612 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
615 for (auto i: f.node_children("CustomLanguage")) {
617 /* This will fail if it's called before dcp::init() as it won't recognise the
618 * tag. That's OK because the Config will be reloaded again later.
620 _custom_languages.push_back (dcp::LanguageTag(i->content()));
621 } catch (std::runtime_error& e) {}
624 for (auto& initial: _initial_paths) {
625 initial.second = f.optional_string_child(initial.first);
627 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
628 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
629 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
630 _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
631 if (auto duration = f.optional_node_child("DefaultKDMDuration")) {
632 _default_kdm_duration = RoughDuration(duration);
634 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
636 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
637 _last_release_notes_version = f.optional_string_child("LastReleaseNotesVersion");
638 _main_divider_sash_position = f.optional_number_child<int>("MainDividerSashPosition");
639 _main_content_divider_sash_position = f.optional_number_child<int>("MainContentDividerSashPosition");
641 if (auto loc = f.optional_string_child("DefaultAddFileLocation")) {
642 if (*loc == "last") {
643 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
644 } else if (*loc == "project") {
645 _default_add_file_location = DefaultAddFileLocation::SAME_AS_PROJECT;
649 _allow_smpte_bv20 = f.optional_bool_child("AllowSMPTEBv20").get_value_or(false);
650 _isdcf_name_part_length = f.optional_number_child<int>("ISDCFNamePartLength").get_value_or(14);
652 #ifdef DCPOMATIC_GROK
653 if (auto grok = f.optional_node_child("Grok")) {
658 _export.read(f.optional_node_child("Export"));
661 if (have_existing("config.xml")) {
663 /* We have a config file but it didn't load */
664 FailedToLoad(LoadFailure::CONFIG);
667 /* Make a new set of signing certificates and key */
668 _signer_chain = create_certificate_chain ();
669 /* And similar for decryption of KDMs */
670 _decryption_chain = create_certificate_chain ();
676 Config::read_cinemas()
678 if (dcp::filesystem::exists(_cinemas_file)) {
680 cxml::Document f("Cinemas");
681 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
685 FailedToLoad(LoadFailure::CINEMAS);
693 Config::read_dkdm_recipients()
695 if (dcp::filesystem::exists(_dkdm_recipients_file)) {
697 cxml::Document f("DKDMRecipients");
698 f.read_file(dcp::filesystem::fix_long_path(_dkdm_recipients_file));
699 read_dkdm_recipients(f);
702 FailedToLoad(LoadFailure::DKDM_RECIPIENTS);
703 write_dkdm_recipients();
709 /** @return Singleton instance */
713 if (_instance == nullptr) {
714 _instance = new Config;
721 /** Write our configuration to disk */
723 Config::write () const
727 write_dkdm_recipients ();
731 Config::write_config () const
734 auto root = doc.create_root_node ("Config");
736 /* [XML] Version The version number of the configuration file format. */
737 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
738 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
739 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
740 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
741 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
742 if (_default_directory) {
743 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
744 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
746 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
747 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
748 by the batch converter to listen for job requests.
750 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
751 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
752 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
754 for (auto i: _servers) {
755 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
758 root->add_child("Server")->add_child_text (i);
761 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
762 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
764 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
765 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
766 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
767 /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */
768 root->add_child("TMSPassive")->add_child_text(_tms_passive ? "1" : "0");
769 /* [XML] TMSIP IP address of TMS. */
770 root->add_child("TMSIP")->add_child_text (_tms_ip);
771 /* [XML] TMSPath Path on the TMS to copy files to. */
772 root->add_child("TMSPath")->add_child_text (_tms_path);
773 /* [XML] TMSUser Username to log into the TMS with. */
774 root->add_child("TMSUser")->add_child_text (_tms_user);
775 /* [XML] TMSPassword Password to log into the TMS with. */
776 root->add_child("TMSPassword")->add_child_text (_tms_password);
778 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
779 root->add_child("Language")->add_child_text (_language.get());
781 if (_default_dcp_content_type) {
782 /* [XML:opt] DefaultDCPContentType Default content type to use when creating new films (<code>FTR</code>, <code>SHR</code>,
783 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
784 <code>PSA</code> or <code>ADV</code>). */
785 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
787 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
788 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
789 /* [XML] DCPIssuer Issuer text to write into CPL files. */
790 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
791 /* [XML] DCPCreator Creator text to write into CPL files. */
792 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
793 /* [XML] Company name to write into MXF files. */
794 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
795 /* [XML] Product name to write into MXF files. */
796 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
797 /* [XML] Product version to write into MXF files. */
798 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
799 /* [XML] Comment to write into JPEG2000 data. */
800 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
801 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
802 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
804 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
805 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
806 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
807 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
808 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
809 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
810 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
811 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
812 if (_default_audio_language) {
813 /* [XML] DefaultAudioLanguage Default audio language to use for new films */
814 root->add_child("DefaultAudioLanguage")->add_child_text(_default_audio_language->to_string());
816 if (_default_territory) {
817 /* [XML] DefaultTerritory Default territory to use for new films */
818 root->add_child("DefaultTerritory")->add_child_text(_default_territory->subtag());
820 for (auto const& i: _default_metadata) {
821 auto c = root->add_child("DefaultMetadata");
822 c->set_attribute("key", i.first);
823 c->add_child_text(i.second);
825 if (_default_kdm_directory) {
826 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
827 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
829 _default_kdm_duration.as_xml(root->add_child("DefaultKDMDuration"));
830 /* [XML] MailServer Hostname of SMTP server to use. */
831 root->add_child("MailServer")->add_child_text (_mail_server);
832 /* [XML] MailPort Port number to use on SMTP server. */
833 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
834 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
835 switch (_mail_protocol) {
836 case EmailProtocol::AUTO:
837 root->add_child("MailProtocol")->add_child_text("Auto");
839 case EmailProtocol::PLAIN:
840 root->add_child("MailProtocol")->add_child_text("Plain");
842 case EmailProtocol::STARTTLS:
843 root->add_child("MailProtocol")->add_child_text("STARTTLS");
845 case EmailProtocol::SSL:
846 root->add_child("MailProtocol")->add_child_text("SSL");
849 /* [XML] MailUser Username to use on SMTP server. */
850 root->add_child("MailUser")->add_child_text (_mail_user);
851 /* [XML] MailPassword Password to use on SMTP server. */
852 root->add_child("MailPassword")->add_child_text (_mail_password);
854 /* [XML] KDMSubject Subject to use for KDM emails. */
855 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
856 /* [XML] KDMFrom From address to use for KDM emails. */
857 root->add_child("KDMFrom")->add_child_text (_kdm_from);
858 for (auto i: _kdm_cc) {
859 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
860 root->add_child("KDMCC")->add_child_text (i);
862 /* [XML] KDMBCC BCC address to use for KDM emails. */
863 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
864 /* [XML] KDMEmail Text of KDM email. */
865 root->add_child("KDMEmail")->add_child_text (_kdm_email);
867 /* [XML] NotificationSubject Subject to use for notification emails. */
868 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
869 /* [XML] NotificationFrom From address to use for notification emails. */
870 root->add_child("NotificationFrom")->add_child_text (_notification_from);
871 /* [XML] NotificationFrom To address to use for notification emails. */
872 root->add_child("NotificationTo")->add_child_text (_notification_to);
873 for (auto i: _notification_cc) {
874 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
875 root->add_child("NotificationCC")->add_child_text (i);
877 /* [XML] NotificationBCC BCC address to use for notification emails. */
878 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
879 /* [XML] NotificationEmail Text of notification email. */
880 root->add_child("NotificationEmail")->add_child_text (_notification_email);
882 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
883 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
884 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
885 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
887 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
888 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
889 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
890 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
891 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
892 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
893 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
894 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
895 /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
896 root->add_child("UseAllAudioChannels")->add_child_text(_use_all_audio_channels ? "1" : "0");
897 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
898 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
899 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
900 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
901 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
902 related to the player, 1024 debug information related to audio analyses.
904 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
905 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
906 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
907 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
908 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
909 #ifdef DCPOMATIC_WINDOWS
910 if (_win32_console) {
911 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
912 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
913 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
915 root->add_child("Win32Console")->add_child_text ("1");
919 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
920 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
922 auto signer = root->add_child ("Signer");
923 DCPOMATIC_ASSERT (_signer_chain);
924 for (auto const& i: _signer_chain->unordered()) {
925 signer->add_child("Certificate")->add_child_text (i.certificate (true));
927 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
929 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
930 auto decryption = root->add_child ("Decryption");
931 DCPOMATIC_ASSERT (_decryption_chain);
932 for (auto const& i: _decryption_chain->unordered()) {
933 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
935 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
937 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
940 for (auto i: _history) {
941 root->add_child("History")->add_child_text (i.string ());
944 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
947 for (auto i: _player_history) {
948 root->add_child("PlayerHistory")->add_child_text (i.string ());
951 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
952 or <code><DKDM></code> tags.
954 /* [XML] DKDM A DKDM as XML */
955 _dkdms->as_xml (root);
957 /* [XML] CinemasFile Filename of cinemas list file. */
958 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
959 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
960 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
961 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
962 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
963 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
964 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
965 /* [XML] KDMFilenameFormat Format for KDM filenames. */
966 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
967 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
968 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
969 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
970 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
971 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
972 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
973 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
974 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
975 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
976 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
977 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
978 for (int i = 0; i < NAG_COUNT; ++i) {
979 xmlpp::Element* e = root->add_child ("Nagged");
980 e->set_attribute("id", raw_convert<string>(i));
981 e->add_child_text (_nagged[i] ? "1" : "0");
983 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
984 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
986 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
987 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
989 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
990 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
991 if (_last_player_load_directory) {
992 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
994 /* [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. */
995 if (_last_kdm_write_type) {
996 switch (_last_kdm_write_type.get()) {
998 root->add_child("LastKDMWriteType")->add_child_text("flat");
1000 case KDM_WRITE_FOLDER:
1001 root->add_child("LastKDMWriteType")->add_child_text("folder");
1004 root->add_child("LastKDMWriteType")->add_child_text("zip");
1008 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
1009 if (_last_dkdm_write_type) {
1010 switch (_last_dkdm_write_type.get()) {
1011 case DKDM_WRITE_INTERNAL:
1012 root->add_child("LastDKDMWriteType")->add_child_text("internal");
1014 case DKDM_WRITE_FILE:
1015 root->add_child("LastDKDMWriteType")->add_child_text("file");
1019 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
1020 frames to be held in memory at once.
1022 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
1024 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
1025 if (_decode_reduction) {
1026 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
1029 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
1030 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
1032 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
1033 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
1034 xmlpp::Element* e = root->add_child ("Notification");
1035 e->set_attribute("id", raw_convert<string>(i));
1036 e->add_child_text (_notification[i] ? "1" : "0");
1039 if (_barco_username) {
1040 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
1041 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
1043 if (_barco_password) {
1044 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
1045 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
1048 if (_christie_username) {
1049 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
1050 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
1052 if (_christie_password) {
1053 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
1054 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
1057 if (_gdc_username) {
1058 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
1059 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
1061 if (_gdc_password) {
1062 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
1063 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
1066 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
1067 with separate (advanced) controls.
1069 switch (_player_mode) {
1070 case PLAYER_MODE_WINDOW:
1071 root->add_child("PlayerMode")->add_child_text("window");
1073 case PLAYER_MODE_FULL:
1074 root->add_child("PlayerMode")->add_child_text("full");
1076 case PLAYER_MODE_DUAL:
1077 root->add_child("PlayerMode")->add_child_text("dual");
1081 if (_player_restricted_menus) {
1082 root->add_child("PlayerRestrictedMenus")->add_child_text("1");
1085 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
1086 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
1087 switch (_video_view_type) {
1088 case VIDEO_VIEW_SIMPLE:
1089 root->add_child("VideoViewType")->add_child_text("simple");
1091 case VIDEO_VIEW_OPENGL:
1092 root->add_child("VideoViewType")->add_child_text("opengl");
1095 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1096 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
1097 if (_player_debug_log_file) {
1098 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1099 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
1101 if (_player_content_directory) {
1102 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1103 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
1105 if (_player_playlist_directory) {
1106 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1107 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1109 if (_player_kdm_directory) {
1110 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1111 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1113 if (_audio_mapping) {
1114 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1116 for (auto const& i: _custom_languages) {
1117 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1119 for (auto const& initial: _initial_paths) {
1120 if (initial.second) {
1121 root->add_child(initial.first)->add_child_text(initial.second->string());
1124 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1125 root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0");
1126 root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0");
1127 root->add_child("DefaultKDMType")->add_child_text(dcp::formulation_to_string(_default_kdm_type));
1128 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1129 if (_last_release_notes_version) {
1130 root->add_child("LastReleaseNotesVersion")->add_child_text(*_last_release_notes_version);
1132 if (_main_divider_sash_position) {
1133 root->add_child("MainDividerSashPosition")->add_child_text(raw_convert<string>(*_main_divider_sash_position));
1135 if (_main_content_divider_sash_position) {
1136 root->add_child("MainContentDividerSashPosition")->add_child_text(raw_convert<string>(*_main_content_divider_sash_position));
1139 root->add_child("DefaultAddFileLocation")->add_child_text(
1140 _default_add_file_location == DefaultAddFileLocation::SAME_AS_LAST_TIME ? "last" : "project"
1143 /* [XML] AllowSMPTEBv20 1 to allow the user to choose SMPTE (Bv2.0 only) as a standard, otherwise 0 */
1144 root->add_child("AllowSMPTEBv20")->add_child_text(_allow_smpte_bv20 ? "1" : "0");
1145 /* [XML] ISDCFNamePartLength Maximum length of the "name" part of an ISDCF name, which should be 14 according to the standard */
1146 root->add_child("ISDCFNamePartLength")->add_child_text(raw_convert<string>(_isdcf_name_part_length));
1148 #ifdef DCPOMATIC_GROK
1150 _grok->as_xml(root->add_child("Grok"));
1154 _export.write(root->add_child("Export"));
1156 auto target = config_write_file();
1159 auto const s = doc.write_to_string_formatted ();
1160 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1161 dcp::File f(tmp, "w");
1163 throw FileError (_("Could not open file for writing"), tmp);
1165 f.checked_write(s.c_str(), s.bytes());
1167 dcp::filesystem::remove(target);
1168 dcp::filesystem::rename(tmp, target);
1169 } catch (xmlpp::exception& e) {
1170 string s = e.what ();
1172 throw FileError (s, target);
1179 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1181 xmlpp::Document doc;
1182 auto root = doc.create_root_node (root_node);
1183 root->add_child("Version")->add_child_text(version);
1185 for (auto i: things) {
1186 i->as_xml (root->add_child(node));
1190 doc.write_to_file_formatted (file.string() + ".tmp");
1191 dcp::filesystem::remove(file);
1192 dcp::filesystem::rename(file.string() + ".tmp", file);
1193 } catch (xmlpp::exception& e) {
1194 string s = e.what ();
1196 throw FileError (s, file);
1202 Config::write_cinemas () const
1204 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1209 Config::write_dkdm_recipients () const
1211 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1215 boost::filesystem::path
1216 Config::default_directory_or (boost::filesystem::path a) const
1218 return directory_or (_default_directory, a);
1221 boost::filesystem::path
1222 Config::default_kdm_directory_or (boost::filesystem::path a) const
1224 return directory_or (_default_kdm_directory, a);
1227 boost::filesystem::path
1228 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1234 boost::system::error_code ec;
1235 auto const e = dcp::filesystem::exists(*dir, ec);
1247 _instance = nullptr;
1251 Config::changed (Property what)
1257 Config::set_kdm_email_to_default ()
1259 _kdm_subject = _("KDM delivery: $CPL_NAME");
1262 "Dear Projectionist\n\n"
1263 "Please find attached KDMs for $CPL_NAME.\n\n"
1264 "Cinema: $CINEMA_NAME\n"
1265 "Screen(s): $SCREENS\n\n"
1266 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1267 "Best regards,\nDCP-o-matic"
1272 Config::set_notification_email_to_default ()
1274 _notification_subject = _("DCP-o-matic notification");
1276 _notification_email = _(
1277 "$JOB_NAME: $JOB_STATUS"
1282 Config::reset_kdm_email ()
1284 set_kdm_email_to_default ();
1289 Config::reset_notification_email ()
1291 set_notification_email_to_default ();
1296 Config::set_cover_sheet_to_default ()
1300 "CPL Filename: $CPL_FILENAME\n"
1302 "Format: $CONTAINER\n"
1304 "Audio Language: $AUDIO_LANGUAGE\n"
1305 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1312 Config::add_to_history (boost::filesystem::path p)
1314 add_to_history_internal (_history, p);
1317 /** Remove non-existent items from the history */
1319 Config::clean_history ()
1321 clean_history_internal (_history);
1325 Config::add_to_player_history (boost::filesystem::path p)
1327 add_to_history_internal (_player_history, p);
1330 /** Remove non-existent items from the player history */
1332 Config::clean_player_history ()
1334 clean_history_internal (_player_history);
1338 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1340 /* Remove existing instances of this path in the history */
1341 h.erase (remove (h.begin(), h.end(), p), h.end ());
1343 h.insert (h.begin (), p);
1344 if (h.size() > HISTORY_SIZE) {
1352 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1358 if (dcp::filesystem::is_directory(i)) {
1362 /* We couldn't find out if it's a directory for some reason; just ignore it */
1369 Config::have_existing (string file)
1371 return dcp::filesystem::exists(read_path(file));
1376 Config::read_cinemas (cxml::Document const & f)
1379 for (auto i: f.node_children("Cinema")) {
1380 /* Slightly grotty two-part construction of Cinema here so that we can use
1383 auto cinema = make_shared<Cinema>(i);
1384 cinema->read_screens (i);
1385 _cinemas.push_back (cinema);
1390 Config::set_cinemas_file (boost::filesystem::path file)
1392 if (file == _cinemas_file) {
1396 _cinemas_file = file;
1398 if (dcp::filesystem::exists(_cinemas_file)) {
1399 /* Existing file; read it in */
1400 cxml::Document f ("Cinemas");
1401 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
1411 Config::read_dkdm_recipients (cxml::Document const & f)
1413 _dkdm_recipients.clear ();
1414 for (auto i: f.node_children("DKDMRecipient")) {
1415 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1421 Config::save_template (shared_ptr<const Film> film, string name) const
1423 film->write_template (template_write_path(name));
1428 Config::templates () const
1430 if (!dcp::filesystem::exists(read_path("templates"))) {
1435 for (auto const& i: dcp::filesystem::directory_iterator(read_path("templates"))) {
1436 n.push_back (i.path().filename().string());
1442 Config::existing_template (string name) const
1444 return dcp::filesystem::exists(template_read_path(name));
1448 boost::filesystem::path
1449 Config::template_read_path (string name) const
1451 return read_path("templates") / tidy_for_filename (name);
1455 boost::filesystem::path
1456 Config::template_write_path (string name) const
1458 return write_path("templates") / tidy_for_filename (name);
1463 Config::rename_template (string old_name, string new_name) const
1465 dcp::filesystem::rename(template_read_path(old_name), template_write_path(new_name));
1469 Config::delete_template (string name) const
1471 dcp::filesystem::remove(template_write_path(name));
1474 /** @return Path to the config.xml containing the actual settings, following a link if required */
1475 boost::filesystem::path
1476 config_file (boost::filesystem::path main)
1478 cxml::Document f ("Config");
1479 if (!dcp::filesystem::exists(main)) {
1480 /* It doesn't exist, so there can't be any links; just return it */
1484 /* See if there's a link */
1486 f.read_file(dcp::filesystem::fix_long_path(main));
1487 auto link = f.optional_string_child("Link");
1491 } catch (xmlpp::exception& e) {
1492 /* There as a problem reading the main configuration file,
1493 so there can't be a link.
1501 boost::filesystem::path
1502 Config::config_read_file ()
1504 return config_file (read_path("config.xml"));
1508 boost::filesystem::path
1509 Config::config_write_file ()
1511 return config_file (write_path("config.xml"));
1516 Config::reset_cover_sheet ()
1518 set_cover_sheet_to_default ();
1523 Config::link (boost::filesystem::path new_file) const
1525 xmlpp::Document doc;
1526 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1528 doc.write_to_file_formatted(write_path("config.xml").string());
1529 } catch (xmlpp::exception& e) {
1530 string s = e.what ();
1532 throw FileError (s, write_path("config.xml"));
1537 Config::copy_and_link (boost::filesystem::path new_file) const
1540 dcp::filesystem::copy_file(config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1545 Config::have_write_permission () const
1547 dcp::File f(config_write_file(), "r+");
1548 return static_cast<bool>(f);
1551 /** @param output_channels Number of output channels in use.
1552 * @return Audio mapping for this output channel count (may be a default).
1555 Config::audio_mapping (int output_channels)
1557 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1558 /* Set up a default */
1559 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1560 if (output_channels == 2) {
1561 /* Special case for stereo output.
1562 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1563 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1565 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1566 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1567 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1568 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1569 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1570 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1571 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1572 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1575 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1576 _audio_mapping->set (i, i, 1);
1581 return *_audio_mapping;
1585 Config::set_audio_mapping (AudioMapping m)
1588 changed (AUDIO_MAPPING);
1592 Config::set_audio_mapping_to_default ()
1594 DCPOMATIC_ASSERT (_audio_mapping);
1595 auto const ch = _audio_mapping->output_channels ();
1596 _audio_mapping = boost::none;
1597 _audio_mapping = audio_mapping (ch);
1598 changed (AUDIO_MAPPING);
1603 Config::add_custom_language (dcp::LanguageTag tag)
1605 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1606 _custom_languages.push_back (tag);
1612 optional<Config::BadReason>
1613 Config::check_certificates () const
1615 optional<BadReason> bad;
1617 for (auto const& i: _signer_chain->unordered()) {
1618 if (i.has_utf8_strings()) {
1619 bad = BAD_SIGNER_UTF8_STRINGS;
1621 if ((i.not_after().year() - i.not_before().year()) > 15) {
1622 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1624 if (dcp::escape_digest(i.subject_dn_qualifier()) != dcp::public_key_digest(i.public_key())) {
1625 bad = BAD_SIGNER_DN_QUALIFIER;
1629 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1630 bad = BAD_SIGNER_INCONSISTENT;
1633 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1634 bad = BAD_DECRYPTION_INCONSISTENT;
1642 save_all_config_as_zip (boost::filesystem::path zip_file)
1644 Zipper zipper (zip_file);
1646 auto config = Config::instance();
1647 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1648 if (dcp::filesystem::exists(config->cinemas_file())) {
1649 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1651 if (dcp::filesystem::exists(config->dkdm_recipients_file())) {
1652 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
1660 Config::load_from_zip(boost::filesystem::path zip_file)
1662 Unzipper unzipper(zip_file);
1663 dcp::write_string_to_file(unzipper.get("config.xml"), config_write_file());
1666 dcp::write_string_to_file(unzipper.get("cinemas.xml"), cinemas_file());
1667 dcp::write_string_to_file(unzipper.get("dkdm_recipient.xml"), dkdm_recipients_file());
1668 } catch (std::runtime_error&) {}
1672 changed(Property::USE_ANY_SERVERS);
1673 changed(Property::SERVERS);
1674 changed(Property::CINEMAS);
1675 changed(Property::DKDM_RECIPIENTS);
1676 changed(Property::SOUND);
1677 changed(Property::SOUND_OUTPUT);
1678 changed(Property::PLAYER_CONTENT_DIRECTORY);
1679 changed(Property::PLAYER_PLAYLIST_DIRECTORY);
1680 changed(Property::PLAYER_DEBUG_LOG);
1681 changed(Property::HISTORY);
1682 changed(Property::SHOW_EXPERIMENTAL_AUDIO_PROCESSORS);
1683 changed(Property::AUDIO_MAPPING);
1684 changed(Property::AUTO_CROP_THRESHOLD);
1685 changed(Property::ALLOW_SMPTE_BV20);
1686 changed(Property::ISDCF_NAME_PART_LENGTH);
1687 changed(Property::OTHER);
1692 Config::set_initial_path(string id, boost::filesystem::path path)
1694 auto iter = _initial_paths.find(id);
1695 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1696 iter->second = path;
1701 optional<boost::filesystem::path>
1702 Config::initial_path(string id) const
1704 auto iter = _initial_paths.find(id);
1705 if (iter == _initial_paths.end()) {
1708 return iter->second;
1712 #ifdef DCPOMATIC_GROK
1714 Config::Grok::Grok(cxml::ConstNodePtr node)
1715 : enable(node->bool_child("Enable"))
1716 , binary_location(node->string_child("BinaryLocation"))
1717 , selected(node->number_child<int>("Selected"))
1718 , licence_server(node->string_child("LicenceServer"))
1719 , licence_port(node->number_child<int>("LicencePort"))
1720 , licence(node->string_child("Licence"))
1727 Config::Grok::as_xml(xmlpp::Element* node) const
1729 node->add_child("BinaryLocation")->add_child_text(binary_location.string());
1730 node->add_child("Enable")->add_child_text((enable ? "1" : "0"));
1731 node->add_child("Selected")->add_child_text(raw_convert<string>(selected));
1732 node->add_child("LicenceServer")->add_child_text(licence_server);
1733 node->add_child("LicencePort")->add_child_text(raw_convert<string>(licence_port));
1734 node->add_child("Licence")->add_child_text(licence);
1739 Config::set_grok(Grok const& grok)