2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
26 #include "dcp_content_type.h"
27 #include "colour_conversion.h"
32 #include "dkdm_wrapper.h"
33 #include "compose.hpp"
35 #include "dkdm_recipient.h"
36 #include <dcp/raw_convert.h>
37 #include <dcp/name_format.h>
38 #include <dcp/certificate_chain.h>
39 #include <libcxml/cxml.h>
41 #include <libxml++/libxml++.h>
42 #include <boost/filesystem.hpp>
43 #include <boost/algorithm/string.hpp>
44 #include <boost/thread.hpp>
59 using std::shared_ptr;
60 using std::make_shared;
61 using boost::optional;
62 using std::dynamic_pointer_cast;
63 using boost::algorithm::trim;
64 using dcp::raw_convert;
66 Config* Config::_instance = 0;
67 int const Config::_current_version = 3;
68 boost::signals2::signal<void ()> Config::FailedToLoad;
69 boost::signals2::signal<void (string)> Config::Warning;
70 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
72 /** Construct default configuration */
74 /* DKDMs are not considered a thing to reset on set_defaults() */
75 : _dkdms (new DKDMGroup ("root"))
81 Config::set_defaults ()
83 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
84 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
85 _server_port_base = 6192;
86 _use_any_servers = true;
88 _only_servers_encode = false;
89 _tms_protocol = FileTransferProtocol::SCP;
94 _allow_any_dcp_frame_rate = false;
95 _allow_any_container = false;
96 _show_experimental_audio_processors = false;
97 _language = optional<string> ();
98 _default_still_length = 10;
99 _default_container = Ratio::from_id ("185");
100 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
101 _default_dcp_audio_channels = 6;
102 _default_j2k_bandwidth = 150000000;
103 _default_audio_delay = 0;
104 _default_interop = false;
105 _default_metadata.clear ();
106 _upload_after_make_dcp = false;
109 _mail_protocol = EmailProtocol::AUTO;
115 _notification_from = "";
116 _notification_to = "";
117 _notification_cc.clear ();
118 _notification_bcc = "";
119 _check_for_updates = false;
120 _check_for_test_updates = false;
121 _maximum_j2k_bandwidth = 250000000;
122 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
123 _analyse_ebur128 = true;
124 _automatic_audio_analysis = false;
125 #ifdef DCPOMATIC_WINDOWS
126 _win32_console = false;
128 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
129 * ~/.config/dcpomatic2 (or equivalent) and written back there.
131 _cinemas_file = read_path ("cinemas.xml");
132 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
133 _show_hints_before_make_dcp = true;
134 _confirm_kdm_email = true;
135 _kdm_container_name_format = dcp::NameFormat ("KDM %f %c");
136 _kdm_filename_format = dcp::NameFormat ("KDM %f %c %s");
137 _dkdm_filename_format = dcp::NameFormat ("DKDM %f %c %s");
138 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
139 _dcp_asset_filename_format = dcp::NameFormat ("%t");
140 _jump_to_selected = true;
141 for (int i = 0; i < NAG_COUNT; ++i) {
145 _sound_output = optional<string> ();
146 _last_kdm_write_type = KDM_WRITE_FLAT;
147 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
149 /* I think the scaling factor here should be the ratio of the longest frame
150 encode time to the shortest; if the thread count is T, longest time is L
151 and the shortest time S we could encode L/S frames per thread whilst waiting
152 for the L frame to encode so we might have to store LT/S frames.
154 However we don't want to use too much memory, so keep it a bit lower than we'd
155 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
156 use about 240Mb with 72 encoding threads.
158 _frames_in_memory_multiplier = 3;
159 _decode_reduction = optional<int>();
160 _default_notify = false;
161 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
162 _notification[i] = false;
164 _barco_username = optional<string>();
165 _barco_password = optional<string>();
166 _christie_username = optional<string>();
167 _christie_password = optional<string>();
168 _gdc_username = optional<string>();
169 _gdc_password = optional<string>();
170 _player_mode = PLAYER_MODE_WINDOW;
172 _video_view_type = VIDEO_VIEW_SIMPLE;
173 _respect_kdm_validity_periods = true;
174 _player_debug_log_file = boost::none;
175 _player_content_directory = boost::none;
176 _player_playlist_directory = boost::none;
177 _player_kdm_directory = boost::none;
178 _audio_mapping = boost::none;
179 _custom_languages.clear ();
180 _add_files_path = boost::none;
182 _allowed_dcp_frame_rates.clear ();
183 _allowed_dcp_frame_rates.push_back (24);
184 _allowed_dcp_frame_rates.push_back (25);
185 _allowed_dcp_frame_rates.push_back (30);
186 _allowed_dcp_frame_rates.push_back (48);
187 _allowed_dcp_frame_rates.push_back (50);
188 _allowed_dcp_frame_rates.push_back (60);
190 set_kdm_email_to_default ();
191 set_notification_email_to_default ();
192 set_cover_sheet_to_default ();
196 Config::restore_defaults ()
198 Config::instance()->set_defaults ();
199 Config::instance()->changed ();
202 shared_ptr<dcp::CertificateChain>
203 Config::create_certificate_chain ()
205 return make_shared<dcp::CertificateChain> (
207 CERTIFICATE_VALIDITY_PERIOD,
210 ".dcpomatic.smpte-430-2.ROOT",
211 ".dcpomatic.smpte-430-2.INTERMEDIATE",
212 "CS.dcpomatic.smpte-430-2.LEAF"
219 using namespace boost::filesystem;
221 auto copy_adding_number = [](path const& path_to_copy) {
223 auto add_number = [](path const& path, int number) {
224 return String::compose("%1.%2", path, number);
228 while (n < 100 && exists(add_number(path_to_copy, n))) {
231 boost::system::error_code ec;
232 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
235 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
236 * to write over. This is more intended for the situation where we have a corrupted config.xml,
237 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
238 * file). But we might as well back up the other files while we're about it.
241 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
242 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
244 copy_adding_number (config_write_file());
246 /* These do not use State::write_path, so whatever path is in the Config we will copy
249 copy_adding_number (_cinemas_file);
250 copy_adding_number (_dkdm_recipients_file);
257 cxml::Document f ("Config");
258 f.read_file (config_read_file());
260 auto version = f.optional_number_child<int> ("Version");
261 if (version && *version < _current_version) {
262 /* Back up the old config before we re-write it in a back-incompatible way */
266 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
267 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
269 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
270 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
273 _default_directory = f.optional_string_child ("DefaultDirectory");
274 if (_default_directory && _default_directory->empty ()) {
275 /* We used to store an empty value for this to mean "none set" */
276 _default_directory = boost::optional<boost::filesystem::path> ();
279 auto b = f.optional_number_child<int> ("ServerPort");
281 b = f.optional_number_child<int> ("ServerPortBase");
283 _server_port_base = b.get ();
285 auto u = f.optional_bool_child ("UseAnyServers");
286 _use_any_servers = u.get_value_or (true);
288 for (auto i: f.node_children("Server")) {
289 if (i->node_children("HostName").size() == 1) {
290 _servers.push_back (i->string_child ("HostName"));
292 _servers.push_back (i->content ());
296 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
297 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
298 _tms_ip = f.string_child ("TMSIP");
299 _tms_path = f.string_child ("TMSPath");
300 _tms_user = f.string_child ("TMSUser");
301 _tms_password = f.string_child ("TMSPassword");
303 _language = f.optional_string_child ("Language");
305 auto c = f.optional_string_child ("DefaultContainer");
307 _default_container = Ratio::from_id (c.get ());
310 if (_default_container && !_default_container->used_for_container()) {
311 Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)"));
312 _default_container = Ratio::from_id ("185");
315 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
316 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
318 if (f.optional_string_child ("DCPMetadataIssuer")) {
319 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
320 } else if (f.optional_string_child ("DCPIssuer")) {
321 _dcp_issuer = f.string_child ("DCPIssuer");
324 auto up = f.optional_bool_child("UploadAfterMakeDCP");
326 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
328 _upload_after_make_dcp = up.get_value_or (false);
329 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
330 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
331 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
332 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
333 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
335 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
336 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
337 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
338 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
340 for (auto const& i: f.node_children("DefaultMetadata")) {
341 _default_metadata[i->string_attribute("key")] = i->content();
344 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
346 /* Read any cinemas that are still lying around in the config file
347 * from an old version.
351 _mail_server = f.string_child ("MailServer");
352 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
355 /* Make sure this matches the code in write_config */
356 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
357 if (protocol == "Auto") {
358 _mail_protocol = EmailProtocol::AUTO;
359 } else if (protocol == "Plain") {
360 _mail_protocol = EmailProtocol::PLAIN;
361 } else if (protocol == "STARTTLS") {
362 _mail_protocol = EmailProtocol::STARTTLS;
363 } else if (protocol == "SSL") {
364 _mail_protocol = EmailProtocol::SSL;
368 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
369 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
371 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
372 _kdm_from = f.string_child ("KDMFrom");
373 for (auto i: f.node_children("KDMCC")) {
374 if (!i->content().empty()) {
375 _kdm_cc.push_back (i->content ());
378 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
379 _kdm_email = f.string_child ("KDMEmail");
381 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
382 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
383 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
384 for (auto i: f.node_children("NotificationCC")) {
385 if (!i->content().empty()) {
386 _notification_cc.push_back (i->content ());
389 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
390 if (f.optional_string_child("NotificationEmail")) {
391 _notification_email = f.string_child("NotificationEmail");
394 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
395 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
397 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
398 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
399 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
400 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
402 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
403 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
404 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
405 #ifdef DCPOMATIC_WINDOWS
406 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
409 for (auto i: f.node_children("History")) {
410 _history.push_back (i->content ());
413 for (auto i: f.node_children("PlayerHistory")) {
414 _player_history.push_back (i->content ());
417 auto signer = f.optional_node_child ("Signer");
419 auto c = make_shared<dcp::CertificateChain>();
420 /* Read the signing certificates and private key in from the config file */
421 for (auto i: signer->node_children ("Certificate")) {
422 c->add (dcp::Certificate (i->content ()));
424 c->set_key (signer->string_child ("PrivateKey"));
427 /* Make a new set of signing certificates and key */
428 _signer_chain = create_certificate_chain ();
431 auto decryption = f.optional_node_child ("Decryption");
433 auto c = make_shared<dcp::CertificateChain>();
434 for (auto i: decryption->node_children ("Certificate")) {
435 c->add (dcp::Certificate (i->content ()));
437 c->set_key (decryption->string_child ("PrivateKey"));
438 _decryption_chain = c;
440 _decryption_chain = create_certificate_chain ();
443 /* These must be done before we call Bad as that might set one
446 for (auto i: f.node_children("Nagged")) {
447 auto const id = i->number_attribute<int>("Id");
448 if (id >= 0 && id < NAG_COUNT) {
449 _nagged[id] = raw_convert<int>(i->content());
453 optional<BadReason> bad;
455 for (auto const& i: _signer_chain->unordered()) {
456 if (i.has_utf8_strings()) {
457 bad = BAD_SIGNER_UTF8_STRINGS;
461 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
462 bad = BAD_SIGNER_INCONSISTENT;
465 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
466 bad = BAD_DECRYPTION_INCONSISTENT;
470 auto const remake = Bad(*bad);
471 if (remake && *remake) {
473 case BAD_SIGNER_UTF8_STRINGS:
474 case BAD_SIGNER_INCONSISTENT:
475 _signer_chain = create_certificate_chain ();
477 case BAD_DECRYPTION_INCONSISTENT:
478 _decryption_chain = create_certificate_chain ();
484 if (f.optional_node_child("DKDMGroup")) {
485 /* New-style: all DKDMs in a group */
486 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
488 /* Old-style: one or more DKDM nodes */
489 _dkdms.reset (new DKDMGroup ("root"));
490 for (auto i: f.node_children("DKDM")) {
491 _dkdms->add (DKDMBase::read (i));
494 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
495 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
496 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
497 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
498 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
499 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
500 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
501 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
502 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
503 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
504 /* The variable was renamed but not the XML tag */
505 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
506 _sound_output = f.optional_string_child("PreviewSoundOutput");
507 if (f.optional_string_child("CoverSheet")) {
508 _cover_sheet = f.optional_string_child("CoverSheet").get();
510 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
511 if (f.optional_string_child("LastKDMWriteType")) {
512 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
513 _last_kdm_write_type = KDM_WRITE_FLAT;
514 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
515 _last_kdm_write_type = KDM_WRITE_FOLDER;
516 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
517 _last_kdm_write_type = KDM_WRITE_ZIP;
520 if (f.optional_string_child("LastDKDMWriteType")) {
521 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
522 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
523 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
524 _last_dkdm_write_type = DKDM_WRITE_FILE;
527 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
528 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
529 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
531 for (auto i: f.node_children("Notification")) {
532 int const id = i->number_attribute<int>("Id");
533 if (id >= 0 && id < NOTIFICATION_COUNT) {
534 _notification[id] = raw_convert<int>(i->content());
538 _barco_username = f.optional_string_child("BarcoUsername");
539 _barco_password = f.optional_string_child("BarcoPassword");
540 _christie_username = f.optional_string_child("ChristieUsername");
541 _christie_password = f.optional_string_child("ChristiePassword");
542 _gdc_username = f.optional_string_child("GDCUsername");
543 _gdc_password = f.optional_string_child("GDCPassword");
545 auto pm = f.optional_string_child("PlayerMode");
546 if (pm && *pm == "window") {
547 _player_mode = PLAYER_MODE_WINDOW;
548 } else if (pm && *pm == "full") {
549 _player_mode = PLAYER_MODE_FULL;
550 } else if (pm && *pm == "dual") {
551 _player_mode = PLAYER_MODE_DUAL;
554 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
555 auto vc = f.optional_string_child("VideoViewType");
556 if (vc && *vc == "opengl") {
557 _video_view_type = VIDEO_VIEW_OPENGL;
558 } else if (vc && *vc == "simple") {
559 _video_view_type = VIDEO_VIEW_SIMPLE;
561 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
562 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
563 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
564 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
565 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
567 if (f.optional_node_child("AudioMapping")) {
568 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
571 for (auto i: f.node_children("CustomLanguage")) {
573 /* This will fail if it's called before dcp::init() as it won't recognise the
574 * tag. That's OK because the Config will be reloaded again later.
576 _custom_languages.push_back (dcp::LanguageTag(i->content()));
577 } catch (std::runtime_error& e) {}
580 _add_files_path = f.optional_string_child("AddFilesPath");
582 if (boost::filesystem::exists (_cinemas_file)) {
583 cxml::Document f ("Cinemas");
584 f.read_file (_cinemas_file);
588 if (boost::filesystem::exists (_dkdm_recipients_file)) {
589 cxml::Document f ("DKDMRecipients");
590 f.read_file (_dkdm_recipients_file);
591 read_dkdm_recipients (f);
595 if (have_existing("config.xml") || have_existing("cinemas.xml") || have_existing("dkdm_recipients.xml")) {
597 /* We have a config file but it didn't load */
601 /* Make a new set of signing certificates and key */
602 _signer_chain = create_certificate_chain ();
603 /* And similar for decryption of KDMs */
604 _decryption_chain = create_certificate_chain ();
608 /** @return Singleton instance */
612 if (_instance == 0) {
613 _instance = new Config;
620 /** Write our configuration to disk */
622 Config::write () const
626 write_dkdm_recipients ();
630 Config::write_config () const
633 auto root = doc.create_root_node ("Config");
635 /* [XML] Version The version number of the configuration file format. */
636 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
637 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
638 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
639 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
640 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
641 if (_default_directory) {
642 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
643 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
645 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
646 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
647 by the batch converter to listen for job requests.
649 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
650 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
651 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
653 for (auto i: _servers) {
654 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
657 root->add_child("Server")->add_child_text (i);
660 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
661 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
663 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
664 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
665 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
666 /* [XML] TMSIP IP address of TMS. */
667 root->add_child("TMSIP")->add_child_text (_tms_ip);
668 /* [XML] TMSPath Path on the TMS to copy files to. */
669 root->add_child("TMSPath")->add_child_text (_tms_path);
670 /* [XML] TMSUser Username to log into the TMS with. */
671 root->add_child("TMSUser")->add_child_text (_tms_user);
672 /* [XML] TMSPassword Password to log into the TMS with. */
673 root->add_child("TMSPassword")->add_child_text (_tms_password);
675 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
676 root->add_child("Language")->add_child_text (_language.get());
678 if (_default_container) {
679 /* [XML:opt] DefaultContainer ID of default container
680 to use when creating new films (<code>185</code>,<code>239</code> or
683 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
685 if (_default_dcp_content_type) {
686 /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
687 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
688 <code>PSA</code> or <code>ADV</code>). */
689 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
691 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
692 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
693 /* [XML] DCPIssuer Issuer text to write into CPL files. */
694 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
695 /* [XML] DCPCreator Creator text to write into CPL files. */
696 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
697 /* [XML] Company name to write into MXF files. */
698 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
699 /* [XML] Product name to write into MXF files. */
700 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
701 /* [XML] Product version to write into MXF files. */
702 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
703 /* [XML] Comment to write into JPEG2000 data. */
704 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
705 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
706 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
708 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
709 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
710 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
711 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
712 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
713 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
714 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
715 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
716 for (auto const& i: _default_metadata) {
717 auto c = root->add_child("DefaultMetadata");
718 c->set_attribute("key", i.first);
719 c->add_child_text(i.second);
721 if (_default_kdm_directory) {
722 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
723 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
725 /* [XML] MailServer Hostname of SMTP server to use. */
726 root->add_child("MailServer")->add_child_text (_mail_server);
727 /* [XML] MailPort Port number to use on SMTP server. */
728 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
729 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
730 switch (_mail_protocol) {
731 case EmailProtocol::AUTO:
732 root->add_child("MailProtocol")->add_child_text("Auto");
734 case EmailProtocol::PLAIN:
735 root->add_child("MailProtocol")->add_child_text("Plain");
737 case EmailProtocol::STARTTLS:
738 root->add_child("MailProtocol")->add_child_text("STARTTLS");
740 case EmailProtocol::SSL:
741 root->add_child("MailProtocol")->add_child_text("SSL");
744 /* [XML] MailUser Username to use on SMTP server. */
745 root->add_child("MailUser")->add_child_text (_mail_user);
746 /* [XML] MailPassword Password to use on SMTP server. */
747 root->add_child("MailPassword")->add_child_text (_mail_password);
749 /* [XML] KDMSubject Subject to use for KDM emails. */
750 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
751 /* [XML] KDMFrom From address to use for KDM emails. */
752 root->add_child("KDMFrom")->add_child_text (_kdm_from);
753 for (auto i: _kdm_cc) {
754 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
755 root->add_child("KDMCC")->add_child_text (i);
757 /* [XML] KDMBCC BCC address to use for KDM emails. */
758 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
759 /* [XML] KDMEmail Text of KDM email. */
760 root->add_child("KDMEmail")->add_child_text (_kdm_email);
762 /* [XML] NotificationSubject Subject to use for notification emails. */
763 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
764 /* [XML] NotificationFrom From address to use for notification emails. */
765 root->add_child("NotificationFrom")->add_child_text (_notification_from);
766 /* [XML] NotificationFrom To address to use for notification emails. */
767 root->add_child("NotificationTo")->add_child_text (_notification_to);
768 for (auto i: _notification_cc) {
769 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
770 root->add_child("NotificationCC")->add_child_text (i);
772 /* [XML] NotificationBCC BCC address to use for notification emails. */
773 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
774 /* [XML] NotificationEmail Text of notification email. */
775 root->add_child("NotificationEmail")->add_child_text (_notification_email);
777 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
778 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
779 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
780 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
782 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
783 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
784 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
785 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
786 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
787 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
788 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
789 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
790 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
791 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
792 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
793 related to the player, 1024 debug information related to audio analyses.
795 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
796 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
797 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
798 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
799 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
800 #ifdef DCPOMATIC_WINDOWS
801 if (_win32_console) {
802 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
803 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
804 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
806 root->add_child("Win32Console")->add_child_text ("1");
810 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
811 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
813 auto signer = root->add_child ("Signer");
814 DCPOMATIC_ASSERT (_signer_chain);
815 for (auto const& i: _signer_chain->unordered()) {
816 signer->add_child("Certificate")->add_child_text (i.certificate (true));
818 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
820 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
821 auto decryption = root->add_child ("Decryption");
822 DCPOMATIC_ASSERT (_decryption_chain);
823 for (auto const& i: _decryption_chain->unordered()) {
824 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
826 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
828 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
831 for (auto i: _history) {
832 root->add_child("History")->add_child_text (i.string ());
835 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
838 for (auto i: _player_history) {
839 root->add_child("PlayerHistory")->add_child_text (i.string ());
842 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
843 or <code><DKDM></code> tags.
845 /* [XML] DKDM A DKDM as XML */
846 _dkdms->as_xml (root);
848 /* [XML] CinemasFile Filename of cinemas list file. */
849 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
850 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
851 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
852 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
853 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
854 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
855 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
856 /* [XML] KDMFilenameFormat Format for KDM filenames. */
857 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
858 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
859 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
860 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
861 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
862 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
863 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
864 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
865 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
866 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
867 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
868 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
869 for (int i = 0; i < NAG_COUNT; ++i) {
870 xmlpp::Element* e = root->add_child ("Nagged");
871 e->set_attribute ("Id", raw_convert<string>(i));
872 e->add_child_text (_nagged[i] ? "1" : "0");
874 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
875 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
877 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
878 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
880 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
881 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
882 if (_last_player_load_directory) {
883 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
885 /* [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. */
886 if (_last_kdm_write_type) {
887 switch (_last_kdm_write_type.get()) {
889 root->add_child("LastKDMWriteType")->add_child_text("flat");
891 case KDM_WRITE_FOLDER:
892 root->add_child("LastKDMWriteType")->add_child_text("folder");
895 root->add_child("LastKDMWriteType")->add_child_text("zip");
899 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
900 if (_last_dkdm_write_type) {
901 switch (_last_dkdm_write_type.get()) {
902 case DKDM_WRITE_INTERNAL:
903 root->add_child("LastDKDMWriteType")->add_child_text("internal");
905 case DKDM_WRITE_FILE:
906 root->add_child("LastDKDMWriteType")->add_child_text("file");
910 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
911 frames to be held in memory at once.
913 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
915 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
916 if (_decode_reduction) {
917 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
920 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
921 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
923 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
924 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
925 xmlpp::Element* e = root->add_child ("Notification");
926 e->set_attribute ("Id", raw_convert<string>(i));
927 e->add_child_text (_notification[i] ? "1" : "0");
930 if (_barco_username) {
931 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
932 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
934 if (_barco_password) {
935 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
936 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
939 if (_christie_username) {
940 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
941 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
943 if (_christie_password) {
944 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
945 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
949 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
950 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
953 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
954 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
957 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
958 with controls on another monitor.
960 switch (_player_mode) {
961 case PLAYER_MODE_WINDOW:
962 root->add_child("PlayerMode")->add_child_text("window");
964 case PLAYER_MODE_FULL:
965 root->add_child("PlayerMode")->add_child_text("full");
967 case PLAYER_MODE_DUAL:
968 root->add_child("PlayerMode")->add_child_text("dual");
972 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
973 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
974 switch (_video_view_type) {
975 case VIDEO_VIEW_SIMPLE:
976 root->add_child("VideoViewType")->add_child_text("simple");
978 case VIDEO_VIEW_OPENGL:
979 root->add_child("VideoViewType")->add_child_text("opengl");
982 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
983 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
984 if (_player_debug_log_file) {
985 /* [XML] PlayerLogFile Filename to use for player debug logs. */
986 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
988 if (_player_content_directory) {
989 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
990 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
992 if (_player_playlist_directory) {
993 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
994 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
996 if (_player_kdm_directory) {
997 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
998 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1000 if (_audio_mapping) {
1001 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1003 for (auto const& i: _custom_languages) {
1004 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1006 if (_add_files_path) {
1007 /* [XML] AddFilesPath The default path that will be offered in the picker when adding files to a film. */
1008 root->add_child("AddFilesPath")->add_child_text(_add_files_path->string());
1011 auto target = config_write_file();
1014 auto const s = doc.write_to_string_formatted ();
1015 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1016 auto f = fopen_boost (tmp, "w");
1018 throw FileError (_("Could not open file for writing"), tmp);
1020 checked_fwrite (s.c_str(), s.bytes(), f, tmp);
1022 boost::filesystem::remove (target);
1023 boost::filesystem::rename (tmp, target);
1024 } catch (xmlpp::exception& e) {
1025 string s = e.what ();
1027 throw FileError (s, target);
1034 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1036 xmlpp::Document doc;
1037 auto root = doc.create_root_node (root_node);
1038 root->add_child("Version")->add_child_text(version);
1040 for (auto i: things) {
1041 i->as_xml (root->add_child(node));
1045 doc.write_to_file_formatted (file.string() + ".tmp");
1046 boost::filesystem::remove (file);
1047 boost::filesystem::rename (file.string() + ".tmp", file);
1048 } catch (xmlpp::exception& e) {
1049 string s = e.what ();
1051 throw FileError (s, file);
1057 Config::write_cinemas () const
1059 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1064 Config::write_dkdm_recipients () const
1066 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1070 boost::filesystem::path
1071 Config::default_directory_or (boost::filesystem::path a) const
1073 return directory_or (_default_directory, a);
1076 boost::filesystem::path
1077 Config::default_kdm_directory_or (boost::filesystem::path a) const
1079 return directory_or (_default_kdm_directory, a);
1082 boost::filesystem::path
1083 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1089 boost::system::error_code ec;
1090 auto const e = boost::filesystem::exists (*dir, ec);
1106 Config::changed (Property what)
1112 Config::set_kdm_email_to_default ()
1114 _kdm_subject = _("KDM delivery: $CPL_NAME");
1117 "Dear Projectionist\n\n"
1118 "Please find attached KDMs for $CPL_NAME.\n\n"
1119 "Cinema: $CINEMA_NAME\n"
1120 "Screen(s): $SCREENS\n\n"
1121 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1122 "Best regards,\nDCP-o-matic"
1127 Config::set_notification_email_to_default ()
1129 _notification_subject = _("DCP-o-matic notification");
1131 _notification_email = _(
1132 "$JOB_NAME: $JOB_STATUS"
1137 Config::reset_kdm_email ()
1139 set_kdm_email_to_default ();
1144 Config::reset_notification_email ()
1146 set_notification_email_to_default ();
1151 Config::set_cover_sheet_to_default ()
1156 "Format: $CONTAINER\n"
1158 "Audio Language: $AUDIO_LANGUAGE\n"
1159 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1166 Config::add_to_history (boost::filesystem::path p)
1168 add_to_history_internal (_history, p);
1171 /** Remove non-existant items from the history */
1173 Config::clean_history ()
1175 clean_history_internal (_history);
1179 Config::add_to_player_history (boost::filesystem::path p)
1181 add_to_history_internal (_player_history, p);
1184 /** Remove non-existant items from the player history */
1186 Config::clean_player_history ()
1188 clean_history_internal (_player_history);
1192 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1194 /* Remove existing instances of this path in the history */
1195 h.erase (remove (h.begin(), h.end(), p), h.end ());
1197 h.insert (h.begin (), p);
1198 if (h.size() > HISTORY_SIZE) {
1206 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1212 if (boost::filesystem::is_directory(i)) {
1216 /* We couldn't find out if it's a directory for some reason; just ignore it */
1223 Config::have_existing (string file)
1225 return boost::filesystem::exists (read_path(file));
1230 Config::read_cinemas (cxml::Document const & f)
1233 for (auto i: f.node_children("Cinema")) {
1234 /* Slightly grotty two-part construction of Cinema here so that we can use
1237 auto cinema = make_shared<Cinema>(i);
1238 cinema->read_screens (i);
1239 _cinemas.push_back (cinema);
1244 Config::set_cinemas_file (boost::filesystem::path file)
1246 if (file == _cinemas_file) {
1250 _cinemas_file = file;
1252 if (boost::filesystem::exists (_cinemas_file)) {
1253 /* Existing file; read it in */
1254 cxml::Document f ("Cinemas");
1255 f.read_file (_cinemas_file);
1264 Config::read_dkdm_recipients (cxml::Document const & f)
1266 _dkdm_recipients.clear ();
1267 for (auto i: f.node_children("DKDMRecipient")) {
1268 _dkdm_recipients.push_back (shared_ptr<DKDMRecipient>(new DKDMRecipient(i)));
1274 Config::save_template (shared_ptr<const Film> film, string name) const
1276 film->write_template (template_write_path(name));
1281 Config::templates () const
1283 if (!boost::filesystem::exists(read_path("templates"))) {
1288 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1289 n.push_back (i.path().filename().string());
1295 Config::existing_template (string name) const
1297 return boost::filesystem::exists (template_read_path(name));
1301 boost::filesystem::path
1302 Config::template_read_path (string name) const
1304 return read_path("templates") / tidy_for_filename (name);
1308 boost::filesystem::path
1309 Config::template_write_path (string name) const
1311 return write_path("templates") / tidy_for_filename (name);
1316 Config::rename_template (string old_name, string new_name) const
1318 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1322 Config::delete_template (string name) const
1324 boost::filesystem::remove (template_write_path(name));
1327 /** @return Path to the config.xml containing the actual settings, following a link if required */
1328 boost::filesystem::path
1329 config_file (boost::filesystem::path main)
1331 cxml::Document f ("Config");
1332 if (!boost::filesystem::exists (main)) {
1333 /* It doesn't exist, so there can't be any links; just return it */
1337 /* See if there's a link */
1340 auto link = f.optional_string_child("Link");
1344 } catch (xmlpp::exception& e) {
1345 /* There as a problem reading the main configuration file,
1346 so there can't be a link.
1354 boost::filesystem::path
1355 Config::config_read_file ()
1357 return config_file (read_path("config.xml"));
1361 boost::filesystem::path
1362 Config::config_write_file ()
1364 return config_file (write_path("config.xml"));
1369 Config::reset_cover_sheet ()
1371 set_cover_sheet_to_default ();
1376 Config::link (boost::filesystem::path new_file) const
1378 xmlpp::Document doc;
1379 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1381 doc.write_to_file_formatted(write_path("config.xml").string());
1382 } catch (xmlpp::exception& e) {
1383 string s = e.what ();
1385 throw FileError (s, write_path("config.xml"));
1390 Config::copy_and_link (boost::filesystem::path new_file) const
1393 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1398 Config::have_write_permission () const
1400 auto f = fopen_boost (config_write_file(), "r+");
1409 /** @param output_channels Number of output channels in use.
1410 * @return Audio mapping for this output channel count (may be a default).
1413 Config::audio_mapping (int output_channels)
1415 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1416 /* Set up a default */
1417 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1418 if (output_channels == 2) {
1419 /* Special case for stereo output.
1420 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1421 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1423 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1424 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1425 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1426 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1427 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1428 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1429 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1430 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1433 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1434 _audio_mapping->set (i, i, 1);
1439 return *_audio_mapping;
1443 Config::set_audio_mapping (AudioMapping m)
1446 changed (AUDIO_MAPPING);
1450 Config::set_audio_mapping_to_default ()
1452 DCPOMATIC_ASSERT (_audio_mapping);
1453 auto const ch = _audio_mapping->output_channels ();
1454 _audio_mapping = boost::none;
1455 _audio_mapping = audio_mapping (ch);
1456 changed (AUDIO_MAPPING);
1461 Config::add_custom_language (dcp::LanguageTag tag)
1463 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1464 _custom_languages.push_back (tag);