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> (
217 ".dcpomatic.smpte-430-2.ROOT",
218 ".dcpomatic.smpte-430-2.INTERMEDIATE",
219 "CS.dcpomatic.smpte-430-2.LEAF"
226 using namespace boost::filesystem;
228 auto copy_adding_number = [](path const& path_to_copy) {
230 auto add_number = [](path const& path, int number) {
231 return String::compose("%1.%2", path, number);
235 while (n < 100 && exists(add_number(path_to_copy, n))) {
238 boost::system::error_code ec;
239 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
242 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
243 * to write over. This is more intended for the situation where we have a corrupted config.xml,
244 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
245 * file). But we might as well back up the other files while we're about it.
248 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
249 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
251 copy_adding_number (config_write_file());
253 /* These do not use State::write_path, so whatever path is in the Config we will copy
256 copy_adding_number (_cinemas_file);
257 copy_adding_number (_dkdm_recipients_file);
264 cxml::Document f ("Config");
265 f.read_file (config_read_file());
267 auto version = f.optional_number_child<int> ("Version");
268 if (version && *version < _current_version) {
269 /* Back up the old config before we re-write it in a back-incompatible way */
273 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
274 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
276 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
277 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
280 _default_directory = f.optional_string_child ("DefaultDirectory");
281 if (_default_directory && _default_directory->empty ()) {
282 /* We used to store an empty value for this to mean "none set" */
283 _default_directory = boost::optional<boost::filesystem::path> ();
286 auto b = f.optional_number_child<int> ("ServerPort");
288 b = f.optional_number_child<int> ("ServerPortBase");
290 _server_port_base = b.get ();
292 auto u = f.optional_bool_child ("UseAnyServers");
293 _use_any_servers = u.get_value_or (true);
295 for (auto i: f.node_children("Server")) {
296 if (i->node_children("HostName").size() == 1) {
297 _servers.push_back (i->string_child ("HostName"));
299 _servers.push_back (i->content ());
303 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
304 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
305 _tms_ip = f.string_child ("TMSIP");
306 _tms_path = f.string_child ("TMSPath");
307 _tms_user = f.string_child ("TMSUser");
308 _tms_password = f.string_child ("TMSPassword");
310 _language = f.optional_string_child ("Language");
312 auto c = f.optional_string_child ("DefaultContainer");
314 _default_container = Ratio::from_id (c.get ());
317 if (_default_container && !_default_container->used_for_container()) {
318 Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)"));
319 _default_container = Ratio::from_id ("185");
322 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
323 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
325 if (f.optional_string_child ("DCPMetadataIssuer")) {
326 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
327 } else if (f.optional_string_child ("DCPIssuer")) {
328 _dcp_issuer = f.string_child ("DCPIssuer");
331 auto up = f.optional_bool_child("UploadAfterMakeDCP");
333 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
335 _upload_after_make_dcp = up.get_value_or (false);
336 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
337 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
338 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
339 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
340 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
342 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
343 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
344 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
345 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
347 for (auto const& i: f.node_children("DefaultMetadata")) {
348 _default_metadata[i->string_attribute("key")] = i->content();
351 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
353 /* Read any cinemas that are still lying around in the config file
354 * from an old version.
358 _mail_server = f.string_child ("MailServer");
359 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
362 /* Make sure this matches the code in write_config */
363 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
364 if (protocol == "Auto") {
365 _mail_protocol = EmailProtocol::AUTO;
366 } else if (protocol == "Plain") {
367 _mail_protocol = EmailProtocol::PLAIN;
368 } else if (protocol == "STARTTLS") {
369 _mail_protocol = EmailProtocol::STARTTLS;
370 } else if (protocol == "SSL") {
371 _mail_protocol = EmailProtocol::SSL;
375 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
376 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
378 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
379 _kdm_from = f.string_child ("KDMFrom");
380 for (auto i: f.node_children("KDMCC")) {
381 if (!i->content().empty()) {
382 _kdm_cc.push_back (i->content ());
385 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
386 _kdm_email = f.string_child ("KDMEmail");
388 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
389 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
390 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
391 for (auto i: f.node_children("NotificationCC")) {
392 if (!i->content().empty()) {
393 _notification_cc.push_back (i->content ());
396 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
397 if (f.optional_string_child("NotificationEmail")) {
398 _notification_email = f.string_child("NotificationEmail");
401 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
402 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
404 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
405 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
406 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
407 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
408 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
410 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
411 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
412 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
413 #ifdef DCPOMATIC_WINDOWS
414 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
417 for (auto i: f.node_children("History")) {
418 _history.push_back (i->content ());
421 for (auto i: f.node_children("PlayerHistory")) {
422 _player_history.push_back (i->content ());
425 auto signer = f.optional_node_child ("Signer");
427 auto c = make_shared<dcp::CertificateChain>();
428 /* Read the signing certificates and private key in from the config file */
429 for (auto i: signer->node_children ("Certificate")) {
430 c->add (dcp::Certificate (i->content ()));
432 c->set_key (signer->string_child ("PrivateKey"));
435 /* Make a new set of signing certificates and key */
436 _signer_chain = create_certificate_chain ();
439 auto decryption = f.optional_node_child ("Decryption");
441 auto c = make_shared<dcp::CertificateChain>();
442 for (auto i: decryption->node_children ("Certificate")) {
443 c->add (dcp::Certificate (i->content ()));
445 c->set_key (decryption->string_child ("PrivateKey"));
446 _decryption_chain = c;
448 _decryption_chain = create_certificate_chain ();
451 /* These must be done before we call Bad as that might set one
454 for (auto i: f.node_children("Nagged")) {
455 auto const id = i->number_attribute<int>("Id");
456 if (id >= 0 && id < NAG_COUNT) {
457 _nagged[id] = raw_convert<int>(i->content());
461 optional<BadReason> bad;
463 for (auto const& i: _signer_chain->unordered()) {
464 if (i.has_utf8_strings()) {
465 bad = BAD_SIGNER_UTF8_STRINGS;
469 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
470 bad = BAD_SIGNER_INCONSISTENT;
473 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
474 bad = BAD_DECRYPTION_INCONSISTENT;
478 auto const remake = Bad(*bad);
479 if (remake && *remake) {
481 case BAD_SIGNER_UTF8_STRINGS:
482 case BAD_SIGNER_INCONSISTENT:
483 _signer_chain = create_certificate_chain ();
485 case BAD_DECRYPTION_INCONSISTENT:
486 _decryption_chain = create_certificate_chain ();
492 if (f.optional_node_child("DKDMGroup")) {
493 /* New-style: all DKDMs in a group */
494 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
496 /* Old-style: one or more DKDM nodes */
497 _dkdms = make_shared<DKDMGroup>("root");
498 for (auto i: f.node_children("DKDM")) {
499 _dkdms->add (DKDMBase::read (i));
502 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
503 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
504 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
505 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
506 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
507 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
508 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
509 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
510 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
511 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
512 /* The variable was renamed but not the XML tag */
513 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
514 _sound_output = f.optional_string_child("PreviewSoundOutput");
515 if (f.optional_string_child("CoverSheet")) {
516 _cover_sheet = f.optional_string_child("CoverSheet").get();
518 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
519 if (f.optional_string_child("LastKDMWriteType")) {
520 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
521 _last_kdm_write_type = KDM_WRITE_FLAT;
522 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
523 _last_kdm_write_type = KDM_WRITE_FOLDER;
524 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
525 _last_kdm_write_type = KDM_WRITE_ZIP;
528 if (f.optional_string_child("LastDKDMWriteType")) {
529 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
530 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
531 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
532 _last_dkdm_write_type = DKDM_WRITE_FILE;
535 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
536 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
537 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
539 for (auto i: f.node_children("Notification")) {
540 int const id = i->number_attribute<int>("Id");
541 if (id >= 0 && id < NOTIFICATION_COUNT) {
542 _notification[id] = raw_convert<int>(i->content());
546 _barco_username = f.optional_string_child("BarcoUsername");
547 _barco_password = f.optional_string_child("BarcoPassword");
548 _christie_username = f.optional_string_child("ChristieUsername");
549 _christie_password = f.optional_string_child("ChristiePassword");
550 _gdc_username = f.optional_string_child("GDCUsername");
551 _gdc_password = f.optional_string_child("GDCPassword");
553 auto pm = f.optional_string_child("PlayerMode");
554 if (pm && *pm == "window") {
555 _player_mode = PLAYER_MODE_WINDOW;
556 } else if (pm && *pm == "full") {
557 _player_mode = PLAYER_MODE_FULL;
558 } else if (pm && *pm == "dual") {
559 _player_mode = PLAYER_MODE_DUAL;
562 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
563 auto vc = f.optional_string_child("VideoViewType");
564 if (vc && *vc == "opengl") {
565 _video_view_type = VIDEO_VIEW_OPENGL;
566 } else if (vc && *vc == "simple") {
567 _video_view_type = VIDEO_VIEW_SIMPLE;
569 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
570 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
571 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
572 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
573 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
575 if (f.optional_node_child("AudioMapping")) {
576 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
579 for (auto i: f.node_children("CustomLanguage")) {
581 /* This will fail if it's called before dcp::init() as it won't recognise the
582 * tag. That's OK because the Config will be reloaded again later.
584 _custom_languages.push_back (dcp::LanguageTag(i->content()));
585 } catch (std::runtime_error& e) {}
588 _add_files_path = f.optional_string_child("AddFilesPath");
589 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
590 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
592 if (boost::filesystem::exists (_cinemas_file)) {
593 cxml::Document f ("Cinemas");
594 f.read_file (_cinemas_file);
598 if (boost::filesystem::exists (_dkdm_recipients_file)) {
599 cxml::Document f ("DKDMRecipients");
600 f.read_file (_dkdm_recipients_file);
601 read_dkdm_recipients (f);
605 if (have_existing("config.xml") || have_existing("cinemas.xml") || have_existing("dkdm_recipients.xml")) {
607 /* We have a config file but it didn't load */
611 /* Make a new set of signing certificates and key */
612 _signer_chain = create_certificate_chain ();
613 /* And similar for decryption of KDMs */
614 _decryption_chain = create_certificate_chain ();
618 /** @return Singleton instance */
622 if (_instance == nullptr) {
623 _instance = new Config;
630 /** Write our configuration to disk */
632 Config::write () const
636 write_dkdm_recipients ();
640 Config::write_config () const
643 auto root = doc.create_root_node ("Config");
645 /* [XML] Version The version number of the configuration file format. */
646 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
647 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
648 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
649 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
650 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
651 if (_default_directory) {
652 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
653 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
655 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
656 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
657 by the batch converter to listen for job requests.
659 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
660 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
661 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
663 for (auto i: _servers) {
664 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
667 root->add_child("Server")->add_child_text (i);
670 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
671 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
673 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
674 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
675 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
676 /* [XML] TMSIP IP address of TMS. */
677 root->add_child("TMSIP")->add_child_text (_tms_ip);
678 /* [XML] TMSPath Path on the TMS to copy files to. */
679 root->add_child("TMSPath")->add_child_text (_tms_path);
680 /* [XML] TMSUser Username to log into the TMS with. */
681 root->add_child("TMSUser")->add_child_text (_tms_user);
682 /* [XML] TMSPassword Password to log into the TMS with. */
683 root->add_child("TMSPassword")->add_child_text (_tms_password);
685 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
686 root->add_child("Language")->add_child_text (_language.get());
688 if (_default_container) {
689 /* [XML:opt] DefaultContainer ID of default container
690 to use when creating new films (<code>185</code>,<code>239</code> or
693 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
695 if (_default_dcp_content_type) {
696 /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
697 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
698 <code>PSA</code> or <code>ADV</code>). */
699 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
701 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
702 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
703 /* [XML] DCPIssuer Issuer text to write into CPL files. */
704 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
705 /* [XML] DCPCreator Creator text to write into CPL files. */
706 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
707 /* [XML] Company name to write into MXF files. */
708 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
709 /* [XML] Product name to write into MXF files. */
710 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
711 /* [XML] Product version to write into MXF files. */
712 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
713 /* [XML] Comment to write into JPEG2000 data. */
714 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
715 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
716 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
718 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
719 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
720 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
721 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
722 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
723 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
724 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
725 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
726 for (auto const& i: _default_metadata) {
727 auto c = root->add_child("DefaultMetadata");
728 c->set_attribute("key", i.first);
729 c->add_child_text(i.second);
731 if (_default_kdm_directory) {
732 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
733 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
735 /* [XML] MailServer Hostname of SMTP server to use. */
736 root->add_child("MailServer")->add_child_text (_mail_server);
737 /* [XML] MailPort Port number to use on SMTP server. */
738 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
739 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
740 switch (_mail_protocol) {
741 case EmailProtocol::AUTO:
742 root->add_child("MailProtocol")->add_child_text("Auto");
744 case EmailProtocol::PLAIN:
745 root->add_child("MailProtocol")->add_child_text("Plain");
747 case EmailProtocol::STARTTLS:
748 root->add_child("MailProtocol")->add_child_text("STARTTLS");
750 case EmailProtocol::SSL:
751 root->add_child("MailProtocol")->add_child_text("SSL");
754 /* [XML] MailUser Username to use on SMTP server. */
755 root->add_child("MailUser")->add_child_text (_mail_user);
756 /* [XML] MailPassword Password to use on SMTP server. */
757 root->add_child("MailPassword")->add_child_text (_mail_password);
759 /* [XML] KDMSubject Subject to use for KDM emails. */
760 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
761 /* [XML] KDMFrom From address to use for KDM emails. */
762 root->add_child("KDMFrom")->add_child_text (_kdm_from);
763 for (auto i: _kdm_cc) {
764 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
765 root->add_child("KDMCC")->add_child_text (i);
767 /* [XML] KDMBCC BCC address to use for KDM emails. */
768 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
769 /* [XML] KDMEmail Text of KDM email. */
770 root->add_child("KDMEmail")->add_child_text (_kdm_email);
772 /* [XML] NotificationSubject Subject to use for notification emails. */
773 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
774 /* [XML] NotificationFrom From address to use for notification emails. */
775 root->add_child("NotificationFrom")->add_child_text (_notification_from);
776 /* [XML] NotificationFrom To address to use for notification emails. */
777 root->add_child("NotificationTo")->add_child_text (_notification_to);
778 for (auto i: _notification_cc) {
779 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
780 root->add_child("NotificationCC")->add_child_text (i);
782 /* [XML] NotificationBCC BCC address to use for notification emails. */
783 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
784 /* [XML] NotificationEmail Text of notification email. */
785 root->add_child("NotificationEmail")->add_child_text (_notification_email);
787 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
788 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
789 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
790 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
792 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
793 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
794 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
795 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
796 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
797 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
798 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
799 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
800 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
801 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
802 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
803 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
804 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
805 related to the player, 1024 debug information related to audio analyses.
807 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
808 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
809 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
810 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
811 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
812 #ifdef DCPOMATIC_WINDOWS
813 if (_win32_console) {
814 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
815 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
816 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
818 root->add_child("Win32Console")->add_child_text ("1");
822 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
823 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
825 auto signer = root->add_child ("Signer");
826 DCPOMATIC_ASSERT (_signer_chain);
827 for (auto const& i: _signer_chain->unordered()) {
828 signer->add_child("Certificate")->add_child_text (i.certificate (true));
830 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
832 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
833 auto decryption = root->add_child ("Decryption");
834 DCPOMATIC_ASSERT (_decryption_chain);
835 for (auto const& i: _decryption_chain->unordered()) {
836 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
838 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
840 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
843 for (auto i: _history) {
844 root->add_child("History")->add_child_text (i.string ());
847 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
850 for (auto i: _player_history) {
851 root->add_child("PlayerHistory")->add_child_text (i.string ());
854 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
855 or <code><DKDM></code> tags.
857 /* [XML] DKDM A DKDM as XML */
858 _dkdms->as_xml (root);
860 /* [XML] CinemasFile Filename of cinemas list file. */
861 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
862 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
863 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
864 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
865 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
866 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
867 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
868 /* [XML] KDMFilenameFormat Format for KDM filenames. */
869 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
870 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
871 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
872 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
873 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
874 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
875 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
876 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
877 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
878 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
879 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
880 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
881 for (int i = 0; i < NAG_COUNT; ++i) {
882 xmlpp::Element* e = root->add_child ("Nagged");
883 e->set_attribute ("Id", raw_convert<string>(i));
884 e->add_child_text (_nagged[i] ? "1" : "0");
886 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
887 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
889 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
890 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
892 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
893 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
894 if (_last_player_load_directory) {
895 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
897 /* [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. */
898 if (_last_kdm_write_type) {
899 switch (_last_kdm_write_type.get()) {
901 root->add_child("LastKDMWriteType")->add_child_text("flat");
903 case KDM_WRITE_FOLDER:
904 root->add_child("LastKDMWriteType")->add_child_text("folder");
907 root->add_child("LastKDMWriteType")->add_child_text("zip");
911 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
912 if (_last_dkdm_write_type) {
913 switch (_last_dkdm_write_type.get()) {
914 case DKDM_WRITE_INTERNAL:
915 root->add_child("LastDKDMWriteType")->add_child_text("internal");
917 case DKDM_WRITE_FILE:
918 root->add_child("LastDKDMWriteType")->add_child_text("file");
922 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
923 frames to be held in memory at once.
925 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
927 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
928 if (_decode_reduction) {
929 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
932 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
933 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
935 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
936 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
937 xmlpp::Element* e = root->add_child ("Notification");
938 e->set_attribute ("Id", raw_convert<string>(i));
939 e->add_child_text (_notification[i] ? "1" : "0");
942 if (_barco_username) {
943 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
944 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
946 if (_barco_password) {
947 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
948 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
951 if (_christie_username) {
952 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
953 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
955 if (_christie_password) {
956 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
957 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
961 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
962 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
965 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
966 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
969 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
970 with controls on another monitor.
972 switch (_player_mode) {
973 case PLAYER_MODE_WINDOW:
974 root->add_child("PlayerMode")->add_child_text("window");
976 case PLAYER_MODE_FULL:
977 root->add_child("PlayerMode")->add_child_text("full");
979 case PLAYER_MODE_DUAL:
980 root->add_child("PlayerMode")->add_child_text("dual");
984 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
985 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
986 switch (_video_view_type) {
987 case VIDEO_VIEW_SIMPLE:
988 root->add_child("VideoViewType")->add_child_text("simple");
990 case VIDEO_VIEW_OPENGL:
991 root->add_child("VideoViewType")->add_child_text("opengl");
994 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
995 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
996 if (_player_debug_log_file) {
997 /* [XML] PlayerLogFile Filename to use for player debug logs. */
998 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
1000 if (_player_content_directory) {
1001 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1002 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
1004 if (_player_playlist_directory) {
1005 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1006 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1008 if (_player_kdm_directory) {
1009 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1010 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1012 if (_audio_mapping) {
1013 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1015 for (auto const& i: _custom_languages) {
1016 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1018 if (_add_files_path) {
1019 /* [XML] AddFilesPath The default path that will be offered in the picker when adding files to a film. */
1020 root->add_child("AddFilesPath")->add_child_text(_add_files_path->string());
1022 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1023 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1025 auto target = config_write_file();
1028 auto const s = doc.write_to_string_formatted ();
1029 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1030 auto f = fopen_boost (tmp, "w");
1032 throw FileError (_("Could not open file for writing"), tmp);
1034 checked_fwrite (s.c_str(), s.bytes(), f, tmp);
1036 boost::filesystem::remove (target);
1037 boost::filesystem::rename (tmp, target);
1038 } catch (xmlpp::exception& e) {
1039 string s = e.what ();
1041 throw FileError (s, target);
1048 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1050 xmlpp::Document doc;
1051 auto root = doc.create_root_node (root_node);
1052 root->add_child("Version")->add_child_text(version);
1054 for (auto i: things) {
1055 i->as_xml (root->add_child(node));
1059 doc.write_to_file_formatted (file.string() + ".tmp");
1060 boost::filesystem::remove (file);
1061 boost::filesystem::rename (file.string() + ".tmp", file);
1062 } catch (xmlpp::exception& e) {
1063 string s = e.what ();
1065 throw FileError (s, file);
1071 Config::write_cinemas () const
1073 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1078 Config::write_dkdm_recipients () const
1080 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1084 boost::filesystem::path
1085 Config::default_directory_or (boost::filesystem::path a) const
1087 return directory_or (_default_directory, a);
1090 boost::filesystem::path
1091 Config::default_kdm_directory_or (boost::filesystem::path a) const
1093 return directory_or (_default_kdm_directory, a);
1096 boost::filesystem::path
1097 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1103 boost::system::error_code ec;
1104 auto const e = boost::filesystem::exists (*dir, ec);
1120 Config::changed (Property what)
1126 Config::set_kdm_email_to_default ()
1128 _kdm_subject = _("KDM delivery: $CPL_NAME");
1131 "Dear Projectionist\n\n"
1132 "Please find attached KDMs for $CPL_NAME.\n\n"
1133 "Cinema: $CINEMA_NAME\n"
1134 "Screen(s): $SCREENS\n\n"
1135 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1136 "Best regards,\nDCP-o-matic"
1141 Config::set_notification_email_to_default ()
1143 _notification_subject = _("DCP-o-matic notification");
1145 _notification_email = _(
1146 "$JOB_NAME: $JOB_STATUS"
1151 Config::reset_kdm_email ()
1153 set_kdm_email_to_default ();
1158 Config::reset_notification_email ()
1160 set_notification_email_to_default ();
1165 Config::set_cover_sheet_to_default ()
1169 "CPL Filename: $CPL_FILENAME\n"
1171 "Format: $CONTAINER\n"
1173 "Audio Language: $AUDIO_LANGUAGE\n"
1174 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1181 Config::add_to_history (boost::filesystem::path p)
1183 add_to_history_internal (_history, p);
1186 /** Remove non-existant items from the history */
1188 Config::clean_history ()
1190 clean_history_internal (_history);
1194 Config::add_to_player_history (boost::filesystem::path p)
1196 add_to_history_internal (_player_history, p);
1199 /** Remove non-existant items from the player history */
1201 Config::clean_player_history ()
1203 clean_history_internal (_player_history);
1207 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1209 /* Remove existing instances of this path in the history */
1210 h.erase (remove (h.begin(), h.end(), p), h.end ());
1212 h.insert (h.begin (), p);
1213 if (h.size() > HISTORY_SIZE) {
1221 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1227 if (boost::filesystem::is_directory(i)) {
1231 /* We couldn't find out if it's a directory for some reason; just ignore it */
1238 Config::have_existing (string file)
1240 return boost::filesystem::exists (read_path(file));
1245 Config::read_cinemas (cxml::Document const & f)
1248 for (auto i: f.node_children("Cinema")) {
1249 /* Slightly grotty two-part construction of Cinema here so that we can use
1252 auto cinema = make_shared<Cinema>(i);
1253 cinema->read_screens (i);
1254 _cinemas.push_back (cinema);
1259 Config::set_cinemas_file (boost::filesystem::path file)
1261 if (file == _cinemas_file) {
1265 _cinemas_file = file;
1267 if (boost::filesystem::exists (_cinemas_file)) {
1268 /* Existing file; read it in */
1269 cxml::Document f ("Cinemas");
1270 f.read_file (_cinemas_file);
1279 Config::read_dkdm_recipients (cxml::Document const & f)
1281 _dkdm_recipients.clear ();
1282 for (auto i: f.node_children("DKDMRecipient")) {
1283 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1289 Config::save_template (shared_ptr<const Film> film, string name) const
1291 film->write_template (template_write_path(name));
1296 Config::templates () const
1298 if (!boost::filesystem::exists(read_path("templates"))) {
1303 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1304 n.push_back (i.path().filename().string());
1310 Config::existing_template (string name) const
1312 return boost::filesystem::exists (template_read_path(name));
1316 boost::filesystem::path
1317 Config::template_read_path (string name) const
1319 return read_path("templates") / tidy_for_filename (name);
1323 boost::filesystem::path
1324 Config::template_write_path (string name) const
1326 return write_path("templates") / tidy_for_filename (name);
1331 Config::rename_template (string old_name, string new_name) const
1333 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1337 Config::delete_template (string name) const
1339 boost::filesystem::remove (template_write_path(name));
1342 /** @return Path to the config.xml containing the actual settings, following a link if required */
1343 boost::filesystem::path
1344 config_file (boost::filesystem::path main)
1346 cxml::Document f ("Config");
1347 if (!boost::filesystem::exists (main)) {
1348 /* It doesn't exist, so there can't be any links; just return it */
1352 /* See if there's a link */
1355 auto link = f.optional_string_child("Link");
1359 } catch (xmlpp::exception& e) {
1360 /* There as a problem reading the main configuration file,
1361 so there can't be a link.
1369 boost::filesystem::path
1370 Config::config_read_file ()
1372 return config_file (read_path("config.xml"));
1376 boost::filesystem::path
1377 Config::config_write_file ()
1379 return config_file (write_path("config.xml"));
1384 Config::reset_cover_sheet ()
1386 set_cover_sheet_to_default ();
1391 Config::link (boost::filesystem::path new_file) const
1393 xmlpp::Document doc;
1394 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1396 doc.write_to_file_formatted(write_path("config.xml").string());
1397 } catch (xmlpp::exception& e) {
1398 string s = e.what ();
1400 throw FileError (s, write_path("config.xml"));
1405 Config::copy_and_link (boost::filesystem::path new_file) const
1408 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1413 Config::have_write_permission () const
1415 auto f = fopen_boost (config_write_file(), "r+");
1424 /** @param output_channels Number of output channels in use.
1425 * @return Audio mapping for this output channel count (may be a default).
1428 Config::audio_mapping (int output_channels)
1430 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1431 /* Set up a default */
1432 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1433 if (output_channels == 2) {
1434 /* Special case for stereo output.
1435 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1436 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1438 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1439 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1440 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1441 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1442 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1443 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1444 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1445 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1448 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1449 _audio_mapping->set (i, i, 1);
1454 return *_audio_mapping;
1458 Config::set_audio_mapping (AudioMapping m)
1461 changed (AUDIO_MAPPING);
1465 Config::set_audio_mapping_to_default ()
1467 DCPOMATIC_ASSERT (_audio_mapping);
1468 auto const ch = _audio_mapping->output_channels ();
1469 _audio_mapping = boost::none;
1470 _audio_mapping = audio_mapping (ch);
1471 changed (AUDIO_MAPPING);
1476 Config::add_custom_language (dcp::LanguageTag tag)
1478 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1479 _custom_languages.push_back (tag);
1486 save_all_config_as_zip (boost::filesystem::path zip_file)
1488 Zipper zipper (zip_file);
1490 auto config = Config::instance();
1491 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1492 if (boost::filesystem::exists(config->cinemas_file())) {
1493 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1495 if (boost::filesystem::exists(config->dkdm_recipients_file())) {
1496 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));