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] DCPIssuer 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 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0. */
778 root->add_child("Win32Console")->add_child_text (_win32_console ? "1" : "0");
781 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
782 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
784 auto signer = root->add_child ("Signer");
785 DCPOMATIC_ASSERT (_signer_chain);
786 for (auto const& i: _signer_chain->unordered()) {
787 signer->add_child("Certificate")->add_child_text (i.certificate (true));
789 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
791 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
792 auto decryption = root->add_child ("Decryption");
793 DCPOMATIC_ASSERT (_decryption_chain);
794 for (auto const& i: _decryption_chain->unordered()) {
795 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
797 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
799 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
802 for (auto i: _history) {
803 root->add_child("History")->add_child_text (i.string ());
806 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
809 for (auto i: _player_history) {
810 root->add_child("PlayerHistory")->add_child_text (i.string ());
813 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
814 or <code><DKDM></code> tags.
816 /* [XML] DKDM A DKDM as XML */
817 _dkdms->as_xml (root);
819 /* [XML] CinemasFile Filename of cinemas list file. */
820 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
821 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
822 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
823 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
824 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
825 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
826 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
827 /* [XML] KDMFilenameFormat Format for KDM filenames. */
828 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
829 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
830 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
831 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
832 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
833 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
834 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
835 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
836 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
837 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
838 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
839 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
840 for (int i = 0; i < NAG_COUNT; ++i) {
841 xmlpp::Element* e = root->add_child ("Nagged");
842 e->set_attribute ("Id", raw_convert<string>(i));
843 e->add_child_text (_nagged[i] ? "1" : "0");
845 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
846 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
848 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
849 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
851 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
852 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
853 if (_last_player_load_directory) {
854 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
856 /* [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. */
857 if (_last_kdm_write_type) {
858 switch (_last_kdm_write_type.get()) {
860 root->add_child("LastKDMWriteType")->add_child_text("flat");
862 case KDM_WRITE_FOLDER:
863 root->add_child("LastKDMWriteType")->add_child_text("folder");
866 root->add_child("LastKDMWriteType")->add_child_text("zip");
870 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
871 if (_last_dkdm_write_type) {
872 switch (_last_dkdm_write_type.get()) {
873 case DKDM_WRITE_INTERNAL:
874 root->add_child("LastDKDMWriteType")->add_child_text("internal");
876 case DKDM_WRITE_FILE:
877 root->add_child("LastDKDMWriteType")->add_child_text("file");
881 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
882 frames to be held in memory at once.
884 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
886 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
887 if (_decode_reduction) {
888 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
891 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
892 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
894 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
895 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
896 xmlpp::Element* e = root->add_child ("Notification");
897 e->set_attribute ("Id", raw_convert<string>(i));
898 e->add_child_text (_notification[i] ? "1" : "0");
901 if (_barco_username) {
902 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
903 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
905 if (_barco_password) {
906 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
907 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
910 if (_christie_username) {
911 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
912 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
914 if (_christie_password) {
915 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
916 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
920 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
921 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
924 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
925 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
928 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
929 with controls on another monitor.
931 switch (_player_mode) {
932 case PLAYER_MODE_WINDOW:
933 root->add_child("PlayerMode")->add_child_text("window");
935 case PLAYER_MODE_FULL:
936 root->add_child("PlayerMode")->add_child_text("full");
938 case PLAYER_MODE_DUAL:
939 root->add_child("PlayerMode")->add_child_text("dual");
943 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
944 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
945 switch (_video_view_type) {
946 case VIDEO_VIEW_SIMPLE:
947 root->add_child("VideoViewType")->add_child_text("simple");
949 case VIDEO_VIEW_OPENGL:
950 root->add_child("VideoViewType")->add_child_text("opengl");
953 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
954 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
955 if (_player_activity_log_file) {
956 /* [XML] PlayerLogFile Filename to use for player activity logs (e.g starting, stopping, playlist loads) */
957 root->add_child("PlayerActivityLogFile")->add_child_text(_player_activity_log_file->string());
959 if (_player_debug_log_file) {
960 /* [XML] PlayerLogFile Filename to use for player debug logs */
961 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
963 if (_player_content_directory) {
964 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
965 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
967 if (_player_playlist_directory) {
968 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
969 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
971 if (_player_kdm_directory) {
972 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
973 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
975 if (_audio_mapping) {
976 _audio_mapping->as_xml (root->add_child("AudioMapping"));
978 root->add_child("MinimumFrameSize")->add_child_text(raw_convert<string>(_minimum_frame_size));
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.length(), 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);