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"
38 #include <dcp/certificate_chain.h>
39 #include <dcp/name_format.h>
40 #include <dcp/raw_convert.h>
41 #include <libcxml/cxml.h>
43 #include <libxml++/libxml++.h>
44 #include <boost/filesystem.hpp>
45 #include <boost/algorithm/string.hpp>
46 #include <boost/thread.hpp>
55 using std::dynamic_pointer_cast;
58 using std::make_shared;
62 using std::shared_ptr;
65 using boost::algorithm::trim;
66 using boost::optional;
67 using dcp::raw_convert;
70 Config* Config::_instance = 0;
71 int const Config::_current_version = 3;
72 boost::signals2::signal<void (Config::LoadFailure)> Config::FailedToLoad;
73 boost::signals2::signal<void (string)> Config::Warning;
74 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
77 /** Construct default configuration */
79 /* DKDMs are not considered a thing to reset on set_defaults() */
80 : _dkdms (new DKDMGroup ("root"))
81 , _default_kdm_duration (1, RoughDuration::Unit::WEEKS)
88 Config::set_defaults ()
90 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
91 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
92 _server_port_base = 6192;
93 _use_any_servers = true;
95 _only_servers_encode = false;
96 _tms_protocol = FileTransferProtocol::SCP;
102 _allow_any_dcp_frame_rate = false;
103 _allow_any_container = false;
104 _allow_96khz_audio = false;
105 _use_all_audio_channels = false;
106 _show_experimental_audio_processors = false;
107 _language = optional<string> ();
108 _default_still_length = 10;
109 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
110 _default_dcp_audio_channels = 8;
111 _default_video_bit_rate[VideoEncoding::JPEG2000] = 150000000;
112 _default_video_bit_rate[VideoEncoding::MPEG2] = 5000000;
113 _default_audio_delay = 0;
114 _default_interop = false;
115 _default_metadata.clear ();
116 _upload_after_make_dcp = false;
119 _mail_protocol = EmailProtocol::AUTO;
125 _notification_from = "";
126 _notification_to = "";
127 _notification_cc.clear ();
128 _notification_bcc = "";
129 _check_for_updates = false;
130 _check_for_test_updates = false;
131 _maximum_video_bit_rate[VideoEncoding::JPEG2000] = 250000000;
132 _maximum_video_bit_rate[VideoEncoding::MPEG2] = 50000000;
133 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
134 _analyse_ebur128 = true;
135 _automatic_audio_analysis = false;
136 #ifdef DCPOMATIC_WINDOWS
137 _win32_console = false;
139 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
140 * ~/.config/dcpomatic2 (or equivalent) and written back there.
142 _cinemas_file = read_path ("cinemas.xml");
143 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
144 _show_hints_before_make_dcp = true;
145 _confirm_kdm_email = true;
146 _kdm_container_name_format = dcp::NameFormat("KDM_%f_%c");
147 _kdm_filename_format = dcp::NameFormat("KDM_%f_%c_%s");
148 _dkdm_filename_format = dcp::NameFormat("DKDM_%f_%c_%s");
149 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
150 _dcp_asset_filename_format = dcp::NameFormat ("%t");
151 _jump_to_selected = true;
152 for (int i = 0; i < NAG_COUNT; ++i) {
156 _sound_output = optional<string> ();
157 _last_kdm_write_type = KDM_WRITE_FLAT;
158 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
159 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
161 /* I think the scaling factor here should be the ratio of the longest frame
162 encode time to the shortest; if the thread count is T, longest time is L
163 and the shortest time S we could encode L/S frames per thread whilst waiting
164 for the L frame to encode so we might have to store LT/S frames.
166 However we don't want to use too much memory, so keep it a bit lower than we'd
167 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
168 use about 240Mb with 72 encoding threads.
170 _frames_in_memory_multiplier = 3;
171 _decode_reduction = optional<int>();
172 _default_notify = false;
173 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
174 _notification[i] = false;
176 _barco_username = optional<string>();
177 _barco_password = optional<string>();
178 _christie_username = optional<string>();
179 _christie_password = optional<string>();
180 _gdc_username = optional<string>();
181 _gdc_password = optional<string>();
182 _player_mode = PLAYER_MODE_WINDOW;
183 _player_restricted_menus = false;
184 _playlist_editor_restricted_menus = false;
186 _video_view_type = VIDEO_VIEW_SIMPLE;
187 _respect_kdm_validity_periods = true;
188 _player_debug_log_file = boost::none;
189 _player_content_directory = boost::none;
190 _player_playlist_directory = boost::none;
191 _player_kdm_directory = boost::none;
192 _audio_mapping = boost::none;
193 _custom_languages.clear ();
194 _initial_paths.clear();
195 _initial_paths["AddFilesPath"] = boost::none;
196 _initial_paths["AddKDMPath"] = boost::none;
197 _initial_paths["AddDKDMPath"] = boost::none;
198 _initial_paths["SelectCertificatePath"] = boost::none;
199 _initial_paths["AddCombinerInputPath"] = boost::none;
200 _initial_paths["ExportSubtitlesPath"] = boost::none;
201 _initial_paths["ExportVideoPath"] = boost::none;
202 _initial_paths["DebugLogPath"] = boost::none;
203 _initial_paths["CinemaDatabasePath"] = boost::none;
204 _initial_paths["ConfigFilePath"] = boost::none;
205 _initial_paths["Preferences"] = boost::none;
206 _initial_paths["SaveVerificationReport"] = boost::none;
207 _use_isdcf_name_by_default = true;
208 _write_kdms_to_disk = true;
210 _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
211 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
212 _auto_crop_threshold = 0.1;
213 _last_release_notes_version = boost::none;
214 _allow_smpte_bv20 = false;
215 _isdcf_name_part_length = 14;
217 _allowed_dcp_frame_rates.clear ();
218 _allowed_dcp_frame_rates.push_back (24);
219 _allowed_dcp_frame_rates.push_back (25);
220 _allowed_dcp_frame_rates.push_back (30);
221 _allowed_dcp_frame_rates.push_back (48);
222 _allowed_dcp_frame_rates.push_back (50);
223 _allowed_dcp_frame_rates.push_back (60);
225 set_kdm_email_to_default ();
226 set_notification_email_to_default ();
227 set_cover_sheet_to_default ();
229 #ifdef DCPOMATIC_GROK
233 _main_divider_sash_position = {};
234 _main_content_divider_sash_position = {};
236 _export.set_defaults();
240 Config::restore_defaults ()
242 Config::instance()->set_defaults ();
243 Config::instance()->changed ();
246 shared_ptr<dcp::CertificateChain>
247 Config::create_certificate_chain ()
249 return make_shared<dcp::CertificateChain> (
251 CERTIFICATE_VALIDITY_PERIOD,
254 ".dcpomatic.smpte-430-2.ROOT",
255 ".dcpomatic.smpte-430-2.INTERMEDIATE",
256 "CS.dcpomatic.smpte-430-2.LEAF"
263 using namespace boost::filesystem;
265 auto copy_adding_number = [](path const& path_to_copy) {
267 auto add_number = [](path const& path, int number) {
268 return String::compose("%1.%2", path, number);
272 while (n < 100 && exists(add_number(path_to_copy, n))) {
275 boost::system::error_code ec;
276 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
279 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
280 * to write over. This is more intended for the situation where we have a corrupted config.xml,
281 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
282 * file). But we might as well back up the other files while we're about it.
285 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
286 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
288 copy_adding_number (config_write_file());
290 /* These do not use State::write_path, so whatever path is in the Config we will copy
293 copy_adding_number (_cinemas_file);
294 copy_adding_number (_dkdm_recipients_file);
302 read_dkdm_recipients();
307 Config::read_config()
310 cxml::Document f ("Config");
311 f.read_file(dcp::filesystem::fix_long_path(config_read_file()));
313 auto version = f.optional_number_child<int> ("Version");
314 if (version && *version < _current_version) {
315 /* Back up the old config before we re-write it in a back-incompatible way */
319 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
320 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
322 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
323 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
326 _default_directory = f.optional_string_child ("DefaultDirectory");
327 if (_default_directory && _default_directory->empty ()) {
328 /* We used to store an empty value for this to mean "none set" */
329 _default_directory = boost::optional<boost::filesystem::path> ();
332 auto b = f.optional_number_child<int> ("ServerPort");
334 b = f.optional_number_child<int> ("ServerPortBase");
336 _server_port_base = b.get ();
338 auto u = f.optional_bool_child ("UseAnyServers");
339 _use_any_servers = u.get_value_or (true);
341 for (auto i: f.node_children("Server")) {
342 if (i->node_children("HostName").size() == 1) {
343 _servers.push_back (i->string_child ("HostName"));
345 _servers.push_back (i->content ());
349 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
350 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
351 _tms_passive = f.optional_bool_child("TMSPassive").get_value_or(true);
352 _tms_ip = f.string_child ("TMSIP");
353 _tms_path = f.string_child ("TMSPath");
354 _tms_user = f.string_child ("TMSUser");
355 _tms_password = f.string_child ("TMSPassword");
357 _language = f.optional_string_child ("Language");
359 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
360 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
362 if (f.optional_string_child ("DCPMetadataIssuer")) {
363 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
364 } else if (f.optional_string_child ("DCPIssuer")) {
365 _dcp_issuer = f.string_child ("DCPIssuer");
368 auto up = f.optional_bool_child("UploadAfterMakeDCP");
370 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
372 _upload_after_make_dcp = up.get_value_or (false);
373 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
374 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
375 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
376 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
377 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
379 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
380 if (auto j2k = f.optional_number_child<int>("DefaultJ2KBandwidth")) {
381 _default_video_bit_rate[VideoEncoding::JPEG2000] = *j2k;
383 _default_video_bit_rate[VideoEncoding::JPEG2000] = f.optional_number_child<int64_t>("DefaultJ2KVideoBitRate").get_value_or(200000000);
385 _default_video_bit_rate[VideoEncoding::MPEG2] = f.optional_number_child<int64_t>("DefaultMPEG2VideoBitRate").get_value_or(5000000);
386 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
387 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
390 auto al = f.optional_string_child("DefaultAudioLanguage");
392 _default_audio_language = dcp::LanguageTag(*al);
394 } catch (std::runtime_error&) {}
397 auto te = f.optional_string_child("DefaultTerritory");
399 _default_territory = dcp::LanguageTag::RegionSubtag(*te);
401 } catch (std::runtime_error&) {}
403 for (auto const& i: f.node_children("DefaultMetadata")) {
404 _default_metadata[i->string_attribute("key")] = i->content();
407 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
409 /* Read any cinemas that are still lying around in the config file
410 * from an old version.
414 _mail_server = f.string_child ("MailServer");
415 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
418 /* Make sure this matches the code in write_config */
419 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
420 if (protocol == "Auto") {
421 _mail_protocol = EmailProtocol::AUTO;
422 } else if (protocol == "Plain") {
423 _mail_protocol = EmailProtocol::PLAIN;
424 } else if (protocol == "STARTTLS") {
425 _mail_protocol = EmailProtocol::STARTTLS;
426 } else if (protocol == "SSL") {
427 _mail_protocol = EmailProtocol::SSL;
431 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
432 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
434 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
435 _kdm_from = f.string_child ("KDMFrom");
436 for (auto i: f.node_children("KDMCC")) {
437 if (!i->content().empty()) {
438 _kdm_cc.push_back (i->content ());
441 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
442 _kdm_email = f.string_child ("KDMEmail");
444 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(variant::insert_dcpomatic(_("%1 notification")));
445 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
446 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
447 for (auto i: f.node_children("NotificationCC")) {
448 if (!i->content().empty()) {
449 _notification_cc.push_back (i->content ());
452 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
453 if (f.optional_string_child("NotificationEmail")) {
454 _notification_email = f.string_child("NotificationEmail");
457 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
458 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
460 if (auto j2k = f.optional_number_child<int>("MaximumJ2KBandwidth")) {
461 _maximum_video_bit_rate[VideoEncoding::JPEG2000] = *j2k;
463 _maximum_video_bit_rate[VideoEncoding::JPEG2000] = f.optional_number_child<int64_t>("MaximumJ2KVideoBitRate").get_value_or(250000000);
465 _maximum_video_bit_rate[VideoEncoding::MPEG2] = f.optional_number_child<int64_t>("MaximumMPEG2VideoBitRate").get_value_or(50000000);
466 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
467 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
468 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
469 _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false);
470 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
472 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
473 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
474 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
475 #ifdef DCPOMATIC_WINDOWS
476 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
479 for (auto i: f.node_children("History")) {
480 _history.push_back (i->content ());
483 for (auto i: f.node_children("PlayerHistory")) {
484 _player_history.push_back (i->content ());
487 auto signer = f.optional_node_child ("Signer");
489 auto c = make_shared<dcp::CertificateChain>();
490 /* Read the signing certificates and private key in from the config file */
491 for (auto i: signer->node_children ("Certificate")) {
492 c->add (dcp::Certificate (i->content ()));
494 c->set_key (signer->string_child ("PrivateKey"));
497 /* Make a new set of signing certificates and key */
498 _signer_chain = create_certificate_chain ();
501 auto decryption = f.optional_node_child ("Decryption");
503 auto c = make_shared<dcp::CertificateChain>();
504 for (auto i: decryption->node_children ("Certificate")) {
505 c->add (dcp::Certificate (i->content ()));
507 c->set_key (decryption->string_child ("PrivateKey"));
508 _decryption_chain = c;
510 _decryption_chain = create_certificate_chain ();
513 /* These must be done before we call Bad as that might set one
516 for (auto i: f.node_children("Nagged")) {
517 auto const id = number_attribute<int>(i, "Id", "id");
518 if (id >= 0 && id < NAG_COUNT) {
519 _nagged[id] = raw_convert<int>(i->content());
523 auto bad = check_certificates ();
525 auto const remake = Bad(*bad);
526 if (remake && *remake) {
528 case BAD_SIGNER_UTF8_STRINGS:
529 case BAD_SIGNER_INCONSISTENT:
530 case BAD_SIGNER_VALIDITY_TOO_LONG:
531 case BAD_SIGNER_DN_QUALIFIER:
532 _signer_chain = create_certificate_chain ();
534 case BAD_DECRYPTION_INCONSISTENT:
535 _decryption_chain = create_certificate_chain ();
541 if (f.optional_node_child("DKDMGroup")) {
542 /* New-style: all DKDMs in a group */
543 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
545 /* Old-style: one or more DKDM nodes */
546 _dkdms = make_shared<DKDMGroup>("root");
547 for (auto i: f.node_children("DKDM")) {
548 _dkdms->add (DKDMBase::read (i));
551 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
552 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
553 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
554 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
555 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
556 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
557 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
558 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
559 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
560 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
561 /* The variable was renamed but not the XML tag */
562 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
563 _sound_output = f.optional_string_child("PreviewSoundOutput");
564 if (f.optional_string_child("CoverSheet")) {
565 _cover_sheet = f.optional_string_child("CoverSheet").get();
567 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
568 if (f.optional_string_child("LastKDMWriteType")) {
569 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
570 _last_kdm_write_type = KDM_WRITE_FLAT;
571 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
572 _last_kdm_write_type = KDM_WRITE_FOLDER;
573 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
574 _last_kdm_write_type = KDM_WRITE_ZIP;
577 if (f.optional_string_child("LastDKDMWriteType")) {
578 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
579 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
580 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
581 _last_dkdm_write_type = DKDM_WRITE_FILE;
584 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
585 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
586 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
588 for (auto i: f.node_children("Notification")) {
589 int const id = number_attribute<int>(i, "Id", "id");
590 if (id >= 0 && id < NOTIFICATION_COUNT) {
591 _notification[id] = raw_convert<int>(i->content());
595 _barco_username = f.optional_string_child("BarcoUsername");
596 _barco_password = f.optional_string_child("BarcoPassword");
597 _christie_username = f.optional_string_child("ChristieUsername");
598 _christie_password = f.optional_string_child("ChristiePassword");
599 _gdc_username = f.optional_string_child("GDCUsername");
600 _gdc_password = f.optional_string_child("GDCPassword");
602 auto pm = f.optional_string_child("PlayerMode");
603 if (pm && *pm == "window") {
604 _player_mode = PLAYER_MODE_WINDOW;
605 } else if (pm && *pm == "full") {
606 _player_mode = PLAYER_MODE_FULL;
607 } else if (pm && *pm == "dual") {
608 _player_mode = PLAYER_MODE_DUAL;
611 _player_restricted_menus = f.optional_bool_child("PlayerRestrictedMenus").get_value_or(false);
612 _playlist_editor_restricted_menus = f.optional_bool_child("PlaylistEditorRestrictedMenus").get_value_or(false);
614 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
615 auto vc = f.optional_string_child("VideoViewType");
616 if (vc && *vc == "opengl") {
617 _video_view_type = VIDEO_VIEW_OPENGL;
618 } else if (vc && *vc == "simple") {
619 _video_view_type = VIDEO_VIEW_SIMPLE;
621 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
622 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
623 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
624 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
625 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
627 if (f.optional_node_child("AudioMapping")) {
628 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
631 for (auto i: f.node_children("CustomLanguage")) {
633 /* This will fail if it's called before dcp::init() as it won't recognise the
634 * tag. That's OK because the Config will be reloaded again later.
636 _custom_languages.push_back (dcp::LanguageTag(i->content()));
637 } catch (std::runtime_error& e) {}
640 for (auto& initial: _initial_paths) {
641 initial.second = f.optional_string_child(initial.first);
643 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
644 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
645 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
646 _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
647 if (auto duration = f.optional_node_child("DefaultKDMDuration")) {
648 _default_kdm_duration = RoughDuration(duration);
650 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
652 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
653 _last_release_notes_version = f.optional_string_child("LastReleaseNotesVersion");
654 _main_divider_sash_position = f.optional_number_child<int>("MainDividerSashPosition");
655 _main_content_divider_sash_position = f.optional_number_child<int>("MainContentDividerSashPosition");
657 if (auto loc = f.optional_string_child("DefaultAddFileLocation")) {
658 if (*loc == "last") {
659 _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
660 } else if (*loc == "project") {
661 _default_add_file_location = DefaultAddFileLocation::SAME_AS_PROJECT;
665 _allow_smpte_bv20 = f.optional_bool_child("AllowSMPTEBv20").get_value_or(false);
666 _isdcf_name_part_length = f.optional_number_child<int>("ISDCFNamePartLength").get_value_or(14);
668 #ifdef DCPOMATIC_GROK
669 if (auto grok = f.optional_node_child("Grok")) {
674 _export.read(f.optional_node_child("Export"));
677 if (have_existing("config.xml")) {
679 /* We have a config file but it didn't load */
680 FailedToLoad(LoadFailure::CONFIG);
683 /* Make a new set of signing certificates and key */
684 _signer_chain = create_certificate_chain ();
685 /* And similar for decryption of KDMs */
686 _decryption_chain = create_certificate_chain ();
692 Config::read_cinemas()
694 if (dcp::filesystem::exists(_cinemas_file)) {
696 cxml::Document f("Cinemas");
697 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
701 FailedToLoad(LoadFailure::CINEMAS);
709 Config::read_dkdm_recipients()
711 if (dcp::filesystem::exists(_dkdm_recipients_file)) {
713 cxml::Document f("DKDMRecipients");
714 f.read_file(dcp::filesystem::fix_long_path(_dkdm_recipients_file));
715 read_dkdm_recipients(f);
718 FailedToLoad(LoadFailure::DKDM_RECIPIENTS);
719 write_dkdm_recipients();
725 /** @return Singleton instance */
729 if (_instance == nullptr) {
730 _instance = new Config;
737 /** Write our configuration to disk */
739 Config::write () const
743 write_dkdm_recipients ();
747 Config::write_config () const
750 auto root = doc.create_root_node ("Config");
752 /* [XML] Version The version number of the configuration file format. */
753 cxml::add_text_child(root, "Version", raw_convert<string>(_current_version));
754 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
755 cxml::add_text_child(root, "MasterEncodingThreads", raw_convert<string>(_master_encoding_threads));
756 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
757 cxml::add_text_child(root, "ServerEncodingThreads", raw_convert<string>(_server_encoding_threads));
758 if (_default_directory) {
759 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
760 cxml::add_text_child(root, "DefaultDirectory", _default_directory->string());
762 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
763 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
764 by the batch converter to listen for job requests.
766 cxml::add_text_child(root, "ServerPortBase", raw_convert<string>(_server_port_base));
767 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
768 cxml::add_text_child(root, "UseAnyServers", _use_any_servers ? "1" : "0");
770 for (auto i: _servers) {
771 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
774 cxml::add_text_child(root, "Server", i);
777 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
778 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
780 cxml::add_text_child(root, "OnlyServersEncode", _only_servers_encode ? "1" : "0");
781 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
782 cxml::add_text_child(root, "TMSProtocol", raw_convert<string>(static_cast<int>(_tms_protocol)));
783 /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */
784 cxml::add_text_child(root, "TMSPassive", _tms_passive ? "1" : "0");
785 /* [XML] TMSIP IP address of TMS. */
786 cxml::add_text_child(root, "TMSIP", _tms_ip);
787 /* [XML] TMSPath Path on the TMS to copy files to. */
788 cxml::add_text_child(root, "TMSPath", _tms_path);
789 /* [XML] TMSUser Username to log into the TMS with. */
790 cxml::add_text_child(root, "TMSUser", _tms_user);
791 /* [XML] TMSPassword Password to log into the TMS with. */
792 cxml::add_text_child(root, "TMSPassword", _tms_password);
794 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
795 cxml::add_text_child(root, "Language", _language.get());
797 if (_default_dcp_content_type) {
798 /* [XML:opt] DefaultDCPContentType Default content type to use when creating new films (<code>FTR</code>, <code>SHR</code>,
799 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
800 <code>PSA</code> or <code>ADV</code>). */
801 cxml::add_text_child(root, "DefaultDCPContentType", _default_dcp_content_type->isdcf_name());
803 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
804 cxml::add_text_child(root, "DefaultDCPAudioChannels", raw_convert<string>(_default_dcp_audio_channels));
805 /* [XML] DCPIssuer Issuer text to write into CPL files. */
806 cxml::add_text_child(root, "DCPIssuer", _dcp_issuer);
807 /* [XML] DCPCreator Creator text to write into CPL files. */
808 cxml::add_text_child(root, "DCPCreator", _dcp_creator);
809 /* [XML] Company name to write into MXF files. */
810 cxml::add_text_child(root, "DCPCompanyName", _dcp_company_name);
811 /* [XML] Product name to write into MXF files. */
812 cxml::add_text_child(root, "DCPProductName", _dcp_product_name);
813 /* [XML] Product version to write into MXF files. */
814 cxml::add_text_child(root, "DCPProductVersion", _dcp_product_version);
815 /* [XML] Comment to write into JPEG2000 data. */
816 cxml::add_text_child(root, "DCPJ2KComment", _dcp_j2k_comment);
817 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
818 cxml::add_text_child(root, "UploadAfterMakeDCP", _upload_after_make_dcp ? "1" : "0");
820 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
821 cxml::add_text_child(root, "DefaultStillLength", raw_convert<string>(_default_still_length));
822 /* [XML] DefaultJ2KVideoBitRate Default bitrate (in bits per second) for JPEG2000 data in new films. */
823 cxml::add_text_child(root, "DefaultJ2KVideoBitRate", raw_convert<string>(_default_video_bit_rate[VideoEncoding::JPEG2000]));
824 /* [XML] DefaultMPEG2VideoBitRate Default bitrate (in bits per second) for MPEG2 data in new films. */
825 cxml::add_text_child(root, "DefaultMPEG2VideoBitRate", raw_convert<string>(_default_video_bit_rate[VideoEncoding::MPEG2]));
826 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
827 cxml::add_text_child(root, "DefaultAudioDelay", raw_convert<string>(_default_audio_delay));
828 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
829 cxml::add_text_child(root, "DefaultInterop", _default_interop ? "1" : "0");
830 if (_default_audio_language) {
831 /* [XML] DefaultAudioLanguage Default audio language to use for new films */
832 cxml::add_text_child(root, "DefaultAudioLanguage", _default_audio_language->to_string());
834 if (_default_territory) {
835 /* [XML] DefaultTerritory Default territory to use for new films */
836 cxml::add_text_child(root, "DefaultTerritory", _default_territory->subtag());
838 for (auto const& i: _default_metadata) {
839 auto c = cxml::add_child(root, "DefaultMetadata");
840 c->set_attribute("key", i.first);
841 c->add_child_text(i.second);
843 if (_default_kdm_directory) {
844 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
845 cxml::add_text_child(root, "DefaultKDMDirectory", _default_kdm_directory->string ());
847 _default_kdm_duration.as_xml(cxml::add_child(root, "DefaultKDMDuration"));
848 /* [XML] MailServer Hostname of SMTP server to use. */
849 cxml::add_text_child(root, "MailServer", _mail_server);
850 /* [XML] MailPort Port number to use on SMTP server. */
851 cxml::add_text_child(root, "MailPort", raw_convert<string>(_mail_port));
852 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
853 switch (_mail_protocol) {
854 case EmailProtocol::AUTO:
855 cxml::add_text_child(root, "MailProtocol", "Auto");
857 case EmailProtocol::PLAIN:
858 cxml::add_text_child(root, "MailProtocol", "Plain");
860 case EmailProtocol::STARTTLS:
861 cxml::add_text_child(root, "MailProtocol", "STARTTLS");
863 case EmailProtocol::SSL:
864 cxml::add_text_child(root, "MailProtocol", "SSL");
867 /* [XML] MailUser Username to use on SMTP server. */
868 cxml::add_text_child(root, "MailUser", _mail_user);
869 /* [XML] MailPassword Password to use on SMTP server. */
870 cxml::add_text_child(root, "MailPassword", _mail_password);
872 /* [XML] KDMSubject Subject to use for KDM emails. */
873 cxml::add_text_child(root, "KDMSubject", _kdm_subject);
874 /* [XML] KDMFrom From address to use for KDM emails. */
875 cxml::add_text_child(root, "KDMFrom", _kdm_from);
876 for (auto i: _kdm_cc) {
877 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
878 cxml::add_text_child(root, "KDMCC", i);
880 /* [XML] KDMBCC BCC address to use for KDM emails. */
881 cxml::add_text_child(root, "KDMBCC", _kdm_bcc);
882 /* [XML] KDMEmail Text of KDM email. */
883 cxml::add_text_child(root, "KDMEmail", _kdm_email);
885 /* [XML] NotificationSubject Subject to use for notification emails. */
886 cxml::add_text_child(root, "NotificationSubject", _notification_subject);
887 /* [XML] NotificationFrom From address to use for notification emails. */
888 cxml::add_text_child(root, "NotificationFrom", _notification_from);
889 /* [XML] NotificationFrom To address to use for notification emails. */
890 cxml::add_text_child(root, "NotificationTo", _notification_to);
891 for (auto i: _notification_cc) {
892 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
893 cxml::add_text_child(root, "NotificationCC", i);
895 /* [XML] NotificationBCC BCC address to use for notification emails. */
896 cxml::add_text_child(root, "NotificationBCC", _notification_bcc);
897 /* [XML] NotificationEmail Text of notification email. */
898 cxml::add_text_child(root, "NotificationEmail", _notification_email);
900 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
901 cxml::add_text_child(root, "CheckForUpdates", _check_for_updates ? "1" : "0");
902 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
903 cxml::add_text_child(root, "CheckForTestUpdates", _check_for_test_updates ? "1" : "0");
905 /* [XML] MaximumJ2KVideoBitRate Maximum video bit rate (in bits per second) that can be specified in the GUI for JPEG2000 encodes. */
906 cxml::add_text_child(root, "MaximumJ2KVideoBitRate", raw_convert<string>(_maximum_video_bit_rate[VideoEncoding::JPEG2000]));
907 /* [XML] MaximumMPEG2VideoBitRate Maximum video bit rate (in bits per second) that can be specified in the GUI for MPEG2 encodes. */
908 cxml::add_text_child(root, "MaximumMPEG2VideoBitRate", raw_convert<string>(_maximum_video_bit_rate[VideoEncoding::MPEG2]));
909 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
910 cxml::add_text_child(root, "AllowAnyDCPFrameRate", _allow_any_dcp_frame_rate ? "1" : "0");
911 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
912 cxml::add_text_child(root, "AllowAnyContainer", _allow_any_container ? "1" : "0");
913 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
914 cxml::add_text_child(root, "Allow96kHzAudio", _allow_96khz_audio ? "1" : "0");
915 /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
916 cxml::add_text_child(root, "UseAllAudioChannels", _use_all_audio_channels ? "1" : "0");
917 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
918 cxml::add_text_child(root, "ShowExperimentalAudioProcessors", _show_experimental_audio_processors ? "1" : "0");
919 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
920 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
921 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
922 related to the player, 1024 debug information related to audio analyses.
924 cxml::add_text_child(root, "LogTypes", raw_convert<string> (_log_types));
925 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
926 cxml::add_text_child(root, "AnalyseEBUR128", _analyse_ebur128 ? "1" : "0");
927 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
928 cxml::add_text_child(root, "AutomaticAudioAnalysis", _automatic_audio_analysis ? "1" : "0");
929 #ifdef DCPOMATIC_WINDOWS
930 if (_win32_console) {
931 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
932 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
933 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
935 cxml::add_text_child(root, "Win32Console", "1");
939 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
940 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
942 auto signer = cxml::add_child(root, "Signer");
943 DCPOMATIC_ASSERT (_signer_chain);
944 for (auto const& i: _signer_chain->unordered()) {
945 cxml::add_text_child(signer, "Certificate", i.certificate (true));
947 cxml::add_text_child(signer, "PrivateKey", _signer_chain->key().get ());
949 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
950 auto decryption = cxml::add_child(root, "Decryption");
951 DCPOMATIC_ASSERT (_decryption_chain);
952 for (auto const& i: _decryption_chain->unordered()) {
953 cxml::add_text_child(decryption, "Certificate", i.certificate (true));
955 cxml::add_text_child(decryption, "PrivateKey", _decryption_chain->key().get());
957 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
960 for (auto i: _history) {
961 cxml::add_text_child(root, "History", i.string());
964 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
967 for (auto i: _player_history) {
968 cxml::add_text_child(root, "PlayerHistory", i.string());
971 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
972 or <code><DKDM></code> tags.
974 /* [XML] DKDM A DKDM as XML */
975 _dkdms->as_xml (root);
977 /* [XML] CinemasFile Filename of cinemas list file. */
978 cxml::add_text_child(root, "CinemasFile", _cinemas_file.string());
979 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
980 cxml::add_text_child(root, "DKDMRecipientsFile", _dkdm_recipients_file.string());
981 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
982 cxml::add_text_child(root, "ShowHintsBeforeMakeDCP", _show_hints_before_make_dcp ? "1" : "0");
983 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
984 cxml::add_text_child(root, "ConfirmKDMEmail", _confirm_kdm_email ? "1" : "0");
985 /* [XML] KDMFilenameFormat Format for KDM filenames. */
986 cxml::add_text_child(root, "KDMFilenameFormat", _kdm_filename_format.specification());
987 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
988 cxml::add_text_child(root, "DKDMFilenameFormat", _dkdm_filename_format.specification());
989 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
990 cxml::add_text_child(root, "KDMContainerNameFormat", _kdm_container_name_format.specification());
991 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
992 cxml::add_text_child(root, "DCPMetadataFilenameFormat", _dcp_metadata_filename_format.specification());
993 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
994 cxml::add_text_child(root, "DCPAssetFilenameFormat", _dcp_asset_filename_format.specification());
995 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
996 cxml::add_text_child(root, "JumpToSelected", _jump_to_selected ? "1" : "0");
997 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
998 for (int i = 0; i < NAG_COUNT; ++i) {
999 auto e = cxml::add_child(root, "Nagged");
1000 e->set_attribute("id", raw_convert<string>(i));
1001 e->add_child_text (_nagged[i] ? "1" : "0");
1003 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
1004 cxml::add_text_child(root, "PreviewSound", _sound ? "1" : "0");
1005 if (_sound_output) {
1006 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
1007 cxml::add_text_child(root, "PreviewSoundOutput", _sound_output.get());
1009 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
1010 cxml::add_text_child(root, "CoverSheet", _cover_sheet);
1011 if (_last_player_load_directory) {
1012 cxml::add_text_child(root, "LastPlayerLoadDirectory", _last_player_load_directory->string());
1014 /* [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. */
1015 if (_last_kdm_write_type) {
1016 switch (_last_kdm_write_type.get()) {
1017 case KDM_WRITE_FLAT:
1018 cxml::add_text_child(root, "LastKDMWriteType", "flat");
1020 case KDM_WRITE_FOLDER:
1021 cxml::add_text_child(root, "LastKDMWriteType", "folder");
1024 cxml::add_text_child(root, "LastKDMWriteType", "zip");
1028 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
1029 if (_last_dkdm_write_type) {
1030 switch (_last_dkdm_write_type.get()) {
1031 case DKDM_WRITE_INTERNAL:
1032 cxml::add_text_child(root, "LastDKDMWriteType", "internal");
1034 case DKDM_WRITE_FILE:
1035 cxml::add_text_child(root, "LastDKDMWriteType", "file");
1039 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
1040 frames to be held in memory at once.
1042 cxml::add_text_child(root, "FramesInMemoryMultiplier", raw_convert<string>(_frames_in_memory_multiplier));
1044 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
1045 if (_decode_reduction) {
1046 cxml::add_text_child(root, "DecodeReduction", raw_convert<string>(_decode_reduction.get()));
1049 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
1050 cxml::add_text_child(root, "DefaultNotify", _default_notify ? "1" : "0");
1052 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
1053 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
1054 auto e = cxml::add_child(root, "Notification");
1055 e->set_attribute ("id", raw_convert<string>(i));
1056 e->add_child_text (_notification[i] ? "1" : "0");
1059 if (_barco_username) {
1060 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
1061 cxml::add_text_child(root, "BarcoUsername", *_barco_username);
1063 if (_barco_password) {
1064 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
1065 cxml::add_text_child(root, "BarcoPassword", *_barco_password);
1068 if (_christie_username) {
1069 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
1070 cxml::add_text_child(root, "ChristieUsername", *_christie_username);
1072 if (_christie_password) {
1073 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
1074 cxml::add_text_child(root, "ChristiePassword", *_christie_password);
1077 if (_gdc_username) {
1078 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
1079 cxml::add_text_child(root, "GDCUsername", *_gdc_username);
1081 if (_gdc_password) {
1082 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
1083 cxml::add_text_child(root, "GDCPassword", *_gdc_password);
1086 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
1087 with separate (advanced) controls.
1089 switch (_player_mode) {
1090 case PLAYER_MODE_WINDOW:
1091 cxml::add_text_child(root, "PlayerMode", "window");
1093 case PLAYER_MODE_FULL:
1094 cxml::add_text_child(root, "PlayerMode", "full");
1096 case PLAYER_MODE_DUAL:
1097 cxml::add_text_child(root, "PlayerMode", "dual");
1101 if (_player_restricted_menus) {
1102 cxml::add_text_child(root, "PlayerRestrictedMenus", "1");
1105 if (_playlist_editor_restricted_menus) {
1106 cxml::add_text_child(root, "PlaylistEditorRestrictedMenus", "1");
1109 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
1110 cxml::add_text_child(root, "ImageDisplay", raw_convert<string>(_image_display));
1111 switch (_video_view_type) {
1112 case VIDEO_VIEW_SIMPLE:
1113 cxml::add_text_child(root, "VideoViewType", "simple");
1115 case VIDEO_VIEW_OPENGL:
1116 cxml::add_text_child(root, "VideoViewType", "opengl");
1119 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1120 cxml::add_text_child(root, "RespectKDMValidityPeriods", _respect_kdm_validity_periods ? "1" : "0");
1121 if (_player_debug_log_file) {
1122 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1123 cxml::add_text_child(root, "PlayerDebugLogFile", _player_debug_log_file->string());
1125 if (_player_content_directory) {
1126 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1127 cxml::add_text_child(root, "PlayerContentDirectory", _player_content_directory->string());
1129 if (_player_playlist_directory) {
1130 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1131 cxml::add_text_child(root, "PlayerPlaylistDirectory", _player_playlist_directory->string());
1133 if (_player_kdm_directory) {
1134 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1135 cxml::add_text_child(root, "PlayerKDMDirectory", _player_kdm_directory->string());
1137 if (_audio_mapping) {
1138 _audio_mapping->as_xml(cxml::add_child(root, "AudioMapping"));
1140 for (auto const& i: _custom_languages) {
1141 cxml::add_text_child(root, "CustomLanguage", i.to_string());
1143 for (auto const& initial: _initial_paths) {
1144 if (initial.second) {
1145 cxml::add_text_child(root, initial.first, initial.second->string());
1148 cxml::add_text_child(root, "UseISDCFNameByDefault", _use_isdcf_name_by_default ? "1" : "0");
1149 cxml::add_text_child(root, "WriteKDMsToDisk", _write_kdms_to_disk ? "1" : "0");
1150 cxml::add_text_child(root, "EmailKDMs", _email_kdms ? "1" : "0");
1151 cxml::add_text_child(root, "DefaultKDMType", dcp::formulation_to_string(_default_kdm_type));
1152 cxml::add_text_child(root, "AutoCropThreshold", raw_convert<string>(_auto_crop_threshold));
1153 if (_last_release_notes_version) {
1154 cxml::add_text_child(root, "LastReleaseNotesVersion", *_last_release_notes_version);
1156 if (_main_divider_sash_position) {
1157 cxml::add_text_child(root, "MainDividerSashPosition", raw_convert<string>(*_main_divider_sash_position));
1159 if (_main_content_divider_sash_position) {
1160 cxml::add_text_child(root, "MainContentDividerSashPosition", raw_convert<string>(*_main_content_divider_sash_position));
1163 cxml::add_text_child(root, "DefaultAddFileLocation",
1164 _default_add_file_location == DefaultAddFileLocation::SAME_AS_LAST_TIME ? "last" : "project"
1167 /* [XML] AllowSMPTEBv20 1 to allow the user to choose SMPTE (Bv2.0 only) as a standard, otherwise 0 */
1168 cxml::add_text_child(root, "AllowSMPTEBv20", _allow_smpte_bv20 ? "1" : "0");
1169 /* [XML] ISDCFNamePartLength Maximum length of the "name" part of an ISDCF name, which should be 14 according to the standard */
1170 cxml::add_text_child(root, "ISDCFNamePartLength", raw_convert<string>(_isdcf_name_part_length));
1172 #ifdef DCPOMATIC_GROK
1174 _grok->as_xml(cxml::add_child(root, "Grok"));
1178 _export.write(cxml::add_child(root, "Export"));
1180 auto target = config_write_file();
1183 auto const s = doc.write_to_string_formatted ();
1184 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1185 dcp::File f(tmp, "w");
1187 throw FileError (_("Could not open file for writing"), tmp);
1189 f.checked_write(s.c_str(), s.bytes());
1191 dcp::filesystem::remove(target);
1192 dcp::filesystem::rename(tmp, target);
1193 } catch (xmlpp::exception& e) {
1194 string s = e.what ();
1196 throw FileError (s, target);
1203 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1205 xmlpp::Document doc;
1206 auto root = doc.create_root_node (root_node);
1207 cxml::add_text_child(root, "Version", version);
1209 for (auto i: things) {
1210 i->as_xml(cxml::add_child(root, node));
1214 doc.write_to_file_formatted (file.string() + ".tmp");
1215 dcp::filesystem::remove(file);
1216 dcp::filesystem::rename(file.string() + ".tmp", file);
1217 } catch (xmlpp::exception& e) {
1218 string s = e.what ();
1220 throw FileError (s, file);
1226 Config::write_cinemas () const
1228 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1233 Config::write_dkdm_recipients () const
1235 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1239 boost::filesystem::path
1240 Config::default_directory_or (boost::filesystem::path a) const
1242 return directory_or (_default_directory, a);
1245 boost::filesystem::path
1246 Config::default_kdm_directory_or (boost::filesystem::path a) const
1248 return directory_or (_default_kdm_directory, a);
1251 boost::filesystem::path
1252 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1258 boost::system::error_code ec;
1259 auto const e = dcp::filesystem::exists(*dir, ec);
1271 _instance = nullptr;
1275 Config::changed (Property what)
1281 Config::set_kdm_email_to_default ()
1283 _kdm_subject = _("KDM delivery: $CPL_NAME");
1285 _kdm_email = variant::insert_dcpomatic(_(
1286 "Dear Projectionist\n\n"
1287 "Please find attached KDMs for $CPL_NAME.\n\n"
1288 "Cinema: $CINEMA_NAME\n"
1289 "Screen(s): $SCREENS\n\n"
1290 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1296 Config::set_notification_email_to_default ()
1298 _notification_subject = variant::insert_dcpomatic(_("%1 notification"));
1300 _notification_email = _(
1301 "$JOB_NAME: $JOB_STATUS"
1306 Config::reset_kdm_email ()
1308 set_kdm_email_to_default ();
1313 Config::reset_notification_email ()
1315 set_notification_email_to_default ();
1320 Config::set_cover_sheet_to_default ()
1324 "CPL Filename: $CPL_FILENAME\n"
1326 "Format: $CONTAINER\n"
1328 "Audio Language: $AUDIO_LANGUAGE\n"
1329 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1336 Config::add_to_history (boost::filesystem::path p)
1338 add_to_history_internal (_history, p);
1341 /** Remove non-existent items from the history */
1343 Config::clean_history ()
1345 clean_history_internal (_history);
1349 Config::add_to_player_history (boost::filesystem::path p)
1351 add_to_history_internal (_player_history, p);
1354 /** Remove non-existent items from the player history */
1356 Config::clean_player_history ()
1358 clean_history_internal (_player_history);
1362 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1364 /* Remove existing instances of this path in the history */
1365 h.erase (remove (h.begin(), h.end(), p), h.end ());
1367 h.insert (h.begin (), p);
1368 if (h.size() > HISTORY_SIZE) {
1376 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1382 if (dcp::filesystem::is_directory(i)) {
1386 /* We couldn't find out if it's a directory for some reason; just ignore it */
1393 Config::have_existing (string file)
1395 return dcp::filesystem::exists(read_path(file));
1400 Config::read_cinemas (cxml::Document const & f)
1403 for (auto i: f.node_children("Cinema")) {
1404 /* Slightly grotty two-part construction of Cinema here so that we can use
1407 auto cinema = make_shared<Cinema>(i);
1408 cinema->read_screens (i);
1409 _cinemas.push_back (cinema);
1414 Config::set_cinemas_file (boost::filesystem::path file)
1416 if (file == _cinemas_file) {
1420 _cinemas_file = file;
1422 if (dcp::filesystem::exists(_cinemas_file)) {
1423 /* Existing file; read it in */
1424 cxml::Document f ("Cinemas");
1425 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
1435 Config::read_dkdm_recipients (cxml::Document const & f)
1437 _dkdm_recipients.clear ();
1438 for (auto i: f.node_children("DKDMRecipient")) {
1439 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1445 Config::save_template (shared_ptr<const Film> film, string name) const
1447 film->write_template (template_write_path(name));
1452 Config::templates () const
1454 if (!dcp::filesystem::exists(read_path("templates"))) {
1459 for (auto const& i: dcp::filesystem::directory_iterator(read_path("templates"))) {
1460 n.push_back (i.path().filename().string());
1466 Config::existing_template (string name) const
1468 return dcp::filesystem::exists(template_read_path(name));
1472 boost::filesystem::path
1473 Config::template_read_path (string name) const
1475 return read_path("templates") / tidy_for_filename (name);
1479 boost::filesystem::path
1480 Config::template_write_path (string name) const
1482 return write_path("templates") / tidy_for_filename (name);
1487 Config::rename_template (string old_name, string new_name) const
1489 dcp::filesystem::rename(template_read_path(old_name), template_write_path(new_name));
1493 Config::delete_template (string name) const
1495 dcp::filesystem::remove(template_write_path(name));
1498 /** @return Path to the config.xml containing the actual settings, following a link if required */
1499 boost::filesystem::path
1500 config_file (boost::filesystem::path main)
1502 cxml::Document f ("Config");
1503 if (!dcp::filesystem::exists(main)) {
1504 /* It doesn't exist, so there can't be any links; just return it */
1508 /* See if there's a link */
1510 f.read_file(dcp::filesystem::fix_long_path(main));
1511 auto link = f.optional_string_child("Link");
1515 } catch (xmlpp::exception& e) {
1516 /* There as a problem reading the main configuration file,
1517 so there can't be a link.
1525 boost::filesystem::path
1526 Config::config_read_file ()
1528 return config_file (read_path("config.xml"));
1532 boost::filesystem::path
1533 Config::config_write_file ()
1535 return config_file (write_path("config.xml"));
1540 Config::reset_cover_sheet ()
1542 set_cover_sheet_to_default ();
1547 Config::link (boost::filesystem::path new_file) const
1549 xmlpp::Document doc;
1550 cxml::add_text_child(doc.create_root_node("Config"), "Link", new_file.string());
1552 doc.write_to_file_formatted(write_path("config.xml").string());
1553 } catch (xmlpp::exception& e) {
1554 string s = e.what ();
1556 throw FileError (s, write_path("config.xml"));
1561 Config::copy_and_link (boost::filesystem::path new_file) const
1564 dcp::filesystem::copy_file(config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1569 Config::have_write_permission () const
1571 dcp::File f(config_write_file(), "r+");
1572 return static_cast<bool>(f);
1575 /** @param output_channels Number of output channels in use.
1576 * @return Audio mapping for this output channel count (may be a default).
1579 Config::audio_mapping (int output_channels)
1581 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1582 /* Set up a default */
1583 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1584 if (output_channels == 2) {
1585 /* Special case for stereo output.
1586 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1587 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1589 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1590 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1591 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1592 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1593 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1594 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1595 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1596 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1599 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1600 _audio_mapping->set (i, i, 1);
1605 return *_audio_mapping;
1609 Config::set_audio_mapping (AudioMapping m)
1612 changed (AUDIO_MAPPING);
1616 Config::set_audio_mapping_to_default ()
1618 DCPOMATIC_ASSERT (_audio_mapping);
1619 auto const ch = _audio_mapping->output_channels ();
1620 _audio_mapping = boost::none;
1621 _audio_mapping = audio_mapping (ch);
1622 changed (AUDIO_MAPPING);
1627 Config::add_custom_language (dcp::LanguageTag tag)
1629 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1630 _custom_languages.push_back (tag);
1636 optional<Config::BadReason>
1637 Config::check_certificates () const
1639 optional<BadReason> bad;
1641 for (auto const& i: _signer_chain->unordered()) {
1642 if (i.has_utf8_strings()) {
1643 bad = BAD_SIGNER_UTF8_STRINGS;
1645 if ((i.not_after().year() - i.not_before().year()) > 15) {
1646 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1648 if (dcp::escape_digest(i.subject_dn_qualifier()) != dcp::public_key_digest(i.public_key())) {
1649 bad = BAD_SIGNER_DN_QUALIFIER;
1653 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1654 bad = BAD_SIGNER_INCONSISTENT;
1657 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1658 bad = BAD_DECRYPTION_INCONSISTENT;
1666 save_all_config_as_zip (boost::filesystem::path zip_file)
1668 Zipper zipper (zip_file);
1670 auto config = Config::instance();
1671 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1672 if (dcp::filesystem::exists(config->cinemas_file())) {
1673 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1675 if (dcp::filesystem::exists(config->dkdm_recipients_file())) {
1676 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
1684 Config::load_from_zip(boost::filesystem::path zip_file)
1686 Unzipper unzipper(zip_file);
1687 dcp::write_string_to_file(unzipper.get("config.xml"), config_write_file());
1690 dcp::write_string_to_file(unzipper.get("cinemas.xml"), cinemas_file());
1691 dcp::write_string_to_file(unzipper.get("dkdm_recipient.xml"), dkdm_recipients_file());
1692 } catch (std::runtime_error&) {}
1696 changed(Property::USE_ANY_SERVERS);
1697 changed(Property::SERVERS);
1698 changed(Property::CINEMAS);
1699 changed(Property::DKDM_RECIPIENTS);
1700 changed(Property::SOUND);
1701 changed(Property::SOUND_OUTPUT);
1702 changed(Property::PLAYER_CONTENT_DIRECTORY);
1703 changed(Property::PLAYER_PLAYLIST_DIRECTORY);
1704 changed(Property::PLAYER_DEBUG_LOG);
1705 changed(Property::HISTORY);
1706 changed(Property::SHOW_EXPERIMENTAL_AUDIO_PROCESSORS);
1707 changed(Property::AUDIO_MAPPING);
1708 changed(Property::AUTO_CROP_THRESHOLD);
1709 changed(Property::ALLOW_SMPTE_BV20);
1710 changed(Property::ISDCF_NAME_PART_LENGTH);
1711 changed(Property::OTHER);
1716 Config::set_initial_path(string id, boost::filesystem::path path)
1718 auto iter = _initial_paths.find(id);
1719 DCPOMATIC_ASSERT(iter != _initial_paths.end());
1720 iter->second = path;
1725 optional<boost::filesystem::path>
1726 Config::initial_path(string id) const
1728 auto iter = _initial_paths.find(id);
1729 if (iter == _initial_paths.end()) {
1732 return iter->second;
1736 #ifdef DCPOMATIC_GROK
1738 Config::Grok::Grok(cxml::ConstNodePtr node)
1739 : enable(node->bool_child("Enable"))
1740 , binary_location(node->string_child("BinaryLocation"))
1741 , selected(node->number_child<int>("Selected"))
1742 , licence_server(node->string_child("LicenceServer"))
1743 , licence_port(node->number_child<int>("LicencePort"))
1744 , licence(node->string_child("Licence"))
1751 Config::Grok::as_xml(xmlpp::Element* node) const
1753 node->add_child("BinaryLocation")->add_child_text(binary_location.string());
1754 node->add_child("Enable")->add_child_text((enable ? "1" : "0"));
1755 node->add_child("Selected")->add_child_text(raw_convert<string>(selected));
1756 node->add_child("LicenceServer")->add_child_text(licence_server);
1757 node->add_child("LicencePort")->add_child_text(raw_convert<string>(licence_port));
1758 node->add_child("Licence")->add_child_text(licence);
1763 Config::set_grok(Grok const& grok)