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>
61 using std::shared_ptr;
62 using std::make_shared;
63 using boost::optional;
64 using std::dynamic_pointer_cast;
65 using boost::algorithm::trim;
66 using dcp::raw_convert;
68 Config* Config::_instance = 0;
69 int const Config::_current_version = 3;
70 boost::signals2::signal<void ()> Config::FailedToLoad;
71 boost::signals2::signal<void (string)> Config::Warning;
72 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
74 /** Construct default configuration */
76 /* DKDMs are not considered a thing to reset on set_defaults() */
77 : _dkdms (new DKDMGroup ("root"))
83 Config::set_defaults ()
85 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
86 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
87 _server_port_base = 6192;
88 _use_any_servers = true;
90 _only_servers_encode = false;
91 _tms_protocol = FileTransferProtocol::SCP;
96 _allow_any_dcp_frame_rate = false;
97 _allow_any_container = false;
98 _show_experimental_audio_processors = false;
99 _language = optional<string> ();
100 _default_still_length = 10;
101 _default_container = Ratio::from_id ("185");
102 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
103 _default_dcp_audio_channels = 6;
104 _default_j2k_bandwidth = 150000000;
105 _default_audio_delay = 0;
106 _default_interop = false;
107 _upload_after_make_dcp = false;
110 _mail_protocol = EmailProtocol::AUTO;
116 _notification_from = "";
117 _notification_to = "";
118 _notification_cc.clear ();
119 _notification_bcc = "";
120 _check_for_updates = false;
121 _check_for_test_updates = false;
122 _maximum_j2k_bandwidth = 250000000;
123 _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
124 _analyse_ebur128 = true;
125 _automatic_audio_analysis = false;
126 #ifdef DCPOMATIC_WINDOWS
127 _win32_console = false;
129 _cinemas_file = path ("cinemas.xml");
130 _dkdm_recipients_file = path ("dkdm_recipients.xml");
131 _show_hints_before_make_dcp = true;
132 _confirm_kdm_email = true;
133 _kdm_container_name_format = dcp::NameFormat ("KDM %f %c");
134 _kdm_filename_format = dcp::NameFormat ("KDM %f %c %s");
135 _dkdm_filename_format = dcp::NameFormat ("DKDM %f %c %s");
136 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
137 _dcp_asset_filename_format = dcp::NameFormat ("%t");
138 _jump_to_selected = true;
139 for (int i = 0; i < NAG_COUNT; ++i) {
143 _sound_output = optional<string> ();
144 _last_kdm_write_type = KDM_WRITE_FLAT;
145 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
147 /* I think the scaling factor here should be the ratio of the longest frame
148 encode time to the shortest; if the thread count is T, longest time is L
149 and the shortest time S we could encode L/S frames per thread whilst waiting
150 for the L frame to encode so we might have to store LT/S frames.
152 However we don't want to use too much memory, so keep it a bit lower than we'd
153 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
154 use about 240Mb with 72 encoding threads.
156 _frames_in_memory_multiplier = 3;
157 _decode_reduction = optional<int>();
158 _default_notify = false;
159 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
160 _notification[i] = false;
162 _barco_username = optional<string>();
163 _barco_password = optional<string>();
164 _christie_username = optional<string>();
165 _christie_password = optional<string>();
166 _gdc_username = optional<string>();
167 _gdc_password = optional<string>();
168 _player_mode = PLAYER_MODE_WINDOW;
170 _video_view_type = VIDEO_VIEW_SIMPLE;
171 _respect_kdm_validity_periods = true;
172 _player_activity_log_file = boost::none;
173 _player_debug_log_file = boost::none;
174 _player_content_directory = boost::none;
175 _player_playlist_directory = boost::none;
176 _player_kdm_directory = boost::none;
177 _audio_mapping = boost::none;
178 _custom_languages.clear ();
180 _allowed_dcp_frame_rates.clear ();
181 _allowed_dcp_frame_rates.push_back (24);
182 _allowed_dcp_frame_rates.push_back (25);
183 _allowed_dcp_frame_rates.push_back (30);
184 _allowed_dcp_frame_rates.push_back (48);
185 _allowed_dcp_frame_rates.push_back (50);
186 _allowed_dcp_frame_rates.push_back (60);
188 set_kdm_email_to_default ();
189 set_notification_email_to_default ();
190 set_cover_sheet_to_default ();
194 Config::restore_defaults ()
196 Config::instance()->set_defaults ();
197 Config::instance()->changed ();
200 shared_ptr<dcp::CertificateChain>
201 Config::create_certificate_chain ()
203 return make_shared<dcp::CertificateChain> (
207 ".dcpomatic.smpte-430-2.ROOT",
208 ".dcpomatic.smpte-430-2.INTERMEDIATE",
209 "CS.dcpomatic.smpte-430-2.LEAF"
216 /* Make a copy of the configuration */
219 while (n < 100 && boost::filesystem::exists(path(String::compose("config.xml.%1", n)))) {
223 boost::filesystem::copy_file(path("config.xml", false), path(String::compose("config.xml.%1", n), false));
224 boost::filesystem::copy_file(path("cinemas.xml", false), path(String::compose("cinemas.xml.%1", n), false));
225 boost::filesystem::copy_file(path("dkdm_recipients.xml", false), path(String::compose("dkdm_recipients.xml.%1", n), false));
233 cxml::Document f ("Config");
234 f.read_file (config_file ());
236 auto version = f.optional_number_child<int> ("Version");
237 if (version && *version < _current_version) {
238 /* Back up the old config before we re-write it in a back-incompatible way */
242 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
243 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
245 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
246 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
249 _default_directory = f.optional_string_child ("DefaultDirectory");
250 if (_default_directory && _default_directory->empty ()) {
251 /* We used to store an empty value for this to mean "none set" */
252 _default_directory = boost::optional<boost::filesystem::path> ();
255 auto b = f.optional_number_child<int> ("ServerPort");
257 b = f.optional_number_child<int> ("ServerPortBase");
259 _server_port_base = b.get ();
261 auto u = f.optional_bool_child ("UseAnyServers");
262 _use_any_servers = u.get_value_or (true);
264 for (auto i: f.node_children("Server")) {
265 if (i->node_children("HostName").size() == 1) {
266 _servers.push_back (i->string_child ("HostName"));
268 _servers.push_back (i->content ());
272 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
273 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
274 _tms_ip = f.string_child ("TMSIP");
275 _tms_path = f.string_child ("TMSPath");
276 _tms_user = f.string_child ("TMSUser");
277 _tms_password = f.string_child ("TMSPassword");
279 _language = f.optional_string_child ("Language");
281 auto c = f.optional_string_child ("DefaultContainer");
283 _default_container = Ratio::from_id (c.get ());
286 if (_default_container && !_default_container->used_for_container()) {
287 Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)"));
288 _default_container = Ratio::from_id ("185");
291 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
292 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
294 if (f.optional_string_child ("DCPMetadataIssuer")) {
295 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
296 } else if (f.optional_string_child ("DCPIssuer")) {
297 _dcp_issuer = f.string_child ("DCPIssuer");
300 auto up = f.optional_bool_child("UploadAfterMakeDCP");
302 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
304 _upload_after_make_dcp = up.get_value_or (false);
305 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
306 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
307 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
308 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
309 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
311 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
312 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
313 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
314 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
315 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
317 /* Read any cinemas that are still lying around in the config file
318 * from an old version.
322 _mail_server = f.string_child ("MailServer");
323 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
326 /* Make sure this matches the code in write_config */
327 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
328 if (protocol == "Auto") {
329 _mail_protocol = EmailProtocol::AUTO;
330 } else if (protocol == "Plain") {
331 _mail_protocol = EmailProtocol::PLAIN;
332 } else if (protocol == "STARTTLS") {
333 _mail_protocol = EmailProtocol::STARTTLS;
334 } else if (protocol == "SSL") {
335 _mail_protocol = EmailProtocol::SSL;
339 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
340 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
342 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
343 _kdm_from = f.string_child ("KDMFrom");
344 for (auto i: f.node_children("KDMCC")) {
345 if (!i->content().empty()) {
346 _kdm_cc.push_back (i->content ());
349 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
350 _kdm_email = f.string_child ("KDMEmail");
352 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
353 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
354 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
355 for (auto i: f.node_children("NotificationCC")) {
356 if (!i->content().empty()) {
357 _notification_cc.push_back (i->content ());
360 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
361 if (f.optional_string_child("NotificationEmail")) {
362 _notification_email = f.string_child("NotificationEmail");
365 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
366 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
368 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
369 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
370 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
371 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
373 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
374 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
375 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
376 #ifdef DCPOMATIC_WINDOWS
377 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
380 for (auto i: f.node_children("History")) {
381 _history.push_back (i->content ());
384 for (auto i: f.node_children("PlayerHistory")) {
385 _player_history.push_back (i->content ());
388 auto signer = f.optional_node_child ("Signer");
390 auto c = make_shared<dcp::CertificateChain>();
391 /* Read the signing certificates and private key in from the config file */
392 for (auto i: signer->node_children ("Certificate")) {
393 c->add (dcp::Certificate (i->content ()));
395 c->set_key (signer->string_child ("PrivateKey"));
398 /* Make a new set of signing certificates and key */
399 _signer_chain = create_certificate_chain ();
402 auto decryption = f.optional_node_child ("Decryption");
404 auto c = make_shared<dcp::CertificateChain>();
405 for (auto i: decryption->node_children ("Certificate")) {
406 c->add (dcp::Certificate (i->content ()));
408 c->set_key (decryption->string_child ("PrivateKey"));
409 _decryption_chain = c;
411 _decryption_chain = create_certificate_chain ();
414 /* These must be done before we call Bad as that might set one
417 for (auto i: f.node_children("Nagged")) {
418 auto const id = i->number_attribute<int>("Id");
419 if (id >= 0 && id < NAG_COUNT) {
420 _nagged[id] = raw_convert<int>(i->content());
424 optional<BadReason> bad;
426 for (auto const& i: _signer_chain->unordered()) {
427 if (i.has_utf8_strings()) {
428 bad = BAD_SIGNER_UTF8_STRINGS;
432 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
433 bad = BAD_SIGNER_INCONSISTENT;
436 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
437 bad = BAD_DECRYPTION_INCONSISTENT;
441 auto const remake = Bad(*bad);
442 if (remake && *remake) {
444 case BAD_SIGNER_UTF8_STRINGS:
445 case BAD_SIGNER_INCONSISTENT:
446 _signer_chain = create_certificate_chain ();
448 case BAD_DECRYPTION_INCONSISTENT:
449 _decryption_chain = create_certificate_chain ();
455 if (f.optional_node_child("DKDMGroup")) {
456 /* New-style: all DKDMs in a group */
457 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
459 /* Old-style: one or more DKDM nodes */
460 _dkdms.reset (new DKDMGroup ("root"));
461 for (auto i: f.node_children("DKDM")) {
462 _dkdms->add (DKDMBase::read (i));
465 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or (path ("cinemas.xml").string ());
466 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or (path("dkdm_recipients.xml").string());
467 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
468 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
469 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
470 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
471 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
472 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
473 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
474 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
475 /* The variable was renamed but not the XML tag */
476 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
477 _sound_output = f.optional_string_child("PreviewSoundOutput");
478 if (f.optional_string_child("CoverSheet")) {
479 _cover_sheet = f.optional_string_child("CoverSheet").get();
481 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
482 if (f.optional_string_child("LastKDMWriteType")) {
483 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
484 _last_kdm_write_type = KDM_WRITE_FLAT;
485 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
486 _last_kdm_write_type = KDM_WRITE_FOLDER;
487 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
488 _last_kdm_write_type = KDM_WRITE_ZIP;
491 if (f.optional_string_child("LastDKDMWriteType")) {
492 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
493 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
494 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
495 _last_dkdm_write_type = DKDM_WRITE_FILE;
498 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
499 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
500 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
502 for (auto i: f.node_children("Notification")) {
503 int const id = i->number_attribute<int>("Id");
504 if (id >= 0 && id < NOTIFICATION_COUNT) {
505 _notification[id] = raw_convert<int>(i->content());
509 _barco_username = f.optional_string_child("BarcoUsername");
510 _barco_password = f.optional_string_child("BarcoPassword");
511 _christie_username = f.optional_string_child("ChristieUsername");
512 _christie_password = f.optional_string_child("ChristiePassword");
513 _gdc_username = f.optional_string_child("GDCUsername");
514 _gdc_password = f.optional_string_child("GDCPassword");
516 auto pm = f.optional_string_child("PlayerMode");
517 if (pm && *pm == "window") {
518 _player_mode = PLAYER_MODE_WINDOW;
519 } else if (pm && *pm == "full") {
520 _player_mode = PLAYER_MODE_FULL;
521 } else if (pm && *pm == "dual") {
522 _player_mode = PLAYER_MODE_DUAL;
525 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
526 auto vc = f.optional_string_child("VideoViewType");
527 if (vc && *vc == "opengl") {
528 _video_view_type = VIDEO_VIEW_OPENGL;
529 } else if (vc && *vc == "simple") {
530 _video_view_type = VIDEO_VIEW_SIMPLE;
532 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
533 /* PlayerLogFile is old name */
534 _player_activity_log_file = f.optional_string_child("PlayerLogFile");
535 if (!_player_activity_log_file) {
536 _player_activity_log_file = f.optional_string_child("PlayerActivityLogFile");
538 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
539 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
540 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
541 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
543 if (f.optional_node_child("AudioMapping")) {
544 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
547 for (auto i: f.node_children("CustomLanguage")) {
549 /* This will fail if it's called before dcp::init() as it won't recognise the
550 * tag. That's OK because the Config will be reloaded again later.
552 _custom_languages.push_back (dcp::LanguageTag(i->content()));
553 } catch (std::runtime_error& e) {}
556 if (boost::filesystem::exists (_cinemas_file)) {
557 cxml::Document f ("Cinemas");
558 f.read_file (_cinemas_file);
562 if (boost::filesystem::exists (_dkdm_recipients_file)) {
563 cxml::Document f ("DKDMRecipients");
564 f.read_file (_dkdm_recipients_file);
565 read_dkdm_recipients (f);
569 if (have_existing ("config.xml")) {
571 /* We have a config file but it didn't load */
575 /* Make a new set of signing certificates and key */
576 _signer_chain = create_certificate_chain ();
577 /* And similar for decryption of KDMs */
578 _decryption_chain = create_certificate_chain ();
582 /** @return Singleton instance */
586 if (_instance == 0) {
587 _instance = new Config;
594 /** Write our configuration to disk */
596 Config::write () const
600 write_dkdm_recipients ();
604 Config::write_config () const
607 auto root = doc.create_root_node ("Config");
609 /* [XML] Version The version number of the configuration file format. */
610 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
611 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
612 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
613 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
614 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
615 if (_default_directory) {
616 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
617 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
619 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
620 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
621 by the batch converter to listen for job requests.
623 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
624 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
625 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
627 for (auto i: _servers) {
628 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
631 root->add_child("Server")->add_child_text (i);
634 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
635 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
637 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
638 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
639 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
640 /* [XML] TMSIP IP address of TMS. */
641 root->add_child("TMSIP")->add_child_text (_tms_ip);
642 /* [XML] TMSPath Path on the TMS to copy files to. */
643 root->add_child("TMSPath")->add_child_text (_tms_path);
644 /* [XML] TMSUser Username to log into the TMS with. */
645 root->add_child("TMSUser")->add_child_text (_tms_user);
646 /* [XML] TMSPassword Password to log into the TMS with. */
647 root->add_child("TMSPassword")->add_child_text (_tms_password);
649 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
650 root->add_child("Language")->add_child_text (_language.get());
652 if (_default_container) {
653 /* [XML:opt] DefaultContainer ID of default container
654 to use when creating new films (<code>185</code>,<code>239</code> or
657 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
659 if (_default_dcp_content_type) {
660 /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
661 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
662 <code>PSA</code> or <code>ADV</code>). */
663 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
665 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
666 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
667 /* [XML] DCPIssuer Issuer text to write into CPL files. */
668 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
669 /* [XML] DCPCreator Creator text to write into CPL files. */
670 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
671 /* [XML] Company name to write into MXF files. */
672 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
673 /* [XML] Product name to write into MXF files. */
674 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
675 /* [XML] Product version to write into MXF files. */
676 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
677 /* [XML] Comment to write into JPEG2000 data. */
678 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
679 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
680 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
682 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
683 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
684 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
685 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
686 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
687 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
688 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
689 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
690 if (_default_kdm_directory) {
691 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
692 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
694 /* [XML] MailServer Hostname of SMTP server to use. */
695 root->add_child("MailServer")->add_child_text (_mail_server);
696 /* [XML] MailPort Port number to use on SMTP server. */
697 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
698 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
699 switch (_mail_protocol) {
700 case EmailProtocol::AUTO:
701 root->add_child("MailProtocol")->add_child_text("Auto");
703 case EmailProtocol::PLAIN:
704 root->add_child("MailProtocol")->add_child_text("Plain");
706 case EmailProtocol::STARTTLS:
707 root->add_child("MailProtocol")->add_child_text("STARTTLS");
709 case EmailProtocol::SSL:
710 root->add_child("MailProtocol")->add_child_text("SSL");
713 /* [XML] MailUser Username to use on SMTP server. */
714 root->add_child("MailUser")->add_child_text (_mail_user);
715 /* [XML] MailPassword Password to use on SMTP server. */
716 root->add_child("MailPassword")->add_child_text (_mail_password);
718 /* [XML] KDMSubject Subject to use for KDM emails. */
719 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
720 /* [XML] KDMFrom From address to use for KDM emails. */
721 root->add_child("KDMFrom")->add_child_text (_kdm_from);
722 for (auto i: _kdm_cc) {
723 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
724 root->add_child("KDMCC")->add_child_text (i);
726 /* [XML] KDMBCC BCC address to use for KDM emails. */
727 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
728 /* [XML] KDMEmail Text of KDM email. */
729 root->add_child("KDMEmail")->add_child_text (_kdm_email);
731 /* [XML] NotificationSubject Subject to use for notification emails. */
732 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
733 /* [XML] NotificationFrom From address to use for notification emails. */
734 root->add_child("NotificationFrom")->add_child_text (_notification_from);
735 /* [XML] NotificationFrom To address to use for notification emails. */
736 root->add_child("NotificationTo")->add_child_text (_notification_to);
737 for (auto i: _notification_cc) {
738 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
739 root->add_child("NotificationCC")->add_child_text (i);
741 /* [XML] NotificationBCC BCC address to use for notification emails. */
742 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
743 /* [XML] NotificationEmail Text of notification email. */
744 root->add_child("NotificationEmail")->add_child_text (_notification_email);
746 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
747 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
748 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
749 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
751 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
752 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
753 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
754 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
755 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
756 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
757 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
758 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
759 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
760 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
761 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
762 related to the player, 1024 debug information related to audio analyses.
764 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
765 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
766 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
767 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
768 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
769 #ifdef DCPOMATIC_WINDOWS
770 if (_win32_console) {
771 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
772 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
773 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
775 root->add_child("Win32Console")->add_child_text ("1");
779 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
780 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
782 auto signer = root->add_child ("Signer");
783 DCPOMATIC_ASSERT (_signer_chain);
784 for (auto const& i: _signer_chain->unordered()) {
785 signer->add_child("Certificate")->add_child_text (i.certificate (true));
787 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
789 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
790 auto decryption = root->add_child ("Decryption");
791 DCPOMATIC_ASSERT (_decryption_chain);
792 for (auto const& i: _decryption_chain->unordered()) {
793 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
795 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
797 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
800 for (auto i: _history) {
801 root->add_child("History")->add_child_text (i.string ());
804 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
807 for (auto i: _player_history) {
808 root->add_child("PlayerHistory")->add_child_text (i.string ());
811 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
812 or <code><DKDM></code> tags.
814 /* [XML] DKDM A DKDM as XML */
815 _dkdms->as_xml (root);
817 /* [XML] CinemasFile Filename of cinemas list file. */
818 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
819 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
820 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
821 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
822 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
823 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
824 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
825 /* [XML] KDMFilenameFormat Format for KDM filenames. */
826 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
827 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
828 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
829 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
830 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
831 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
832 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
833 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
834 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
835 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
836 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
837 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
838 for (int i = 0; i < NAG_COUNT; ++i) {
839 xmlpp::Element* e = root->add_child ("Nagged");
840 e->set_attribute ("Id", raw_convert<string>(i));
841 e->add_child_text (_nagged[i] ? "1" : "0");
843 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
844 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
846 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
847 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
849 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
850 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
851 if (_last_player_load_directory) {
852 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
854 /* [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. */
855 if (_last_kdm_write_type) {
856 switch (_last_kdm_write_type.get()) {
858 root->add_child("LastKDMWriteType")->add_child_text("flat");
860 case KDM_WRITE_FOLDER:
861 root->add_child("LastKDMWriteType")->add_child_text("folder");
864 root->add_child("LastKDMWriteType")->add_child_text("zip");
868 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
869 if (_last_dkdm_write_type) {
870 switch (_last_dkdm_write_type.get()) {
871 case DKDM_WRITE_INTERNAL:
872 root->add_child("LastDKDMWriteType")->add_child_text("internal");
874 case DKDM_WRITE_FILE:
875 root->add_child("LastDKDMWriteType")->add_child_text("file");
879 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
880 frames to be held in memory at once.
882 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
884 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
885 if (_decode_reduction) {
886 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
889 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
890 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
892 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
893 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
894 xmlpp::Element* e = root->add_child ("Notification");
895 e->set_attribute ("Id", raw_convert<string>(i));
896 e->add_child_text (_notification[i] ? "1" : "0");
899 if (_barco_username) {
900 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
901 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
903 if (_barco_password) {
904 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
905 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
908 if (_christie_username) {
909 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
910 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
912 if (_christie_password) {
913 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
914 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
918 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
919 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
922 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
923 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
926 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
927 with controls on another monitor.
929 switch (_player_mode) {
930 case PLAYER_MODE_WINDOW:
931 root->add_child("PlayerMode")->add_child_text("window");
933 case PLAYER_MODE_FULL:
934 root->add_child("PlayerMode")->add_child_text("full");
936 case PLAYER_MODE_DUAL:
937 root->add_child("PlayerMode")->add_child_text("dual");
941 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
942 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
943 switch (_video_view_type) {
944 case VIDEO_VIEW_SIMPLE:
945 root->add_child("VideoViewType")->add_child_text("simple");
947 case VIDEO_VIEW_OPENGL:
948 root->add_child("VideoViewType")->add_child_text("opengl");
951 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
952 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
953 if (_player_activity_log_file) {
954 /* [XML] PlayerLogFile Filename to use for player activity logs (e.g starting, stopping, playlist loads) */
955 root->add_child("PlayerActivityLogFile")->add_child_text(_player_activity_log_file->string());
957 if (_player_debug_log_file) {
958 /* [XML] PlayerLogFile Filename to use for player debug logs */
959 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
961 if (_player_content_directory) {
962 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
963 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
965 if (_player_playlist_directory) {
966 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
967 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
969 if (_player_kdm_directory) {
970 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
971 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
973 if (_audio_mapping) {
974 _audio_mapping->as_xml (root->add_child("AudioMapping"));
976 for (auto const& i: _custom_languages) {
977 root->add_child("CustomLanguage")->add_child_text(i.to_string());
981 auto const s = doc.write_to_string_formatted ();
982 boost::filesystem::path tmp (string(config_file().string()).append(".tmp"));
983 auto f = fopen_boost (tmp, "w");
985 throw FileError (_("Could not open file for writing"), tmp);
987 checked_fwrite (s.c_str(), s.bytes(), f, tmp);
989 boost::filesystem::remove (config_file());
990 boost::filesystem::rename (tmp, config_file());
991 } catch (xmlpp::exception& e) {
992 string s = e.what ();
994 throw FileError (s, config_file());
1001 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1003 xmlpp::Document doc;
1004 auto root = doc.create_root_node (root_node);
1005 root->add_child("Version")->add_child_text(version);
1007 for (auto i: things) {
1008 i->as_xml (root->add_child(node));
1012 doc.write_to_file_formatted (file.string() + ".tmp");
1013 boost::filesystem::remove (file);
1014 boost::filesystem::rename (file.string() + ".tmp", file);
1015 } catch (xmlpp::exception& e) {
1016 string s = e.what ();
1018 throw FileError (s, file);
1024 Config::write_cinemas () const
1026 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1031 Config::write_dkdm_recipients () const
1033 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1037 boost::filesystem::path
1038 Config::default_directory_or (boost::filesystem::path a) const
1040 return directory_or (_default_directory, a);
1043 boost::filesystem::path
1044 Config::default_kdm_directory_or (boost::filesystem::path a) const
1046 return directory_or (_default_kdm_directory, a);
1049 boost::filesystem::path
1050 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1056 boost::system::error_code ec;
1057 auto const e = boost::filesystem::exists (*dir, ec);
1073 Config::changed (Property what)
1079 Config::set_kdm_email_to_default ()
1081 _kdm_subject = _("KDM delivery: $CPL_NAME");
1084 "Dear Projectionist\n\n"
1085 "Please find attached KDMs for $CPL_NAME.\n\n"
1086 "Cinema: $CINEMA_NAME\n"
1087 "Screen(s): $SCREENS\n\n"
1088 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1089 "Best regards,\nDCP-o-matic"
1094 Config::set_notification_email_to_default ()
1096 _notification_subject = _("DCP-o-matic notification");
1098 _notification_email = _(
1099 "$JOB_NAME: $JOB_STATUS"
1104 Config::reset_kdm_email ()
1106 set_kdm_email_to_default ();
1111 Config::reset_notification_email ()
1113 set_notification_email_to_default ();
1118 Config::set_cover_sheet_to_default ()
1123 "Format: $CONTAINER\n"
1125 "Audio Language: $AUDIO_LANGUAGE\n"
1126 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1133 Config::add_to_history (boost::filesystem::path p)
1135 add_to_history_internal (_history, p);
1138 /** Remove non-existant items from the history */
1140 Config::clean_history ()
1142 clean_history_internal (_history);
1146 Config::add_to_player_history (boost::filesystem::path p)
1148 add_to_history_internal (_player_history, p);
1151 /** Remove non-existant items from the player history */
1153 Config::clean_player_history ()
1155 clean_history_internal (_player_history);
1159 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1161 /* Remove existing instances of this path in the history */
1162 h.erase (remove (h.begin(), h.end(), p), h.end ());
1164 h.insert (h.begin (), p);
1165 if (h.size() > HISTORY_SIZE) {
1173 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1179 if (boost::filesystem::is_directory(i)) {
1183 /* We couldn't find out if it's a directory for some reason; just ignore it */
1189 Config::have_existing (string file)
1191 return boost::filesystem::exists (path (file, false));
1195 Config::read_cinemas (cxml::Document const & f)
1198 for (auto i: f.node_children("Cinema")) {
1199 /* Slightly grotty two-part construction of Cinema here so that we can use
1202 auto cinema = make_shared<Cinema>(i);
1203 cinema->read_screens (i);
1204 _cinemas.push_back (cinema);
1209 Config::set_cinemas_file (boost::filesystem::path file)
1211 if (file == _cinemas_file) {
1215 _cinemas_file = file;
1217 if (boost::filesystem::exists (_cinemas_file)) {
1218 /* Existing file; read it in */
1219 cxml::Document f ("Cinemas");
1220 f.read_file (_cinemas_file);
1229 Config::read_dkdm_recipients (cxml::Document const & f)
1231 _dkdm_recipients.clear ();
1232 for (auto i: f.node_children("DKDMRecipient")) {
1233 _dkdm_recipients.push_back (shared_ptr<DKDMRecipient>(new DKDMRecipient(i)));
1238 Config::set_dkdm_recipients_file (boost::filesystem::path file)
1240 if (file == _dkdm_recipients_file) {
1244 _dkdm_recipients_file = file;
1246 if (boost::filesystem::exists (_dkdm_recipients_file)) {
1247 /* Existing file; read it in */
1248 cxml::Document f ("DKDMRecipients");
1249 f.read_file (_dkdm_recipients_file);
1250 read_dkdm_recipients (f);
1258 Config::save_template (shared_ptr<const Film> film, string name) const
1260 film->write_template (template_path (name));
1264 Config::templates () const
1266 if (!boost::filesystem::exists (path ("templates"))) {
1271 for (auto const& i: boost::filesystem::directory_iterator(path("templates"))) {
1272 n.push_back (i.path().filename().string());
1278 Config::existing_template (string name) const
1280 return boost::filesystem::exists (template_path (name));
1283 boost::filesystem::path
1284 Config::template_path (string name) const
1286 return path("templates") / tidy_for_filename (name);
1290 Config::rename_template (string old_name, string new_name) const
1292 boost::filesystem::rename (template_path (old_name), template_path (new_name));
1296 Config::delete_template (string name) const
1298 boost::filesystem::remove (template_path (name));
1301 /** @return Path to the config.xml containing the actual settings, following a link if required */
1302 boost::filesystem::path
1303 Config::config_file ()
1305 cxml::Document f ("Config");
1306 auto main = path("config.xml", false);
1307 if (!boost::filesystem::exists (main)) {
1308 /* It doesn't exist, so there can't be any links; just return it */
1312 /* See if there's a link */
1315 auto link = f.optional_string_child("Link");
1319 } catch (xmlpp::exception& e) {
1320 /* There as a problem reading the main configuration file,
1321 so there can't be a link.
1329 Config::reset_cover_sheet ()
1331 set_cover_sheet_to_default ();
1336 Config::link (boost::filesystem::path new_file) const
1338 xmlpp::Document doc;
1339 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1341 doc.write_to_file_formatted(path("config.xml", true).string());
1342 } catch (xmlpp::exception& e) {
1343 string s = e.what ();
1345 throw FileError (s, path("config.xml"));
1350 Config::copy_and_link (boost::filesystem::path new_file) const
1353 boost::filesystem::copy_file (config_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1358 Config::have_write_permission () const
1360 auto f = fopen_boost (config_file(), "r+");
1369 /** @param output_channels Number of output channels in use.
1370 * @return Audio mapping for this output channel count (may be a default).
1373 Config::audio_mapping (int output_channels)
1375 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1376 /* Set up a default */
1377 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1378 if (output_channels == 2) {
1379 /* Special case for stereo output.
1380 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1381 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1383 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1384 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1385 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1386 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1387 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1388 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1389 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1390 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1393 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1394 _audio_mapping->set (i, i, 1);
1399 return *_audio_mapping;
1403 Config::set_audio_mapping (AudioMapping m)
1406 changed (AUDIO_MAPPING);
1410 Config::set_audio_mapping_to_default ()
1412 DCPOMATIC_ASSERT (_audio_mapping);
1413 auto const ch = _audio_mapping->output_channels ();
1414 _audio_mapping = boost::none;
1415 _audio_mapping = audio_mapping (ch);
1416 changed (AUDIO_MAPPING);
1421 Config::add_custom_language (dcp::LanguageTag tag)
1423 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1424 _custom_languages.push_back (tag);