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"
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::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"))
86 Config::set_defaults ()
88 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
89 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
90 _server_port_base = 6192;
91 _use_any_servers = true;
93 _only_servers_encode = false;
94 _tms_protocol = FileTransferProtocol::SCP;
99 _allow_any_dcp_frame_rate = false;
100 _allow_any_container = false;
101 _allow_96khz_audio = false;
102 _show_experimental_audio_processors = false;
103 _language = optional<string> ();
104 _default_still_length = 10;
105 _default_container = Ratio::from_id ("185");
106 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
107 _default_dcp_audio_channels = 6;
108 _default_j2k_bandwidth = 150000000;
109 _default_audio_delay = 0;
110 _default_interop = false;
111 _default_metadata.clear ();
112 _upload_after_make_dcp = false;
115 _mail_protocol = EmailProtocol::AUTO;
121 _notification_from = "";
122 _notification_to = "";
123 _notification_cc.clear ();
124 _notification_bcc = "";
125 _check_for_updates = false;
126 _check_for_test_updates = false;
127 _maximum_j2k_bandwidth = 250000000;
128 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
129 _analyse_ebur128 = true;
130 _automatic_audio_analysis = false;
131 #ifdef DCPOMATIC_WINDOWS
132 _win32_console = false;
134 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
135 * ~/.config/dcpomatic2 (or equivalent) and written back there.
137 _cinemas_file = read_path ("cinemas.xml");
138 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
139 _show_hints_before_make_dcp = true;
140 _confirm_kdm_email = true;
141 _kdm_container_name_format = dcp::NameFormat ("KDM %f %c");
142 _kdm_filename_format = dcp::NameFormat ("KDM %f %c %s");
143 _dkdm_filename_format = dcp::NameFormat ("DKDM %f %c %s");
144 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
145 _dcp_asset_filename_format = dcp::NameFormat ("%t");
146 _jump_to_selected = true;
147 for (int i = 0; i < NAG_COUNT; ++i) {
151 _sound_output = optional<string> ();
152 _last_kdm_write_type = KDM_WRITE_FLAT;
153 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
155 /* I think the scaling factor here should be the ratio of the longest frame
156 encode time to the shortest; if the thread count is T, longest time is L
157 and the shortest time S we could encode L/S frames per thread whilst waiting
158 for the L frame to encode so we might have to store LT/S frames.
160 However we don't want to use too much memory, so keep it a bit lower than we'd
161 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
162 use about 240Mb with 72 encoding threads.
164 _frames_in_memory_multiplier = 3;
165 _decode_reduction = optional<int>();
166 _default_notify = false;
167 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
168 _notification[i] = false;
170 _barco_username = optional<string>();
171 _barco_password = optional<string>();
172 _christie_username = optional<string>();
173 _christie_password = optional<string>();
174 _gdc_username = optional<string>();
175 _gdc_password = optional<string>();
176 _player_mode = PLAYER_MODE_WINDOW;
178 _video_view_type = VIDEO_VIEW_SIMPLE;
179 _respect_kdm_validity_periods = true;
180 _player_debug_log_file = boost::none;
181 _player_content_directory = boost::none;
182 _player_playlist_directory = boost::none;
183 _player_kdm_directory = boost::none;
184 _audio_mapping = boost::none;
185 _custom_languages.clear ();
186 _add_files_path = boost::none;
187 _use_isdcf_name_by_default = true;
188 _write_kdms_to_disk = true;
190 _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
191 _auto_crop_threshold = 0.1;
193 _allowed_dcp_frame_rates.clear ();
194 _allowed_dcp_frame_rates.push_back (24);
195 _allowed_dcp_frame_rates.push_back (25);
196 _allowed_dcp_frame_rates.push_back (30);
197 _allowed_dcp_frame_rates.push_back (48);
198 _allowed_dcp_frame_rates.push_back (50);
199 _allowed_dcp_frame_rates.push_back (60);
201 set_kdm_email_to_default ();
202 set_notification_email_to_default ();
203 set_cover_sheet_to_default ();
207 Config::restore_defaults ()
209 Config::instance()->set_defaults ();
210 Config::instance()->changed ();
213 shared_ptr<dcp::CertificateChain>
214 Config::create_certificate_chain ()
216 return make_shared<dcp::CertificateChain> (
218 CERTIFICATE_VALIDITY_PERIOD,
221 ".dcpomatic.smpte-430-2.ROOT",
222 ".dcpomatic.smpte-430-2.INTERMEDIATE",
223 "CS.dcpomatic.smpte-430-2.LEAF"
230 using namespace boost::filesystem;
232 auto copy_adding_number = [](path const& path_to_copy) {
234 auto add_number = [](path const& path, int number) {
235 return String::compose("%1.%2", path, number);
239 while (n < 100 && exists(add_number(path_to_copy, n))) {
242 boost::system::error_code ec;
243 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
246 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
247 * to write over. This is more intended for the situation where we have a corrupted config.xml,
248 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
249 * file). But we might as well back up the other files while we're about it.
252 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
253 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
255 copy_adding_number (config_write_file());
257 /* These do not use State::write_path, so whatever path is in the Config we will copy
260 copy_adding_number (_cinemas_file);
261 copy_adding_number (_dkdm_recipients_file);
268 cxml::Document f ("Config");
269 f.read_file (config_read_file());
271 auto version = f.optional_number_child<int> ("Version");
272 if (version && *version < _current_version) {
273 /* Back up the old config before we re-write it in a back-incompatible way */
277 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
278 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
280 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
281 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
284 _default_directory = f.optional_string_child ("DefaultDirectory");
285 if (_default_directory && _default_directory->empty ()) {
286 /* We used to store an empty value for this to mean "none set" */
287 _default_directory = boost::optional<boost::filesystem::path> ();
290 auto b = f.optional_number_child<int> ("ServerPort");
292 b = f.optional_number_child<int> ("ServerPortBase");
294 _server_port_base = b.get ();
296 auto u = f.optional_bool_child ("UseAnyServers");
297 _use_any_servers = u.get_value_or (true);
299 for (auto i: f.node_children("Server")) {
300 if (i->node_children("HostName").size() == 1) {
301 _servers.push_back (i->string_child ("HostName"));
303 _servers.push_back (i->content ());
307 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
308 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
309 _tms_ip = f.string_child ("TMSIP");
310 _tms_path = f.string_child ("TMSPath");
311 _tms_user = f.string_child ("TMSUser");
312 _tms_password = f.string_child ("TMSPassword");
314 _language = f.optional_string_child ("Language");
316 auto c = f.optional_string_child ("DefaultContainer");
318 _default_container = Ratio::from_id (c.get ());
321 if (_default_container && !_default_container->used_for_container()) {
322 Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)"));
323 _default_container = Ratio::from_id ("185");
326 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
327 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
329 if (f.optional_string_child ("DCPMetadataIssuer")) {
330 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
331 } else if (f.optional_string_child ("DCPIssuer")) {
332 _dcp_issuer = f.string_child ("DCPIssuer");
335 auto up = f.optional_bool_child("UploadAfterMakeDCP");
337 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
339 _upload_after_make_dcp = up.get_value_or (false);
340 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
341 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
342 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
343 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
344 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
346 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
347 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
348 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
349 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
351 for (auto const& i: f.node_children("DefaultMetadata")) {
352 _default_metadata[i->string_attribute("key")] = i->content();
355 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
357 /* Read any cinemas that are still lying around in the config file
358 * from an old version.
362 _mail_server = f.string_child ("MailServer");
363 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
366 /* Make sure this matches the code in write_config */
367 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
368 if (protocol == "Auto") {
369 _mail_protocol = EmailProtocol::AUTO;
370 } else if (protocol == "Plain") {
371 _mail_protocol = EmailProtocol::PLAIN;
372 } else if (protocol == "STARTTLS") {
373 _mail_protocol = EmailProtocol::STARTTLS;
374 } else if (protocol == "SSL") {
375 _mail_protocol = EmailProtocol::SSL;
379 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
380 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
382 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
383 _kdm_from = f.string_child ("KDMFrom");
384 for (auto i: f.node_children("KDMCC")) {
385 if (!i->content().empty()) {
386 _kdm_cc.push_back (i->content ());
389 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
390 _kdm_email = f.string_child ("KDMEmail");
392 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
393 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
394 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
395 for (auto i: f.node_children("NotificationCC")) {
396 if (!i->content().empty()) {
397 _notification_cc.push_back (i->content ());
400 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
401 if (f.optional_string_child("NotificationEmail")) {
402 _notification_email = f.string_child("NotificationEmail");
405 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
406 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
408 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
409 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
410 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
411 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
412 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
414 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
415 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
416 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
417 #ifdef DCPOMATIC_WINDOWS
418 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
421 for (auto i: f.node_children("History")) {
422 _history.push_back (i->content ());
425 for (auto i: f.node_children("PlayerHistory")) {
426 _player_history.push_back (i->content ());
429 auto signer = f.optional_node_child ("Signer");
431 auto c = make_shared<dcp::CertificateChain>();
432 /* Read the signing certificates and private key in from the config file */
433 for (auto i: signer->node_children ("Certificate")) {
434 c->add (dcp::Certificate (i->content ()));
436 c->set_key (signer->string_child ("PrivateKey"));
439 /* Make a new set of signing certificates and key */
440 _signer_chain = create_certificate_chain ();
443 auto decryption = f.optional_node_child ("Decryption");
445 auto c = make_shared<dcp::CertificateChain>();
446 for (auto i: decryption->node_children ("Certificate")) {
447 c->add (dcp::Certificate (i->content ()));
449 c->set_key (decryption->string_child ("PrivateKey"));
450 _decryption_chain = c;
452 _decryption_chain = create_certificate_chain ();
455 /* These must be done before we call Bad as that might set one
458 for (auto i: f.node_children("Nagged")) {
459 auto const id = i->number_attribute<int>("Id");
460 if (id >= 0 && id < NAG_COUNT) {
461 _nagged[id] = raw_convert<int>(i->content());
465 auto bad = check_certificates ();
467 auto const remake = Bad(*bad);
468 if (remake && *remake) {
470 case BAD_SIGNER_UTF8_STRINGS:
471 case BAD_SIGNER_INCONSISTENT:
472 case BAD_SIGNER_VALIDITY_TOO_LONG:
473 _signer_chain = create_certificate_chain ();
475 case BAD_DECRYPTION_INCONSISTENT:
476 _decryption_chain = create_certificate_chain ();
482 if (f.optional_node_child("DKDMGroup")) {
483 /* New-style: all DKDMs in a group */
484 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
486 /* Old-style: one or more DKDM nodes */
487 _dkdms = make_shared<DKDMGroup>("root");
488 for (auto i: f.node_children("DKDM")) {
489 _dkdms->add (DKDMBase::read (i));
492 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
493 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
494 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
495 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
496 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
497 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
498 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
499 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
500 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
501 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
502 /* The variable was renamed but not the XML tag */
503 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
504 _sound_output = f.optional_string_child("PreviewSoundOutput");
505 if (f.optional_string_child("CoverSheet")) {
506 _cover_sheet = f.optional_string_child("CoverSheet").get();
508 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
509 if (f.optional_string_child("LastKDMWriteType")) {
510 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
511 _last_kdm_write_type = KDM_WRITE_FLAT;
512 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
513 _last_kdm_write_type = KDM_WRITE_FOLDER;
514 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
515 _last_kdm_write_type = KDM_WRITE_ZIP;
518 if (f.optional_string_child("LastDKDMWriteType")) {
519 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
520 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
521 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
522 _last_dkdm_write_type = DKDM_WRITE_FILE;
525 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
526 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
527 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
529 for (auto i: f.node_children("Notification")) {
530 int const id = i->number_attribute<int>("Id");
531 if (id >= 0 && id < NOTIFICATION_COUNT) {
532 _notification[id] = raw_convert<int>(i->content());
536 _barco_username = f.optional_string_child("BarcoUsername");
537 _barco_password = f.optional_string_child("BarcoPassword");
538 _christie_username = f.optional_string_child("ChristieUsername");
539 _christie_password = f.optional_string_child("ChristiePassword");
540 _gdc_username = f.optional_string_child("GDCUsername");
541 _gdc_password = f.optional_string_child("GDCPassword");
543 auto pm = f.optional_string_child("PlayerMode");
544 if (pm && *pm == "window") {
545 _player_mode = PLAYER_MODE_WINDOW;
546 } else if (pm && *pm == "full") {
547 _player_mode = PLAYER_MODE_FULL;
548 } else if (pm && *pm == "dual") {
549 _player_mode = PLAYER_MODE_DUAL;
552 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
553 auto vc = f.optional_string_child("VideoViewType");
554 if (vc && *vc == "opengl") {
555 _video_view_type = VIDEO_VIEW_OPENGL;
556 } else if (vc && *vc == "simple") {
557 _video_view_type = VIDEO_VIEW_SIMPLE;
559 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
560 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
561 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
562 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
563 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
565 if (f.optional_node_child("AudioMapping")) {
566 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
569 for (auto i: f.node_children("CustomLanguage")) {
571 /* This will fail if it's called before dcp::init() as it won't recognise the
572 * tag. That's OK because the Config will be reloaded again later.
574 _custom_languages.push_back (dcp::LanguageTag(i->content()));
575 } catch (std::runtime_error& e) {}
578 _add_files_path = f.optional_string_child("AddFilesPath");
579 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
580 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
581 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
582 _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
583 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
585 if (boost::filesystem::exists (_cinemas_file)) {
586 cxml::Document f ("Cinemas");
587 f.read_file (_cinemas_file);
591 if (boost::filesystem::exists (_dkdm_recipients_file)) {
592 cxml::Document f ("DKDMRecipients");
593 f.read_file (_dkdm_recipients_file);
594 read_dkdm_recipients (f);
598 if (have_existing("config.xml") || have_existing("cinemas.xml") || have_existing("dkdm_recipients.xml")) {
600 /* We have a config file but it didn't load */
604 /* Make a new set of signing certificates and key */
605 _signer_chain = create_certificate_chain ();
606 /* And similar for decryption of KDMs */
607 _decryption_chain = create_certificate_chain ();
611 /** @return Singleton instance */
615 if (_instance == nullptr) {
616 _instance = new Config;
623 /** Write our configuration to disk */
625 Config::write () const
629 write_dkdm_recipients ();
633 Config::write_config () const
636 auto root = doc.create_root_node ("Config");
638 /* [XML] Version The version number of the configuration file format. */
639 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
640 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
641 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
642 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
643 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
644 if (_default_directory) {
645 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
646 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
648 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
649 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
650 by the batch converter to listen for job requests.
652 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
653 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
654 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
656 for (auto i: _servers) {
657 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
660 root->add_child("Server")->add_child_text (i);
663 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
664 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
666 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
667 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
668 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
669 /* [XML] TMSIP IP address of TMS. */
670 root->add_child("TMSIP")->add_child_text (_tms_ip);
671 /* [XML] TMSPath Path on the TMS to copy files to. */
672 root->add_child("TMSPath")->add_child_text (_tms_path);
673 /* [XML] TMSUser Username to log into the TMS with. */
674 root->add_child("TMSUser")->add_child_text (_tms_user);
675 /* [XML] TMSPassword Password to log into the TMS with. */
676 root->add_child("TMSPassword")->add_child_text (_tms_password);
678 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
679 root->add_child("Language")->add_child_text (_language.get());
681 if (_default_container) {
682 /* [XML:opt] DefaultContainer ID of default container
683 to use when creating new films (<code>185</code>,<code>239</code> or
686 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
688 if (_default_dcp_content_type) {
689 /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
690 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
691 <code>PSA</code> or <code>ADV</code>). */
692 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
694 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
695 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
696 /* [XML] DCPIssuer Issuer text to write into CPL files. */
697 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
698 /* [XML] DCPCreator Creator text to write into CPL files. */
699 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
700 /* [XML] Company name to write into MXF files. */
701 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
702 /* [XML] Product name to write into MXF files. */
703 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
704 /* [XML] Product version to write into MXF files. */
705 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
706 /* [XML] Comment to write into JPEG2000 data. */
707 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
708 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
709 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
711 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
712 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
713 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
714 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
715 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
716 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
717 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
718 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
719 for (auto const& i: _default_metadata) {
720 auto c = root->add_child("DefaultMetadata");
721 c->set_attribute("key", i.first);
722 c->add_child_text(i.second);
724 if (_default_kdm_directory) {
725 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
726 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
728 /* [XML] MailServer Hostname of SMTP server to use. */
729 root->add_child("MailServer")->add_child_text (_mail_server);
730 /* [XML] MailPort Port number to use on SMTP server. */
731 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
732 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
733 switch (_mail_protocol) {
734 case EmailProtocol::AUTO:
735 root->add_child("MailProtocol")->add_child_text("Auto");
737 case EmailProtocol::PLAIN:
738 root->add_child("MailProtocol")->add_child_text("Plain");
740 case EmailProtocol::STARTTLS:
741 root->add_child("MailProtocol")->add_child_text("STARTTLS");
743 case EmailProtocol::SSL:
744 root->add_child("MailProtocol")->add_child_text("SSL");
747 /* [XML] MailUser Username to use on SMTP server. */
748 root->add_child("MailUser")->add_child_text (_mail_user);
749 /* [XML] MailPassword Password to use on SMTP server. */
750 root->add_child("MailPassword")->add_child_text (_mail_password);
752 /* [XML] KDMSubject Subject to use for KDM emails. */
753 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
754 /* [XML] KDMFrom From address to use for KDM emails. */
755 root->add_child("KDMFrom")->add_child_text (_kdm_from);
756 for (auto i: _kdm_cc) {
757 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
758 root->add_child("KDMCC")->add_child_text (i);
760 /* [XML] KDMBCC BCC address to use for KDM emails. */
761 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
762 /* [XML] KDMEmail Text of KDM email. */
763 root->add_child("KDMEmail")->add_child_text (_kdm_email);
765 /* [XML] NotificationSubject Subject to use for notification emails. */
766 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
767 /* [XML] NotificationFrom From address to use for notification emails. */
768 root->add_child("NotificationFrom")->add_child_text (_notification_from);
769 /* [XML] NotificationFrom To address to use for notification emails. */
770 root->add_child("NotificationTo")->add_child_text (_notification_to);
771 for (auto i: _notification_cc) {
772 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
773 root->add_child("NotificationCC")->add_child_text (i);
775 /* [XML] NotificationBCC BCC address to use for notification emails. */
776 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
777 /* [XML] NotificationEmail Text of notification email. */
778 root->add_child("NotificationEmail")->add_child_text (_notification_email);
780 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
781 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
782 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
783 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
785 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
786 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
787 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
788 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
789 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
790 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
791 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
792 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
793 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
794 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
795 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
796 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
797 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
798 related to the player, 1024 debug information related to audio analyses.
800 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
801 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
802 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
803 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
804 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
805 #ifdef DCPOMATIC_WINDOWS
806 if (_win32_console) {
807 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
808 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
809 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
811 root->add_child("Win32Console")->add_child_text ("1");
815 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
816 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
818 auto signer = root->add_child ("Signer");
819 DCPOMATIC_ASSERT (_signer_chain);
820 for (auto const& i: _signer_chain->unordered()) {
821 signer->add_child("Certificate")->add_child_text (i.certificate (true));
823 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
825 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
826 auto decryption = root->add_child ("Decryption");
827 DCPOMATIC_ASSERT (_decryption_chain);
828 for (auto const& i: _decryption_chain->unordered()) {
829 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
831 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
833 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
836 for (auto i: _history) {
837 root->add_child("History")->add_child_text (i.string ());
840 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
843 for (auto i: _player_history) {
844 root->add_child("PlayerHistory")->add_child_text (i.string ());
847 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
848 or <code><DKDM></code> tags.
850 /* [XML] DKDM A DKDM as XML */
851 _dkdms->as_xml (root);
853 /* [XML] CinemasFile Filename of cinemas list file. */
854 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
855 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
856 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
857 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
858 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
859 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
860 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
861 /* [XML] KDMFilenameFormat Format for KDM filenames. */
862 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
863 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
864 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
865 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
866 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
867 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
868 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
869 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
870 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
871 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
872 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
873 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
874 for (int i = 0; i < NAG_COUNT; ++i) {
875 xmlpp::Element* e = root->add_child ("Nagged");
876 e->set_attribute ("Id", raw_convert<string>(i));
877 e->add_child_text (_nagged[i] ? "1" : "0");
879 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
880 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
882 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
883 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
885 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
886 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
887 if (_last_player_load_directory) {
888 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
890 /* [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. */
891 if (_last_kdm_write_type) {
892 switch (_last_kdm_write_type.get()) {
894 root->add_child("LastKDMWriteType")->add_child_text("flat");
896 case KDM_WRITE_FOLDER:
897 root->add_child("LastKDMWriteType")->add_child_text("folder");
900 root->add_child("LastKDMWriteType")->add_child_text("zip");
904 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
905 if (_last_dkdm_write_type) {
906 switch (_last_dkdm_write_type.get()) {
907 case DKDM_WRITE_INTERNAL:
908 root->add_child("LastDKDMWriteType")->add_child_text("internal");
910 case DKDM_WRITE_FILE:
911 root->add_child("LastDKDMWriteType")->add_child_text("file");
915 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
916 frames to be held in memory at once.
918 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
920 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
921 if (_decode_reduction) {
922 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
925 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
926 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
928 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
929 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
930 xmlpp::Element* e = root->add_child ("Notification");
931 e->set_attribute ("Id", raw_convert<string>(i));
932 e->add_child_text (_notification[i] ? "1" : "0");
935 if (_barco_username) {
936 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
937 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
939 if (_barco_password) {
940 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
941 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
944 if (_christie_username) {
945 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
946 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
948 if (_christie_password) {
949 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
950 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
954 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
955 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
958 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
959 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
962 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
963 with controls on another monitor.
965 switch (_player_mode) {
966 case PLAYER_MODE_WINDOW:
967 root->add_child("PlayerMode")->add_child_text("window");
969 case PLAYER_MODE_FULL:
970 root->add_child("PlayerMode")->add_child_text("full");
972 case PLAYER_MODE_DUAL:
973 root->add_child("PlayerMode")->add_child_text("dual");
977 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
978 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
979 switch (_video_view_type) {
980 case VIDEO_VIEW_SIMPLE:
981 root->add_child("VideoViewType")->add_child_text("simple");
983 case VIDEO_VIEW_OPENGL:
984 root->add_child("VideoViewType")->add_child_text("opengl");
987 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
988 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
989 if (_player_debug_log_file) {
990 /* [XML] PlayerLogFile Filename to use for player debug logs. */
991 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
993 if (_player_content_directory) {
994 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
995 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
997 if (_player_playlist_directory) {
998 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
999 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1001 if (_player_kdm_directory) {
1002 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1003 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1005 if (_audio_mapping) {
1006 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1008 for (auto const& i: _custom_languages) {
1009 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1011 if (_add_files_path) {
1012 /* [XML] AddFilesPath The default path that will be offered in the picker when adding files to a film. */
1013 root->add_child("AddFilesPath")->add_child_text(_add_files_path->string());
1015 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1016 root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0");
1017 root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0");
1018 root->add_child("DefaultKDMType")->add_child_text(dcp::formulation_to_string(_default_kdm_type));
1019 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1021 auto target = config_write_file();
1024 auto const s = doc.write_to_string_formatted ();
1025 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1026 auto f = fopen_boost (tmp, "w");
1028 throw FileError (_("Could not open file for writing"), tmp);
1030 checked_fwrite (s.c_str(), s.bytes(), f, tmp);
1032 boost::filesystem::remove (target);
1033 boost::filesystem::rename (tmp, target);
1034 } catch (xmlpp::exception& e) {
1035 string s = e.what ();
1037 throw FileError (s, target);
1044 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1046 xmlpp::Document doc;
1047 auto root = doc.create_root_node (root_node);
1048 root->add_child("Version")->add_child_text(version);
1050 for (auto i: things) {
1051 i->as_xml (root->add_child(node));
1055 doc.write_to_file_formatted (file.string() + ".tmp");
1056 boost::filesystem::remove (file);
1057 boost::filesystem::rename (file.string() + ".tmp", file);
1058 } catch (xmlpp::exception& e) {
1059 string s = e.what ();
1061 throw FileError (s, file);
1067 Config::write_cinemas () const
1069 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1074 Config::write_dkdm_recipients () const
1076 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1080 boost::filesystem::path
1081 Config::default_directory_or (boost::filesystem::path a) const
1083 return directory_or (_default_directory, a);
1086 boost::filesystem::path
1087 Config::default_kdm_directory_or (boost::filesystem::path a) const
1089 return directory_or (_default_kdm_directory, a);
1092 boost::filesystem::path
1093 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1099 boost::system::error_code ec;
1100 auto const e = boost::filesystem::exists (*dir, ec);
1116 Config::changed (Property what)
1122 Config::set_kdm_email_to_default ()
1124 _kdm_subject = _("KDM delivery: $CPL_NAME");
1127 "Dear Projectionist\n\n"
1128 "Please find attached KDMs for $CPL_NAME.\n\n"
1129 "Cinema: $CINEMA_NAME\n"
1130 "Screen(s): $SCREENS\n\n"
1131 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1132 "Best regards,\nDCP-o-matic"
1137 Config::set_notification_email_to_default ()
1139 _notification_subject = _("DCP-o-matic notification");
1141 _notification_email = _(
1142 "$JOB_NAME: $JOB_STATUS"
1147 Config::reset_kdm_email ()
1149 set_kdm_email_to_default ();
1154 Config::reset_notification_email ()
1156 set_notification_email_to_default ();
1161 Config::set_cover_sheet_to_default ()
1165 "CPL Filename: $CPL_FILENAME\n"
1167 "Format: $CONTAINER\n"
1169 "Audio Language: $AUDIO_LANGUAGE\n"
1170 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1177 Config::add_to_history (boost::filesystem::path p)
1179 add_to_history_internal (_history, p);
1182 /** Remove non-existant items from the history */
1184 Config::clean_history ()
1186 clean_history_internal (_history);
1190 Config::add_to_player_history (boost::filesystem::path p)
1192 add_to_history_internal (_player_history, p);
1195 /** Remove non-existant items from the player history */
1197 Config::clean_player_history ()
1199 clean_history_internal (_player_history);
1203 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1205 /* Remove existing instances of this path in the history */
1206 h.erase (remove (h.begin(), h.end(), p), h.end ());
1208 h.insert (h.begin (), p);
1209 if (h.size() > HISTORY_SIZE) {
1217 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1223 if (boost::filesystem::is_directory(i)) {
1227 /* We couldn't find out if it's a directory for some reason; just ignore it */
1234 Config::have_existing (string file)
1236 return boost::filesystem::exists (read_path(file));
1241 Config::read_cinemas (cxml::Document const & f)
1244 for (auto i: f.node_children("Cinema")) {
1245 /* Slightly grotty two-part construction of Cinema here so that we can use
1248 auto cinema = make_shared<Cinema>(i);
1249 cinema->read_screens (i);
1250 _cinemas.push_back (cinema);
1255 Config::set_cinemas_file (boost::filesystem::path file)
1257 if (file == _cinemas_file) {
1261 _cinemas_file = file;
1263 if (boost::filesystem::exists (_cinemas_file)) {
1264 /* Existing file; read it in */
1265 cxml::Document f ("Cinemas");
1266 f.read_file (_cinemas_file);
1275 Config::read_dkdm_recipients (cxml::Document const & f)
1277 _dkdm_recipients.clear ();
1278 for (auto i: f.node_children("DKDMRecipient")) {
1279 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1285 Config::save_template (shared_ptr<const Film> film, string name) const
1287 film->write_template (template_write_path(name));
1292 Config::templates () const
1294 if (!boost::filesystem::exists(read_path("templates"))) {
1299 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1300 n.push_back (i.path().filename().string());
1306 Config::existing_template (string name) const
1308 return boost::filesystem::exists (template_read_path(name));
1312 boost::filesystem::path
1313 Config::template_read_path (string name) const
1315 return read_path("templates") / tidy_for_filename (name);
1319 boost::filesystem::path
1320 Config::template_write_path (string name) const
1322 return write_path("templates") / tidy_for_filename (name);
1327 Config::rename_template (string old_name, string new_name) const
1329 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1333 Config::delete_template (string name) const
1335 boost::filesystem::remove (template_write_path(name));
1338 /** @return Path to the config.xml containing the actual settings, following a link if required */
1339 boost::filesystem::path
1340 config_file (boost::filesystem::path main)
1342 cxml::Document f ("Config");
1343 if (!boost::filesystem::exists (main)) {
1344 /* It doesn't exist, so there can't be any links; just return it */
1348 /* See if there's a link */
1351 auto link = f.optional_string_child("Link");
1355 } catch (xmlpp::exception& e) {
1356 /* There as a problem reading the main configuration file,
1357 so there can't be a link.
1365 boost::filesystem::path
1366 Config::config_read_file ()
1368 return config_file (read_path("config.xml"));
1372 boost::filesystem::path
1373 Config::config_write_file ()
1375 return config_file (write_path("config.xml"));
1380 Config::reset_cover_sheet ()
1382 set_cover_sheet_to_default ();
1387 Config::link (boost::filesystem::path new_file) const
1389 xmlpp::Document doc;
1390 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1392 doc.write_to_file_formatted(write_path("config.xml").string());
1393 } catch (xmlpp::exception& e) {
1394 string s = e.what ();
1396 throw FileError (s, write_path("config.xml"));
1401 Config::copy_and_link (boost::filesystem::path new_file) const
1404 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1409 Config::have_write_permission () const
1411 auto f = fopen_boost (config_write_file(), "r+");
1420 /** @param output_channels Number of output channels in use.
1421 * @return Audio mapping for this output channel count (may be a default).
1424 Config::audio_mapping (int output_channels)
1426 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1427 /* Set up a default */
1428 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1429 if (output_channels == 2) {
1430 /* Special case for stereo output.
1431 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1432 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1434 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1435 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1436 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1437 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1438 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1439 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1440 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1441 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1444 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1445 _audio_mapping->set (i, i, 1);
1450 return *_audio_mapping;
1454 Config::set_audio_mapping (AudioMapping m)
1457 changed (AUDIO_MAPPING);
1461 Config::set_audio_mapping_to_default ()
1463 DCPOMATIC_ASSERT (_audio_mapping);
1464 auto const ch = _audio_mapping->output_channels ();
1465 _audio_mapping = boost::none;
1466 _audio_mapping = audio_mapping (ch);
1467 changed (AUDIO_MAPPING);
1472 Config::add_custom_language (dcp::LanguageTag tag)
1474 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1475 _custom_languages.push_back (tag);
1481 optional<Config::BadReason>
1482 Config::check_certificates () const
1484 optional<BadReason> bad;
1486 for (auto const& i: _signer_chain->unordered()) {
1487 if (i.has_utf8_strings()) {
1488 bad = BAD_SIGNER_UTF8_STRINGS;
1490 if ((i.not_after().year() - i.not_before().year()) > 15) {
1491 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1495 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1496 bad = BAD_SIGNER_INCONSISTENT;
1499 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1500 bad = BAD_DECRYPTION_INCONSISTENT;
1508 save_all_config_as_zip (boost::filesystem::path zip_file)
1510 Zipper zipper (zip_file);
1512 auto config = Config::instance();
1513 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1514 if (boost::filesystem::exists(config->cinemas_file())) {
1515 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1517 if (boost::filesystem::exists(config->dkdm_recipients_file())) {
1518 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));