2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
26 #include "dcp_content_type.h"
27 #include "colour_conversion.h"
32 #include "dkdm_wrapper.h"
33 #include "compose.hpp"
35 #include "dkdm_recipient.h"
36 #include <dcp/raw_convert.h>
37 #include <dcp/name_format.h>
38 #include <dcp/certificate_chain.h>
39 #include <libcxml/cxml.h>
41 #include <libxml++/libxml++.h>
42 #include <boost/filesystem.hpp>
43 #include <boost/algorithm/string.hpp>
44 #include <boost/thread.hpp>
59 using std::shared_ptr;
60 using std::make_shared;
61 using boost::optional;
62 using std::dynamic_pointer_cast;
63 using boost::algorithm::trim;
64 using dcp::raw_convert;
66 Config* Config::_instance = 0;
67 int const Config::_current_version = 3;
68 boost::signals2::signal<void ()> Config::FailedToLoad;
69 boost::signals2::signal<void (string)> Config::Warning;
70 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
72 /** Construct default configuration */
74 /* DKDMs are not considered a thing to reset on set_defaults() */
75 : _dkdms (new DKDMGroup ("root"))
81 Config::set_defaults ()
83 _encoding_backend = EncodingBackend::CPU;
84 _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
85 _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
86 _server_port_base = 6192;
87 _use_any_servers = true;
89 _only_servers_encode = false;
90 _tms_protocol = FileTransferProtocol::SCP;
95 _allow_any_dcp_frame_rate = false;
96 _allow_any_container = false;
97 _show_experimental_audio_processors = false;
98 _language = optional<string> ();
99 _default_still_length = 10;
100 _default_container = Ratio::from_id ("185");
101 _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
102 _default_dcp_audio_channels = 6;
103 _default_j2k_bandwidth = 150000000;
104 _default_audio_delay = 0;
105 _default_interop = false;
106 _default_metadata.clear ();
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 = read_path ("cinemas.xml");
130 _dkdm_recipients_file = read_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_debug_log_file = boost::none;
173 _player_content_directory = boost::none;
174 _player_playlist_directory = boost::none;
175 _player_kdm_directory = boost::none;
176 _audio_mapping = boost::none;
177 _custom_languages.clear ();
178 _add_files_path = boost::none;
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(write_path(String::compose("config.xml.%1", n)))) {
223 boost::filesystem::copy_file(read_path("config.xml"), write_path(String::compose("config.xml.%1", n)));
224 boost::filesystem::copy_file(read_path("cinemas.xml"), write_path(String::compose("cinemas.xml.%1", n)));
225 boost::filesystem::copy_file(read_path("dkdm_recipients.xml"), write_path(String::compose("dkdm_recipients.xml.%1", n)));
233 cxml::Document f ("Config");
234 f.read_file (config_read_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 (auto encoding_backend = f.optional_string_child("EncodingBackend")) {
243 _encoding_backend = *encoding_backend == "cpu" ? EncodingBackend::CPU : EncodingBackend::FASTVIDEO;
246 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
247 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
249 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
250 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
253 _default_directory = f.optional_string_child ("DefaultDirectory");
254 if (_default_directory && _default_directory->empty ()) {
255 /* We used to store an empty value for this to mean "none set" */
256 _default_directory = boost::optional<boost::filesystem::path> ();
259 auto b = f.optional_number_child<int> ("ServerPort");
261 b = f.optional_number_child<int> ("ServerPortBase");
263 _server_port_base = b.get ();
265 auto u = f.optional_bool_child ("UseAnyServers");
266 _use_any_servers = u.get_value_or (true);
268 for (auto i: f.node_children("Server")) {
269 if (i->node_children("HostName").size() == 1) {
270 _servers.push_back (i->string_child ("HostName"));
272 _servers.push_back (i->content ());
276 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
277 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
278 _tms_ip = f.string_child ("TMSIP");
279 _tms_path = f.string_child ("TMSPath");
280 _tms_user = f.string_child ("TMSUser");
281 _tms_password = f.string_child ("TMSPassword");
283 _language = f.optional_string_child ("Language");
285 auto c = f.optional_string_child ("DefaultContainer");
287 _default_container = Ratio::from_id (c.get ());
290 if (_default_container && !_default_container->used_for_container()) {
291 Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)"));
292 _default_container = Ratio::from_id ("185");
295 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
296 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
298 if (f.optional_string_child ("DCPMetadataIssuer")) {
299 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
300 } else if (f.optional_string_child ("DCPIssuer")) {
301 _dcp_issuer = f.string_child ("DCPIssuer");
304 auto up = f.optional_bool_child("UploadAfterMakeDCP");
306 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
308 _upload_after_make_dcp = up.get_value_or (false);
309 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
310 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
311 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
312 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
313 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
315 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
316 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
317 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
318 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
320 for (auto const& i: f.node_children("DefaultMetadata")) {
321 _default_metadata[i->string_attribute("key")] = i->content();
324 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
326 /* Read any cinemas that are still lying around in the config file
327 * from an old version.
331 _mail_server = f.string_child ("MailServer");
332 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
335 /* Make sure this matches the code in write_config */
336 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
337 if (protocol == "Auto") {
338 _mail_protocol = EmailProtocol::AUTO;
339 } else if (protocol == "Plain") {
340 _mail_protocol = EmailProtocol::PLAIN;
341 } else if (protocol == "STARTTLS") {
342 _mail_protocol = EmailProtocol::STARTTLS;
343 } else if (protocol == "SSL") {
344 _mail_protocol = EmailProtocol::SSL;
348 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
349 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
351 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
352 _kdm_from = f.string_child ("KDMFrom");
353 for (auto i: f.node_children("KDMCC")) {
354 if (!i->content().empty()) {
355 _kdm_cc.push_back (i->content ());
358 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
359 _kdm_email = f.string_child ("KDMEmail");
361 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
362 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
363 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
364 for (auto i: f.node_children("NotificationCC")) {
365 if (!i->content().empty()) {
366 _notification_cc.push_back (i->content ());
369 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
370 if (f.optional_string_child("NotificationEmail")) {
371 _notification_email = f.string_child("NotificationEmail");
374 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
375 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
377 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
378 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
379 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
380 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
382 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
383 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
384 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
385 #ifdef DCPOMATIC_WINDOWS
386 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
389 for (auto i: f.node_children("History")) {
390 _history.push_back (i->content ());
393 for (auto i: f.node_children("PlayerHistory")) {
394 _player_history.push_back (i->content ());
397 auto signer = f.optional_node_child ("Signer");
399 auto c = make_shared<dcp::CertificateChain>();
400 /* Read the signing certificates and private key in from the config file */
401 for (auto i: signer->node_children ("Certificate")) {
402 c->add (dcp::Certificate (i->content ()));
404 c->set_key (signer->string_child ("PrivateKey"));
407 /* Make a new set of signing certificates and key */
408 _signer_chain = create_certificate_chain ();
411 auto decryption = f.optional_node_child ("Decryption");
413 auto c = make_shared<dcp::CertificateChain>();
414 for (auto i: decryption->node_children ("Certificate")) {
415 c->add (dcp::Certificate (i->content ()));
417 c->set_key (decryption->string_child ("PrivateKey"));
418 _decryption_chain = c;
420 _decryption_chain = create_certificate_chain ();
423 /* These must be done before we call Bad as that might set one
426 for (auto i: f.node_children("Nagged")) {
427 auto const id = i->number_attribute<int>("Id");
428 if (id >= 0 && id < NAG_COUNT) {
429 _nagged[id] = raw_convert<int>(i->content());
433 optional<BadReason> bad;
435 for (auto const& i: _signer_chain->unordered()) {
436 if (i.has_utf8_strings()) {
437 bad = BAD_SIGNER_UTF8_STRINGS;
441 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
442 bad = BAD_SIGNER_INCONSISTENT;
445 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
446 bad = BAD_DECRYPTION_INCONSISTENT;
450 auto const remake = Bad(*bad);
451 if (remake && *remake) {
453 case BAD_SIGNER_UTF8_STRINGS:
454 case BAD_SIGNER_INCONSISTENT:
455 _signer_chain = create_certificate_chain ();
457 case BAD_DECRYPTION_INCONSISTENT:
458 _decryption_chain = create_certificate_chain ();
464 if (f.optional_node_child("DKDMGroup")) {
465 /* New-style: all DKDMs in a group */
466 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
468 /* Old-style: one or more DKDM nodes */
469 _dkdms.reset (new DKDMGroup ("root"));
470 for (auto i: f.node_children("DKDM")) {
471 _dkdms->add (DKDMBase::read (i));
474 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
475 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
476 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
477 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
478 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
479 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
480 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
481 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
482 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
483 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
484 /* The variable was renamed but not the XML tag */
485 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
486 _sound_output = f.optional_string_child("PreviewSoundOutput");
487 if (f.optional_string_child("CoverSheet")) {
488 _cover_sheet = f.optional_string_child("CoverSheet").get();
490 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
491 if (f.optional_string_child("LastKDMWriteType")) {
492 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
493 _last_kdm_write_type = KDM_WRITE_FLAT;
494 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
495 _last_kdm_write_type = KDM_WRITE_FOLDER;
496 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
497 _last_kdm_write_type = KDM_WRITE_ZIP;
500 if (f.optional_string_child("LastDKDMWriteType")) {
501 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
502 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
503 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
504 _last_dkdm_write_type = DKDM_WRITE_FILE;
507 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
508 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
509 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
511 for (auto i: f.node_children("Notification")) {
512 int const id = i->number_attribute<int>("Id");
513 if (id >= 0 && id < NOTIFICATION_COUNT) {
514 _notification[id] = raw_convert<int>(i->content());
518 _barco_username = f.optional_string_child("BarcoUsername");
519 _barco_password = f.optional_string_child("BarcoPassword");
520 _christie_username = f.optional_string_child("ChristieUsername");
521 _christie_password = f.optional_string_child("ChristiePassword");
522 _gdc_username = f.optional_string_child("GDCUsername");
523 _gdc_password = f.optional_string_child("GDCPassword");
525 auto pm = f.optional_string_child("PlayerMode");
526 if (pm && *pm == "window") {
527 _player_mode = PLAYER_MODE_WINDOW;
528 } else if (pm && *pm == "full") {
529 _player_mode = PLAYER_MODE_FULL;
530 } else if (pm && *pm == "dual") {
531 _player_mode = PLAYER_MODE_DUAL;
534 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
535 auto vc = f.optional_string_child("VideoViewType");
536 if (vc && *vc == "opengl") {
537 _video_view_type = VIDEO_VIEW_OPENGL;
538 } else if (vc && *vc == "simple") {
539 _video_view_type = VIDEO_VIEW_SIMPLE;
541 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
542 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
543 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
544 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
545 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
547 if (f.optional_node_child("AudioMapping")) {
548 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
551 for (auto i: f.node_children("CustomLanguage")) {
553 /* This will fail if it's called before dcp::init() as it won't recognise the
554 * tag. That's OK because the Config will be reloaded again later.
556 _custom_languages.push_back (dcp::LanguageTag(i->content()));
557 } catch (std::runtime_error& e) {}
560 _add_files_path = f.optional_string_child("AddFilesPath");
562 if (boost::filesystem::exists (_cinemas_file)) {
563 cxml::Document f ("Cinemas");
564 f.read_file (_cinemas_file);
568 if (boost::filesystem::exists (_dkdm_recipients_file)) {
569 cxml::Document f ("DKDMRecipients");
570 f.read_file (_dkdm_recipients_file);
571 read_dkdm_recipients (f);
575 if (have_existing ("config.xml")) {
577 /* We have a config file but it didn't load */
581 /* Make a new set of signing certificates and key */
582 _signer_chain = create_certificate_chain ();
583 /* And similar for decryption of KDMs */
584 _decryption_chain = create_certificate_chain ();
588 /** @return Singleton instance */
592 if (_instance == 0) {
593 _instance = new Config;
600 /** Write our configuration to disk */
602 Config::write () const
606 write_dkdm_recipients ();
610 Config::write_config () const
613 auto root = doc.create_root_node ("Config");
615 /* [XML] Version The version number of the configuration file format. */
616 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
617 root->add_child("EncodingBackend")->add_child_text(_encoding_backend == EncodingBackend::CPU ? "cpu" : "fastvideo");
618 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
619 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
620 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
621 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
622 if (_default_directory) {
623 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
624 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
626 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
627 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
628 by the batch converter to listen for job requests.
630 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
631 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
632 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
634 for (auto i: _servers) {
635 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
638 root->add_child("Server")->add_child_text (i);
641 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
642 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
644 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
645 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
646 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
647 /* [XML] TMSIP IP address of TMS. */
648 root->add_child("TMSIP")->add_child_text (_tms_ip);
649 /* [XML] TMSPath Path on the TMS to copy files to. */
650 root->add_child("TMSPath")->add_child_text (_tms_path);
651 /* [XML] TMSUser Username to log into the TMS with. */
652 root->add_child("TMSUser")->add_child_text (_tms_user);
653 /* [XML] TMSPassword Password to log into the TMS with. */
654 root->add_child("TMSPassword")->add_child_text (_tms_password);
656 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
657 root->add_child("Language")->add_child_text (_language.get());
659 if (_default_container) {
660 /* [XML:opt] DefaultContainer ID of default container
661 to use when creating new films (<code>185</code>,<code>239</code> or
664 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
666 if (_default_dcp_content_type) {
667 /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
668 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
669 <code>PSA</code> or <code>ADV</code>). */
670 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
672 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
673 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
674 /* [XML] DCPIssuer Issuer text to write into CPL files. */
675 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
676 /* [XML] DCPCreator Creator text to write into CPL files. */
677 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
678 /* [XML] Company name to write into MXF files. */
679 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
680 /* [XML] Product name to write into MXF files. */
681 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
682 /* [XML] Product version to write into MXF files. */
683 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
684 /* [XML] Comment to write into JPEG2000 data. */
685 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
686 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
687 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
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 for (auto const& i: _default_metadata) {
698 auto c = root->add_child("DefaultMetadata");
699 c->set_attribute("key", i.first);
700 c->add_child_text(i.second);
702 if (_default_kdm_directory) {
703 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
704 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
706 /* [XML] MailServer Hostname of SMTP server to use. */
707 root->add_child("MailServer")->add_child_text (_mail_server);
708 /* [XML] MailPort Port number to use on SMTP server. */
709 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
710 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
711 switch (_mail_protocol) {
712 case EmailProtocol::AUTO:
713 root->add_child("MailProtocol")->add_child_text("Auto");
715 case EmailProtocol::PLAIN:
716 root->add_child("MailProtocol")->add_child_text("Plain");
718 case EmailProtocol::STARTTLS:
719 root->add_child("MailProtocol")->add_child_text("STARTTLS");
721 case EmailProtocol::SSL:
722 root->add_child("MailProtocol")->add_child_text("SSL");
725 /* [XML] MailUser Username to use on SMTP server. */
726 root->add_child("MailUser")->add_child_text (_mail_user);
727 /* [XML] MailPassword Password to use on SMTP server. */
728 root->add_child("MailPassword")->add_child_text (_mail_password);
730 /* [XML] KDMSubject Subject to use for KDM emails. */
731 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
732 /* [XML] KDMFrom From address to use for KDM emails. */
733 root->add_child("KDMFrom")->add_child_text (_kdm_from);
734 for (auto i: _kdm_cc) {
735 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
736 root->add_child("KDMCC")->add_child_text (i);
738 /* [XML] KDMBCC BCC address to use for KDM emails. */
739 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
740 /* [XML] KDMEmail Text of KDM email. */
741 root->add_child("KDMEmail")->add_child_text (_kdm_email);
743 /* [XML] NotificationSubject Subject to use for notification emails. */
744 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
745 /* [XML] NotificationFrom From address to use for notification emails. */
746 root->add_child("NotificationFrom")->add_child_text (_notification_from);
747 /* [XML] NotificationFrom To address to use for notification emails. */
748 root->add_child("NotificationTo")->add_child_text (_notification_to);
749 for (auto i: _notification_cc) {
750 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
751 root->add_child("NotificationCC")->add_child_text (i);
753 /* [XML] NotificationBCC BCC address to use for notification emails. */
754 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
755 /* [XML] NotificationEmail Text of notification email. */
756 root->add_child("NotificationEmail")->add_child_text (_notification_email);
758 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
759 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
760 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
761 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
763 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
764 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
765 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
766 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
767 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
768 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
769 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
770 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
771 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
772 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
773 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
774 related to the player, 1024 debug information related to audio analyses.
776 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
777 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
778 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
779 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
780 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
781 #ifdef DCPOMATIC_WINDOWS
782 if (_win32_console) {
783 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
784 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
785 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
787 root->add_child("Win32Console")->add_child_text ("1");
791 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
792 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
794 auto signer = root->add_child ("Signer");
795 DCPOMATIC_ASSERT (_signer_chain);
796 for (auto const& i: _signer_chain->unordered()) {
797 signer->add_child("Certificate")->add_child_text (i.certificate (true));
799 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
801 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
802 auto decryption = root->add_child ("Decryption");
803 DCPOMATIC_ASSERT (_decryption_chain);
804 for (auto const& i: _decryption_chain->unordered()) {
805 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
807 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
809 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
812 for (auto i: _history) {
813 root->add_child("History")->add_child_text (i.string ());
816 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
819 for (auto i: _player_history) {
820 root->add_child("PlayerHistory")->add_child_text (i.string ());
823 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
824 or <code><DKDM></code> tags.
826 /* [XML] DKDM A DKDM as XML */
827 _dkdms->as_xml (root);
829 /* [XML] CinemasFile Filename of cinemas list file. */
830 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
831 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
832 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
833 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
834 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
835 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
836 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
837 /* [XML] KDMFilenameFormat Format for KDM filenames. */
838 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
839 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
840 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
841 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
842 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
843 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
844 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
845 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
846 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
847 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
848 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
849 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
850 for (int i = 0; i < NAG_COUNT; ++i) {
851 xmlpp::Element* e = root->add_child ("Nagged");
852 e->set_attribute ("Id", raw_convert<string>(i));
853 e->add_child_text (_nagged[i] ? "1" : "0");
855 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
856 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
858 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
859 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
861 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
862 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
863 if (_last_player_load_directory) {
864 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
866 /* [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. */
867 if (_last_kdm_write_type) {
868 switch (_last_kdm_write_type.get()) {
870 root->add_child("LastKDMWriteType")->add_child_text("flat");
872 case KDM_WRITE_FOLDER:
873 root->add_child("LastKDMWriteType")->add_child_text("folder");
876 root->add_child("LastKDMWriteType")->add_child_text("zip");
880 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
881 if (_last_dkdm_write_type) {
882 switch (_last_dkdm_write_type.get()) {
883 case DKDM_WRITE_INTERNAL:
884 root->add_child("LastDKDMWriteType")->add_child_text("internal");
886 case DKDM_WRITE_FILE:
887 root->add_child("LastDKDMWriteType")->add_child_text("file");
891 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
892 frames to be held in memory at once.
894 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
896 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
897 if (_decode_reduction) {
898 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
901 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
902 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
904 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
905 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
906 xmlpp::Element* e = root->add_child ("Notification");
907 e->set_attribute ("Id", raw_convert<string>(i));
908 e->add_child_text (_notification[i] ? "1" : "0");
911 if (_barco_username) {
912 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
913 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
915 if (_barco_password) {
916 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
917 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
920 if (_christie_username) {
921 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
922 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
924 if (_christie_password) {
925 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
926 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
930 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
931 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
934 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
935 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
938 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
939 with controls on another monitor.
941 switch (_player_mode) {
942 case PLAYER_MODE_WINDOW:
943 root->add_child("PlayerMode")->add_child_text("window");
945 case PLAYER_MODE_FULL:
946 root->add_child("PlayerMode")->add_child_text("full");
948 case PLAYER_MODE_DUAL:
949 root->add_child("PlayerMode")->add_child_text("dual");
953 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
954 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
955 switch (_video_view_type) {
956 case VIDEO_VIEW_SIMPLE:
957 root->add_child("VideoViewType")->add_child_text("simple");
959 case VIDEO_VIEW_OPENGL:
960 root->add_child("VideoViewType")->add_child_text("opengl");
963 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
964 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
965 if (_player_debug_log_file) {
966 /* [XML] PlayerLogFile Filename to use for player debug logs. */
967 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
969 if (_player_content_directory) {
970 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
971 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
973 if (_player_playlist_directory) {
974 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
975 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
977 if (_player_kdm_directory) {
978 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
979 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
981 if (_audio_mapping) {
982 _audio_mapping->as_xml (root->add_child("AudioMapping"));
984 for (auto const& i: _custom_languages) {
985 root->add_child("CustomLanguage")->add_child_text(i.to_string());
987 if (_add_files_path) {
988 /* [XML] AddFilesPath The default path that will be offered in the picker when adding files to a film. */
989 root->add_child("AddFilesPath")->add_child_text(_add_files_path->string());
992 auto target = config_write_file();
995 auto const s = doc.write_to_string_formatted ();
996 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
997 auto f = fopen_boost (tmp, "w");
999 throw FileError (_("Could not open file for writing"), tmp);
1001 checked_fwrite (s.c_str(), s.bytes(), f, tmp);
1003 boost::filesystem::remove (target);
1004 boost::filesystem::rename (tmp, target);
1005 } catch (xmlpp::exception& e) {
1006 string s = e.what ();
1008 throw FileError (s, target);
1015 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1017 xmlpp::Document doc;
1018 auto root = doc.create_root_node (root_node);
1019 root->add_child("Version")->add_child_text(version);
1021 for (auto i: things) {
1022 i->as_xml (root->add_child(node));
1026 doc.write_to_file_formatted (file.string() + ".tmp");
1027 boost::filesystem::remove (file);
1028 boost::filesystem::rename (file.string() + ".tmp", file);
1029 } catch (xmlpp::exception& e) {
1030 string s = e.what ();
1032 throw FileError (s, file);
1038 Config::write_cinemas () const
1040 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1045 Config::write_dkdm_recipients () const
1047 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1051 boost::filesystem::path
1052 Config::default_directory_or (boost::filesystem::path a) const
1054 return directory_or (_default_directory, a);
1057 boost::filesystem::path
1058 Config::default_kdm_directory_or (boost::filesystem::path a) const
1060 return directory_or (_default_kdm_directory, a);
1063 boost::filesystem::path
1064 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1070 boost::system::error_code ec;
1071 auto const e = boost::filesystem::exists (*dir, ec);
1087 Config::changed (Property what)
1093 Config::set_kdm_email_to_default ()
1095 _kdm_subject = _("KDM delivery: $CPL_NAME");
1098 "Dear Projectionist\n\n"
1099 "Please find attached KDMs for $CPL_NAME.\n\n"
1100 "Cinema: $CINEMA_NAME\n"
1101 "Screen(s): $SCREENS\n\n"
1102 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1103 "Best regards,\nDCP-o-matic"
1108 Config::set_notification_email_to_default ()
1110 _notification_subject = _("DCP-o-matic notification");
1112 _notification_email = _(
1113 "$JOB_NAME: $JOB_STATUS"
1118 Config::reset_kdm_email ()
1120 set_kdm_email_to_default ();
1125 Config::reset_notification_email ()
1127 set_notification_email_to_default ();
1132 Config::set_cover_sheet_to_default ()
1136 "CPL Filename: $CPL_FILENAME\n"
1138 "Format: $CONTAINER\n"
1140 "Audio Language: $AUDIO_LANGUAGE\n"
1141 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1148 Config::add_to_history (boost::filesystem::path p)
1150 add_to_history_internal (_history, p);
1153 /** Remove non-existant items from the history */
1155 Config::clean_history ()
1157 clean_history_internal (_history);
1161 Config::add_to_player_history (boost::filesystem::path p)
1163 add_to_history_internal (_player_history, p);
1166 /** Remove non-existant items from the player history */
1168 Config::clean_player_history ()
1170 clean_history_internal (_player_history);
1174 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1176 /* Remove existing instances of this path in the history */
1177 h.erase (remove (h.begin(), h.end(), p), h.end ());
1179 h.insert (h.begin (), p);
1180 if (h.size() > HISTORY_SIZE) {
1188 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1194 if (boost::filesystem::is_directory(i)) {
1198 /* We couldn't find out if it's a directory for some reason; just ignore it */
1205 Config::have_existing (string file)
1207 return boost::filesystem::exists (read_path(file));
1212 Config::read_cinemas (cxml::Document const & f)
1215 for (auto i: f.node_children("Cinema")) {
1216 /* Slightly grotty two-part construction of Cinema here so that we can use
1219 auto cinema = make_shared<Cinema>(i);
1220 cinema->read_screens (i);
1221 _cinemas.push_back (cinema);
1226 Config::set_cinemas_file (boost::filesystem::path file)
1228 if (file == _cinemas_file) {
1232 _cinemas_file = file;
1234 if (boost::filesystem::exists (_cinemas_file)) {
1235 /* Existing file; read it in */
1236 cxml::Document f ("Cinemas");
1237 f.read_file (_cinemas_file);
1246 Config::read_dkdm_recipients (cxml::Document const & f)
1248 _dkdm_recipients.clear ();
1249 for (auto i: f.node_children("DKDMRecipient")) {
1250 _dkdm_recipients.push_back (shared_ptr<DKDMRecipient>(new DKDMRecipient(i)));
1256 Config::save_template (shared_ptr<const Film> film, string name) const
1258 film->write_template (template_write_path(name));
1263 Config::templates () const
1265 if (!boost::filesystem::exists(read_path("templates"))) {
1270 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1271 n.push_back (i.path().filename().string());
1277 Config::existing_template (string name) const
1279 return boost::filesystem::exists (template_read_path(name));
1283 boost::filesystem::path
1284 Config::template_read_path (string name) const
1286 return read_path("templates") / tidy_for_filename (name);
1290 boost::filesystem::path
1291 Config::template_write_path (string name) const
1293 return write_path("templates") / tidy_for_filename (name);
1298 Config::rename_template (string old_name, string new_name) const
1300 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1304 Config::delete_template (string name) const
1306 boost::filesystem::remove (template_write_path(name));
1309 /** @return Path to the config.xml containing the actual settings, following a link if required */
1310 boost::filesystem::path
1311 config_file (boost::filesystem::path main)
1313 cxml::Document f ("Config");
1314 if (!boost::filesystem::exists (main)) {
1315 /* It doesn't exist, so there can't be any links; just return it */
1319 /* See if there's a link */
1322 auto link = f.optional_string_child("Link");
1326 } catch (xmlpp::exception& e) {
1327 /* There as a problem reading the main configuration file,
1328 so there can't be a link.
1336 boost::filesystem::path
1337 Config::config_read_file ()
1339 return config_file (read_path("config.xml"));
1343 boost::filesystem::path
1344 Config::config_write_file ()
1346 return config_file (write_path("config.xml"));
1351 Config::reset_cover_sheet ()
1353 set_cover_sheet_to_default ();
1358 Config::link (boost::filesystem::path new_file) const
1360 xmlpp::Document doc;
1361 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1363 doc.write_to_file_formatted(write_path("config.xml").string());
1364 } catch (xmlpp::exception& e) {
1365 string s = e.what ();
1367 throw FileError (s, write_path("config.xml"));
1372 Config::copy_and_link (boost::filesystem::path new_file) const
1375 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1380 Config::have_write_permission () const
1382 auto f = fopen_boost (config_write_file(), "r+");
1391 /** @param output_channels Number of output channels in use.
1392 * @return Audio mapping for this output channel count (may be a default).
1395 Config::audio_mapping (int output_channels)
1397 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1398 /* Set up a default */
1399 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1400 if (output_channels == 2) {
1401 /* Special case for stereo output.
1402 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1403 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1405 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1406 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1407 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1408 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1409 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1410 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1411 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1412 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1415 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1416 _audio_mapping->set (i, i, 1);
1421 return *_audio_mapping;
1425 Config::set_audio_mapping (AudioMapping m)
1428 changed (AUDIO_MAPPING);
1432 Config::set_audio_mapping_to_default ()
1434 DCPOMATIC_ASSERT (_audio_mapping);
1435 auto const ch = _audio_mapping->output_channels ();
1436 _audio_mapping = boost::none;
1437 _audio_mapping = audio_mapping (ch);
1438 changed (AUDIO_MAPPING);
1443 Config::add_custom_language (dcp::LanguageTag tag)
1445 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1446 _custom_languages.push_back (tag);