2 Copyright (C) 2012-2021 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/>.
26 #include "dcp_content_type.h"
27 #include "colour_conversion.h"
32 #include "dkdm_wrapper.h"
33 #include "compose.hpp"
35 #include "dkdm_recipient.h"
37 #include <dcp/certificate_chain.h>
38 #include <dcp/name_format.h>
39 #include <dcp/raw_convert.h>
40 #include <libcxml/cxml.h>
42 #include <libxml++/libxml++.h>
43 #include <boost/filesystem.hpp>
44 #include <boost/algorithm/string.hpp>
45 #include <boost/thread.hpp>
60 using std::shared_ptr;
61 using std::make_shared;
62 using boost::optional;
63 using std::dynamic_pointer_cast;
64 using boost::algorithm::trim;
65 using dcp::raw_convert;
67 Config* Config::_instance = 0;
68 int const Config::_current_version = 3;
69 boost::signals2::signal<void ()> Config::FailedToLoad;
70 boost::signals2::signal<void (string)> Config::Warning;
71 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
73 /** Construct default configuration */
75 /* DKDMs are not considered a thing to reset on set_defaults() */
76 : _dkdms (new DKDMGroup ("root"))
82 Config::set_defaults ()
84 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
85 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
86 _server_port_base = 6192;
87 _use_any_servers = true;
89 _only_servers_encode = false;
90 _tms_protocol = FileTransferProtocol::SCP;
95 _allow_any_dcp_frame_rate = false;
96 _allow_any_container = false;
97 _show_experimental_audio_processors = false;
98 _language = optional<string> ();
99 _default_still_length = 10;
100 _default_container = Ratio::from_id ("185");
101 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
102 _default_dcp_audio_channels = 6;
103 _default_j2k_bandwidth = 150000000;
104 _default_audio_delay = 0;
105 _default_interop = false;
106 _default_metadata.clear ();
107 _upload_after_make_dcp = false;
110 _mail_protocol = EmailProtocol::AUTO;
116 _notification_from = "";
117 _notification_to = "";
118 _notification_cc.clear ();
119 _notification_bcc = "";
120 _check_for_updates = false;
121 _check_for_test_updates = false;
122 _maximum_j2k_bandwidth = 250000000;
123 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
124 _analyse_ebur128 = true;
125 _automatic_audio_analysis = false;
126 #ifdef DCPOMATIC_WINDOWS
127 _win32_console = false;
129 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
130 * ~/.config/dcpomatic2 (or equivalent) and written back there.
132 _cinemas_file = read_path ("cinemas.xml");
133 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
134 _show_hints_before_make_dcp = true;
135 _confirm_kdm_email = true;
136 _kdm_container_name_format = dcp::NameFormat ("KDM %f %c");
137 _kdm_filename_format = dcp::NameFormat ("KDM %f %c %s");
138 _dkdm_filename_format = dcp::NameFormat ("DKDM %f %c %s");
139 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
140 _dcp_asset_filename_format = dcp::NameFormat ("%t");
141 _jump_to_selected = true;
142 for (int i = 0; i < NAG_COUNT; ++i) {
146 _sound_output = optional<string> ();
147 _last_kdm_write_type = KDM_WRITE_FLAT;
148 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
150 /* I think the scaling factor here should be the ratio of the longest frame
151 encode time to the shortest; if the thread count is T, longest time is L
152 and the shortest time S we could encode L/S frames per thread whilst waiting
153 for the L frame to encode so we might have to store LT/S frames.
155 However we don't want to use too much memory, so keep it a bit lower than we'd
156 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
157 use about 240Mb with 72 encoding threads.
159 _frames_in_memory_multiplier = 3;
160 _decode_reduction = optional<int>();
161 _default_notify = false;
162 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
163 _notification[i] = false;
165 _barco_username = optional<string>();
166 _barco_password = optional<string>();
167 _christie_username = optional<string>();
168 _christie_password = optional<string>();
169 _gdc_username = optional<string>();
170 _gdc_password = optional<string>();
171 _player_mode = PLAYER_MODE_WINDOW;
173 _video_view_type = VIDEO_VIEW_SIMPLE;
174 _respect_kdm_validity_periods = true;
175 _player_debug_log_file = boost::none;
176 _player_content_directory = boost::none;
177 _player_playlist_directory = boost::none;
178 _player_kdm_directory = boost::none;
179 _audio_mapping = boost::none;
180 _custom_languages.clear ();
181 _add_files_path = boost::none;
182 _use_isdcf_name_by_default = true;
184 _allowed_dcp_frame_rates.clear ();
185 _allowed_dcp_frame_rates.push_back (24);
186 _allowed_dcp_frame_rates.push_back (25);
187 _allowed_dcp_frame_rates.push_back (30);
188 _allowed_dcp_frame_rates.push_back (48);
189 _allowed_dcp_frame_rates.push_back (50);
190 _allowed_dcp_frame_rates.push_back (60);
192 set_kdm_email_to_default ();
193 set_notification_email_to_default ();
194 set_cover_sheet_to_default ();
198 Config::restore_defaults ()
200 Config::instance()->set_defaults ();
201 Config::instance()->changed ();
204 shared_ptr<dcp::CertificateChain>
205 Config::create_certificate_chain ()
207 return make_shared<dcp::CertificateChain> (
209 CERTIFICATE_VALIDITY_PERIOD,
212 ".dcpomatic.smpte-430-2.ROOT",
213 ".dcpomatic.smpte-430-2.INTERMEDIATE",
214 "CS.dcpomatic.smpte-430-2.LEAF"
221 using namespace boost::filesystem;
223 auto copy_adding_number = [](path const& path_to_copy) {
225 auto add_number = [](path const& path, int number) {
226 return String::compose("%1.%2", path, number);
230 while (n < 100 && exists(add_number(path_to_copy, n))) {
233 boost::system::error_code ec;
234 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
237 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
238 * to write over. This is more intended for the situation where we have a corrupted config.xml,
239 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
240 * file). But we might as well back up the other files while we're about it.
243 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
244 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
246 copy_adding_number (config_write_file());
248 /* These do not use State::write_path, so whatever path is in the Config we will copy
251 copy_adding_number (_cinemas_file);
252 copy_adding_number (_dkdm_recipients_file);
259 cxml::Document f ("Config");
260 f.read_file (config_read_file());
262 auto version = f.optional_number_child<int> ("Version");
263 if (version && *version < _current_version) {
264 /* Back up the old config before we re-write it in a back-incompatible way */
268 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
269 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
271 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
272 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
275 _default_directory = f.optional_string_child ("DefaultDirectory");
276 if (_default_directory && _default_directory->empty ()) {
277 /* We used to store an empty value for this to mean "none set" */
278 _default_directory = boost::optional<boost::filesystem::path> ();
281 auto b = f.optional_number_child<int> ("ServerPort");
283 b = f.optional_number_child<int> ("ServerPortBase");
285 _server_port_base = b.get ();
287 auto u = f.optional_bool_child ("UseAnyServers");
288 _use_any_servers = u.get_value_or (true);
290 for (auto i: f.node_children("Server")) {
291 if (i->node_children("HostName").size() == 1) {
292 _servers.push_back (i->string_child ("HostName"));
294 _servers.push_back (i->content ());
298 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
299 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
300 _tms_ip = f.string_child ("TMSIP");
301 _tms_path = f.string_child ("TMSPath");
302 _tms_user = f.string_child ("TMSUser");
303 _tms_password = f.string_child ("TMSPassword");
305 _language = f.optional_string_child ("Language");
307 auto c = f.optional_string_child ("DefaultContainer");
309 _default_container = Ratio::from_id (c.get ());
312 if (_default_container && !_default_container->used_for_container()) {
313 Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)"));
314 _default_container = Ratio::from_id ("185");
317 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
318 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
320 if (f.optional_string_child ("DCPMetadataIssuer")) {
321 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
322 } else if (f.optional_string_child ("DCPIssuer")) {
323 _dcp_issuer = f.string_child ("DCPIssuer");
326 auto up = f.optional_bool_child("UploadAfterMakeDCP");
328 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
330 _upload_after_make_dcp = up.get_value_or (false);
331 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
332 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
333 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
334 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
335 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
337 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
338 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
339 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
340 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
342 for (auto const& i: f.node_children("DefaultMetadata")) {
343 _default_metadata[i->string_attribute("key")] = i->content();
346 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
348 /* Read any cinemas that are still lying around in the config file
349 * from an old version.
353 _mail_server = f.string_child ("MailServer");
354 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
357 /* Make sure this matches the code in write_config */
358 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
359 if (protocol == "Auto") {
360 _mail_protocol = EmailProtocol::AUTO;
361 } else if (protocol == "Plain") {
362 _mail_protocol = EmailProtocol::PLAIN;
363 } else if (protocol == "STARTTLS") {
364 _mail_protocol = EmailProtocol::STARTTLS;
365 } else if (protocol == "SSL") {
366 _mail_protocol = EmailProtocol::SSL;
370 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
371 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
373 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
374 _kdm_from = f.string_child ("KDMFrom");
375 for (auto i: f.node_children("KDMCC")) {
376 if (!i->content().empty()) {
377 _kdm_cc.push_back (i->content ());
380 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
381 _kdm_email = f.string_child ("KDMEmail");
383 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
384 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
385 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
386 for (auto i: f.node_children("NotificationCC")) {
387 if (!i->content().empty()) {
388 _notification_cc.push_back (i->content ());
391 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
392 if (f.optional_string_child("NotificationEmail")) {
393 _notification_email = f.string_child("NotificationEmail");
396 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
397 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
399 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
400 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
401 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
402 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
404 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
405 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
406 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
407 #ifdef DCPOMATIC_WINDOWS
408 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
411 for (auto i: f.node_children("History")) {
412 _history.push_back (i->content ());
415 for (auto i: f.node_children("PlayerHistory")) {
416 _player_history.push_back (i->content ());
419 auto signer = f.optional_node_child ("Signer");
421 auto c = make_shared<dcp::CertificateChain>();
422 /* Read the signing certificates and private key in from the config file */
423 for (auto i: signer->node_children ("Certificate")) {
424 c->add (dcp::Certificate (i->content ()));
426 c->set_key (signer->string_child ("PrivateKey"));
429 /* Make a new set of signing certificates and key */
430 _signer_chain = create_certificate_chain ();
433 auto decryption = f.optional_node_child ("Decryption");
435 auto c = make_shared<dcp::CertificateChain>();
436 for (auto i: decryption->node_children ("Certificate")) {
437 c->add (dcp::Certificate (i->content ()));
439 c->set_key (decryption->string_child ("PrivateKey"));
440 _decryption_chain = c;
442 _decryption_chain = create_certificate_chain ();
445 /* These must be done before we call Bad as that might set one
448 for (auto i: f.node_children("Nagged")) {
449 auto const id = i->number_attribute<int>("Id");
450 if (id >= 0 && id < NAG_COUNT) {
451 _nagged[id] = raw_convert<int>(i->content());
455 auto bad = check_certificates ();
457 auto const remake = Bad(*bad);
458 if (remake && *remake) {
460 case BAD_SIGNER_UTF8_STRINGS:
461 case BAD_SIGNER_INCONSISTENT:
462 case BAD_SIGNER_VALIDITY_TOO_LONG:
463 _signer_chain = create_certificate_chain ();
465 case BAD_DECRYPTION_INCONSISTENT:
466 _decryption_chain = create_certificate_chain ();
472 if (f.optional_node_child("DKDMGroup")) {
473 /* New-style: all DKDMs in a group */
474 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
476 /* Old-style: one or more DKDM nodes */
477 _dkdms.reset (new DKDMGroup ("root"));
478 for (auto i: f.node_children("DKDM")) {
479 _dkdms->add (DKDMBase::read (i));
482 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
483 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
484 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
485 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
486 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
487 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
488 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
489 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
490 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
491 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
492 /* The variable was renamed but not the XML tag */
493 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
494 _sound_output = f.optional_string_child("PreviewSoundOutput");
495 if (f.optional_string_child("CoverSheet")) {
496 _cover_sheet = f.optional_string_child("CoverSheet").get();
498 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
499 if (f.optional_string_child("LastKDMWriteType")) {
500 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
501 _last_kdm_write_type = KDM_WRITE_FLAT;
502 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
503 _last_kdm_write_type = KDM_WRITE_FOLDER;
504 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
505 _last_kdm_write_type = KDM_WRITE_ZIP;
508 if (f.optional_string_child("LastDKDMWriteType")) {
509 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
510 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
511 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
512 _last_dkdm_write_type = DKDM_WRITE_FILE;
515 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
516 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
517 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
519 for (auto i: f.node_children("Notification")) {
520 int const id = i->number_attribute<int>("Id");
521 if (id >= 0 && id < NOTIFICATION_COUNT) {
522 _notification[id] = raw_convert<int>(i->content());
526 _barco_username = f.optional_string_child("BarcoUsername");
527 _barco_password = f.optional_string_child("BarcoPassword");
528 _christie_username = f.optional_string_child("ChristieUsername");
529 _christie_password = f.optional_string_child("ChristiePassword");
530 _gdc_username = f.optional_string_child("GDCUsername");
531 _gdc_password = f.optional_string_child("GDCPassword");
533 auto pm = f.optional_string_child("PlayerMode");
534 if (pm && *pm == "window") {
535 _player_mode = PLAYER_MODE_WINDOW;
536 } else if (pm && *pm == "full") {
537 _player_mode = PLAYER_MODE_FULL;
538 } else if (pm && *pm == "dual") {
539 _player_mode = PLAYER_MODE_DUAL;
542 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
543 auto vc = f.optional_string_child("VideoViewType");
544 if (vc && *vc == "opengl") {
545 _video_view_type = VIDEO_VIEW_OPENGL;
546 } else if (vc && *vc == "simple") {
547 _video_view_type = VIDEO_VIEW_SIMPLE;
549 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
550 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
551 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
552 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
553 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
555 if (f.optional_node_child("AudioMapping")) {
556 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
559 for (auto i: f.node_children("CustomLanguage")) {
561 /* This will fail if it's called before dcp::init() as it won't recognise the
562 * tag. That's OK because the Config will be reloaded again later.
564 _custom_languages.push_back (dcp::LanguageTag(i->content()));
565 } catch (std::runtime_error& e) {}
568 _add_files_path = f.optional_string_child("AddFilesPath");
569 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
571 if (boost::filesystem::exists (_cinemas_file)) {
572 cxml::Document f ("Cinemas");
573 f.read_file (_cinemas_file);
577 if (boost::filesystem::exists (_dkdm_recipients_file)) {
578 cxml::Document f ("DKDMRecipients");
579 f.read_file (_dkdm_recipients_file);
580 read_dkdm_recipients (f);
584 if (have_existing("config.xml") || have_existing("cinemas.xml") || have_existing("dkdm_recipients.xml")) {
586 /* We have a config file but it didn't load */
590 /* Make a new set of signing certificates and key */
591 _signer_chain = create_certificate_chain ();
592 /* And similar for decryption of KDMs */
593 _decryption_chain = create_certificate_chain ();
597 /** @return Singleton instance */
601 if (_instance == 0) {
602 _instance = new Config;
609 /** Write our configuration to disk */
611 Config::write () const
615 write_dkdm_recipients ();
619 Config::write_config () const
622 auto root = doc.create_root_node ("Config");
624 /* [XML] Version The version number of the configuration file format. */
625 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
626 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
627 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
628 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
629 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
630 if (_default_directory) {
631 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
632 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
634 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
635 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
636 by the batch converter to listen for job requests.
638 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
639 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
640 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
642 for (auto i: _servers) {
643 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
646 root->add_child("Server")->add_child_text (i);
649 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
650 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
652 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
653 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
654 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
655 /* [XML] TMSIP IP address of TMS. */
656 root->add_child("TMSIP")->add_child_text (_tms_ip);
657 /* [XML] TMSPath Path on the TMS to copy files to. */
658 root->add_child("TMSPath")->add_child_text (_tms_path);
659 /* [XML] TMSUser Username to log into the TMS with. */
660 root->add_child("TMSUser")->add_child_text (_tms_user);
661 /* [XML] TMSPassword Password to log into the TMS with. */
662 root->add_child("TMSPassword")->add_child_text (_tms_password);
664 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
665 root->add_child("Language")->add_child_text (_language.get());
667 if (_default_container) {
668 /* [XML:opt] DefaultContainer ID of default container
669 to use when creating new films (<code>185</code>,<code>239</code> or
672 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
674 if (_default_dcp_content_type) {
675 /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
676 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
677 <code>PSA</code> or <code>ADV</code>). */
678 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
680 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
681 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
682 /* [XML] DCPIssuer Issuer text to write into CPL files. */
683 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
684 /* [XML] DCPCreator Creator text to write into CPL files. */
685 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
686 /* [XML] Company name to write into MXF files. */
687 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
688 /* [XML] Product name to write into MXF files. */
689 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
690 /* [XML] Product version to write into MXF files. */
691 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
692 /* [XML] Comment to write into JPEG2000 data. */
693 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
694 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
695 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
697 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
698 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
699 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
700 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
701 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
702 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
703 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
704 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
705 for (auto const& i: _default_metadata) {
706 auto c = root->add_child("DefaultMetadata");
707 c->set_attribute("key", i.first);
708 c->add_child_text(i.second);
710 if (_default_kdm_directory) {
711 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
712 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
714 /* [XML] MailServer Hostname of SMTP server to use. */
715 root->add_child("MailServer")->add_child_text (_mail_server);
716 /* [XML] MailPort Port number to use on SMTP server. */
717 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
718 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
719 switch (_mail_protocol) {
720 case EmailProtocol::AUTO:
721 root->add_child("MailProtocol")->add_child_text("Auto");
723 case EmailProtocol::PLAIN:
724 root->add_child("MailProtocol")->add_child_text("Plain");
726 case EmailProtocol::STARTTLS:
727 root->add_child("MailProtocol")->add_child_text("STARTTLS");
729 case EmailProtocol::SSL:
730 root->add_child("MailProtocol")->add_child_text("SSL");
733 /* [XML] MailUser Username to use on SMTP server. */
734 root->add_child("MailUser")->add_child_text (_mail_user);
735 /* [XML] MailPassword Password to use on SMTP server. */
736 root->add_child("MailPassword")->add_child_text (_mail_password);
738 /* [XML] KDMSubject Subject to use for KDM emails. */
739 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
740 /* [XML] KDMFrom From address to use for KDM emails. */
741 root->add_child("KDMFrom")->add_child_text (_kdm_from);
742 for (auto i: _kdm_cc) {
743 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
744 root->add_child("KDMCC")->add_child_text (i);
746 /* [XML] KDMBCC BCC address to use for KDM emails. */
747 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
748 /* [XML] KDMEmail Text of KDM email. */
749 root->add_child("KDMEmail")->add_child_text (_kdm_email);
751 /* [XML] NotificationSubject Subject to use for notification emails. */
752 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
753 /* [XML] NotificationFrom From address to use for notification emails. */
754 root->add_child("NotificationFrom")->add_child_text (_notification_from);
755 /* [XML] NotificationFrom To address to use for notification emails. */
756 root->add_child("NotificationTo")->add_child_text (_notification_to);
757 for (auto i: _notification_cc) {
758 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
759 root->add_child("NotificationCC")->add_child_text (i);
761 /* [XML] NotificationBCC BCC address to use for notification emails. */
762 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
763 /* [XML] NotificationEmail Text of notification email. */
764 root->add_child("NotificationEmail")->add_child_text (_notification_email);
766 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
767 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
768 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
769 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
771 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
772 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
773 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
774 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
775 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
776 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
777 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
778 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
779 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
780 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
781 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
782 related to the player, 1024 debug information related to audio analyses.
784 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
785 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
786 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
787 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
788 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
789 #ifdef DCPOMATIC_WINDOWS
790 if (_win32_console) {
791 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
792 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
793 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
795 root->add_child("Win32Console")->add_child_text ("1");
799 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
800 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
802 auto signer = root->add_child ("Signer");
803 DCPOMATIC_ASSERT (_signer_chain);
804 for (auto const& i: _signer_chain->unordered()) {
805 signer->add_child("Certificate")->add_child_text (i.certificate (true));
807 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
809 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
810 auto decryption = root->add_child ("Decryption");
811 DCPOMATIC_ASSERT (_decryption_chain);
812 for (auto const& i: _decryption_chain->unordered()) {
813 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
815 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
817 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
820 for (auto i: _history) {
821 root->add_child("History")->add_child_text (i.string ());
824 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
827 for (auto i: _player_history) {
828 root->add_child("PlayerHistory")->add_child_text (i.string ());
831 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
832 or <code><DKDM></code> tags.
834 /* [XML] DKDM A DKDM as XML */
835 _dkdms->as_xml (root);
837 /* [XML] CinemasFile Filename of cinemas list file. */
838 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
839 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
840 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
841 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
842 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
843 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
844 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
845 /* [XML] KDMFilenameFormat Format for KDM filenames. */
846 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
847 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
848 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
849 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
850 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
851 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
852 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
853 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
854 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
855 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
856 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
857 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
858 for (int i = 0; i < NAG_COUNT; ++i) {
859 xmlpp::Element* e = root->add_child ("Nagged");
860 e->set_attribute ("Id", raw_convert<string>(i));
861 e->add_child_text (_nagged[i] ? "1" : "0");
863 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
864 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
866 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
867 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
869 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
870 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
871 if (_last_player_load_directory) {
872 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
874 /* [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. */
875 if (_last_kdm_write_type) {
876 switch (_last_kdm_write_type.get()) {
878 root->add_child("LastKDMWriteType")->add_child_text("flat");
880 case KDM_WRITE_FOLDER:
881 root->add_child("LastKDMWriteType")->add_child_text("folder");
884 root->add_child("LastKDMWriteType")->add_child_text("zip");
888 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
889 if (_last_dkdm_write_type) {
890 switch (_last_dkdm_write_type.get()) {
891 case DKDM_WRITE_INTERNAL:
892 root->add_child("LastDKDMWriteType")->add_child_text("internal");
894 case DKDM_WRITE_FILE:
895 root->add_child("LastDKDMWriteType")->add_child_text("file");
899 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
900 frames to be held in memory at once.
902 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
904 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
905 if (_decode_reduction) {
906 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
909 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
910 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
912 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
913 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
914 xmlpp::Element* e = root->add_child ("Notification");
915 e->set_attribute ("Id", raw_convert<string>(i));
916 e->add_child_text (_notification[i] ? "1" : "0");
919 if (_barco_username) {
920 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
921 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
923 if (_barco_password) {
924 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
925 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
928 if (_christie_username) {
929 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
930 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
932 if (_christie_password) {
933 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
934 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
938 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
939 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
942 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
943 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
946 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
947 with controls on another monitor.
949 switch (_player_mode) {
950 case PLAYER_MODE_WINDOW:
951 root->add_child("PlayerMode")->add_child_text("window");
953 case PLAYER_MODE_FULL:
954 root->add_child("PlayerMode")->add_child_text("full");
956 case PLAYER_MODE_DUAL:
957 root->add_child("PlayerMode")->add_child_text("dual");
961 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
962 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
963 switch (_video_view_type) {
964 case VIDEO_VIEW_SIMPLE:
965 root->add_child("VideoViewType")->add_child_text("simple");
967 case VIDEO_VIEW_OPENGL:
968 root->add_child("VideoViewType")->add_child_text("opengl");
971 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
972 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
973 if (_player_debug_log_file) {
974 /* [XML] PlayerLogFile Filename to use for player debug logs. */
975 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
977 if (_player_content_directory) {
978 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
979 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
981 if (_player_playlist_directory) {
982 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
983 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
985 if (_player_kdm_directory) {
986 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
987 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
989 if (_audio_mapping) {
990 _audio_mapping->as_xml (root->add_child("AudioMapping"));
992 for (auto const& i: _custom_languages) {
993 root->add_child("CustomLanguage")->add_child_text(i.to_string());
995 if (_add_files_path) {
996 /* [XML] AddFilesPath The default path that will be offered in the picker when adding files to a film. */
997 root->add_child("AddFilesPath")->add_child_text(_add_files_path->string());
999 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1001 auto target = config_write_file();
1004 auto const s = doc.write_to_string_formatted ();
1005 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1006 auto f = fopen_boost (tmp, "w");
1008 throw FileError (_("Could not open file for writing"), tmp);
1010 checked_fwrite (s.c_str(), s.bytes(), f, tmp);
1012 boost::filesystem::remove (target);
1013 boost::filesystem::rename (tmp, target);
1014 } catch (xmlpp::exception& e) {
1015 string s = e.what ();
1017 throw FileError (s, target);
1024 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1026 xmlpp::Document doc;
1027 auto root = doc.create_root_node (root_node);
1028 root->add_child("Version")->add_child_text(version);
1030 for (auto i: things) {
1031 i->as_xml (root->add_child(node));
1035 doc.write_to_file_formatted (file.string() + ".tmp");
1036 boost::filesystem::remove (file);
1037 boost::filesystem::rename (file.string() + ".tmp", file);
1038 } catch (xmlpp::exception& e) {
1039 string s = e.what ();
1041 throw FileError (s, file);
1047 Config::write_cinemas () const
1049 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1054 Config::write_dkdm_recipients () const
1056 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1060 boost::filesystem::path
1061 Config::default_directory_or (boost::filesystem::path a) const
1063 return directory_or (_default_directory, a);
1066 boost::filesystem::path
1067 Config::default_kdm_directory_or (boost::filesystem::path a) const
1069 return directory_or (_default_kdm_directory, a);
1072 boost::filesystem::path
1073 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1079 boost::system::error_code ec;
1080 auto const e = boost::filesystem::exists (*dir, ec);
1096 Config::changed (Property what)
1102 Config::set_kdm_email_to_default ()
1104 _kdm_subject = _("KDM delivery: $CPL_NAME");
1107 "Dear Projectionist\n\n"
1108 "Please find attached KDMs for $CPL_NAME.\n\n"
1109 "Cinema: $CINEMA_NAME\n"
1110 "Screen(s): $SCREENS\n\n"
1111 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1112 "Best regards,\nDCP-o-matic"
1117 Config::set_notification_email_to_default ()
1119 _notification_subject = _("DCP-o-matic notification");
1121 _notification_email = _(
1122 "$JOB_NAME: $JOB_STATUS"
1127 Config::reset_kdm_email ()
1129 set_kdm_email_to_default ();
1134 Config::reset_notification_email ()
1136 set_notification_email_to_default ();
1141 Config::set_cover_sheet_to_default ()
1146 "Format: $CONTAINER\n"
1148 "Audio Language: $AUDIO_LANGUAGE\n"
1149 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1156 Config::add_to_history (boost::filesystem::path p)
1158 add_to_history_internal (_history, p);
1161 /** Remove non-existant items from the history */
1163 Config::clean_history ()
1165 clean_history_internal (_history);
1169 Config::add_to_player_history (boost::filesystem::path p)
1171 add_to_history_internal (_player_history, p);
1174 /** Remove non-existant items from the player history */
1176 Config::clean_player_history ()
1178 clean_history_internal (_player_history);
1182 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1184 /* Remove existing instances of this path in the history */
1185 h.erase (remove (h.begin(), h.end(), p), h.end ());
1187 h.insert (h.begin (), p);
1188 if (h.size() > HISTORY_SIZE) {
1196 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1202 if (boost::filesystem::is_directory(i)) {
1206 /* We couldn't find out if it's a directory for some reason; just ignore it */
1213 Config::have_existing (string file)
1215 return boost::filesystem::exists (read_path(file));
1220 Config::read_cinemas (cxml::Document const & f)
1223 for (auto i: f.node_children("Cinema")) {
1224 /* Slightly grotty two-part construction of Cinema here so that we can use
1227 auto cinema = make_shared<Cinema>(i);
1228 cinema->read_screens (i);
1229 _cinemas.push_back (cinema);
1234 Config::set_cinemas_file (boost::filesystem::path file)
1236 if (file == _cinemas_file) {
1240 _cinemas_file = file;
1242 if (boost::filesystem::exists (_cinemas_file)) {
1243 /* Existing file; read it in */
1244 cxml::Document f ("Cinemas");
1245 f.read_file (_cinemas_file);
1254 Config::read_dkdm_recipients (cxml::Document const & f)
1256 _dkdm_recipients.clear ();
1257 for (auto i: f.node_children("DKDMRecipient")) {
1258 _dkdm_recipients.push_back (shared_ptr<DKDMRecipient>(new DKDMRecipient(i)));
1264 Config::save_template (shared_ptr<const Film> film, string name) const
1266 film->write_template (template_write_path(name));
1271 Config::templates () const
1273 if (!boost::filesystem::exists(read_path("templates"))) {
1278 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1279 n.push_back (i.path().filename().string());
1285 Config::existing_template (string name) const
1287 return boost::filesystem::exists (template_read_path(name));
1291 boost::filesystem::path
1292 Config::template_read_path (string name) const
1294 return read_path("templates") / tidy_for_filename (name);
1298 boost::filesystem::path
1299 Config::template_write_path (string name) const
1301 return write_path("templates") / tidy_for_filename (name);
1306 Config::rename_template (string old_name, string new_name) const
1308 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1312 Config::delete_template (string name) const
1314 boost::filesystem::remove (template_write_path(name));
1317 /** @return Path to the config.xml containing the actual settings, following a link if required */
1318 boost::filesystem::path
1319 config_file (boost::filesystem::path main)
1321 cxml::Document f ("Config");
1322 if (!boost::filesystem::exists (main)) {
1323 /* It doesn't exist, so there can't be any links; just return it */
1327 /* See if there's a link */
1330 auto link = f.optional_string_child("Link");
1334 } catch (xmlpp::exception& e) {
1335 /* There as a problem reading the main configuration file,
1336 so there can't be a link.
1344 boost::filesystem::path
1345 Config::config_read_file ()
1347 return config_file (read_path("config.xml"));
1351 boost::filesystem::path
1352 Config::config_write_file ()
1354 return config_file (write_path("config.xml"));
1359 Config::reset_cover_sheet ()
1361 set_cover_sheet_to_default ();
1366 Config::link (boost::filesystem::path new_file) const
1368 xmlpp::Document doc;
1369 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1371 doc.write_to_file_formatted(write_path("config.xml").string());
1372 } catch (xmlpp::exception& e) {
1373 string s = e.what ();
1375 throw FileError (s, write_path("config.xml"));
1380 Config::copy_and_link (boost::filesystem::path new_file) const
1383 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1388 Config::have_write_permission () const
1390 auto f = fopen_boost (config_write_file(), "r+");
1399 /** @param output_channels Number of output channels in use.
1400 * @return Audio mapping for this output channel count (may be a default).
1403 Config::audio_mapping (int output_channels)
1405 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1406 /* Set up a default */
1407 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1408 if (output_channels == 2) {
1409 /* Special case for stereo output.
1410 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1411 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1413 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1414 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1415 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1416 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1417 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1418 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1419 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1420 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1423 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1424 _audio_mapping->set (i, i, 1);
1429 return *_audio_mapping;
1433 Config::set_audio_mapping (AudioMapping m)
1436 changed (AUDIO_MAPPING);
1440 Config::set_audio_mapping_to_default ()
1442 DCPOMATIC_ASSERT (_audio_mapping);
1443 auto const ch = _audio_mapping->output_channels ();
1444 _audio_mapping = boost::none;
1445 _audio_mapping = audio_mapping (ch);
1446 changed (AUDIO_MAPPING);
1451 Config::add_custom_language (dcp::LanguageTag tag)
1453 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1454 _custom_languages.push_back (tag);
1460 optional<Config::BadReason>
1461 Config::check_certificates () const
1463 optional<BadReason> bad;
1465 for (auto const& i: _signer_chain->unordered()) {
1466 if (i.has_utf8_strings()) {
1467 bad = BAD_SIGNER_UTF8_STRINGS;
1469 if ((i.not_after().year() - i.not_before().year()) > 15) {
1470 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1474 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1475 bad = BAD_SIGNER_INCONSISTENT;
1478 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1479 bad = BAD_DECRYPTION_INCONSISTENT;
1487 save_all_config_as_zip (boost::filesystem::path zip_file)
1489 Zipper zipper (zip_file);
1491 auto config = Config::instance();
1492 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1493 if (boost::filesystem::exists(config->cinemas_file())) {
1494 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1496 if (boost::filesystem::exists(config->dkdm_recipients_file())) {
1497 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));