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;
189 _allowed_dcp_frame_rates.clear ();
190 _allowed_dcp_frame_rates.push_back (24);
191 _allowed_dcp_frame_rates.push_back (25);
192 _allowed_dcp_frame_rates.push_back (30);
193 _allowed_dcp_frame_rates.push_back (48);
194 _allowed_dcp_frame_rates.push_back (50);
195 _allowed_dcp_frame_rates.push_back (60);
197 set_kdm_email_to_default ();
198 set_notification_email_to_default ();
199 set_cover_sheet_to_default ();
203 Config::restore_defaults ()
205 Config::instance()->set_defaults ();
206 Config::instance()->changed ();
209 shared_ptr<dcp::CertificateChain>
210 Config::create_certificate_chain ()
212 return make_shared<dcp::CertificateChain> (
216 ".dcpomatic.smpte-430-2.ROOT",
217 ".dcpomatic.smpte-430-2.INTERMEDIATE",
218 "CS.dcpomatic.smpte-430-2.LEAF"
225 using namespace boost::filesystem;
227 auto copy_adding_number = [](path const& path_to_copy) {
229 auto add_number = [](path const& path, int number) {
230 return String::compose("%1.%2", path, number);
234 while (n < 100 && exists(add_number(path_to_copy, n))) {
237 copy_file(path_to_copy, add_number(path_to_copy, n));
240 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
241 * to write over. This is more intended for the situation where we have a corrupted config.xml,
242 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
243 * file). But we might as well back up the other files while we're about it.
246 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
247 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
249 copy_adding_number (config_write_file());
251 /* These do not use State::write_path, so whatever path is in the Config we will copy
254 copy_adding_number (_cinemas_file);
255 copy_adding_number (_dkdm_recipients_file);
263 cxml::Document f ("Config");
264 f.read_file (config_read_file());
266 auto version = f.optional_number_child<int> ("Version");
267 if (version && *version < _current_version) {
268 /* Back up the old config before we re-write it in a back-incompatible way */
272 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
273 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
275 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
276 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
279 _default_directory = f.optional_string_child ("DefaultDirectory");
280 if (_default_directory && _default_directory->empty ()) {
281 /* We used to store an empty value for this to mean "none set" */
282 _default_directory = boost::optional<boost::filesystem::path> ();
285 auto b = f.optional_number_child<int> ("ServerPort");
287 b = f.optional_number_child<int> ("ServerPortBase");
289 _server_port_base = b.get ();
291 auto u = f.optional_bool_child ("UseAnyServers");
292 _use_any_servers = u.get_value_or (true);
294 for (auto i: f.node_children("Server")) {
295 if (i->node_children("HostName").size() == 1) {
296 _servers.push_back (i->string_child ("HostName"));
298 _servers.push_back (i->content ());
302 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
303 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
304 _tms_ip = f.string_child ("TMSIP");
305 _tms_path = f.string_child ("TMSPath");
306 _tms_user = f.string_child ("TMSUser");
307 _tms_password = f.string_child ("TMSPassword");
309 _language = f.optional_string_child ("Language");
311 auto c = f.optional_string_child ("DefaultContainer");
313 _default_container = Ratio::from_id (c.get ());
316 if (_default_container && !_default_container->used_for_container()) {
317 Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)"));
318 _default_container = Ratio::from_id ("185");
321 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
322 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
324 if (f.optional_string_child ("DCPMetadataIssuer")) {
325 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
326 } else if (f.optional_string_child ("DCPIssuer")) {
327 _dcp_issuer = f.string_child ("DCPIssuer");
330 auto up = f.optional_bool_child("UploadAfterMakeDCP");
332 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
334 _upload_after_make_dcp = up.get_value_or (false);
335 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
336 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
337 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
338 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
339 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
341 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
342 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
343 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
344 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
346 for (auto const& i: f.node_children("DefaultMetadata")) {
347 _default_metadata[i->string_attribute("key")] = i->content();
350 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
352 /* Read any cinemas that are still lying around in the config file
353 * from an old version.
357 _mail_server = f.string_child ("MailServer");
358 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
361 /* Make sure this matches the code in write_config */
362 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
363 if (protocol == "Auto") {
364 _mail_protocol = EmailProtocol::AUTO;
365 } else if (protocol == "Plain") {
366 _mail_protocol = EmailProtocol::PLAIN;
367 } else if (protocol == "STARTTLS") {
368 _mail_protocol = EmailProtocol::STARTTLS;
369 } else if (protocol == "SSL") {
370 _mail_protocol = EmailProtocol::SSL;
374 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
375 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
377 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
378 _kdm_from = f.string_child ("KDMFrom");
379 for (auto i: f.node_children("KDMCC")) {
380 if (!i->content().empty()) {
381 _kdm_cc.push_back (i->content ());
384 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
385 _kdm_email = f.string_child ("KDMEmail");
387 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
388 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
389 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
390 for (auto i: f.node_children("NotificationCC")) {
391 if (!i->content().empty()) {
392 _notification_cc.push_back (i->content ());
395 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
396 if (f.optional_string_child("NotificationEmail")) {
397 _notification_email = f.string_child("NotificationEmail");
400 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
401 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
403 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
404 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
405 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
406 _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
407 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
409 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
410 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
411 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
412 #ifdef DCPOMATIC_WINDOWS
413 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
416 for (auto i: f.node_children("History")) {
417 _history.push_back (i->content ());
420 for (auto i: f.node_children("PlayerHistory")) {
421 _player_history.push_back (i->content ());
424 auto signer = f.optional_node_child ("Signer");
426 auto c = make_shared<dcp::CertificateChain>();
427 /* Read the signing certificates and private key in from the config file */
428 for (auto i: signer->node_children ("Certificate")) {
429 c->add (dcp::Certificate (i->content ()));
431 c->set_key (signer->string_child ("PrivateKey"));
434 /* Make a new set of signing certificates and key */
435 _signer_chain = create_certificate_chain ();
438 auto decryption = f.optional_node_child ("Decryption");
440 auto c = make_shared<dcp::CertificateChain>();
441 for (auto i: decryption->node_children ("Certificate")) {
442 c->add (dcp::Certificate (i->content ()));
444 c->set_key (decryption->string_child ("PrivateKey"));
445 _decryption_chain = c;
447 _decryption_chain = create_certificate_chain ();
450 /* These must be done before we call Bad as that might set one
453 for (auto i: f.node_children("Nagged")) {
454 auto const id = i->number_attribute<int>("Id");
455 if (id >= 0 && id < NAG_COUNT) {
456 _nagged[id] = raw_convert<int>(i->content());
460 optional<BadReason> bad;
462 for (auto const& i: _signer_chain->unordered()) {
463 if (i.has_utf8_strings()) {
464 bad = BAD_SIGNER_UTF8_STRINGS;
468 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
469 bad = BAD_SIGNER_INCONSISTENT;
472 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
473 bad = BAD_DECRYPTION_INCONSISTENT;
477 auto const remake = Bad(*bad);
478 if (remake && *remake) {
480 case BAD_SIGNER_UTF8_STRINGS:
481 case BAD_SIGNER_INCONSISTENT:
482 _signer_chain = create_certificate_chain ();
484 case BAD_DECRYPTION_INCONSISTENT:
485 _decryption_chain = create_certificate_chain ();
491 if (f.optional_node_child("DKDMGroup")) {
492 /* New-style: all DKDMs in a group */
493 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
495 /* Old-style: one or more DKDM nodes */
496 _dkdms = make_shared<DKDMGroup>("root");
497 for (auto i: f.node_children("DKDM")) {
498 _dkdms->add (DKDMBase::read (i));
501 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
502 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
503 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
504 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
505 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
506 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
507 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
508 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
509 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
510 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
511 /* The variable was renamed but not the XML tag */
512 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
513 _sound_output = f.optional_string_child("PreviewSoundOutput");
514 if (f.optional_string_child("CoverSheet")) {
515 _cover_sheet = f.optional_string_child("CoverSheet").get();
517 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
518 if (f.optional_string_child("LastKDMWriteType")) {
519 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
520 _last_kdm_write_type = KDM_WRITE_FLAT;
521 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
522 _last_kdm_write_type = KDM_WRITE_FOLDER;
523 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
524 _last_kdm_write_type = KDM_WRITE_ZIP;
527 if (f.optional_string_child("LastDKDMWriteType")) {
528 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
529 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
530 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
531 _last_dkdm_write_type = DKDM_WRITE_FILE;
534 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
535 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
536 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
538 for (auto i: f.node_children("Notification")) {
539 int const id = i->number_attribute<int>("Id");
540 if (id >= 0 && id < NOTIFICATION_COUNT) {
541 _notification[id] = raw_convert<int>(i->content());
545 _barco_username = f.optional_string_child("BarcoUsername");
546 _barco_password = f.optional_string_child("BarcoPassword");
547 _christie_username = f.optional_string_child("ChristieUsername");
548 _christie_password = f.optional_string_child("ChristiePassword");
549 _gdc_username = f.optional_string_child("GDCUsername");
550 _gdc_password = f.optional_string_child("GDCPassword");
552 auto pm = f.optional_string_child("PlayerMode");
553 if (pm && *pm == "window") {
554 _player_mode = PLAYER_MODE_WINDOW;
555 } else if (pm && *pm == "full") {
556 _player_mode = PLAYER_MODE_FULL;
557 } else if (pm && *pm == "dual") {
558 _player_mode = PLAYER_MODE_DUAL;
561 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
562 auto vc = f.optional_string_child("VideoViewType");
563 if (vc && *vc == "opengl") {
564 _video_view_type = VIDEO_VIEW_OPENGL;
565 } else if (vc && *vc == "simple") {
566 _video_view_type = VIDEO_VIEW_SIMPLE;
568 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
569 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
570 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
571 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
572 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
574 if (f.optional_node_child("AudioMapping")) {
575 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
578 for (auto i: f.node_children("CustomLanguage")) {
580 /* This will fail if it's called before dcp::init() as it won't recognise the
581 * tag. That's OK because the Config will be reloaded again later.
583 _custom_languages.push_back (dcp::LanguageTag(i->content()));
584 } catch (std::runtime_error& e) {}
587 _add_files_path = f.optional_string_child("AddFilesPath");
588 _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
590 if (boost::filesystem::exists (_cinemas_file)) {
591 cxml::Document f ("Cinemas");
592 f.read_file (_cinemas_file);
596 if (boost::filesystem::exists (_dkdm_recipients_file)) {
597 cxml::Document f ("DKDMRecipients");
598 f.read_file (_dkdm_recipients_file);
599 read_dkdm_recipients (f);
603 if (have_existing ("config.xml")) {
605 /* We have a config file but it didn't load */
609 /* Make a new set of signing certificates and key */
610 _signer_chain = create_certificate_chain ();
611 /* And similar for decryption of KDMs */
612 _decryption_chain = create_certificate_chain ();
616 /** @return Singleton instance */
620 if (_instance == 0) {
621 _instance = new Config;
628 /** Write our configuration to disk */
630 Config::write () const
634 write_dkdm_recipients ();
638 Config::write_config () const
641 auto root = doc.create_root_node ("Config");
643 /* [XML] Version The version number of the configuration file format. */
644 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
645 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
646 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
647 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
648 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
649 if (_default_directory) {
650 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
651 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
653 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
654 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
655 by the batch converter to listen for job requests.
657 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
658 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
659 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
661 for (auto i: _servers) {
662 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
665 root->add_child("Server")->add_child_text (i);
668 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
669 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
671 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
672 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
673 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
674 /* [XML] TMSIP IP address of TMS. */
675 root->add_child("TMSIP")->add_child_text (_tms_ip);
676 /* [XML] TMSPath Path on the TMS to copy files to. */
677 root->add_child("TMSPath")->add_child_text (_tms_path);
678 /* [XML] TMSUser Username to log into the TMS with. */
679 root->add_child("TMSUser")->add_child_text (_tms_user);
680 /* [XML] TMSPassword Password to log into the TMS with. */
681 root->add_child("TMSPassword")->add_child_text (_tms_password);
683 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
684 root->add_child("Language")->add_child_text (_language.get());
686 if (_default_container) {
687 /* [XML:opt] DefaultContainer ID of default container
688 to use when creating new films (<code>185</code>,<code>239</code> or
691 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
693 if (_default_dcp_content_type) {
694 /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
695 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
696 <code>PSA</code> or <code>ADV</code>). */
697 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
699 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
700 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
701 /* [XML] DCPIssuer Issuer text to write into CPL files. */
702 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
703 /* [XML] DCPCreator Creator text to write into CPL files. */
704 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
705 /* [XML] Company name to write into MXF files. */
706 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
707 /* [XML] Product name to write into MXF files. */
708 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
709 /* [XML] Product version to write into MXF files. */
710 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
711 /* [XML] Comment to write into JPEG2000 data. */
712 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
713 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
714 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
716 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
717 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
718 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
719 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
720 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
721 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
722 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
723 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
724 for (auto const& i: _default_metadata) {
725 auto c = root->add_child("DefaultMetadata");
726 c->set_attribute("key", i.first);
727 c->add_child_text(i.second);
729 if (_default_kdm_directory) {
730 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
731 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
733 /* [XML] MailServer Hostname of SMTP server to use. */
734 root->add_child("MailServer")->add_child_text (_mail_server);
735 /* [XML] MailPort Port number to use on SMTP server. */
736 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
737 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
738 switch (_mail_protocol) {
739 case EmailProtocol::AUTO:
740 root->add_child("MailProtocol")->add_child_text("Auto");
742 case EmailProtocol::PLAIN:
743 root->add_child("MailProtocol")->add_child_text("Plain");
745 case EmailProtocol::STARTTLS:
746 root->add_child("MailProtocol")->add_child_text("STARTTLS");
748 case EmailProtocol::SSL:
749 root->add_child("MailProtocol")->add_child_text("SSL");
752 /* [XML] MailUser Username to use on SMTP server. */
753 root->add_child("MailUser")->add_child_text (_mail_user);
754 /* [XML] MailPassword Password to use on SMTP server. */
755 root->add_child("MailPassword")->add_child_text (_mail_password);
757 /* [XML] KDMSubject Subject to use for KDM emails. */
758 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
759 /* [XML] KDMFrom From address to use for KDM emails. */
760 root->add_child("KDMFrom")->add_child_text (_kdm_from);
761 for (auto i: _kdm_cc) {
762 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
763 root->add_child("KDMCC")->add_child_text (i);
765 /* [XML] KDMBCC BCC address to use for KDM emails. */
766 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
767 /* [XML] KDMEmail Text of KDM email. */
768 root->add_child("KDMEmail")->add_child_text (_kdm_email);
770 /* [XML] NotificationSubject Subject to use for notification emails. */
771 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
772 /* [XML] NotificationFrom From address to use for notification emails. */
773 root->add_child("NotificationFrom")->add_child_text (_notification_from);
774 /* [XML] NotificationFrom To address to use for notification emails. */
775 root->add_child("NotificationTo")->add_child_text (_notification_to);
776 for (auto i: _notification_cc) {
777 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
778 root->add_child("NotificationCC")->add_child_text (i);
780 /* [XML] NotificationBCC BCC address to use for notification emails. */
781 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
782 /* [XML] NotificationEmail Text of notification email. */
783 root->add_child("NotificationEmail")->add_child_text (_notification_email);
785 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
786 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
787 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
788 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
790 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
791 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
792 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
793 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
794 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
795 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
796 /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
797 root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
798 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
799 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
800 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
801 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
802 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
803 related to the player, 1024 debug information related to audio analyses.
805 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
806 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
807 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
808 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
809 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
810 #ifdef DCPOMATIC_WINDOWS
811 if (_win32_console) {
812 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
813 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
814 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
816 root->add_child("Win32Console")->add_child_text ("1");
820 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
821 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
823 auto signer = root->add_child ("Signer");
824 DCPOMATIC_ASSERT (_signer_chain);
825 for (auto const& i: _signer_chain->unordered()) {
826 signer->add_child("Certificate")->add_child_text (i.certificate (true));
828 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
830 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
831 auto decryption = root->add_child ("Decryption");
832 DCPOMATIC_ASSERT (_decryption_chain);
833 for (auto const& i: _decryption_chain->unordered()) {
834 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
836 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
838 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
841 for (auto i: _history) {
842 root->add_child("History")->add_child_text (i.string ());
845 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
848 for (auto i: _player_history) {
849 root->add_child("PlayerHistory")->add_child_text (i.string ());
852 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
853 or <code><DKDM></code> tags.
855 /* [XML] DKDM A DKDM as XML */
856 _dkdms->as_xml (root);
858 /* [XML] CinemasFile Filename of cinemas list file. */
859 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
860 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
861 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
862 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
863 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
864 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
865 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
866 /* [XML] KDMFilenameFormat Format for KDM filenames. */
867 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
868 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
869 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
870 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
871 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
872 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
873 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
874 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
875 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
876 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
877 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
878 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
879 for (int i = 0; i < NAG_COUNT; ++i) {
880 xmlpp::Element* e = root->add_child ("Nagged");
881 e->set_attribute ("Id", raw_convert<string>(i));
882 e->add_child_text (_nagged[i] ? "1" : "0");
884 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
885 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
887 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
888 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
890 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
891 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
892 if (_last_player_load_directory) {
893 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
895 /* [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. */
896 if (_last_kdm_write_type) {
897 switch (_last_kdm_write_type.get()) {
899 root->add_child("LastKDMWriteType")->add_child_text("flat");
901 case KDM_WRITE_FOLDER:
902 root->add_child("LastKDMWriteType")->add_child_text("folder");
905 root->add_child("LastKDMWriteType")->add_child_text("zip");
909 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
910 if (_last_dkdm_write_type) {
911 switch (_last_dkdm_write_type.get()) {
912 case DKDM_WRITE_INTERNAL:
913 root->add_child("LastDKDMWriteType")->add_child_text("internal");
915 case DKDM_WRITE_FILE:
916 root->add_child("LastDKDMWriteType")->add_child_text("file");
920 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
921 frames to be held in memory at once.
923 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
925 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
926 if (_decode_reduction) {
927 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
930 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
931 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
933 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
934 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
935 xmlpp::Element* e = root->add_child ("Notification");
936 e->set_attribute ("Id", raw_convert<string>(i));
937 e->add_child_text (_notification[i] ? "1" : "0");
940 if (_barco_username) {
941 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
942 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
944 if (_barco_password) {
945 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
946 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
949 if (_christie_username) {
950 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
951 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
953 if (_christie_password) {
954 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
955 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
959 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
960 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
963 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
964 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
967 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
968 with controls on another monitor.
970 switch (_player_mode) {
971 case PLAYER_MODE_WINDOW:
972 root->add_child("PlayerMode")->add_child_text("window");
974 case PLAYER_MODE_FULL:
975 root->add_child("PlayerMode")->add_child_text("full");
977 case PLAYER_MODE_DUAL:
978 root->add_child("PlayerMode")->add_child_text("dual");
982 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
983 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
984 switch (_video_view_type) {
985 case VIDEO_VIEW_SIMPLE:
986 root->add_child("VideoViewType")->add_child_text("simple");
988 case VIDEO_VIEW_OPENGL:
989 root->add_child("VideoViewType")->add_child_text("opengl");
992 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
993 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
994 if (_player_debug_log_file) {
995 /* [XML] PlayerLogFile Filename to use for player debug logs. */
996 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
998 if (_player_content_directory) {
999 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1000 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
1002 if (_player_playlist_directory) {
1003 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1004 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1006 if (_player_kdm_directory) {
1007 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1008 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1010 if (_audio_mapping) {
1011 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1013 for (auto const& i: _custom_languages) {
1014 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1016 if (_add_files_path) {
1017 /* [XML] AddFilesPath The default path that will be offered in the picker when adding files to a film. */
1018 root->add_child("AddFilesPath")->add_child_text(_add_files_path->string());
1020 root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1022 auto target = config_write_file();
1025 auto const s = doc.write_to_string_formatted ();
1026 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1027 auto f = fopen_boost (tmp, "w");
1029 throw FileError (_("Could not open file for writing"), tmp);
1031 checked_fwrite (s.c_str(), s.bytes(), f, tmp);
1033 boost::filesystem::remove (target);
1034 boost::filesystem::rename (tmp, target);
1035 } catch (xmlpp::exception& e) {
1036 string s = e.what ();
1038 throw FileError (s, target);
1045 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1047 xmlpp::Document doc;
1048 auto root = doc.create_root_node (root_node);
1049 root->add_child("Version")->add_child_text(version);
1051 for (auto i: things) {
1052 i->as_xml (root->add_child(node));
1056 doc.write_to_file_formatted (file.string() + ".tmp");
1057 boost::filesystem::remove (file);
1058 boost::filesystem::rename (file.string() + ".tmp", file);
1059 } catch (xmlpp::exception& e) {
1060 string s = e.what ();
1062 throw FileError (s, file);
1068 Config::write_cinemas () const
1070 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1075 Config::write_dkdm_recipients () const
1077 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1081 boost::filesystem::path
1082 Config::default_directory_or (boost::filesystem::path a) const
1084 return directory_or (_default_directory, a);
1087 boost::filesystem::path
1088 Config::default_kdm_directory_or (boost::filesystem::path a) const
1090 return directory_or (_default_kdm_directory, a);
1093 boost::filesystem::path
1094 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1100 boost::system::error_code ec;
1101 auto const e = boost::filesystem::exists (*dir, ec);
1117 Config::changed (Property what)
1123 Config::set_kdm_email_to_default ()
1125 _kdm_subject = _("KDM delivery: $CPL_NAME");
1128 "Dear Projectionist\n\n"
1129 "Please find attached KDMs for $CPL_NAME.\n\n"
1130 "Cinema: $CINEMA_NAME\n"
1131 "Screen(s): $SCREENS\n\n"
1132 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1133 "Best regards,\nDCP-o-matic"
1138 Config::set_notification_email_to_default ()
1140 _notification_subject = _("DCP-o-matic notification");
1142 _notification_email = _(
1143 "$JOB_NAME: $JOB_STATUS"
1148 Config::reset_kdm_email ()
1150 set_kdm_email_to_default ();
1155 Config::reset_notification_email ()
1157 set_notification_email_to_default ();
1162 Config::set_cover_sheet_to_default ()
1166 "CPL Filename: $CPL_FILENAME\n"
1168 "Format: $CONTAINER\n"
1170 "Audio Language: $AUDIO_LANGUAGE\n"
1171 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1178 Config::add_to_history (boost::filesystem::path p)
1180 add_to_history_internal (_history, p);
1183 /** Remove non-existant items from the history */
1185 Config::clean_history ()
1187 clean_history_internal (_history);
1191 Config::add_to_player_history (boost::filesystem::path p)
1193 add_to_history_internal (_player_history, p);
1196 /** Remove non-existant items from the player history */
1198 Config::clean_player_history ()
1200 clean_history_internal (_player_history);
1204 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1206 /* Remove existing instances of this path in the history */
1207 h.erase (remove (h.begin(), h.end(), p), h.end ());
1209 h.insert (h.begin (), p);
1210 if (h.size() > HISTORY_SIZE) {
1218 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1224 if (boost::filesystem::is_directory(i)) {
1228 /* We couldn't find out if it's a directory for some reason; just ignore it */
1235 Config::have_existing (string file)
1237 return boost::filesystem::exists (read_path(file));
1242 Config::read_cinemas (cxml::Document const & f)
1245 for (auto i: f.node_children("Cinema")) {
1246 /* Slightly grotty two-part construction of Cinema here so that we can use
1249 auto cinema = make_shared<Cinema>(i);
1250 cinema->read_screens (i);
1251 _cinemas.push_back (cinema);
1256 Config::set_cinemas_file (boost::filesystem::path file)
1258 if (file == _cinemas_file) {
1262 _cinemas_file = file;
1264 if (boost::filesystem::exists (_cinemas_file)) {
1265 /* Existing file; read it in */
1266 cxml::Document f ("Cinemas");
1267 f.read_file (_cinemas_file);
1276 Config::read_dkdm_recipients (cxml::Document const & f)
1278 _dkdm_recipients.clear ();
1279 for (auto i: f.node_children("DKDMRecipient")) {
1280 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1286 Config::save_template (shared_ptr<const Film> film, string name) const
1288 film->write_template (template_write_path(name));
1293 Config::templates () const
1295 if (!boost::filesystem::exists(read_path("templates"))) {
1300 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1301 n.push_back (i.path().filename().string());
1307 Config::existing_template (string name) const
1309 return boost::filesystem::exists (template_read_path(name));
1313 boost::filesystem::path
1314 Config::template_read_path (string name) const
1316 return read_path("templates") / tidy_for_filename (name);
1320 boost::filesystem::path
1321 Config::template_write_path (string name) const
1323 return write_path("templates") / tidy_for_filename (name);
1328 Config::rename_template (string old_name, string new_name) const
1330 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1334 Config::delete_template (string name) const
1336 boost::filesystem::remove (template_write_path(name));
1339 /** @return Path to the config.xml containing the actual settings, following a link if required */
1340 boost::filesystem::path
1341 config_file (boost::filesystem::path main)
1343 cxml::Document f ("Config");
1344 if (!boost::filesystem::exists (main)) {
1345 /* It doesn't exist, so there can't be any links; just return it */
1349 /* See if there's a link */
1352 auto link = f.optional_string_child("Link");
1356 } catch (xmlpp::exception& e) {
1357 /* There as a problem reading the main configuration file,
1358 so there can't be a link.
1366 boost::filesystem::path
1367 Config::config_read_file ()
1369 return config_file (read_path("config.xml"));
1373 boost::filesystem::path
1374 Config::config_write_file ()
1376 return config_file (write_path("config.xml"));
1381 Config::reset_cover_sheet ()
1383 set_cover_sheet_to_default ();
1388 Config::link (boost::filesystem::path new_file) const
1390 xmlpp::Document doc;
1391 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1393 doc.write_to_file_formatted(write_path("config.xml").string());
1394 } catch (xmlpp::exception& e) {
1395 string s = e.what ();
1397 throw FileError (s, write_path("config.xml"));
1402 Config::copy_and_link (boost::filesystem::path new_file) const
1405 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1410 Config::have_write_permission () const
1412 auto f = fopen_boost (config_write_file(), "r+");
1421 /** @param output_channels Number of output channels in use.
1422 * @return Audio mapping for this output channel count (may be a default).
1425 Config::audio_mapping (int output_channels)
1427 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1428 /* Set up a default */
1429 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1430 if (output_channels == 2) {
1431 /* Special case for stereo output.
1432 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1433 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1435 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1436 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1437 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1438 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1439 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1440 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1441 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1442 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1445 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1446 _audio_mapping->set (i, i, 1);
1451 return *_audio_mapping;
1455 Config::set_audio_mapping (AudioMapping m)
1458 changed (AUDIO_MAPPING);
1462 Config::set_audio_mapping_to_default ()
1464 DCPOMATIC_ASSERT (_audio_mapping);
1465 auto const ch = _audio_mapping->output_channels ();
1466 _audio_mapping = boost::none;
1467 _audio_mapping = audio_mapping (ch);
1468 changed (AUDIO_MAPPING);
1473 Config::add_custom_language (dcp::LanguageTag tag)
1475 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1476 _custom_languages.push_back (tag);
1483 save_all_config_as_zip (boost::filesystem::path zip_file)
1485 Zipper zipper (zip_file);
1487 auto config = Config::instance();
1488 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1489 if (boost::filesystem::exists(config->cinemas_file())) {
1490 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1492 if (boost::filesystem::exists(config->dkdm_recipients_file())) {
1493 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));