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/>.
23 #include "colour_conversion.h"
24 #include "compose.hpp"
28 #include "dcp_content_type.h"
29 #include "dkdm_recipient.h"
30 #include "dkdm_wrapper.h"
38 #include <dcp/certificate_chain.h>
39 #include <dcp/name_format.h>
40 #include <dcp/raw_convert.h>
41 #include <libcxml/cxml.h>
43 #include <libxml++/libxml++.h>
44 #include <boost/filesystem.hpp>
45 #include <boost/algorithm/string.hpp>
46 #include <boost/thread.hpp>
55 using std::dynamic_pointer_cast;
58 using std::make_shared;
62 using std::shared_ptr;
65 using boost::algorithm::trim;
66 using boost::optional;
67 using dcp::raw_convert;
70 Config* Config::_instance = 0;
71 int const Config::_current_version = 3;
72 boost::signals2::signal<void ()> Config::FailedToLoad;
73 boost::signals2::signal<void (string)> Config::Warning;
74 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
77 /** Construct default configuration */
79 /* DKDMs are not considered a thing to reset on set_defaults() */
80 : _dkdms (new DKDMGroup ("root"))
86 Config::set_defaults ()
88 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
89 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
90 _server_port_base = 6192;
91 _use_any_servers = true;
93 _only_servers_encode = false;
94 _tms_protocol = FileTransferProtocol::SCP;
99 _allow_any_dcp_frame_rate = false;
100 _allow_any_container = false;
101 _allow_96khz_audio = false;
102 _show_experimental_audio_processors = false;
103 _language = optional<string> ();
104 _default_still_length = 10;
105 _default_container = Ratio::from_id ("185");
106 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
107 _default_dcp_audio_channels = 6;
108 _default_j2k_bandwidth = 150000000;
109 _default_audio_delay = 0;
110 _default_interop = false;
111 _default_metadata.clear ();
112 _upload_after_make_dcp = false;
115 _mail_protocol = EmailProtocol::AUTO;
121 _notification_from = "";
122 _notification_to = "";
123 _notification_cc.clear ();
124 _notification_bcc = "";
125 _check_for_updates = false;
126 _check_for_test_updates = false;
127 _maximum_j2k_bandwidth = 250000000;
128 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
129 _analyse_ebur128 = true;
130 _automatic_audio_analysis = false;
131 #ifdef DCPOMATIC_WINDOWS
132 _win32_console = false;
134 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
135 * ~/.config/dcpomatic2 (or equivalent) and written back there.
137 _cinemas_file = read_path ("cinemas.xml");
138 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
139 _show_hints_before_make_dcp = true;
140 _confirm_kdm_email = true;
141 _kdm_container_name_format = dcp::NameFormat ("KDM %f %c");
142 _kdm_filename_format = dcp::NameFormat ("KDM %f %c %s");
143 _dkdm_filename_format = dcp::NameFormat ("DKDM %f %c %s");
144 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
145 _dcp_asset_filename_format = dcp::NameFormat ("%t");
146 _jump_to_selected = true;
147 for (int i = 0; i < NAG_COUNT; ++i) {
151 _sound_output = optional<string> ();
152 _last_kdm_write_type = KDM_WRITE_FLAT;
153 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
155 /* I think the scaling factor here should be the ratio of the longest frame
156 encode time to the shortest; if the thread count is T, longest time is L
157 and the shortest time S we could encode L/S frames per thread whilst waiting
158 for the L frame to encode so we might have to store LT/S frames.
160 However we don't want to use too much memory, so keep it a bit lower than we'd
161 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
162 use about 240Mb with 72 encoding threads.
164 _frames_in_memory_multiplier = 3;
165 _decode_reduction = optional<int>();
166 _default_notify = false;
167 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
168 _notification[i] = false;
170 _barco_username = optional<string>();
171 _barco_password = optional<string>();
172 _christie_username = optional<string>();
173 _christie_password = optional<string>();
174 _gdc_username = optional<string>();
175 _gdc_password = optional<string>();
176 _player_mode = PLAYER_MODE_WINDOW;
178 _video_view_type = VIDEO_VIEW_SIMPLE;
179 _respect_kdm_validity_periods = true;
180 _player_debug_log_file = boost::none;
181 _player_content_directory = boost::none;
182 _player_playlist_directory = boost::none;
183 _player_kdm_directory = boost::none;
184 _audio_mapping = boost::none;
185 _custom_languages.clear ();
186 _add_files_path = boost::none;
187 _auto_crop_threshold = 0.1;
188 _use_isdcf_name_by_default = true;
190 _allowed_dcp_frame_rates.clear ();
191 _allowed_dcp_frame_rates.push_back (24);
192 _allowed_dcp_frame_rates.push_back (25);
193 _allowed_dcp_frame_rates.push_back (30);
194 _allowed_dcp_frame_rates.push_back (48);
195 _allowed_dcp_frame_rates.push_back (50);
196 _allowed_dcp_frame_rates.push_back (60);
198 set_kdm_email_to_default ();
199 set_notification_email_to_default ();
200 set_cover_sheet_to_default ();
204 Config::restore_defaults ()
206 Config::instance()->set_defaults ();
207 Config::instance()->changed ();
210 shared_ptr<dcp::CertificateChain>
211 Config::create_certificate_chain ()
213 return make_shared<dcp::CertificateChain> (
215 CERTIFICATE_VALIDITY_PERIOD,
218 ".dcpomatic.smpte-430-2.ROOT",
219 ".dcpomatic.smpte-430-2.INTERMEDIATE",
220 "CS.dcpomatic.smpte-430-2.LEAF"
227 using namespace boost::filesystem;
229 auto copy_adding_number = [](path const& path_to_copy) {
231 auto add_number = [](path const& path, int number) {
232 return String::compose("%1.%2", path, number);
236 while (n < 100 && exists(add_number(path_to_copy, n))) {
239 boost::system::error_code ec;
240 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
243 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
244 * to write over. This is more intended for the situation where we have a corrupted config.xml,
245 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
246 * file). But we might as well back up the other files while we're about it.
249 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
250 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
252 copy_adding_number (config_write_file());
254 /* These do not use State::write_path, so whatever path is in the Config we will copy
257 copy_adding_number (_cinemas_file);
258 copy_adding_number (_dkdm_recipients_file);
265 cxml::Document f ("Config");
266 f.read_file (config_read_file());
268 auto version = f.optional_number_child<int> ("Version");
269 if (version && *version < _current_version) {
270 /* Back up the old config before we re-write it in a back-incompatible way */
274 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
275 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
277 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
278 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
281 _default_directory = f.optional_string_child ("DefaultDirectory");
282 if (_default_directory && _default_directory->empty ()) {
283 /* We used to store an empty value for this to mean "none set" */
284 _default_directory = boost::optional<boost::filesystem::path> ();
287 auto b = f.optional_number_child<int> ("ServerPort");
289 b = f.optional_number_child<int> ("ServerPortBase");
291 _server_port_base = b.get ();
293 auto u = f.optional_bool_child ("UseAnyServers");
294 _use_any_servers = u.get_value_or (true);
296 for (auto i: f.node_children("Server")) {
297 if (i->node_children("HostName").size() == 1) {
298 _servers.push_back (i->string_child ("HostName"));
300 _servers.push_back (i->content ());
304 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
305 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
306 _tms_ip = f.string_child ("TMSIP");
307 _tms_path = f.string_child ("TMSPath");
308 _tms_user = f.string_child ("TMSUser");
309 _tms_password = f.string_child ("TMSPassword");
311 _language = f.optional_string_child ("Language");
313 auto c = f.optional_string_child ("DefaultContainer");
315 _default_container = Ratio::from_id (c.get ());
318 if (_default_container && !_default_container->used_for_container()) {
319 Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)"));
320 _default_container = Ratio::from_id ("185");
323 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
324 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
326 if (f.optional_string_child ("DCPMetadataIssuer")) {
327 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
328 } else if (f.optional_string_child ("DCPIssuer")) {
329 _dcp_issuer = f.string_child ("DCPIssuer");
332 auto up = f.optional_bool_child("UploadAfterMakeDCP");
334 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
336 _upload_after_make_dcp = up.get_value_or (false);
337 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
338 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
339 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
340 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
341 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
343 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
344 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
345 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
346 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
348 for (auto const& i: f.node_children("DefaultMetadata")) {
349 _default_metadata[i->string_attribute("key")] = i->content();
352 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
354 /* Read any cinemas that are still lying around in the config file
355 * from an old version.
359 _mail_server = f.string_child ("MailServer");
360 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
363 /* Make sure this matches the code in write_config */
364 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
365 if (protocol == "Auto") {
366 _mail_protocol = EmailProtocol::AUTO;
367 } else if (protocol == "Plain") {
368 _mail_protocol = EmailProtocol::PLAIN;
369 } else if (protocol == "STARTTLS") {
370 _mail_protocol = EmailProtocol::STARTTLS;
371 } else if (protocol == "SSL") {
372 _mail_protocol = EmailProtocol::SSL;
376 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
377 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
379 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
380 _kdm_from = f.string_child ("KDMFrom");
381 for (auto i: f.node_children("KDMCC")) {
382 if (!i->content().empty()) {
383 _kdm_cc.push_back (i->content ());
386 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
387 _kdm_email = f.string_child ("KDMEmail");
389 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
390 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
391 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
392 for (auto i: f.node_children("NotificationCC")) {
393 if (!i->content().empty()) {
394 _notification_cc.push_back (i->content ());
397 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
398 if (f.optional_string_child("NotificationEmail")) {
399 _notification_email = f.string_child("NotificationEmail");
402 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
403 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
405 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
406 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
407 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
408 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
409 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
411 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
412 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
413 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
414 #ifdef DCPOMATIC_WINDOWS
415 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
418 for (auto i: f.node_children("History")) {
419 _history.push_back (i->content ());
422 for (auto i: f.node_children("PlayerHistory")) {
423 _player_history.push_back (i->content ());
426 auto signer = f.optional_node_child ("Signer");
428 auto c = make_shared<dcp::CertificateChain>();
429 /* Read the signing certificates and private key in from the config file */
430 for (auto i: signer->node_children ("Certificate")) {
431 c->add (dcp::Certificate (i->content ()));
433 c->set_key (signer->string_child ("PrivateKey"));
436 /* Make a new set of signing certificates and key */
437 _signer_chain = create_certificate_chain ();
440 auto decryption = f.optional_node_child ("Decryption");
442 auto c = make_shared<dcp::CertificateChain>();
443 for (auto i: decryption->node_children ("Certificate")) {
444 c->add (dcp::Certificate (i->content ()));
446 c->set_key (decryption->string_child ("PrivateKey"));
447 _decryption_chain = c;
449 _decryption_chain = create_certificate_chain ();
452 /* These must be done before we call Bad as that might set one
455 for (auto i: f.node_children("Nagged")) {
456 auto const id = i->number_attribute<int>("Id");
457 if (id >= 0 && id < NAG_COUNT) {
458 _nagged[id] = raw_convert<int>(i->content());
462 auto bad = check_certificates ();
464 auto const remake = Bad(*bad);
465 if (remake && *remake) {
467 case BAD_SIGNER_UTF8_STRINGS:
468 case BAD_SIGNER_INCONSISTENT:
469 case BAD_SIGNER_VALIDITY_TOO_LONG:
470 _signer_chain = create_certificate_chain ();
472 case BAD_DECRYPTION_INCONSISTENT:
473 _decryption_chain = create_certificate_chain ();
479 if (f.optional_node_child("DKDMGroup")) {
480 /* New-style: all DKDMs in a group */
481 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
483 /* Old-style: one or more DKDM nodes */
484 _dkdms = make_shared<DKDMGroup>("root");
485 for (auto i: f.node_children("DKDM")) {
486 _dkdms->add (DKDMBase::read (i));
489 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
490 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
491 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
492 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
493 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
494 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
495 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
496 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
497 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
498 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
499 /* The variable was renamed but not the XML tag */
500 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
501 _sound_output = f.optional_string_child("PreviewSoundOutput");
502 if (f.optional_string_child("CoverSheet")) {
503 _cover_sheet = f.optional_string_child("CoverSheet").get();
505 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
506 if (f.optional_string_child("LastKDMWriteType")) {
507 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
508 _last_kdm_write_type = KDM_WRITE_FLAT;
509 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
510 _last_kdm_write_type = KDM_WRITE_FOLDER;
511 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
512 _last_kdm_write_type = KDM_WRITE_ZIP;
515 if (f.optional_string_child("LastDKDMWriteType")) {
516 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
517 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
518 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
519 _last_dkdm_write_type = DKDM_WRITE_FILE;
522 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
523 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
524 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
526 for (auto i: f.node_children("Notification")) {
527 int const id = i->number_attribute<int>("Id");
528 if (id >= 0 && id < NOTIFICATION_COUNT) {
529 _notification[id] = raw_convert<int>(i->content());
533 _barco_username = f.optional_string_child("BarcoUsername");
534 _barco_password = f.optional_string_child("BarcoPassword");
535 _christie_username = f.optional_string_child("ChristieUsername");
536 _christie_password = f.optional_string_child("ChristiePassword");
537 _gdc_username = f.optional_string_child("GDCUsername");
538 _gdc_password = f.optional_string_child("GDCPassword");
540 auto pm = f.optional_string_child("PlayerMode");
541 if (pm && *pm == "window") {
542 _player_mode = PLAYER_MODE_WINDOW;
543 } else if (pm && *pm == "full") {
544 _player_mode = PLAYER_MODE_FULL;
545 } else if (pm && *pm == "dual") {
546 _player_mode = PLAYER_MODE_DUAL;
549 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
550 auto vc = f.optional_string_child("VideoViewType");
551 if (vc && *vc == "opengl") {
552 _video_view_type = VIDEO_VIEW_OPENGL;
553 } else if (vc && *vc == "simple") {
554 _video_view_type = VIDEO_VIEW_SIMPLE;
556 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
557 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
558 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
559 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
560 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
562 if (f.optional_node_child("AudioMapping")) {
563 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
566 for (auto i: f.node_children("CustomLanguage")) {
568 /* This will fail if it's called before dcp::init() as it won't recognise the
569 * tag. That's OK because the Config will be reloaded again later.
571 _custom_languages.push_back (dcp::LanguageTag(i->content()));
572 } catch (std::runtime_error& e) {}
575 _add_files_path = f.optional_string_child("AddFilesPath");
576 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
577 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
579 if (boost::filesystem::exists (_cinemas_file)) {
580 cxml::Document f ("Cinemas");
581 f.read_file (_cinemas_file);
585 if (boost::filesystem::exists (_dkdm_recipients_file)) {
586 cxml::Document f ("DKDMRecipients");
587 f.read_file (_dkdm_recipients_file);
588 read_dkdm_recipients (f);
592 if (have_existing("config.xml") || have_existing("cinemas.xml") || have_existing("dkdm_recipients.xml")) {
594 /* We have a config file but it didn't load */
598 /* Make a new set of signing certificates and key */
599 _signer_chain = create_certificate_chain ();
600 /* And similar for decryption of KDMs */
601 _decryption_chain = create_certificate_chain ();
605 /** @return Singleton instance */
609 if (_instance == nullptr) {
610 _instance = new Config;
617 /** Write our configuration to disk */
619 Config::write () const
623 write_dkdm_recipients ();
627 Config::write_config () const
630 auto root = doc.create_root_node ("Config");
632 /* [XML] Version The version number of the configuration file format. */
633 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
634 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
635 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
636 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
637 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
638 if (_default_directory) {
639 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
640 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
642 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
643 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
644 by the batch converter to listen for job requests.
646 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
647 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
648 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
650 for (auto i: _servers) {
651 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
654 root->add_child("Server")->add_child_text (i);
657 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
658 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
660 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
661 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
662 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
663 /* [XML] TMSIP IP address of TMS. */
664 root->add_child("TMSIP")->add_child_text (_tms_ip);
665 /* [XML] TMSPath Path on the TMS to copy files to. */
666 root->add_child("TMSPath")->add_child_text (_tms_path);
667 /* [XML] TMSUser Username to log into the TMS with. */
668 root->add_child("TMSUser")->add_child_text (_tms_user);
669 /* [XML] TMSPassword Password to log into the TMS with. */
670 root->add_child("TMSPassword")->add_child_text (_tms_password);
672 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
673 root->add_child("Language")->add_child_text (_language.get());
675 if (_default_container) {
676 /* [XML:opt] DefaultContainer ID of default container
677 to use when creating new films (<code>185</code>,<code>239</code> or
680 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
682 if (_default_dcp_content_type) {
683 /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
684 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
685 <code>PSA</code> or <code>ADV</code>). */
686 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
688 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
689 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
690 /* [XML] DCPIssuer Issuer text to write into CPL files. */
691 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
692 /* [XML] DCPCreator Creator text to write into CPL files. */
693 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
694 /* [XML] Company name to write into MXF files. */
695 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
696 /* [XML] Product name to write into MXF files. */
697 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
698 /* [XML] Product version to write into MXF files. */
699 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
700 /* [XML] Comment to write into JPEG2000 data. */
701 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
702 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
703 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
705 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
706 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
707 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
708 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
709 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
710 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
711 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
712 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
713 for (auto const& i: _default_metadata) {
714 auto c = root->add_child("DefaultMetadata");
715 c->set_attribute("key", i.first);
716 c->add_child_text(i.second);
718 if (_default_kdm_directory) {
719 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
720 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
722 /* [XML] MailServer Hostname of SMTP server to use. */
723 root->add_child("MailServer")->add_child_text (_mail_server);
724 /* [XML] MailPort Port number to use on SMTP server. */
725 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
726 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
727 switch (_mail_protocol) {
728 case EmailProtocol::AUTO:
729 root->add_child("MailProtocol")->add_child_text("Auto");
731 case EmailProtocol::PLAIN:
732 root->add_child("MailProtocol")->add_child_text("Plain");
734 case EmailProtocol::STARTTLS:
735 root->add_child("MailProtocol")->add_child_text("STARTTLS");
737 case EmailProtocol::SSL:
738 root->add_child("MailProtocol")->add_child_text("SSL");
741 /* [XML] MailUser Username to use on SMTP server. */
742 root->add_child("MailUser")->add_child_text (_mail_user);
743 /* [XML] MailPassword Password to use on SMTP server. */
744 root->add_child("MailPassword")->add_child_text (_mail_password);
746 /* [XML] KDMSubject Subject to use for KDM emails. */
747 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
748 /* [XML] KDMFrom From address to use for KDM emails. */
749 root->add_child("KDMFrom")->add_child_text (_kdm_from);
750 for (auto i: _kdm_cc) {
751 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
752 root->add_child("KDMCC")->add_child_text (i);
754 /* [XML] KDMBCC BCC address to use for KDM emails. */
755 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
756 /* [XML] KDMEmail Text of KDM email. */
757 root->add_child("KDMEmail")->add_child_text (_kdm_email);
759 /* [XML] NotificationSubject Subject to use for notification emails. */
760 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
761 /* [XML] NotificationFrom From address to use for notification emails. */
762 root->add_child("NotificationFrom")->add_child_text (_notification_from);
763 /* [XML] NotificationFrom To address to use for notification emails. */
764 root->add_child("NotificationTo")->add_child_text (_notification_to);
765 for (auto i: _notification_cc) {
766 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
767 root->add_child("NotificationCC")->add_child_text (i);
769 /* [XML] NotificationBCC BCC address to use for notification emails. */
770 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
771 /* [XML] NotificationEmail Text of notification email. */
772 root->add_child("NotificationEmail")->add_child_text (_notification_email);
774 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
775 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
776 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
777 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
779 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
780 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
781 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
782 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
783 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
784 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
785 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
786 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
787 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
788 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
789 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
790 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
791 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
792 related to the player, 1024 debug information related to audio analyses.
794 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
795 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
796 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
797 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
798 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
799 #ifdef DCPOMATIC_WINDOWS
800 if (_win32_console) {
801 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
802 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
803 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
805 root->add_child("Win32Console")->add_child_text ("1");
809 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
810 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
812 auto signer = root->add_child ("Signer");
813 DCPOMATIC_ASSERT (_signer_chain);
814 for (auto const& i: _signer_chain->unordered()) {
815 signer->add_child("Certificate")->add_child_text (i.certificate (true));
817 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
819 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
820 auto decryption = root->add_child ("Decryption");
821 DCPOMATIC_ASSERT (_decryption_chain);
822 for (auto const& i: _decryption_chain->unordered()) {
823 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
825 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
827 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
830 for (auto i: _history) {
831 root->add_child("History")->add_child_text (i.string ());
834 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
837 for (auto i: _player_history) {
838 root->add_child("PlayerHistory")->add_child_text (i.string ());
841 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
842 or <code><DKDM></code> tags.
844 /* [XML] DKDM A DKDM as XML */
845 _dkdms->as_xml (root);
847 /* [XML] CinemasFile Filename of cinemas list file. */
848 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
849 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
850 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
851 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
852 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
853 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
854 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
855 /* [XML] KDMFilenameFormat Format for KDM filenames. */
856 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
857 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
858 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
859 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
860 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
861 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
862 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
863 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
864 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
865 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
866 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
867 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
868 for (int i = 0; i < NAG_COUNT; ++i) {
869 xmlpp::Element* e = root->add_child ("Nagged");
870 e->set_attribute ("Id", raw_convert<string>(i));
871 e->add_child_text (_nagged[i] ? "1" : "0");
873 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
874 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
876 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
877 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
879 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
880 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
881 if (_last_player_load_directory) {
882 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
884 /* [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. */
885 if (_last_kdm_write_type) {
886 switch (_last_kdm_write_type.get()) {
888 root->add_child("LastKDMWriteType")->add_child_text("flat");
890 case KDM_WRITE_FOLDER:
891 root->add_child("LastKDMWriteType")->add_child_text("folder");
894 root->add_child("LastKDMWriteType")->add_child_text("zip");
898 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
899 if (_last_dkdm_write_type) {
900 switch (_last_dkdm_write_type.get()) {
901 case DKDM_WRITE_INTERNAL:
902 root->add_child("LastDKDMWriteType")->add_child_text("internal");
904 case DKDM_WRITE_FILE:
905 root->add_child("LastDKDMWriteType")->add_child_text("file");
909 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
910 frames to be held in memory at once.
912 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
914 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
915 if (_decode_reduction) {
916 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
919 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
920 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
922 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
923 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
924 xmlpp::Element* e = root->add_child ("Notification");
925 e->set_attribute ("Id", raw_convert<string>(i));
926 e->add_child_text (_notification[i] ? "1" : "0");
929 if (_barco_username) {
930 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
931 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
933 if (_barco_password) {
934 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
935 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
938 if (_christie_username) {
939 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
940 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
942 if (_christie_password) {
943 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
944 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
948 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
949 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
952 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
953 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
956 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
957 with controls on another monitor.
959 switch (_player_mode) {
960 case PLAYER_MODE_WINDOW:
961 root->add_child("PlayerMode")->add_child_text("window");
963 case PLAYER_MODE_FULL:
964 root->add_child("PlayerMode")->add_child_text("full");
966 case PLAYER_MODE_DUAL:
967 root->add_child("PlayerMode")->add_child_text("dual");
971 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
972 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
973 switch (_video_view_type) {
974 case VIDEO_VIEW_SIMPLE:
975 root->add_child("VideoViewType")->add_child_text("simple");
977 case VIDEO_VIEW_OPENGL:
978 root->add_child("VideoViewType")->add_child_text("opengl");
981 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
982 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
983 if (_player_debug_log_file) {
984 /* [XML] PlayerLogFile Filename to use for player debug logs. */
985 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
987 if (_player_content_directory) {
988 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
989 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
991 if (_player_playlist_directory) {
992 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
993 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
995 if (_player_kdm_directory) {
996 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
997 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
999 if (_audio_mapping) {
1000 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1002 for (auto const& i: _custom_languages) {
1003 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1005 if (_add_files_path) {
1006 /* [XML] AddFilesPath The default path that will be offered in the picker when adding files to a film. */
1007 root->add_child("AddFilesPath")->add_child_text(_add_files_path->string());
1009 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1010 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1012 auto target = config_write_file();
1015 auto const s = doc.write_to_string_formatted ();
1016 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1017 auto f = fopen_boost (tmp, "w");
1019 throw FileError (_("Could not open file for writing"), tmp);
1021 checked_fwrite (s.c_str(), s.bytes(), f, tmp);
1023 boost::filesystem::remove (target);
1024 boost::filesystem::rename (tmp, target);
1025 } catch (xmlpp::exception& e) {
1026 string s = e.what ();
1028 throw FileError (s, target);
1035 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1037 xmlpp::Document doc;
1038 auto root = doc.create_root_node (root_node);
1039 root->add_child("Version")->add_child_text(version);
1041 for (auto i: things) {
1042 i->as_xml (root->add_child(node));
1046 doc.write_to_file_formatted (file.string() + ".tmp");
1047 boost::filesystem::remove (file);
1048 boost::filesystem::rename (file.string() + ".tmp", file);
1049 } catch (xmlpp::exception& e) {
1050 string s = e.what ();
1052 throw FileError (s, file);
1058 Config::write_cinemas () const
1060 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1065 Config::write_dkdm_recipients () const
1067 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1071 boost::filesystem::path
1072 Config::default_directory_or (boost::filesystem::path a) const
1074 return directory_or (_default_directory, a);
1077 boost::filesystem::path
1078 Config::default_kdm_directory_or (boost::filesystem::path a) const
1080 return directory_or (_default_kdm_directory, a);
1083 boost::filesystem::path
1084 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1090 boost::system::error_code ec;
1091 auto const e = boost::filesystem::exists (*dir, ec);
1107 Config::changed (Property what)
1113 Config::set_kdm_email_to_default ()
1115 _kdm_subject = _("KDM delivery: $CPL_NAME");
1118 "Dear Projectionist\n\n"
1119 "Please find attached KDMs for $CPL_NAME.\n\n"
1120 "Cinema: $CINEMA_NAME\n"
1121 "Screen(s): $SCREENS\n\n"
1122 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1123 "Best regards,\nDCP-o-matic"
1128 Config::set_notification_email_to_default ()
1130 _notification_subject = _("DCP-o-matic notification");
1132 _notification_email = _(
1133 "$JOB_NAME: $JOB_STATUS"
1138 Config::reset_kdm_email ()
1140 set_kdm_email_to_default ();
1145 Config::reset_notification_email ()
1147 set_notification_email_to_default ();
1152 Config::set_cover_sheet_to_default ()
1156 "CPL Filename: $CPL_FILENAME\n"
1158 "Format: $CONTAINER\n"
1160 "Audio Language: $AUDIO_LANGUAGE\n"
1161 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1168 Config::add_to_history (boost::filesystem::path p)
1170 add_to_history_internal (_history, p);
1173 /** Remove non-existant items from the history */
1175 Config::clean_history ()
1177 clean_history_internal (_history);
1181 Config::add_to_player_history (boost::filesystem::path p)
1183 add_to_history_internal (_player_history, p);
1186 /** Remove non-existant items from the player history */
1188 Config::clean_player_history ()
1190 clean_history_internal (_player_history);
1194 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1196 /* Remove existing instances of this path in the history */
1197 h.erase (remove (h.begin(), h.end(), p), h.end ());
1199 h.insert (h.begin (), p);
1200 if (h.size() > HISTORY_SIZE) {
1208 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1214 if (boost::filesystem::is_directory(i)) {
1218 /* We couldn't find out if it's a directory for some reason; just ignore it */
1225 Config::have_existing (string file)
1227 return boost::filesystem::exists (read_path(file));
1232 Config::read_cinemas (cxml::Document const & f)
1235 for (auto i: f.node_children("Cinema")) {
1236 /* Slightly grotty two-part construction of Cinema here so that we can use
1239 auto cinema = make_shared<Cinema>(i);
1240 cinema->read_screens (i);
1241 _cinemas.push_back (cinema);
1246 Config::set_cinemas_file (boost::filesystem::path file)
1248 if (file == _cinemas_file) {
1252 _cinemas_file = file;
1254 if (boost::filesystem::exists (_cinemas_file)) {
1255 /* Existing file; read it in */
1256 cxml::Document f ("Cinemas");
1257 f.read_file (_cinemas_file);
1266 Config::read_dkdm_recipients (cxml::Document const & f)
1268 _dkdm_recipients.clear ();
1269 for (auto i: f.node_children("DKDMRecipient")) {
1270 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1276 Config::save_template (shared_ptr<const Film> film, string name) const
1278 film->write_template (template_write_path(name));
1283 Config::templates () const
1285 if (!boost::filesystem::exists(read_path("templates"))) {
1290 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1291 n.push_back (i.path().filename().string());
1297 Config::existing_template (string name) const
1299 return boost::filesystem::exists (template_read_path(name));
1303 boost::filesystem::path
1304 Config::template_read_path (string name) const
1306 return read_path("templates") / tidy_for_filename (name);
1310 boost::filesystem::path
1311 Config::template_write_path (string name) const
1313 return write_path("templates") / tidy_for_filename (name);
1318 Config::rename_template (string old_name, string new_name) const
1320 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1324 Config::delete_template (string name) const
1326 boost::filesystem::remove (template_write_path(name));
1329 /** @return Path to the config.xml containing the actual settings, following a link if required */
1330 boost::filesystem::path
1331 config_file (boost::filesystem::path main)
1333 cxml::Document f ("Config");
1334 if (!boost::filesystem::exists (main)) {
1335 /* It doesn't exist, so there can't be any links; just return it */
1339 /* See if there's a link */
1342 auto link = f.optional_string_child("Link");
1346 } catch (xmlpp::exception& e) {
1347 /* There as a problem reading the main configuration file,
1348 so there can't be a link.
1356 boost::filesystem::path
1357 Config::config_read_file ()
1359 return config_file (read_path("config.xml"));
1363 boost::filesystem::path
1364 Config::config_write_file ()
1366 return config_file (write_path("config.xml"));
1371 Config::reset_cover_sheet ()
1373 set_cover_sheet_to_default ();
1378 Config::link (boost::filesystem::path new_file) const
1380 xmlpp::Document doc;
1381 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1383 doc.write_to_file_formatted(write_path("config.xml").string());
1384 } catch (xmlpp::exception& e) {
1385 string s = e.what ();
1387 throw FileError (s, write_path("config.xml"));
1392 Config::copy_and_link (boost::filesystem::path new_file) const
1395 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1400 Config::have_write_permission () const
1402 auto f = fopen_boost (config_write_file(), "r+");
1411 /** @param output_channels Number of output channels in use.
1412 * @return Audio mapping for this output channel count (may be a default).
1415 Config::audio_mapping (int output_channels)
1417 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1418 /* Set up a default */
1419 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1420 if (output_channels == 2) {
1421 /* Special case for stereo output.
1422 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1423 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1425 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1426 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1427 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1428 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1429 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1430 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1431 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1432 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1435 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1436 _audio_mapping->set (i, i, 1);
1441 return *_audio_mapping;
1445 Config::set_audio_mapping (AudioMapping m)
1448 changed (AUDIO_MAPPING);
1452 Config::set_audio_mapping_to_default ()
1454 DCPOMATIC_ASSERT (_audio_mapping);
1455 auto const ch = _audio_mapping->output_channels ();
1456 _audio_mapping = boost::none;
1457 _audio_mapping = audio_mapping (ch);
1458 changed (AUDIO_MAPPING);
1463 Config::add_custom_language (dcp::LanguageTag tag)
1465 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1466 _custom_languages.push_back (tag);
1473 save_all_config_as_zip (boost::filesystem::path zip_file)
1475 Zipper zipper (zip_file);
1477 auto config = Config::instance();
1478 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1479 if (boost::filesystem::exists(config->cinemas_file())) {
1480 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1482 if (boost::filesystem::exists(config->dkdm_recipients_file())) {
1483 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
1490 optional<Config::BadReason>
1491 Config::check_certificates () const
1493 optional<BadReason> bad;
1495 for (auto const& i: _signer_chain->unordered()) {
1496 if (i.has_utf8_strings()) {
1497 bad = BAD_SIGNER_UTF8_STRINGS;
1499 if ((i.not_after().year() - i.not_before().year()) > 15) {
1500 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1504 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1505 bad = BAD_SIGNER_INCONSISTENT;
1508 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1509 bad = BAD_DECRYPTION_INCONSISTENT;