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"))
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;
101 _allow_any_dcp_frame_rate = false;
102 _allow_any_container = false;
103 _allow_96khz_audio = false;
104 _use_all_audio_channels = false;
105 _show_experimental_audio_processors = false;
106 _language = optional<string> ();
107 _default_still_length = 10;
108 _default_container = Ratio::from_id ("185");
109 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
110 _default_dcp_audio_channels = 6;
111 _default_j2k_bandwidth = 150000000;
112 _default_audio_delay = 0;
113 _default_interop = false;
114 _default_metadata.clear ();
115 _upload_after_make_dcp = false;
118 _mail_protocol = EmailProtocol::AUTO;
124 _notification_from = "";
125 _notification_to = "";
126 _notification_cc.clear ();
127 _notification_bcc = "";
128 _check_for_updates = false;
129 _check_for_test_updates = false;
130 _maximum_j2k_bandwidth = 250000000;
131 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
132 _analyse_ebur128 = true;
133 _automatic_audio_analysis = false;
134 #ifdef DCPOMATIC_WINDOWS
135 _win32_console = false;
137 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
138 * ~/.config/dcpomatic2 (or equivalent) and written back there.
140 _cinemas_file = read_path ("cinemas.xml");
141 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
142 _show_hints_before_make_dcp = true;
143 _confirm_kdm_email = true;
144 _kdm_container_name_format = dcp::NameFormat ("KDM %f %c");
145 _kdm_filename_format = dcp::NameFormat ("KDM %f %c %s");
146 _dkdm_filename_format = dcp::NameFormat ("DKDM %f %c %s");
147 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
148 _dcp_asset_filename_format = dcp::NameFormat ("%t");
149 _jump_to_selected = true;
150 for (int i = 0; i < NAG_COUNT; ++i) {
154 _sound_output = optional<string> ();
155 _last_kdm_write_type = KDM_WRITE_FLAT;
156 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
158 /* I think the scaling factor here should be the ratio of the longest frame
159 encode time to the shortest; if the thread count is T, longest time is L
160 and the shortest time S we could encode L/S frames per thread whilst waiting
161 for the L frame to encode so we might have to store LT/S frames.
163 However we don't want to use too much memory, so keep it a bit lower than we'd
164 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
165 use about 240Mb with 72 encoding threads.
167 _frames_in_memory_multiplier = 3;
168 _decode_reduction = optional<int>();
169 _default_notify = false;
170 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
171 _notification[i] = false;
173 _barco_username = optional<string>();
174 _barco_password = optional<string>();
175 _christie_username = optional<string>();
176 _christie_password = optional<string>();
177 _gdc_username = optional<string>();
178 _gdc_password = optional<string>();
179 _player_mode = PLAYER_MODE_WINDOW;
181 _video_view_type = VIDEO_VIEW_SIMPLE;
182 _respect_kdm_validity_periods = true;
183 _player_debug_log_file = boost::none;
184 _player_content_directory = boost::none;
185 _player_playlist_directory = boost::none;
186 _player_kdm_directory = boost::none;
187 _audio_mapping = boost::none;
188 _custom_languages.clear ();
189 _add_files_path = boost::none;
190 _use_isdcf_name_by_default = true;
191 _write_kdms_to_disk = true;
193 _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
194 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
195 _auto_crop_threshold = 0.1;
197 _allowed_dcp_frame_rates.clear ();
198 _allowed_dcp_frame_rates.push_back (24);
199 _allowed_dcp_frame_rates.push_back (25);
200 _allowed_dcp_frame_rates.push_back (30);
201 _allowed_dcp_frame_rates.push_back (48);
202 _allowed_dcp_frame_rates.push_back (50);
203 _allowed_dcp_frame_rates.push_back (60);
205 set_kdm_email_to_default ();
206 set_notification_email_to_default ();
207 set_cover_sheet_to_default ();
209 _export.set_defaults();
213 Config::restore_defaults ()
215 Config::instance()->set_defaults ();
216 Config::instance()->changed ();
219 shared_ptr<dcp::CertificateChain>
220 Config::create_certificate_chain ()
222 return make_shared<dcp::CertificateChain> (
224 CERTIFICATE_VALIDITY_PERIOD,
227 ".dcpomatic.smpte-430-2.ROOT",
228 ".dcpomatic.smpte-430-2.INTERMEDIATE",
229 "CS.dcpomatic.smpte-430-2.LEAF"
236 using namespace boost::filesystem;
238 auto copy_adding_number = [](path const& path_to_copy) {
240 auto add_number = [](path const& path, int number) {
241 return String::compose("%1.%2", path, number);
245 while (n < 100 && exists(add_number(path_to_copy, n))) {
248 boost::system::error_code ec;
249 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
252 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
253 * to write over. This is more intended for the situation where we have a corrupted config.xml,
254 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
255 * file). But we might as well back up the other files while we're about it.
258 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
259 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
261 copy_adding_number (config_write_file());
263 /* These do not use State::write_path, so whatever path is in the Config we will copy
266 copy_adding_number (_cinemas_file);
267 copy_adding_number (_dkdm_recipients_file);
274 cxml::Document f ("Config");
275 f.read_file (config_read_file());
277 auto version = f.optional_number_child<int> ("Version");
278 if (version && *version < _current_version) {
279 /* Back up the old config before we re-write it in a back-incompatible way */
283 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
284 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
286 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
287 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
290 _default_directory = f.optional_string_child ("DefaultDirectory");
291 if (_default_directory && _default_directory->empty ()) {
292 /* We used to store an empty value for this to mean "none set" */
293 _default_directory = boost::optional<boost::filesystem::path> ();
296 auto b = f.optional_number_child<int> ("ServerPort");
298 b = f.optional_number_child<int> ("ServerPortBase");
300 _server_port_base = b.get ();
302 auto u = f.optional_bool_child ("UseAnyServers");
303 _use_any_servers = u.get_value_or (true);
305 for (auto i: f.node_children("Server")) {
306 if (i->node_children("HostName").size() == 1) {
307 _servers.push_back (i->string_child ("HostName"));
309 _servers.push_back (i->content ());
313 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
314 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
315 _tms_ip = f.string_child ("TMSIP");
316 _tms_path = f.string_child ("TMSPath");
317 _tms_user = f.string_child ("TMSUser");
318 _tms_password = f.string_child ("TMSPassword");
320 _language = f.optional_string_child ("Language");
322 auto c = f.optional_string_child ("DefaultContainer");
324 _default_container = Ratio::from_id (c.get ());
327 if (_default_container && !_default_container->used_for_container()) {
328 Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)"));
329 _default_container = Ratio::from_id ("185");
332 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
333 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
335 if (f.optional_string_child ("DCPMetadataIssuer")) {
336 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
337 } else if (f.optional_string_child ("DCPIssuer")) {
338 _dcp_issuer = f.string_child ("DCPIssuer");
341 auto up = f.optional_bool_child("UploadAfterMakeDCP");
343 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
345 _upload_after_make_dcp = up.get_value_or (false);
346 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
347 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
348 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
349 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
350 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
352 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
353 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
354 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
355 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
357 for (auto const& i: f.node_children("DefaultMetadata")) {
358 _default_metadata[i->string_attribute("key")] = i->content();
361 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
363 /* Read any cinemas that are still lying around in the config file
364 * from an old version.
368 _mail_server = f.string_child ("MailServer");
369 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
372 /* Make sure this matches the code in write_config */
373 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
374 if (protocol == "Auto") {
375 _mail_protocol = EmailProtocol::AUTO;
376 } else if (protocol == "Plain") {
377 _mail_protocol = EmailProtocol::PLAIN;
378 } else if (protocol == "STARTTLS") {
379 _mail_protocol = EmailProtocol::STARTTLS;
380 } else if (protocol == "SSL") {
381 _mail_protocol = EmailProtocol::SSL;
385 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
386 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
388 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
389 _kdm_from = f.string_child ("KDMFrom");
390 for (auto i: f.node_children("KDMCC")) {
391 if (!i->content().empty()) {
392 _kdm_cc.push_back (i->content ());
395 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
396 _kdm_email = f.string_child ("KDMEmail");
398 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
399 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
400 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
401 for (auto i: f.node_children("NotificationCC")) {
402 if (!i->content().empty()) {
403 _notification_cc.push_back (i->content ());
406 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
407 if (f.optional_string_child("NotificationEmail")) {
408 _notification_email = f.string_child("NotificationEmail");
411 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
412 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
414 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
415 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
416 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
417 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
418 _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false);
419 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
421 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
422 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
423 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
424 #ifdef DCPOMATIC_WINDOWS
425 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
428 for (auto i: f.node_children("History")) {
429 _history.push_back (i->content ());
432 for (auto i: f.node_children("PlayerHistory")) {
433 _player_history.push_back (i->content ());
436 auto signer = f.optional_node_child ("Signer");
438 auto c = make_shared<dcp::CertificateChain>();
439 /* Read the signing certificates and private key in from the config file */
440 for (auto i: signer->node_children ("Certificate")) {
441 c->add (dcp::Certificate (i->content ()));
443 c->set_key (signer->string_child ("PrivateKey"));
446 /* Make a new set of signing certificates and key */
447 _signer_chain = create_certificate_chain ();
450 auto decryption = f.optional_node_child ("Decryption");
452 auto c = make_shared<dcp::CertificateChain>();
453 for (auto i: decryption->node_children ("Certificate")) {
454 c->add (dcp::Certificate (i->content ()));
456 c->set_key (decryption->string_child ("PrivateKey"));
457 _decryption_chain = c;
459 _decryption_chain = create_certificate_chain ();
462 /* These must be done before we call Bad as that might set one
465 for (auto i: f.node_children("Nagged")) {
466 auto const id = i->number_attribute<int>("Id");
467 if (id >= 0 && id < NAG_COUNT) {
468 _nagged[id] = raw_convert<int>(i->content());
472 auto bad = check_certificates ();
474 auto const remake = Bad(*bad);
475 if (remake && *remake) {
477 case BAD_SIGNER_UTF8_STRINGS:
478 case BAD_SIGNER_INCONSISTENT:
479 case BAD_SIGNER_VALIDITY_TOO_LONG:
480 _signer_chain = create_certificate_chain ();
482 case BAD_DECRYPTION_INCONSISTENT:
483 _decryption_chain = create_certificate_chain ();
489 if (f.optional_node_child("DKDMGroup")) {
490 /* New-style: all DKDMs in a group */
491 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
493 /* Old-style: one or more DKDM nodes */
494 _dkdms = make_shared<DKDMGroup>("root");
495 for (auto i: f.node_children("DKDM")) {
496 _dkdms->add (DKDMBase::read (i));
499 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
500 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
501 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
502 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
503 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
504 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
505 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
506 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
507 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
508 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
509 /* The variable was renamed but not the XML tag */
510 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
511 _sound_output = f.optional_string_child("PreviewSoundOutput");
512 if (f.optional_string_child("CoverSheet")) {
513 _cover_sheet = f.optional_string_child("CoverSheet").get();
515 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
516 if (f.optional_string_child("LastKDMWriteType")) {
517 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
518 _last_kdm_write_type = KDM_WRITE_FLAT;
519 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
520 _last_kdm_write_type = KDM_WRITE_FOLDER;
521 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
522 _last_kdm_write_type = KDM_WRITE_ZIP;
525 if (f.optional_string_child("LastDKDMWriteType")) {
526 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
527 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
528 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
529 _last_dkdm_write_type = DKDM_WRITE_FILE;
532 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
533 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
534 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
536 for (auto i: f.node_children("Notification")) {
537 int const id = i->number_attribute<int>("Id");
538 if (id >= 0 && id < NOTIFICATION_COUNT) {
539 _notification[id] = raw_convert<int>(i->content());
543 _barco_username = f.optional_string_child("BarcoUsername");
544 _barco_password = f.optional_string_child("BarcoPassword");
545 _christie_username = f.optional_string_child("ChristieUsername");
546 _christie_password = f.optional_string_child("ChristiePassword");
547 _gdc_username = f.optional_string_child("GDCUsername");
548 _gdc_password = f.optional_string_child("GDCPassword");
550 auto pm = f.optional_string_child("PlayerMode");
551 if (pm && *pm == "window") {
552 _player_mode = PLAYER_MODE_WINDOW;
553 } else if (pm && *pm == "full") {
554 _player_mode = PLAYER_MODE_FULL;
555 } else if (pm && *pm == "dual") {
556 _player_mode = PLAYER_MODE_DUAL;
559 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
560 auto vc = f.optional_string_child("VideoViewType");
561 if (vc && *vc == "opengl") {
562 _video_view_type = VIDEO_VIEW_OPENGL;
563 } else if (vc && *vc == "simple") {
564 _video_view_type = VIDEO_VIEW_SIMPLE;
566 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
567 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
568 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
569 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
570 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
572 if (f.optional_node_child("AudioMapping")) {
573 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
576 for (auto i: f.node_children("CustomLanguage")) {
578 /* This will fail if it's called before dcp::init() as it won't recognise the
579 * tag. That's OK because the Config will be reloaded again later.
581 _custom_languages.push_back (dcp::LanguageTag(i->content()));
582 } catch (std::runtime_error& e) {}
585 _add_files_path = f.optional_string_child("AddFilesPath");
586 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
587 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
588 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
589 _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
590 if (auto duration = f.optional_node_child("DefaultKDMDuration")) {
591 _default_kdm_duration = RoughDuration(duration);
593 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
595 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
597 _export.read(f.optional_node_child("Export"));
599 if (boost::filesystem::exists (_cinemas_file)) {
600 cxml::Document f ("Cinemas");
601 f.read_file (_cinemas_file);
605 if (boost::filesystem::exists (_dkdm_recipients_file)) {
606 cxml::Document f ("DKDMRecipients");
607 f.read_file (_dkdm_recipients_file);
608 read_dkdm_recipients (f);
612 if (have_existing("config.xml") || have_existing("cinemas.xml") || have_existing("dkdm_recipients.xml")) {
614 /* We have a config file but it didn't load */
618 /* Make a new set of signing certificates and key */
619 _signer_chain = create_certificate_chain ();
620 /* And similar for decryption of KDMs */
621 _decryption_chain = create_certificate_chain ();
625 /** @return Singleton instance */
629 if (_instance == nullptr) {
630 _instance = new Config;
637 /** Write our configuration to disk */
639 Config::write () const
643 write_dkdm_recipients ();
647 Config::write_config () const
650 auto root = doc.create_root_node ("Config");
652 /* [XML] Version The version number of the configuration file format. */
653 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
654 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
655 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
656 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
657 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
658 if (_default_directory) {
659 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
660 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
662 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
663 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
664 by the batch converter to listen for job requests.
666 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
667 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
668 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
670 for (auto i: _servers) {
671 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
674 root->add_child("Server")->add_child_text (i);
677 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
678 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
680 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
681 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
682 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
683 /* [XML] TMSIP IP address of TMS. */
684 root->add_child("TMSIP")->add_child_text (_tms_ip);
685 /* [XML] TMSPath Path on the TMS to copy files to. */
686 root->add_child("TMSPath")->add_child_text (_tms_path);
687 /* [XML] TMSUser Username to log into the TMS with. */
688 root->add_child("TMSUser")->add_child_text (_tms_user);
689 /* [XML] TMSPassword Password to log into the TMS with. */
690 root->add_child("TMSPassword")->add_child_text (_tms_password);
692 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
693 root->add_child("Language")->add_child_text (_language.get());
695 if (_default_container) {
696 /* [XML:opt] DefaultContainer ID of default container
697 to use when creating new films (<code>185</code>,<code>239</code> or
700 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
702 if (_default_dcp_content_type) {
703 /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
704 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
705 <code>PSA</code> or <code>ADV</code>). */
706 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
708 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
709 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
710 /* [XML] DCPIssuer Issuer text to write into CPL files. */
711 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
712 /* [XML] DCPCreator Creator text to write into CPL files. */
713 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
714 /* [XML] Company name to write into MXF files. */
715 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
716 /* [XML] Product name to write into MXF files. */
717 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
718 /* [XML] Product version to write into MXF files. */
719 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
720 /* [XML] Comment to write into JPEG2000 data. */
721 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
722 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
723 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
725 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
726 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
727 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
728 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
729 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
730 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
731 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
732 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
733 for (auto const& i: _default_metadata) {
734 auto c = root->add_child("DefaultMetadata");
735 c->set_attribute("key", i.first);
736 c->add_child_text(i.second);
738 if (_default_kdm_directory) {
739 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
740 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
742 _default_kdm_duration.as_xml(root->add_child("DefaultKDMDuration"));
743 /* [XML] MailServer Hostname of SMTP server to use. */
744 root->add_child("MailServer")->add_child_text (_mail_server);
745 /* [XML] MailPort Port number to use on SMTP server. */
746 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
747 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
748 switch (_mail_protocol) {
749 case EmailProtocol::AUTO:
750 root->add_child("MailProtocol")->add_child_text("Auto");
752 case EmailProtocol::PLAIN:
753 root->add_child("MailProtocol")->add_child_text("Plain");
755 case EmailProtocol::STARTTLS:
756 root->add_child("MailProtocol")->add_child_text("STARTTLS");
758 case EmailProtocol::SSL:
759 root->add_child("MailProtocol")->add_child_text("SSL");
762 /* [XML] MailUser Username to use on SMTP server. */
763 root->add_child("MailUser")->add_child_text (_mail_user);
764 /* [XML] MailPassword Password to use on SMTP server. */
765 root->add_child("MailPassword")->add_child_text (_mail_password);
767 /* [XML] KDMSubject Subject to use for KDM emails. */
768 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
769 /* [XML] KDMFrom From address to use for KDM emails. */
770 root->add_child("KDMFrom")->add_child_text (_kdm_from);
771 for (auto i: _kdm_cc) {
772 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
773 root->add_child("KDMCC")->add_child_text (i);
775 /* [XML] KDMBCC BCC address to use for KDM emails. */
776 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
777 /* [XML] KDMEmail Text of KDM email. */
778 root->add_child("KDMEmail")->add_child_text (_kdm_email);
780 /* [XML] NotificationSubject Subject to use for notification emails. */
781 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
782 /* [XML] NotificationFrom From address to use for notification emails. */
783 root->add_child("NotificationFrom")->add_child_text (_notification_from);
784 /* [XML] NotificationFrom To address to use for notification emails. */
785 root->add_child("NotificationTo")->add_child_text (_notification_to);
786 for (auto i: _notification_cc) {
787 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
788 root->add_child("NotificationCC")->add_child_text (i);
790 /* [XML] NotificationBCC BCC address to use for notification emails. */
791 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
792 /* [XML] NotificationEmail Text of notification email. */
793 root->add_child("NotificationEmail")->add_child_text (_notification_email);
795 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
796 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
797 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
798 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
800 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
801 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
802 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
803 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
804 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
805 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
806 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
807 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
808 /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
809 root->add_child("UseAllAudioChannels")->add_child_text(_use_all_audio_channels ? "1" : "0");
810 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
811 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
812 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
813 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
814 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
815 related to the player, 1024 debug information related to audio analyses.
817 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
818 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
819 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
820 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
821 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
822 #ifdef DCPOMATIC_WINDOWS
823 if (_win32_console) {
824 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
825 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
826 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
828 root->add_child("Win32Console")->add_child_text ("1");
832 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
833 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
835 auto signer = root->add_child ("Signer");
836 DCPOMATIC_ASSERT (_signer_chain);
837 for (auto const& i: _signer_chain->unordered()) {
838 signer->add_child("Certificate")->add_child_text (i.certificate (true));
840 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
842 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
843 auto decryption = root->add_child ("Decryption");
844 DCPOMATIC_ASSERT (_decryption_chain);
845 for (auto const& i: _decryption_chain->unordered()) {
846 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
848 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
850 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
853 for (auto i: _history) {
854 root->add_child("History")->add_child_text (i.string ());
857 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
860 for (auto i: _player_history) {
861 root->add_child("PlayerHistory")->add_child_text (i.string ());
864 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
865 or <code><DKDM></code> tags.
867 /* [XML] DKDM A DKDM as XML */
868 _dkdms->as_xml (root);
870 /* [XML] CinemasFile Filename of cinemas list file. */
871 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
872 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
873 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
874 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
875 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
876 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
877 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
878 /* [XML] KDMFilenameFormat Format for KDM filenames. */
879 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
880 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
881 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
882 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
883 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
884 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
885 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
886 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
887 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
888 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
889 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
890 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
891 for (int i = 0; i < NAG_COUNT; ++i) {
892 xmlpp::Element* e = root->add_child ("Nagged");
893 e->set_attribute ("Id", raw_convert<string>(i));
894 e->add_child_text (_nagged[i] ? "1" : "0");
896 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
897 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
899 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
900 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
902 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
903 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
904 if (_last_player_load_directory) {
905 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
907 /* [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. */
908 if (_last_kdm_write_type) {
909 switch (_last_kdm_write_type.get()) {
911 root->add_child("LastKDMWriteType")->add_child_text("flat");
913 case KDM_WRITE_FOLDER:
914 root->add_child("LastKDMWriteType")->add_child_text("folder");
917 root->add_child("LastKDMWriteType")->add_child_text("zip");
921 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
922 if (_last_dkdm_write_type) {
923 switch (_last_dkdm_write_type.get()) {
924 case DKDM_WRITE_INTERNAL:
925 root->add_child("LastDKDMWriteType")->add_child_text("internal");
927 case DKDM_WRITE_FILE:
928 root->add_child("LastDKDMWriteType")->add_child_text("file");
932 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
933 frames to be held in memory at once.
935 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
937 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
938 if (_decode_reduction) {
939 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
942 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
943 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
945 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
946 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
947 xmlpp::Element* e = root->add_child ("Notification");
948 e->set_attribute ("Id", raw_convert<string>(i));
949 e->add_child_text (_notification[i] ? "1" : "0");
952 if (_barco_username) {
953 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
954 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
956 if (_barco_password) {
957 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
958 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
961 if (_christie_username) {
962 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
963 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
965 if (_christie_password) {
966 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
967 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
971 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
972 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
975 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
976 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
979 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
980 with controls on another monitor.
982 switch (_player_mode) {
983 case PLAYER_MODE_WINDOW:
984 root->add_child("PlayerMode")->add_child_text("window");
986 case PLAYER_MODE_FULL:
987 root->add_child("PlayerMode")->add_child_text("full");
989 case PLAYER_MODE_DUAL:
990 root->add_child("PlayerMode")->add_child_text("dual");
994 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
995 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
996 switch (_video_view_type) {
997 case VIDEO_VIEW_SIMPLE:
998 root->add_child("VideoViewType")->add_child_text("simple");
1000 case VIDEO_VIEW_OPENGL:
1001 root->add_child("VideoViewType")->add_child_text("opengl");
1004 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1005 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
1006 if (_player_debug_log_file) {
1007 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1008 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
1010 if (_player_content_directory) {
1011 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1012 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
1014 if (_player_playlist_directory) {
1015 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1016 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1018 if (_player_kdm_directory) {
1019 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1020 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1022 if (_audio_mapping) {
1023 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1025 for (auto const& i: _custom_languages) {
1026 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1028 if (_add_files_path) {
1029 /* [XML] AddFilesPath The default path that will be offered in the picker when adding files to a film. */
1030 root->add_child("AddFilesPath")->add_child_text(_add_files_path->string());
1032 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1033 root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0");
1034 root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0");
1035 root->add_child("DefaultKDMType")->add_child_text(dcp::formulation_to_string(_default_kdm_type));
1036 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1038 _export.write(root->add_child("Export"));
1040 auto target = config_write_file();
1043 auto const s = doc.write_to_string_formatted ();
1044 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1045 dcp::File f(tmp, "w");
1047 throw FileError (_("Could not open file for writing"), tmp);
1049 f.checked_write(s.c_str(), s.bytes());
1051 boost::filesystem::remove (target);
1052 boost::filesystem::rename (tmp, target);
1053 } catch (xmlpp::exception& e) {
1054 string s = e.what ();
1056 throw FileError (s, target);
1063 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1065 xmlpp::Document doc;
1066 auto root = doc.create_root_node (root_node);
1067 root->add_child("Version")->add_child_text(version);
1069 for (auto i: things) {
1070 i->as_xml (root->add_child(node));
1074 doc.write_to_file_formatted (file.string() + ".tmp");
1075 boost::filesystem::remove (file);
1076 boost::filesystem::rename (file.string() + ".tmp", file);
1077 } catch (xmlpp::exception& e) {
1078 string s = e.what ();
1080 throw FileError (s, file);
1086 Config::write_cinemas () const
1088 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1093 Config::write_dkdm_recipients () const
1095 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1099 boost::filesystem::path
1100 Config::default_directory_or (boost::filesystem::path a) const
1102 return directory_or (_default_directory, a);
1105 boost::filesystem::path
1106 Config::default_kdm_directory_or (boost::filesystem::path a) const
1108 return directory_or (_default_kdm_directory, a);
1111 boost::filesystem::path
1112 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1118 boost::system::error_code ec;
1119 auto const e = boost::filesystem::exists (*dir, ec);
1135 Config::changed (Property what)
1141 Config::set_kdm_email_to_default ()
1143 _kdm_subject = _("KDM delivery: $CPL_NAME");
1146 "Dear Projectionist\n\n"
1147 "Please find attached KDMs for $CPL_NAME.\n\n"
1148 "Cinema: $CINEMA_NAME\n"
1149 "Screen(s): $SCREENS\n\n"
1150 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1151 "Best regards,\nDCP-o-matic"
1156 Config::set_notification_email_to_default ()
1158 _notification_subject = _("DCP-o-matic notification");
1160 _notification_email = _(
1161 "$JOB_NAME: $JOB_STATUS"
1166 Config::reset_kdm_email ()
1168 set_kdm_email_to_default ();
1173 Config::reset_notification_email ()
1175 set_notification_email_to_default ();
1180 Config::set_cover_sheet_to_default ()
1184 "CPL Filename: $CPL_FILENAME\n"
1186 "Format: $CONTAINER\n"
1188 "Audio Language: $AUDIO_LANGUAGE\n"
1189 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1196 Config::add_to_history (boost::filesystem::path p)
1198 add_to_history_internal (_history, p);
1201 /** Remove non-existant items from the history */
1203 Config::clean_history ()
1205 clean_history_internal (_history);
1209 Config::add_to_player_history (boost::filesystem::path p)
1211 add_to_history_internal (_player_history, p);
1214 /** Remove non-existant items from the player history */
1216 Config::clean_player_history ()
1218 clean_history_internal (_player_history);
1222 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1224 /* Remove existing instances of this path in the history */
1225 h.erase (remove (h.begin(), h.end(), p), h.end ());
1227 h.insert (h.begin (), p);
1228 if (h.size() > HISTORY_SIZE) {
1236 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1242 if (boost::filesystem::is_directory(i)) {
1246 /* We couldn't find out if it's a directory for some reason; just ignore it */
1253 Config::have_existing (string file)
1255 return boost::filesystem::exists (read_path(file));
1260 Config::read_cinemas (cxml::Document const & f)
1263 for (auto i: f.node_children("Cinema")) {
1264 /* Slightly grotty two-part construction of Cinema here so that we can use
1267 auto cinema = make_shared<Cinema>(i);
1268 cinema->read_screens (i);
1269 _cinemas.push_back (cinema);
1274 Config::set_cinemas_file (boost::filesystem::path file)
1276 if (file == _cinemas_file) {
1280 _cinemas_file = file;
1282 if (boost::filesystem::exists (_cinemas_file)) {
1283 /* Existing file; read it in */
1284 cxml::Document f ("Cinemas");
1285 f.read_file (_cinemas_file);
1294 Config::read_dkdm_recipients (cxml::Document const & f)
1296 _dkdm_recipients.clear ();
1297 for (auto i: f.node_children("DKDMRecipient")) {
1298 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1304 Config::save_template (shared_ptr<const Film> film, string name) const
1306 film->write_template (template_write_path(name));
1311 Config::templates () const
1313 if (!boost::filesystem::exists(read_path("templates"))) {
1318 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1319 n.push_back (i.path().filename().string());
1325 Config::existing_template (string name) const
1327 return boost::filesystem::exists (template_read_path(name));
1331 boost::filesystem::path
1332 Config::template_read_path (string name) const
1334 return read_path("templates") / tidy_for_filename (name);
1338 boost::filesystem::path
1339 Config::template_write_path (string name) const
1341 return write_path("templates") / tidy_for_filename (name);
1346 Config::rename_template (string old_name, string new_name) const
1348 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1352 Config::delete_template (string name) const
1354 boost::filesystem::remove (template_write_path(name));
1357 /** @return Path to the config.xml containing the actual settings, following a link if required */
1358 boost::filesystem::path
1359 config_file (boost::filesystem::path main)
1361 cxml::Document f ("Config");
1362 if (!boost::filesystem::exists (main)) {
1363 /* It doesn't exist, so there can't be any links; just return it */
1367 /* See if there's a link */
1370 auto link = f.optional_string_child("Link");
1374 } catch (xmlpp::exception& e) {
1375 /* There as a problem reading the main configuration file,
1376 so there can't be a link.
1384 boost::filesystem::path
1385 Config::config_read_file ()
1387 return config_file (read_path("config.xml"));
1391 boost::filesystem::path
1392 Config::config_write_file ()
1394 return config_file (write_path("config.xml"));
1399 Config::reset_cover_sheet ()
1401 set_cover_sheet_to_default ();
1406 Config::link (boost::filesystem::path new_file) const
1408 xmlpp::Document doc;
1409 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1411 doc.write_to_file_formatted(write_path("config.xml").string());
1412 } catch (xmlpp::exception& e) {
1413 string s = e.what ();
1415 throw FileError (s, write_path("config.xml"));
1420 Config::copy_and_link (boost::filesystem::path new_file) const
1423 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1428 Config::have_write_permission () const
1430 dcp::File f(config_write_file(), "r+");
1431 return static_cast<bool>(f);
1434 /** @param output_channels Number of output channels in use.
1435 * @return Audio mapping for this output channel count (may be a default).
1438 Config::audio_mapping (int output_channels)
1440 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1441 /* Set up a default */
1442 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1443 if (output_channels == 2) {
1444 /* Special case for stereo output.
1445 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1446 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1448 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1449 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1450 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1451 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1452 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1453 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1454 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1455 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1458 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1459 _audio_mapping->set (i, i, 1);
1464 return *_audio_mapping;
1468 Config::set_audio_mapping (AudioMapping m)
1471 changed (AUDIO_MAPPING);
1475 Config::set_audio_mapping_to_default ()
1477 DCPOMATIC_ASSERT (_audio_mapping);
1478 auto const ch = _audio_mapping->output_channels ();
1479 _audio_mapping = boost::none;
1480 _audio_mapping = audio_mapping (ch);
1481 changed (AUDIO_MAPPING);
1486 Config::add_custom_language (dcp::LanguageTag tag)
1488 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1489 _custom_languages.push_back (tag);
1495 optional<Config::BadReason>
1496 Config::check_certificates () const
1498 optional<BadReason> bad;
1500 for (auto const& i: _signer_chain->unordered()) {
1501 if (i.has_utf8_strings()) {
1502 bad = BAD_SIGNER_UTF8_STRINGS;
1504 if ((i.not_after().year() - i.not_before().year()) > 15) {
1505 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1509 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1510 bad = BAD_SIGNER_INCONSISTENT;
1513 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1514 bad = BAD_DECRYPTION_INCONSISTENT;
1522 save_all_config_as_zip (boost::filesystem::path zip_file)
1524 Zipper zipper (zip_file);
1526 auto config = Config::instance();
1527 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1528 if (boost::filesystem::exists(config->cinemas_file())) {
1529 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1531 if (boost::filesystem::exists(config->dkdm_recipients_file())) {
1532 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));