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;
181 _playlist_editor_restricted_menus = false;
183 _video_view_type = VIDEO_VIEW_SIMPLE;
184 _respect_kdm_validity_periods = true;
185 _player_debug_log_file = boost::none;
186 _player_content_directory = boost::none;
187 _player_playlist_directory = boost::none;
188 _player_kdm_directory = boost::none;
189 _audio_mapping = boost::none;
190 _custom_languages.clear ();
191 _initial_paths.clear();
192 _initial_paths["AddFilesPath"] = boost::none;
193 _initial_paths["AddKDMPath"] = boost::none;
194 _initial_paths["AddDKDMPath"] = boost::none;
195 _initial_paths["SelectCertificatePath"] = boost::none;
196 _initial_paths["AddCombinerInputPath"] = boost::none;
197 _initial_paths["ExportSubtitlesPath"] = boost::none;
198 _initial_paths["ExportVideoPath"] = boost::none;
199 _initial_paths["DebugLogPath"] = boost::none;
200 _initial_paths["CinemaDatabasePath"] = boost::none;
201 _initial_paths["ConfigFilePath"] = boost::none;
202 _initial_paths["Preferences"] = boost::none;
203 _initial_paths["SaveVerificationReport"] = boost::none;
204 _use_isdcf_name_by_default = true;
205 _write_kdms_to_disk = true;
207 _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
208 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
209 _auto_crop_threshold = 0.1;
210 _last_release_notes_version = boost::none;
211 _allow_smpte_bv20 = false;
212 _isdcf_name_part_length = 14;
214 _allowed_dcp_frame_rates.clear ();
215 _allowed_dcp_frame_rates.push_back (24);
216 _allowed_dcp_frame_rates.push_back (25);
217 _allowed_dcp_frame_rates.push_back (30);
218 _allowed_dcp_frame_rates.push_back (48);
219 _allowed_dcp_frame_rates.push_back (50);
220 _allowed_dcp_frame_rates.push_back (60);
222 set_kdm_email_to_default ();
223 set_notification_email_to_default ();
224 set_cover_sheet_to_default ();
226 #ifdef DCPOMATIC_GROK
230 _main_divider_sash_position = {};
231 _main_content_divider_sash_position = {};
233 _export.set_defaults();
237 Config::restore_defaults ()
239 Config::instance()->set_defaults ();
240 Config::instance()->changed ();
243 shared_ptr<dcp::CertificateChain>
244 Config::create_certificate_chain ()
246 return make_shared<dcp::CertificateChain> (
248 CERTIFICATE_VALIDITY_PERIOD,
251 ".dcpomatic.smpte-430-2.ROOT",
252 ".dcpomatic.smpte-430-2.INTERMEDIATE",
253 "CS.dcpomatic.smpte-430-2.LEAF"
260 using namespace boost::filesystem;
262 auto copy_adding_number = [](path const& path_to_copy) {
264 auto add_number = [](path const& path, int number) {
265 return String::compose("%1.%2", path, number);
269 while (n < 100 && exists(add_number(path_to_copy, n))) {
272 boost::system::error_code ec;
273 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
276 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
277 * to write over. This is more intended for the situation where we have a corrupted config.xml,
278 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
279 * file). But we might as well back up the other files while we're about it.
282 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
283 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
285 copy_adding_number (config_write_file());
287 /* These do not use State::write_path, so whatever path is in the Config we will copy
290 copy_adding_number (_cinemas_file);
291 copy_adding_number (_dkdm_recipients_file);
299 read_dkdm_recipients();
304 Config::read_config()
307 cxml::Document f ("Config");
308 f.read_file(dcp::filesystem::fix_long_path(config_read_file()));
310 auto version = f.optional_number_child<int> ("Version");
311 if (version && *version < _current_version) {
312 /* Back up the old config before we re-write it in a back-incompatible way */
316 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
317 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
319 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
320 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
323 _default_directory = f.optional_string_child ("DefaultDirectory");
324 if (_default_directory && _default_directory->empty ()) {
325 /* We used to store an empty value for this to mean "none set" */
326 _default_directory = boost::optional<boost::filesystem::path> ();
329 auto b = f.optional_number_child<int> ("ServerPort");
331 b = f.optional_number_child<int> ("ServerPortBase");
333 _server_port_base = b.get ();
335 auto u = f.optional_bool_child ("UseAnyServers");
336 _use_any_servers = u.get_value_or (true);
338 for (auto i: f.node_children("Server")) {
339 if (i->node_children("HostName").size() == 1) {
340 _servers.push_back (i->string_child ("HostName"));
342 _servers.push_back (i->content ());
346 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
347 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
348 _tms_passive = f.optional_bool_child("TMSPassive").get_value_or(true);
349 _tms_ip = f.string_child ("TMSIP");
350 _tms_path = f.string_child ("TMSPath");
351 _tms_user = f.string_child ("TMSUser");
352 _tms_password = f.string_child ("TMSPassword");
354 _language = f.optional_string_child ("Language");
356 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
357 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
359 if (f.optional_string_child ("DCPMetadataIssuer")) {
360 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
361 } else if (f.optional_string_child ("DCPIssuer")) {
362 _dcp_issuer = f.string_child ("DCPIssuer");
365 auto up = f.optional_bool_child("UploadAfterMakeDCP");
367 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
369 _upload_after_make_dcp = up.get_value_or (false);
370 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
371 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
372 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
373 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
374 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
376 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
377 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
378 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
379 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
382 auto al = f.optional_string_child("DefaultAudioLanguage");
384 _default_audio_language = dcp::LanguageTag(*al);
386 } catch (std::runtime_error&) {}
389 auto te = f.optional_string_child("DefaultTerritory");
391 _default_territory = dcp::LanguageTag::RegionSubtag(*te);
393 } catch (std::runtime_error&) {}
395 for (auto const& i: f.node_children("DefaultMetadata")) {
396 _default_metadata[i->string_attribute("key")] = i->content();
399 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
401 /* Read any cinemas that are still lying around in the config file
402 * from an old version.
406 _mail_server = f.string_child ("MailServer");
407 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
410 /* Make sure this matches the code in write_config */
411 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
412 if (protocol == "Auto") {
413 _mail_protocol = EmailProtocol::AUTO;
414 } else if (protocol == "Plain") {
415 _mail_protocol = EmailProtocol::PLAIN;
416 } else if (protocol == "STARTTLS") {
417 _mail_protocol = EmailProtocol::STARTTLS;
418 } else if (protocol == "SSL") {
419 _mail_protocol = EmailProtocol::SSL;
423 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
424 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
426 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
427 _kdm_from = f.string_child ("KDMFrom");
428 for (auto i: f.node_children("KDMCC")) {
429 if (!i->content().empty()) {
430 _kdm_cc.push_back (i->content ());
433 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
434 _kdm_email = f.string_child ("KDMEmail");
436 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
437 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
438 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
439 for (auto i: f.node_children("NotificationCC")) {
440 if (!i->content().empty()) {
441 _notification_cc.push_back (i->content ());
444 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
445 if (f.optional_string_child("NotificationEmail")) {
446 _notification_email = f.string_child("NotificationEmail");
449 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
450 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
452 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
453 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
454 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
455 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
456 _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false);
457 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
459 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
460 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
461 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
462 #ifdef DCPOMATIC_WINDOWS
463 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
466 for (auto i: f.node_children("History")) {
467 _history.push_back (i->content ());
470 for (auto i: f.node_children("PlayerHistory")) {
471 _player_history.push_back (i->content ());
474 auto signer = f.optional_node_child ("Signer");
476 auto c = make_shared<dcp::CertificateChain>();
477 /* Read the signing certificates and private key in from the config file */
478 for (auto i: signer->node_children ("Certificate")) {
479 c->add (dcp::Certificate (i->content ()));
481 c->set_key (signer->string_child ("PrivateKey"));
484 /* Make a new set of signing certificates and key */
485 _signer_chain = create_certificate_chain ();
488 auto decryption = f.optional_node_child ("Decryption");
490 auto c = make_shared<dcp::CertificateChain>();
491 for (auto i: decryption->node_children ("Certificate")) {
492 c->add (dcp::Certificate (i->content ()));
494 c->set_key (decryption->string_child ("PrivateKey"));
495 _decryption_chain = c;
497 _decryption_chain = create_certificate_chain ();
500 /* These must be done before we call Bad as that might set one
503 for (auto i: f.node_children("Nagged")) {
504 auto const id = number_attribute<int>(i, "Id", "id");
505 if (id >= 0 && id < NAG_COUNT) {
506 _nagged[id] = raw_convert<int>(i->content());
510 auto bad = check_certificates ();
512 auto const remake = Bad(*bad);
513 if (remake && *remake) {
515 case BAD_SIGNER_UTF8_STRINGS:
516 case BAD_SIGNER_INCONSISTENT:
517 case BAD_SIGNER_VALIDITY_TOO_LONG:
518 case BAD_SIGNER_DN_QUALIFIER:
519 _signer_chain = create_certificate_chain ();
521 case BAD_DECRYPTION_INCONSISTENT:
522 _decryption_chain = create_certificate_chain ();
528 if (f.optional_node_child("DKDMGroup")) {
529 /* New-style: all DKDMs in a group */
530 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
532 /* Old-style: one or more DKDM nodes */
533 _dkdms = make_shared<DKDMGroup>("root");
534 for (auto i: f.node_children("DKDM")) {
535 _dkdms->add (DKDMBase::read (i));
538 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
539 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
540 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
541 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
542 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
543 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
544 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
545 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
546 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
547 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
548 /* The variable was renamed but not the XML tag */
549 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
550 _sound_output = f.optional_string_child("PreviewSoundOutput");
551 if (f.optional_string_child("CoverSheet")) {
552 _cover_sheet = f.optional_string_child("CoverSheet").get();
554 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
555 if (f.optional_string_child("LastKDMWriteType")) {
556 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
557 _last_kdm_write_type = KDM_WRITE_FLAT;
558 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
559 _last_kdm_write_type = KDM_WRITE_FOLDER;
560 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
561 _last_kdm_write_type = KDM_WRITE_ZIP;
564 if (f.optional_string_child("LastDKDMWriteType")) {
565 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
566 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
567 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
568 _last_dkdm_write_type = DKDM_WRITE_FILE;
571 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
572 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
573 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
575 for (auto i: f.node_children("Notification")) {
576 int const id = number_attribute<int>(i, "Id", "id");
577 if (id >= 0 && id < NOTIFICATION_COUNT) {
578 _notification[id] = raw_convert<int>(i->content());
582 _barco_username = f.optional_string_child("BarcoUsername");
583 _barco_password = f.optional_string_child("BarcoPassword");
584 _christie_username = f.optional_string_child("ChristieUsername");
585 _christie_password = f.optional_string_child("ChristiePassword");
586 _gdc_username = f.optional_string_child("GDCUsername");
587 _gdc_password = f.optional_string_child("GDCPassword");
589 auto pm = f.optional_string_child("PlayerMode");
590 if (pm && *pm == "window") {
591 _player_mode = PLAYER_MODE_WINDOW;
592 } else if (pm && *pm == "full") {
593 _player_mode = PLAYER_MODE_FULL;
594 } else if (pm && *pm == "dual") {
595 _player_mode = PLAYER_MODE_DUAL;
598 _player_restricted_menus = f.optional_bool_child("PlayerRestrictedMenus").get_value_or(false);
599 _playlist_editor_restricted_menus = f.optional_bool_child("PlaylistEditorRestrictedMenus").get_value_or(false);
601 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
602 auto vc = f.optional_string_child("VideoViewType");
603 if (vc && *vc == "opengl") {
604 _video_view_type = VIDEO_VIEW_OPENGL;
605 } else if (vc && *vc == "simple") {
606 _video_view_type = VIDEO_VIEW_SIMPLE;
608 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
609 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
610 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
611 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
612 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
614 if (f.optional_node_child("AudioMapping")) {
615 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
618 for (auto i: f.node_children("CustomLanguage")) {
620 /* This will fail if it's called before dcp::init() as it won't recognise the
621 * tag. That's OK because the Config will be reloaded again later.
623 _custom_languages.push_back (dcp::LanguageTag(i->content()));
624 } catch (std::runtime_error& e) {}
627 for (auto& initial: _initial_paths) {
628 initial.second = f.optional_string_child(initial.first);
630 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
631 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
632 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
633 _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
634 if (auto duration = f.optional_node_child("DefaultKDMDuration")) {
635 _default_kdm_duration = RoughDuration(duration);
637 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
639 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
640 _last_release_notes_version = f.optional_string_child("LastReleaseNotesVersion");
641 _main_divider_sash_position = f.optional_number_child<int>("MainDividerSashPosition");
642 _main_content_divider_sash_position = f.optional_number_child<int>("MainContentDividerSashPosition");
644 if (auto loc = f.optional_string_child("DefaultAddFileLocation")) {
645 if (*loc == "last") {
646 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
647 } else if (*loc == "project") {
648 _default_add_file_location = DefaultAddFileLocation::SAME_AS_PROJECT;
652 _allow_smpte_bv20 = f.optional_bool_child("AllowSMPTEBv20").get_value_or(false);
653 _isdcf_name_part_length = f.optional_number_child<int>("ISDCFNamePartLength").get_value_or(14);
655 #ifdef DCPOMATIC_GROK
656 if (auto grok = f.optional_node_child("Grok")) {
661 _export.read(f.optional_node_child("Export"));
664 if (have_existing("config.xml")) {
666 /* We have a config file but it didn't load */
667 FailedToLoad(LoadFailure::CONFIG);
670 /* Make a new set of signing certificates and key */
671 _signer_chain = create_certificate_chain ();
672 /* And similar for decryption of KDMs */
673 _decryption_chain = create_certificate_chain ();
679 Config::read_cinemas()
681 if (dcp::filesystem::exists(_cinemas_file)) {
683 cxml::Document f("Cinemas");
684 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
688 FailedToLoad(LoadFailure::CINEMAS);
696 Config::read_dkdm_recipients()
698 if (dcp::filesystem::exists(_dkdm_recipients_file)) {
700 cxml::Document f("DKDMRecipients");
701 f.read_file(dcp::filesystem::fix_long_path(_dkdm_recipients_file));
702 read_dkdm_recipients(f);
705 FailedToLoad(LoadFailure::DKDM_RECIPIENTS);
706 write_dkdm_recipients();
712 /** @return Singleton instance */
716 if (_instance == nullptr) {
717 _instance = new Config;
724 /** Write our configuration to disk */
726 Config::write () const
730 write_dkdm_recipients ();
734 Config::write_config () const
737 auto root = doc.create_root_node ("Config");
739 /* [XML] Version The version number of the configuration file format. */
740 cxml::add_text_child(root, "Version", raw_convert<string>(_current_version));
741 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
742 cxml::add_text_child(root, "MasterEncodingThreads", raw_convert<string>(_master_encoding_threads));
743 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
744 cxml::add_text_child(root, "ServerEncodingThreads", raw_convert<string>(_server_encoding_threads));
745 if (_default_directory) {
746 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
747 cxml::add_text_child(root, "DefaultDirectory", _default_directory->string());
749 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
750 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
751 by the batch converter to listen for job requests.
753 cxml::add_text_child(root, "ServerPortBase", raw_convert<string>(_server_port_base));
754 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
755 cxml::add_text_child(root, "UseAnyServers", _use_any_servers ? "1" : "0");
757 for (auto i: _servers) {
758 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
761 cxml::add_text_child(root, "Server", i);
764 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
765 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
767 cxml::add_text_child(root, "OnlyServersEncode", _only_servers_encode ? "1" : "0");
768 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
769 cxml::add_text_child(root, "TMSProtocol", raw_convert<string>(static_cast<int>(_tms_protocol)));
770 /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */
771 cxml::add_text_child(root, "TMSPassive", _tms_passive ? "1" : "0");
772 /* [XML] TMSIP IP address of TMS. */
773 cxml::add_text_child(root, "TMSIP", _tms_ip);
774 /* [XML] TMSPath Path on the TMS to copy files to. */
775 cxml::add_text_child(root, "TMSPath", _tms_path);
776 /* [XML] TMSUser Username to log into the TMS with. */
777 cxml::add_text_child(root, "TMSUser", _tms_user);
778 /* [XML] TMSPassword Password to log into the TMS with. */
779 cxml::add_text_child(root, "TMSPassword", _tms_password);
781 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
782 cxml::add_text_child(root, "Language", _language.get());
784 if (_default_dcp_content_type) {
785 /* [XML:opt] DefaultDCPContentType Default content type to use when creating new films (<code>FTR</code>, <code>SHR</code>,
786 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
787 <code>PSA</code> or <code>ADV</code>). */
788 cxml::add_text_child(root, "DefaultDCPContentType", _default_dcp_content_type->isdcf_name());
790 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
791 cxml::add_text_child(root, "DefaultDCPAudioChannels", raw_convert<string>(_default_dcp_audio_channels));
792 /* [XML] DCPIssuer Issuer text to write into CPL files. */
793 cxml::add_text_child(root, "DCPIssuer", _dcp_issuer);
794 /* [XML] DCPCreator Creator text to write into CPL files. */
795 cxml::add_text_child(root, "DCPCreator", _dcp_creator);
796 /* [XML] Company name to write into MXF files. */
797 cxml::add_text_child(root, "DCPCompanyName", _dcp_company_name);
798 /* [XML] Product name to write into MXF files. */
799 cxml::add_text_child(root, "DCPProductName", _dcp_product_name);
800 /* [XML] Product version to write into MXF files. */
801 cxml::add_text_child(root, "DCPProductVersion", _dcp_product_version);
802 /* [XML] Comment to write into JPEG2000 data. */
803 cxml::add_text_child(root, "DCPJ2KComment", _dcp_j2k_comment);
804 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
805 cxml::add_text_child(root, "UploadAfterMakeDCP", _upload_after_make_dcp ? "1" : "0");
807 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
808 cxml::add_text_child(root, "DefaultStillLength", raw_convert<string>(_default_still_length));
809 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
810 cxml::add_text_child(root, "DefaultJ2KBandwidth", raw_convert<string>(_default_j2k_bandwidth));
811 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
812 cxml::add_text_child(root, "DefaultAudioDelay", raw_convert<string>(_default_audio_delay));
813 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
814 cxml::add_text_child(root, "DefaultInterop", _default_interop ? "1" : "0");
815 if (_default_audio_language) {
816 /* [XML] DefaultAudioLanguage Default audio language to use for new films */
817 cxml::add_text_child(root, "DefaultAudioLanguage", _default_audio_language->to_string());
819 if (_default_territory) {
820 /* [XML] DefaultTerritory Default territory to use for new films */
821 cxml::add_text_child(root, "DefaultTerritory", _default_territory->subtag());
823 for (auto const& i: _default_metadata) {
824 auto c = cxml::add_child(root, "DefaultMetadata");
825 c->set_attribute("key", i.first);
826 c->add_child_text(i.second);
828 if (_default_kdm_directory) {
829 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
830 cxml::add_text_child(root, "DefaultKDMDirectory", _default_kdm_directory->string ());
832 _default_kdm_duration.as_xml(cxml::add_child(root, "DefaultKDMDuration"));
833 /* [XML] MailServer Hostname of SMTP server to use. */
834 cxml::add_text_child(root, "MailServer", _mail_server);
835 /* [XML] MailPort Port number to use on SMTP server. */
836 cxml::add_text_child(root, "MailPort", raw_convert<string>(_mail_port));
837 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
838 switch (_mail_protocol) {
839 case EmailProtocol::AUTO:
840 cxml::add_text_child(root, "MailProtocol", "Auto");
842 case EmailProtocol::PLAIN:
843 cxml::add_text_child(root, "MailProtocol", "Plain");
845 case EmailProtocol::STARTTLS:
846 cxml::add_text_child(root, "MailProtocol", "STARTTLS");
848 case EmailProtocol::SSL:
849 cxml::add_text_child(root, "MailProtocol", "SSL");
852 /* [XML] MailUser Username to use on SMTP server. */
853 cxml::add_text_child(root, "MailUser", _mail_user);
854 /* [XML] MailPassword Password to use on SMTP server. */
855 cxml::add_text_child(root, "MailPassword", _mail_password);
857 /* [XML] KDMSubject Subject to use for KDM emails. */
858 cxml::add_text_child(root, "KDMSubject", _kdm_subject);
859 /* [XML] KDMFrom From address to use for KDM emails. */
860 cxml::add_text_child(root, "KDMFrom", _kdm_from);
861 for (auto i: _kdm_cc) {
862 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
863 cxml::add_text_child(root, "KDMCC", i);
865 /* [XML] KDMBCC BCC address to use for KDM emails. */
866 cxml::add_text_child(root, "KDMBCC", _kdm_bcc);
867 /* [XML] KDMEmail Text of KDM email. */
868 cxml::add_text_child(root, "KDMEmail", _kdm_email);
870 /* [XML] NotificationSubject Subject to use for notification emails. */
871 cxml::add_text_child(root, "NotificationSubject", _notification_subject);
872 /* [XML] NotificationFrom From address to use for notification emails. */
873 cxml::add_text_child(root, "NotificationFrom", _notification_from);
874 /* [XML] NotificationFrom To address to use for notification emails. */
875 cxml::add_text_child(root, "NotificationTo", _notification_to);
876 for (auto i: _notification_cc) {
877 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
878 cxml::add_text_child(root, "NotificationCC", i);
880 /* [XML] NotificationBCC BCC address to use for notification emails. */
881 cxml::add_text_child(root, "NotificationBCC", _notification_bcc);
882 /* [XML] NotificationEmail Text of notification email. */
883 cxml::add_text_child(root, "NotificationEmail", _notification_email);
885 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
886 cxml::add_text_child(root, "CheckForUpdates", _check_for_updates ? "1" : "0");
887 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
888 cxml::add_text_child(root, "CheckForTestUpdates", _check_for_test_updates ? "1" : "0");
890 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
891 cxml::add_text_child(root, "MaximumJ2KBandwidth", raw_convert<string>(_maximum_j2k_bandwidth));
892 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
893 cxml::add_text_child(root, "AllowAnyDCPFrameRate", _allow_any_dcp_frame_rate ? "1" : "0");
894 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
895 cxml::add_text_child(root, "AllowAnyContainer", _allow_any_container ? "1" : "0");
896 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
897 cxml::add_text_child(root, "Allow96kHzAudio", _allow_96khz_audio ? "1" : "0");
898 /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
899 cxml::add_text_child(root, "UseAllAudioChannels", _use_all_audio_channels ? "1" : "0");
900 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
901 cxml::add_text_child(root, "ShowExperimentalAudioProcessors", _show_experimental_audio_processors ? "1" : "0");
902 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
903 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
904 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
905 related to the player, 1024 debug information related to audio analyses.
907 cxml::add_text_child(root, "LogTypes", raw_convert<string> (_log_types));
908 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
909 cxml::add_text_child(root, "AnalyseEBUR128", _analyse_ebur128 ? "1" : "0");
910 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
911 cxml::add_text_child(root, "AutomaticAudioAnalysis", _automatic_audio_analysis ? "1" : "0");
912 #ifdef DCPOMATIC_WINDOWS
913 if (_win32_console) {
914 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
915 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
916 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
918 cxml::add_text_child(root, "Win32Console", "1");
922 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
923 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
925 auto signer = cxml::add_child(root, "Signer");
926 DCPOMATIC_ASSERT (_signer_chain);
927 for (auto const& i: _signer_chain->unordered()) {
928 cxml::add_text_child(signer, "Certificate", i.certificate (true));
930 cxml::add_text_child(signer, "PrivateKey", _signer_chain->key().get ());
932 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
933 auto decryption = cxml::add_child(root, "Decryption");
934 DCPOMATIC_ASSERT (_decryption_chain);
935 for (auto const& i: _decryption_chain->unordered()) {
936 cxml::add_text_child(decryption, "Certificate", i.certificate (true));
938 cxml::add_text_child(decryption, "PrivateKey", _decryption_chain->key().get());
940 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
943 for (auto i: _history) {
944 cxml::add_text_child(root, "History", i.string());
947 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
950 for (auto i: _player_history) {
951 cxml::add_text_child(root, "PlayerHistory", i.string());
954 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
955 or <code><DKDM></code> tags.
957 /* [XML] DKDM A DKDM as XML */
958 _dkdms->as_xml (root);
960 /* [XML] CinemasFile Filename of cinemas list file. */
961 cxml::add_text_child(root, "CinemasFile", _cinemas_file.string());
962 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
963 cxml::add_text_child(root, "DKDMRecipientsFile", _dkdm_recipients_file.string());
964 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
965 cxml::add_text_child(root, "ShowHintsBeforeMakeDCP", _show_hints_before_make_dcp ? "1" : "0");
966 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
967 cxml::add_text_child(root, "ConfirmKDMEmail", _confirm_kdm_email ? "1" : "0");
968 /* [XML] KDMFilenameFormat Format for KDM filenames. */
969 cxml::add_text_child(root, "KDMFilenameFormat", _kdm_filename_format.specification());
970 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
971 cxml::add_text_child(root, "DKDMFilenameFormat", _dkdm_filename_format.specification());
972 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
973 cxml::add_text_child(root, "KDMContainerNameFormat", _kdm_container_name_format.specification());
974 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
975 cxml::add_text_child(root, "DCPMetadataFilenameFormat", _dcp_metadata_filename_format.specification());
976 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
977 cxml::add_text_child(root, "DCPAssetFilenameFormat", _dcp_asset_filename_format.specification());
978 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
979 cxml::add_text_child(root, "JumpToSelected", _jump_to_selected ? "1" : "0");
980 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
981 for (int i = 0; i < NAG_COUNT; ++i) {
982 auto e = cxml::add_child(root, "Nagged");
983 e->set_attribute("id", raw_convert<string>(i));
984 e->add_child_text (_nagged[i] ? "1" : "0");
986 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
987 cxml::add_text_child(root, "PreviewSound", _sound ? "1" : "0");
989 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
990 cxml::add_text_child(root, "PreviewSoundOutput", _sound_output.get());
992 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
993 cxml::add_text_child(root, "CoverSheet", _cover_sheet);
994 if (_last_player_load_directory) {
995 cxml::add_text_child(root, "LastPlayerLoadDirectory", _last_player_load_directory->string());
997 /* [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. */
998 if (_last_kdm_write_type) {
999 switch (_last_kdm_write_type.get()) {
1000 case KDM_WRITE_FLAT:
1001 cxml::add_text_child(root, "LastKDMWriteType", "flat");
1003 case KDM_WRITE_FOLDER:
1004 cxml::add_text_child(root, "LastKDMWriteType", "folder");
1007 cxml::add_text_child(root, "LastKDMWriteType", "zip");
1011 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
1012 if (_last_dkdm_write_type) {
1013 switch (_last_dkdm_write_type.get()) {
1014 case DKDM_WRITE_INTERNAL:
1015 cxml::add_text_child(root, "LastDKDMWriteType", "internal");
1017 case DKDM_WRITE_FILE:
1018 cxml::add_text_child(root, "LastDKDMWriteType", "file");
1022 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
1023 frames to be held in memory at once.
1025 cxml::add_text_child(root, "FramesInMemoryMultiplier", raw_convert<string>(_frames_in_memory_multiplier));
1027 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
1028 if (_decode_reduction) {
1029 cxml::add_text_child(root, "DecodeReduction", raw_convert<string>(_decode_reduction.get()));
1032 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
1033 cxml::add_text_child(root, "DefaultNotify", _default_notify ? "1" : "0");
1035 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
1036 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
1037 auto e = cxml::add_child(root, "Notification");
1038 e->set_attribute ("id", raw_convert<string>(i));
1039 e->add_child_text (_notification[i] ? "1" : "0");
1042 if (_barco_username) {
1043 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
1044 cxml::add_text_child(root, "BarcoUsername", *_barco_username);
1046 if (_barco_password) {
1047 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
1048 cxml::add_text_child(root, "BarcoPassword", *_barco_password);
1051 if (_christie_username) {
1052 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
1053 cxml::add_text_child(root, "ChristieUsername", *_christie_username);
1055 if (_christie_password) {
1056 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
1057 cxml::add_text_child(root, "ChristiePassword", *_christie_password);
1060 if (_gdc_username) {
1061 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
1062 cxml::add_text_child(root, "GDCUsername", *_gdc_username);
1064 if (_gdc_password) {
1065 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
1066 cxml::add_text_child(root, "GDCPassword", *_gdc_password);
1069 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
1070 with separate (advanced) controls.
1072 switch (_player_mode) {
1073 case PLAYER_MODE_WINDOW:
1074 cxml::add_text_child(root, "PlayerMode", "window");
1076 case PLAYER_MODE_FULL:
1077 cxml::add_text_child(root, "PlayerMode", "full");
1079 case PLAYER_MODE_DUAL:
1080 cxml::add_text_child(root, "PlayerMode", "dual");
1084 if (_player_restricted_menus) {
1085 cxml::add_text_child(root, "PlayerRestrictedMenus", "1");
1088 if (_playlist_editor_restricted_menus) {
1089 cxml::add_text_child(root, "PlaylistEditorRestrictedMenus", "1");
1092 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
1093 cxml::add_text_child(root, "ImageDisplay", raw_convert<string>(_image_display));
1094 switch (_video_view_type) {
1095 case VIDEO_VIEW_SIMPLE:
1096 cxml::add_text_child(root, "VideoViewType", "simple");
1098 case VIDEO_VIEW_OPENGL:
1099 cxml::add_text_child(root, "VideoViewType", "opengl");
1102 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1103 cxml::add_text_child(root, "RespectKDMValidityPeriods", _respect_kdm_validity_periods ? "1" : "0");
1104 if (_player_debug_log_file) {
1105 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1106 cxml::add_text_child(root, "PlayerDebugLogFile", _player_debug_log_file->string());
1108 if (_player_content_directory) {
1109 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1110 cxml::add_text_child(root, "PlayerContentDirectory", _player_content_directory->string());
1112 if (_player_playlist_directory) {
1113 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1114 cxml::add_text_child(root, "PlayerPlaylistDirectory", _player_playlist_directory->string());
1116 if (_player_kdm_directory) {
1117 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1118 cxml::add_text_child(root, "PlayerKDMDirectory", _player_kdm_directory->string());
1120 if (_audio_mapping) {
1121 _audio_mapping->as_xml(cxml::add_child(root, "AudioMapping"));
1123 for (auto const& i: _custom_languages) {
1124 cxml::add_text_child(root, "CustomLanguage", i.to_string());
1126 for (auto const& initial: _initial_paths) {
1127 if (initial.second) {
1128 cxml::add_text_child(root, initial.first, initial.second->string());
1131 cxml::add_text_child(root, "UseISDCFNameByDefault", _use_isdcf_name_by_default ? "1" : "0");
1132 cxml::add_text_child(root, "WriteKDMsToDisk", _write_kdms_to_disk ? "1" : "0");
1133 cxml::add_text_child(root, "EmailKDMs", _email_kdms ? "1" : "0");
1134 cxml::add_text_child(root, "DefaultKDMType", dcp::formulation_to_string(_default_kdm_type));
1135 cxml::add_text_child(root, "AutoCropThreshold", raw_convert<string>(_auto_crop_threshold));
1136 if (_last_release_notes_version) {
1137 cxml::add_text_child(root, "LastReleaseNotesVersion", *_last_release_notes_version);
1139 if (_main_divider_sash_position) {
1140 cxml::add_text_child(root, "MainDividerSashPosition", raw_convert<string>(*_main_divider_sash_position));
1142 if (_main_content_divider_sash_position) {
1143 cxml::add_text_child(root, "MainContentDividerSashPosition", raw_convert<string>(*_main_content_divider_sash_position));
1146 cxml::add_text_child(root, "DefaultAddFileLocation",
1147 _default_add_file_location == DefaultAddFileLocation::SAME_AS_LAST_TIME ? "last" : "project"
1150 /* [XML] AllowSMPTEBv20 1 to allow the user to choose SMPTE (Bv2.0 only) as a standard, otherwise 0 */
1151 cxml::add_text_child(root, "AllowSMPTEBv20", _allow_smpte_bv20 ? "1" : "0");
1152 /* [XML] ISDCFNamePartLength Maximum length of the "name" part of an ISDCF name, which should be 14 according to the standard */
1153 cxml::add_text_child(root, "ISDCFNamePartLength", raw_convert<string>(_isdcf_name_part_length));
1155 #ifdef DCPOMATIC_GROK
1157 _grok->as_xml(cxml::add_child(root, "Grok"));
1161 _export.write(cxml::add_child(root, "Export"));
1163 auto target = config_write_file();
1166 auto const s = doc.write_to_string_formatted ();
1167 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1168 dcp::File f(tmp, "w");
1170 throw FileError (_("Could not open file for writing"), tmp);
1172 f.checked_write(s.c_str(), s.bytes());
1174 dcp::filesystem::remove(target);
1175 dcp::filesystem::rename(tmp, target);
1176 } catch (xmlpp::exception& e) {
1177 string s = e.what ();
1179 throw FileError (s, target);
1186 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1188 xmlpp::Document doc;
1189 auto root = doc.create_root_node (root_node);
1190 cxml::add_text_child(root, "Version", version);
1192 for (auto i: things) {
1193 i->as_xml(cxml::add_child(root, node));
1197 doc.write_to_file_formatted (file.string() + ".tmp");
1198 dcp::filesystem::remove(file);
1199 dcp::filesystem::rename(file.string() + ".tmp", file);
1200 } catch (xmlpp::exception& e) {
1201 string s = e.what ();
1203 throw FileError (s, file);
1209 Config::write_cinemas () const
1211 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1216 Config::write_dkdm_recipients () const
1218 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1222 boost::filesystem::path
1223 Config::default_directory_or (boost::filesystem::path a) const
1225 return directory_or (_default_directory, a);
1228 boost::filesystem::path
1229 Config::default_kdm_directory_or (boost::filesystem::path a) const
1231 return directory_or (_default_kdm_directory, a);
1234 boost::filesystem::path
1235 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1241 boost::system::error_code ec;
1242 auto const e = dcp::filesystem::exists(*dir, ec);
1254 _instance = nullptr;
1258 Config::changed (Property what)
1264 Config::set_kdm_email_to_default ()
1266 _kdm_subject = _("KDM delivery: $CPL_NAME");
1269 "Dear Projectionist\n\n"
1270 "Please find attached KDMs for $CPL_NAME.\n\n"
1271 "Cinema: $CINEMA_NAME\n"
1272 "Screen(s): $SCREENS\n\n"
1273 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1274 "Best regards,\nDCP-o-matic"
1279 Config::set_notification_email_to_default ()
1281 _notification_subject = _("DCP-o-matic notification");
1283 _notification_email = _(
1284 "$JOB_NAME: $JOB_STATUS"
1289 Config::reset_kdm_email ()
1291 set_kdm_email_to_default ();
1296 Config::reset_notification_email ()
1298 set_notification_email_to_default ();
1303 Config::set_cover_sheet_to_default ()
1307 "CPL Filename: $CPL_FILENAME\n"
1309 "Format: $CONTAINER\n"
1311 "Audio Language: $AUDIO_LANGUAGE\n"
1312 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1319 Config::add_to_history (boost::filesystem::path p)
1321 add_to_history_internal (_history, p);
1324 /** Remove non-existent items from the history */
1326 Config::clean_history ()
1328 clean_history_internal (_history);
1332 Config::add_to_player_history (boost::filesystem::path p)
1334 add_to_history_internal (_player_history, p);
1337 /** Remove non-existent items from the player history */
1339 Config::clean_player_history ()
1341 clean_history_internal (_player_history);
1345 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1347 /* Remove existing instances of this path in the history */
1348 h.erase (remove (h.begin(), h.end(), p), h.end ());
1350 h.insert (h.begin (), p);
1351 if (h.size() > HISTORY_SIZE) {
1359 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1365 if (dcp::filesystem::is_directory(i)) {
1369 /* We couldn't find out if it's a directory for some reason; just ignore it */
1376 Config::have_existing (string file)
1378 return dcp::filesystem::exists(read_path(file));
1383 Config::read_cinemas (cxml::Document const & f)
1386 for (auto i: f.node_children("Cinema")) {
1387 /* Slightly grotty two-part construction of Cinema here so that we can use
1390 auto cinema = make_shared<Cinema>(i);
1391 cinema->read_screens (i);
1392 _cinemas.push_back (cinema);
1397 Config::set_cinemas_file (boost::filesystem::path file)
1399 if (file == _cinemas_file) {
1403 _cinemas_file = file;
1405 if (dcp::filesystem::exists(_cinemas_file)) {
1406 /* Existing file; read it in */
1407 cxml::Document f ("Cinemas");
1408 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
1418 Config::read_dkdm_recipients (cxml::Document const & f)
1420 _dkdm_recipients.clear ();
1421 for (auto i: f.node_children("DKDMRecipient")) {
1422 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1428 Config::save_template (shared_ptr<const Film> film, string name) const
1430 film->write_template (template_write_path(name));
1435 Config::templates () const
1437 if (!dcp::filesystem::exists(read_path("templates"))) {
1442 for (auto const& i: dcp::filesystem::directory_iterator(read_path("templates"))) {
1443 n.push_back (i.path().filename().string());
1449 Config::existing_template (string name) const
1451 return dcp::filesystem::exists(template_read_path(name));
1455 boost::filesystem::path
1456 Config::template_read_path (string name) const
1458 return read_path("templates") / tidy_for_filename (name);
1462 boost::filesystem::path
1463 Config::template_write_path (string name) const
1465 return write_path("templates") / tidy_for_filename (name);
1470 Config::rename_template (string old_name, string new_name) const
1472 dcp::filesystem::rename(template_read_path(old_name), template_write_path(new_name));
1476 Config::delete_template (string name) const
1478 dcp::filesystem::remove(template_write_path(name));
1481 /** @return Path to the config.xml containing the actual settings, following a link if required */
1482 boost::filesystem::path
1483 config_file (boost::filesystem::path main)
1485 cxml::Document f ("Config");
1486 if (!dcp::filesystem::exists(main)) {
1487 /* It doesn't exist, so there can't be any links; just return it */
1491 /* See if there's a link */
1493 f.read_file(dcp::filesystem::fix_long_path(main));
1494 auto link = f.optional_string_child("Link");
1498 } catch (xmlpp::exception& e) {
1499 /* There as a problem reading the main configuration file,
1500 so there can't be a link.
1508 boost::filesystem::path
1509 Config::config_read_file ()
1511 return config_file (read_path("config.xml"));
1515 boost::filesystem::path
1516 Config::config_write_file ()
1518 return config_file (write_path("config.xml"));
1523 Config::reset_cover_sheet ()
1525 set_cover_sheet_to_default ();
1530 Config::link (boost::filesystem::path new_file) const
1532 xmlpp::Document doc;
1533 cxml::add_text_child(doc.create_root_node("Config"), "Link", new_file.string());
1535 doc.write_to_file_formatted(write_path("config.xml").string());
1536 } catch (xmlpp::exception& e) {
1537 string s = e.what ();
1539 throw FileError (s, write_path("config.xml"));
1544 Config::copy_and_link (boost::filesystem::path new_file) const
1547 dcp::filesystem::copy_file(config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1552 Config::have_write_permission () const
1554 dcp::File f(config_write_file(), "r+");
1555 return static_cast<bool>(f);
1558 /** @param output_channels Number of output channels in use.
1559 * @return Audio mapping for this output channel count (may be a default).
1562 Config::audio_mapping (int output_channels)
1564 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1565 /* Set up a default */
1566 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1567 if (output_channels == 2) {
1568 /* Special case for stereo output.
1569 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1570 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1572 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1573 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1574 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1575 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1576 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1577 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1578 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1579 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1582 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1583 _audio_mapping->set (i, i, 1);
1588 return *_audio_mapping;
1592 Config::set_audio_mapping (AudioMapping m)
1595 changed (AUDIO_MAPPING);
1599 Config::set_audio_mapping_to_default ()
1601 DCPOMATIC_ASSERT (_audio_mapping);
1602 auto const ch = _audio_mapping->output_channels ();
1603 _audio_mapping = boost::none;
1604 _audio_mapping = audio_mapping (ch);
1605 changed (AUDIO_MAPPING);
1610 Config::add_custom_language (dcp::LanguageTag tag)
1612 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1613 _custom_languages.push_back (tag);
1619 optional<Config::BadReason>
1620 Config::check_certificates () const
1622 optional<BadReason> bad;
1624 for (auto const& i: _signer_chain->unordered()) {
1625 if (i.has_utf8_strings()) {
1626 bad = BAD_SIGNER_UTF8_STRINGS;
1628 if ((i.not_after().year() - i.not_before().year()) > 15) {
1629 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1631 if (dcp::escape_digest(i.subject_dn_qualifier()) != dcp::public_key_digest(i.public_key())) {
1632 bad = BAD_SIGNER_DN_QUALIFIER;
1636 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1637 bad = BAD_SIGNER_INCONSISTENT;
1640 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1641 bad = BAD_DECRYPTION_INCONSISTENT;
1649 save_all_config_as_zip (boost::filesystem::path zip_file)
1651 Zipper zipper (zip_file);
1653 auto config = Config::instance();
1654 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1655 if (dcp::filesystem::exists(config->cinemas_file())) {
1656 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1658 if (dcp::filesystem::exists(config->dkdm_recipients_file())) {
1659 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
1667 Config::load_from_zip(boost::filesystem::path zip_file)
1669 Unzipper unzipper(zip_file);
1670 dcp::write_string_to_file(unzipper.get("config.xml"), config_write_file());
1673 dcp::write_string_to_file(unzipper.get("cinemas.xml"), cinemas_file());
1674 dcp::write_string_to_file(unzipper.get("dkdm_recipient.xml"), dkdm_recipients_file());
1675 } catch (std::runtime_error&) {}
1679 changed(Property::USE_ANY_SERVERS);
1680 changed(Property::SERVERS);
1681 changed(Property::CINEMAS);
1682 changed(Property::DKDM_RECIPIENTS);
1683 changed(Property::SOUND);
1684 changed(Property::SOUND_OUTPUT);
1685 changed(Property::PLAYER_CONTENT_DIRECTORY);
1686 changed(Property::PLAYER_PLAYLIST_DIRECTORY);
1687 changed(Property::PLAYER_DEBUG_LOG);
1688 changed(Property::HISTORY);
1689 changed(Property::SHOW_EXPERIMENTAL_AUDIO_PROCESSORS);
1690 changed(Property::AUDIO_MAPPING);
1691 changed(Property::AUTO_CROP_THRESHOLD);
1692 changed(Property::ALLOW_SMPTE_BV20);
1693 changed(Property::ISDCF_NAME_PART_LENGTH);
1694 changed(Property::OTHER);
1699 Config::set_initial_path(string id, boost::filesystem::path path)
1701 auto iter = _initial_paths.find(id);
1702 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1703 iter->second = path;
1708 optional<boost::filesystem::path>
1709 Config::initial_path(string id) const
1711 auto iter = _initial_paths.find(id);
1712 if (iter == _initial_paths.end()) {
1715 return iter->second;
1719 #ifdef DCPOMATIC_GROK
1721 Config::Grok::Grok(cxml::ConstNodePtr node)
1722 : enable(node->bool_child("Enable"))
1723 , binary_location(node->string_child("BinaryLocation"))
1724 , selected(node->number_child<int>("Selected"))
1725 , licence_server(node->string_child("LicenceServer"))
1726 , licence_port(node->number_child<int>("LicencePort"))
1727 , licence(node->string_child("Licence"))
1734 Config::Grok::as_xml(xmlpp::Element* node) const
1736 node->add_child("BinaryLocation")->add_child_text(binary_location.string());
1737 node->add_child("Enable")->add_child_text((enable ? "1" : "0"));
1738 node->add_child("Selected")->add_child_text(raw_convert<string>(selected));
1739 node->add_child("LicenceServer")->add_child_text(licence_server);
1740 node->add_child("LicencePort")->add_child_text(raw_convert<string>(licence_port));
1741 node->add_child("Licence")->add_child_text(licence);
1746 Config::set_grok(Grok const& grok)