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 = true;
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 _minimum_frame_size = 65536;
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 if (version && version.get() >= 2) {
312 _default_isdcf_metadata = ISDCFMetadata (f.node_child ("ISDCFMetadata"));
314 _default_isdcf_metadata = ISDCFMetadata (f.node_child ("DCIMetadata"));
317 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
318 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
319 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
320 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
321 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
323 /* Read any cinemas that are still lying around in the config file
324 * from an old version.
328 _mail_server = f.string_child ("MailServer");
329 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
332 /* Make sure this matches the code in write_config */
333 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
334 if (protocol == "Auto") {
335 _mail_protocol = EmailProtocol::AUTO;
336 } else if (protocol == "Plain") {
337 _mail_protocol = EmailProtocol::PLAIN;
338 } else if (protocol == "STARTTLS") {
339 _mail_protocol = EmailProtocol::STARTTLS;
340 } else if (protocol == "SSL") {
341 _mail_protocol = EmailProtocol::SSL;
345 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
346 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
348 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
349 _kdm_from = f.string_child ("KDMFrom");
350 for (auto i: f.node_children("KDMCC")) {
351 if (!i->content().empty()) {
352 _kdm_cc.push_back (i->content ());
355 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
356 _kdm_email = f.string_child ("KDMEmail");
358 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
359 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
360 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
361 for (auto i: f.node_children("NotificationCC")) {
362 if (!i->content().empty()) {
363 _notification_cc.push_back (i->content ());
366 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
367 if (f.optional_string_child("NotificationEmail")) {
368 _notification_email = f.string_child("NotificationEmail");
371 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
372 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
374 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
375 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
376 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
377 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
379 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
380 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
381 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
382 #ifdef DCPOMATIC_WINDOWS
383 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
386 for (auto i: f.node_children("History")) {
387 _history.push_back (i->content ());
390 for (auto i: f.node_children("PlayerHistory")) {
391 _player_history.push_back (i->content ());
394 auto signer = f.optional_node_child ("Signer");
396 auto c = make_shared<dcp::CertificateChain>();
397 /* Read the signing certificates and private key in from the config file */
398 for (auto i: signer->node_children ("Certificate")) {
399 c->add (dcp::Certificate (i->content ()));
401 c->set_key (signer->string_child ("PrivateKey"));
404 /* Make a new set of signing certificates and key */
405 _signer_chain = create_certificate_chain ();
408 auto decryption = f.optional_node_child ("Decryption");
410 auto c = make_shared<dcp::CertificateChain>();
411 for (auto i: decryption->node_children ("Certificate")) {
412 c->add (dcp::Certificate (i->content ()));
414 c->set_key (decryption->string_child ("PrivateKey"));
415 _decryption_chain = c;
417 _decryption_chain = create_certificate_chain ();
420 /* These must be done before we call Bad as that might set one
423 for (auto i: f.node_children("Nagged")) {
424 auto const id = i->number_attribute<int>("Id");
425 if (id >= 0 && id < NAG_COUNT) {
426 _nagged[id] = raw_convert<int>(i->content());
430 optional<BadReason> bad;
432 for (auto const& i: _signer_chain->unordered()) {
433 if (i.has_utf8_strings()) {
434 bad = BAD_SIGNER_UTF8_STRINGS;
438 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
439 bad = BAD_SIGNER_INCONSISTENT;
442 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
443 bad = BAD_DECRYPTION_INCONSISTENT;
447 auto const remake = Bad(*bad);
448 if (remake && *remake) {
450 case BAD_SIGNER_UTF8_STRINGS:
451 case BAD_SIGNER_INCONSISTENT:
452 _signer_chain = create_certificate_chain ();
454 case BAD_DECRYPTION_INCONSISTENT:
455 _decryption_chain = create_certificate_chain ();
461 if (f.optional_node_child("DKDMGroup")) {
462 /* New-style: all DKDMs in a group */
463 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
465 /* Old-style: one or more DKDM nodes */
466 _dkdms.reset (new DKDMGroup ("root"));
467 for (auto i: f.node_children("DKDM")) {
468 _dkdms->add (DKDMBase::read (i));
471 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or (path ("cinemas.xml").string ());
472 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or (path("dkdm_recipients.xml").string());
473 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
474 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
475 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
476 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
477 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
478 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
479 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
480 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
481 /* The variable was renamed but not the XML tag */
482 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
483 _sound_output = f.optional_string_child("PreviewSoundOutput");
484 if (f.optional_string_child("CoverSheet")) {
485 _cover_sheet = f.optional_string_child("CoverSheet").get();
487 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
488 if (f.optional_string_child("LastKDMWriteType")) {
489 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
490 _last_kdm_write_type = KDM_WRITE_FLAT;
491 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
492 _last_kdm_write_type = KDM_WRITE_FOLDER;
493 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
494 _last_kdm_write_type = KDM_WRITE_ZIP;
497 if (f.optional_string_child("LastDKDMWriteType")) {
498 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
499 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
500 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
501 _last_dkdm_write_type = DKDM_WRITE_FILE;
504 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
505 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
506 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
508 for (auto i: f.node_children("Notification")) {
509 int const id = i->number_attribute<int>("Id");
510 if (id >= 0 && id < NOTIFICATION_COUNT) {
511 _notification[id] = raw_convert<int>(i->content());
515 _barco_username = f.optional_string_child("BarcoUsername");
516 _barco_password = f.optional_string_child("BarcoPassword");
517 _christie_username = f.optional_string_child("ChristieUsername");
518 _christie_password = f.optional_string_child("ChristiePassword");
519 _gdc_username = f.optional_string_child("GDCUsername");
520 _gdc_password = f.optional_string_child("GDCPassword");
522 auto pm = f.optional_string_child("PlayerMode");
523 if (pm && *pm == "window") {
524 _player_mode = PLAYER_MODE_WINDOW;
525 } else if (pm && *pm == "full") {
526 _player_mode = PLAYER_MODE_FULL;
527 } else if (pm && *pm == "dual") {
528 _player_mode = PLAYER_MODE_DUAL;
531 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
532 auto vc = f.optional_string_child("VideoViewType");
533 if (vc && *vc == "opengl") {
534 _video_view_type = VIDEO_VIEW_OPENGL;
535 } else if (vc && *vc == "simple") {
536 _video_view_type = VIDEO_VIEW_SIMPLE;
538 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
539 /* PlayerLogFile is old name */
540 _player_activity_log_file = f.optional_string_child("PlayerLogFile");
541 if (!_player_activity_log_file) {
542 _player_activity_log_file = f.optional_string_child("PlayerActivityLogFile");
544 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
545 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
546 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
547 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
549 if (f.optional_node_child("AudioMapping")) {
550 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
553 _minimum_frame_size = f.optional_number_child<int>("MinimumFrameSize").get_value_or(65536);
555 if (boost::filesystem::exists (_cinemas_file)) {
556 cxml::Document f ("Cinemas");
557 f.read_file (_cinemas_file);
561 if (boost::filesystem::exists (_dkdm_recipients_file)) {
562 cxml::Document f ("DKDMRecipients");
563 f.read_file (_dkdm_recipients_file);
564 read_dkdm_recipients (f);
568 if (have_existing ("config.xml")) {
570 /* We have a config file but it didn't load */
574 /* Make a new set of signing certificates and key */
575 _signer_chain = create_certificate_chain ();
576 /* And similar for decryption of KDMs */
577 _decryption_chain = create_certificate_chain ();
581 /** @return Singleton instance */
585 if (_instance == 0) {
586 _instance = new Config;
593 /** Write our configuration to disk */
595 Config::write () const
599 write_dkdm_recipients ();
603 Config::write_config () const
606 auto root = doc.create_root_node ("Config");
608 /* [XML] Version The version number of the configuration file format. */
609 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
610 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
611 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
612 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
613 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
614 if (_default_directory) {
615 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
616 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
618 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
619 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
620 by the batch converter to listen for job requests.
622 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
623 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
624 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
626 for (auto i: _servers) {
627 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
630 root->add_child("Server")->add_child_text (i);
633 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
634 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
636 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
637 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
638 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
639 /* [XML] TMSIP IP address of TMS. */
640 root->add_child("TMSIP")->add_child_text (_tms_ip);
641 /* [XML] TMSPath Path on the TMS to copy files to. */
642 root->add_child("TMSPath")->add_child_text (_tms_path);
643 /* [XML] TMSUser Username to log into the TMS with. */
644 root->add_child("TMSUser")->add_child_text (_tms_user);
645 /* [XML] TMSPassword Password to log into the TMS with. */
646 root->add_child("TMSPassword")->add_child_text (_tms_password);
648 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
649 root->add_child("Language")->add_child_text (_language.get());
651 if (_default_container) {
652 /* [XML:opt] DefaultContainer ID of default container
653 to use when creating new films (<code>185</code>,<code>239</code> or
656 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
658 if (_default_dcp_content_type) {
659 /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
660 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
661 <code>PSA</code> or <code>ADV</code>). */
662 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
664 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
665 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
666 /* [XML] DCPIssuer Issuer text to write into CPL files. */
667 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
668 /* [XML] DCPCreator Creator text to write into CPL files. */
669 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
670 /* [XML] Company name to write into MXF files. */
671 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
672 /* [XML] Product name to write into MXF files. */
673 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
674 /* [XML] Product version to write into MXF files. */
675 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
676 /* [XML] Comment to write into JPEG2000 data. */
677 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
678 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
679 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
681 /* [XML] ISDCFMetadata Default ISDCF metadata to use for new films; child tags are <code><ContentVersion></code>,
682 <code><AudioLanguage></code>, <code><SubtitleLanguage></code>, <code><Territory></code>,
683 <code><Rating></code>, <code><Studio></code>, <code><Facility></code>, <code><TempVersion></code>,
684 <code><PreRelease></code>, <code><RedBand></code>, <code><Chain></code>, <code><TwoDVersionOFThreeD></code>,
685 <code><MasteredLuminance></code>.
687 _default_isdcf_metadata.as_xml (root->add_child ("ISDCFMetadata"));
689 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
690 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
691 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
692 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
693 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
694 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
695 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
696 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
697 if (_default_kdm_directory) {
698 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
699 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
701 /* [XML] MailServer Hostname of SMTP server to use. */
702 root->add_child("MailServer")->add_child_text (_mail_server);
703 /* [XML] MailPort Port number to use on SMTP server. */
704 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
705 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
706 switch (_mail_protocol) {
707 case EmailProtocol::AUTO:
708 root->add_child("MailProtocol")->add_child_text("Auto");
710 case EmailProtocol::PLAIN:
711 root->add_child("MailProtocol")->add_child_text("Plain");
713 case EmailProtocol::STARTTLS:
714 root->add_child("MailProtocol")->add_child_text("STARTTLS");
716 case EmailProtocol::SSL:
717 root->add_child("MailProtocol")->add_child_text("SSL");
720 /* [XML] MailUser Username to use on SMTP server. */
721 root->add_child("MailUser")->add_child_text (_mail_user);
722 /* [XML] MailPassword Password to use on SMTP server. */
723 root->add_child("MailPassword")->add_child_text (_mail_password);
725 /* [XML] KDMSubject Subject to use for KDM emails. */
726 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
727 /* [XML] KDMFrom From address to use for KDM emails. */
728 root->add_child("KDMFrom")->add_child_text (_kdm_from);
729 for (auto i: _kdm_cc) {
730 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
731 root->add_child("KDMCC")->add_child_text (i);
733 /* [XML] KDMBCC BCC address to use for KDM emails. */
734 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
735 /* [XML] KDMEmail Text of KDM email. */
736 root->add_child("KDMEmail")->add_child_text (_kdm_email);
738 /* [XML] NotificationSubject Subject to use for notification emails. */
739 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
740 /* [XML] NotificationFrom From address to use for notification emails. */
741 root->add_child("NotificationFrom")->add_child_text (_notification_from);
742 /* [XML] NotificationFrom To address to use for notification emails. */
743 root->add_child("NotificationTo")->add_child_text (_notification_to);
744 for (auto i: _notification_cc) {
745 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
746 root->add_child("NotificationCC")->add_child_text (i);
748 /* [XML] NotificationBCC BCC address to use for notification emails. */
749 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
750 /* [XML] NotificationEmail Text of notification email. */
751 root->add_child("NotificationEmail")->add_child_text (_notification_email);
753 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
754 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
755 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
756 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
758 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
759 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
760 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
761 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
762 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
763 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
764 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
765 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
766 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
767 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
768 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
769 related to the player, 1024 debug information related to audio analyses.
771 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
772 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
773 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
774 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
775 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
776 #ifdef DCPOMATIC_WINDOWS
777 if (_win32_console) {
778 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
779 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
780 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
782 root->add_child("Win32Console")->add_child_text ("1");
786 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
787 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
789 auto signer = root->add_child ("Signer");
790 DCPOMATIC_ASSERT (_signer_chain);
791 for (auto const& i: _signer_chain->unordered()) {
792 signer->add_child("Certificate")->add_child_text (i.certificate (true));
794 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
796 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
797 auto decryption = root->add_child ("Decryption");
798 DCPOMATIC_ASSERT (_decryption_chain);
799 for (auto const& i: _decryption_chain->unordered()) {
800 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
802 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
804 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
807 for (auto i: _history) {
808 root->add_child("History")->add_child_text (i.string ());
811 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
814 for (auto i: _player_history) {
815 root->add_child("PlayerHistory")->add_child_text (i.string ());
818 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
819 or <code><DKDM></code> tags.
821 /* [XML] DKDM A DKDM as XML */
822 _dkdms->as_xml (root);
824 /* [XML] CinemasFile Filename of cinemas list file. */
825 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
826 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
827 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
828 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
829 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
830 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
831 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
832 /* [XML] KDMFilenameFormat Format for KDM filenames. */
833 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
834 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
835 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
836 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
837 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
838 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
839 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
840 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
841 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
842 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
843 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
844 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
845 for (int i = 0; i < NAG_COUNT; ++i) {
846 xmlpp::Element* e = root->add_child ("Nagged");
847 e->set_attribute ("Id", raw_convert<string>(i));
848 e->add_child_text (_nagged[i] ? "1" : "0");
850 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
851 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
853 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
854 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
856 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
857 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
858 if (_last_player_load_directory) {
859 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
861 /* [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. */
862 if (_last_kdm_write_type) {
863 switch (_last_kdm_write_type.get()) {
865 root->add_child("LastKDMWriteType")->add_child_text("flat");
867 case KDM_WRITE_FOLDER:
868 root->add_child("LastKDMWriteType")->add_child_text("folder");
871 root->add_child("LastKDMWriteType")->add_child_text("zip");
875 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
876 if (_last_dkdm_write_type) {
877 switch (_last_dkdm_write_type.get()) {
878 case DKDM_WRITE_INTERNAL:
879 root->add_child("LastDKDMWriteType")->add_child_text("internal");
881 case DKDM_WRITE_FILE:
882 root->add_child("LastDKDMWriteType")->add_child_text("file");
886 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
887 frames to be held in memory at once.
889 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
891 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
892 if (_decode_reduction) {
893 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
896 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
897 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
899 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
900 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
901 xmlpp::Element* e = root->add_child ("Notification");
902 e->set_attribute ("Id", raw_convert<string>(i));
903 e->add_child_text (_notification[i] ? "1" : "0");
906 if (_barco_username) {
907 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
908 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
910 if (_barco_password) {
911 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
912 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
915 if (_christie_username) {
916 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
917 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
919 if (_christie_password) {
920 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
921 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
925 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
926 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
929 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
930 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
933 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
934 with controls on another monitor.
936 switch (_player_mode) {
937 case PLAYER_MODE_WINDOW:
938 root->add_child("PlayerMode")->add_child_text("window");
940 case PLAYER_MODE_FULL:
941 root->add_child("PlayerMode")->add_child_text("full");
943 case PLAYER_MODE_DUAL:
944 root->add_child("PlayerMode")->add_child_text("dual");
948 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
949 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
950 switch (_video_view_type) {
951 case VIDEO_VIEW_SIMPLE:
952 root->add_child("VideoViewType")->add_child_text("simple");
954 case VIDEO_VIEW_OPENGL:
955 root->add_child("VideoViewType")->add_child_text("opengl");
958 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
959 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
960 if (_player_activity_log_file) {
961 /* [XML] PlayerLogFile Filename to use for player activity logs (e.g starting, stopping, playlist loads) */
962 root->add_child("PlayerActivityLogFile")->add_child_text(_player_activity_log_file->string());
964 if (_player_debug_log_file) {
965 /* [XML] PlayerLogFile Filename to use for player debug logs */
966 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
968 if (_player_content_directory) {
969 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
970 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
972 if (_player_playlist_directory) {
973 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
974 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
976 if (_player_kdm_directory) {
977 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
978 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
980 if (_audio_mapping) {
981 _audio_mapping->as_xml (root->add_child("AudioMapping"));
983 root->add_child("MinimumFrameSize")->add_child_text(raw_convert<string>(_minimum_frame_size));
986 auto const s = doc.write_to_string_formatted ();
987 boost::filesystem::path tmp (string(config_file().string()).append(".tmp"));
988 auto f = fopen_boost (tmp, "w");
990 throw FileError (_("Could not open file for writing"), tmp);
992 checked_fwrite (s.c_str(), s.bytes(), f, tmp);
994 boost::filesystem::remove (config_file());
995 boost::filesystem::rename (tmp, config_file());
996 } catch (xmlpp::exception& e) {
997 string s = e.what ();
999 throw FileError (s, config_file());
1006 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1008 xmlpp::Document doc;
1009 auto root = doc.create_root_node (root_node);
1010 root->add_child("Version")->add_child_text(version);
1012 for (auto i: things) {
1013 i->as_xml (root->add_child(node));
1017 doc.write_to_file_formatted (file.string() + ".tmp");
1018 boost::filesystem::remove (file);
1019 boost::filesystem::rename (file.string() + ".tmp", file);
1020 } catch (xmlpp::exception& e) {
1021 string s = e.what ();
1023 throw FileError (s, file);
1029 Config::write_cinemas () const
1031 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1036 Config::write_dkdm_recipients () const
1038 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1042 boost::filesystem::path
1043 Config::default_directory_or (boost::filesystem::path a) const
1045 return directory_or (_default_directory, a);
1048 boost::filesystem::path
1049 Config::default_kdm_directory_or (boost::filesystem::path a) const
1051 return directory_or (_default_kdm_directory, a);
1054 boost::filesystem::path
1055 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1061 boost::system::error_code ec;
1062 auto const e = boost::filesystem::exists (*dir, ec);
1078 Config::changed (Property what)
1084 Config::set_kdm_email_to_default ()
1086 _kdm_subject = _("KDM delivery: $CPL_NAME");
1089 "Dear Projectionist\n\n"
1090 "Please find attached KDMs for $CPL_NAME.\n\n"
1091 "Cinema: $CINEMA_NAME\n"
1092 "Screen(s): $SCREENS\n\n"
1093 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1094 "Best regards,\nDCP-o-matic"
1099 Config::set_notification_email_to_default ()
1101 _notification_subject = _("DCP-o-matic notification");
1103 _notification_email = _(
1104 "$JOB_NAME: $JOB_STATUS"
1109 Config::reset_kdm_email ()
1111 set_kdm_email_to_default ();
1116 Config::reset_notification_email ()
1118 set_notification_email_to_default ();
1123 Config::set_cover_sheet_to_default ()
1128 "Format: $CONTAINER\n"
1130 "Audio Language: $AUDIO_LANGUAGE\n"
1131 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1138 Config::add_to_history (boost::filesystem::path p)
1140 add_to_history_internal (_history, p);
1143 /** Remove non-existant items from the history */
1145 Config::clean_history ()
1147 clean_history_internal (_history);
1151 Config::add_to_player_history (boost::filesystem::path p)
1153 add_to_history_internal (_player_history, p);
1156 /** Remove non-existant items from the player history */
1158 Config::clean_player_history ()
1160 clean_history_internal (_player_history);
1164 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1166 /* Remove existing instances of this path in the history */
1167 h.erase (remove (h.begin(), h.end(), p), h.end ());
1169 h.insert (h.begin (), p);
1170 if (h.size() > HISTORY_SIZE) {
1178 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1184 if (boost::filesystem::is_directory(i)) {
1188 /* We couldn't find out if it's a directory for some reason; just ignore it */
1194 Config::have_existing (string file)
1196 return boost::filesystem::exists (path (file, false));
1200 Config::read_cinemas (cxml::Document const & f)
1203 for (auto i: f.node_children("Cinema")) {
1204 /* Slightly grotty two-part construction of Cinema here so that we can use
1207 auto cinema = make_shared<Cinema>(i);
1208 cinema->read_screens (i);
1209 _cinemas.push_back (cinema);
1214 Config::set_cinemas_file (boost::filesystem::path file)
1216 if (file == _cinemas_file) {
1220 _cinemas_file = file;
1222 if (boost::filesystem::exists (_cinemas_file)) {
1223 /* Existing file; read it in */
1224 cxml::Document f ("Cinemas");
1225 f.read_file (_cinemas_file);
1234 Config::read_dkdm_recipients (cxml::Document const & f)
1236 _dkdm_recipients.clear ();
1237 for (auto i: f.node_children("DKDMRecipient")) {
1238 _dkdm_recipients.push_back (shared_ptr<DKDMRecipient>(new DKDMRecipient(i)));
1243 Config::set_dkdm_recipients_file (boost::filesystem::path file)
1245 if (file == _dkdm_recipients_file) {
1249 _dkdm_recipients_file = file;
1251 if (boost::filesystem::exists (_dkdm_recipients_file)) {
1252 /* Existing file; read it in */
1253 cxml::Document f ("DKDMRecipients");
1254 f.read_file (_dkdm_recipients_file);
1255 read_dkdm_recipients (f);
1263 Config::save_template (shared_ptr<const Film> film, string name) const
1265 film->write_template (template_path (name));
1269 Config::templates () const
1271 if (!boost::filesystem::exists (path ("templates"))) {
1276 for (auto const& i: boost::filesystem::directory_iterator(path("templates"))) {
1277 n.push_back (i.path().filename().string());
1283 Config::existing_template (string name) const
1285 return boost::filesystem::exists (template_path (name));
1288 boost::filesystem::path
1289 Config::template_path (string name) const
1291 return path("templates") / tidy_for_filename (name);
1295 Config::rename_template (string old_name, string new_name) const
1297 boost::filesystem::rename (template_path (old_name), template_path (new_name));
1301 Config::delete_template (string name) const
1303 boost::filesystem::remove (template_path (name));
1306 /** @return Path to the config.xml containing the actual settings, following a link if required */
1307 boost::filesystem::path
1308 Config::config_file ()
1310 cxml::Document f ("Config");
1311 auto main = path("config.xml", false);
1312 if (!boost::filesystem::exists (main)) {
1313 /* It doesn't exist, so there can't be any links; just return it */
1317 /* See if there's a link */
1320 auto link = f.optional_string_child("Link");
1324 } catch (xmlpp::exception& e) {
1325 /* There as a problem reading the main configuration file,
1326 so there can't be a link.
1334 Config::reset_cover_sheet ()
1336 set_cover_sheet_to_default ();
1341 Config::link (boost::filesystem::path new_file) const
1343 xmlpp::Document doc;
1344 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1346 doc.write_to_file_formatted(path("config.xml", true).string());
1347 } catch (xmlpp::exception& e) {
1348 string s = e.what ();
1350 throw FileError (s, path("config.xml"));
1355 Config::copy_and_link (boost::filesystem::path new_file) const
1358 boost::filesystem::copy_file (config_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1363 Config::have_write_permission () const
1365 auto f = fopen_boost (config_file(), "r+");
1374 /** @param output_channels Number of output channels in use.
1375 * @return Audio mapping for this output channel count (may be a default).
1378 Config::audio_mapping (int output_channels)
1380 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1381 /* Set up a default */
1382 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1383 if (output_channels == 2) {
1384 /* Special case for stereo output.
1385 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1386 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1388 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1389 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1390 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1391 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1392 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1393 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1394 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1395 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1398 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1399 _audio_mapping->set (i, i, 1);
1404 return *_audio_mapping;
1408 Config::set_audio_mapping (AudioMapping m)
1411 changed (AUDIO_MAPPING);
1415 Config::set_audio_mapping_to_default ()
1417 DCPOMATIC_ASSERT (_audio_mapping);
1418 auto const ch = _audio_mapping->output_channels ();
1419 _audio_mapping = boost::none;
1420 _audio_mapping = audio_mapping (ch);
1421 changed (AUDIO_MAPPING);