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"
37 #include <dcp/certificate_chain.h>
38 #include <dcp/name_format.h>
39 #include <dcp/raw_convert.h>
40 #include <libcxml/cxml.h>
42 #include <libxml++/libxml++.h>
43 #include <boost/filesystem.hpp>
44 #include <boost/algorithm/string.hpp>
45 #include <boost/thread.hpp>
60 using std::shared_ptr;
61 using std::make_shared;
62 using boost::optional;
63 using std::dynamic_pointer_cast;
64 using boost::algorithm::trim;
65 using dcp::raw_convert;
67 Config* Config::_instance = 0;
68 int const Config::_current_version = 3;
69 boost::signals2::signal<void ()> Config::FailedToLoad;
70 boost::signals2::signal<void (string)> Config::Warning;
71 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
73 /** Construct default configuration */
75 /* DKDMs are not considered a thing to reset on set_defaults() */
76 : _dkdms (new DKDMGroup ("root"))
82 Config::set_defaults ()
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 /* At the moment we don't write these files anywhere new after a version change, so they will be read from
130 * ~/.config/dcpomatic2 (or equivalent) and written back there.
132 _cinemas_file = read_path ("cinemas.xml");
133 _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
134 _show_hints_before_make_dcp = true;
135 _confirm_kdm_email = true;
136 _kdm_container_name_format = dcp::NameFormat ("KDM %f %c");
137 _kdm_filename_format = dcp::NameFormat ("KDM %f %c %s");
138 _dkdm_filename_format = dcp::NameFormat ("DKDM %f %c %s");
139 _dcp_metadata_filename_format = dcp::NameFormat ("%t");
140 _dcp_asset_filename_format = dcp::NameFormat ("%t");
141 _jump_to_selected = true;
142 for (int i = 0; i < NAG_COUNT; ++i) {
146 _sound_output = optional<string> ();
147 _last_kdm_write_type = KDM_WRITE_FLAT;
148 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
150 /* I think the scaling factor here should be the ratio of the longest frame
151 encode time to the shortest; if the thread count is T, longest time is L
152 and the shortest time S we could encode L/S frames per thread whilst waiting
153 for the L frame to encode so we might have to store LT/S frames.
155 However we don't want to use too much memory, so keep it a bit lower than we'd
156 perhaps like. A J2K frame is typically about 1Mb so 3 here will mean we could
157 use about 240Mb with 72 encoding threads.
159 _frames_in_memory_multiplier = 3;
160 _decode_reduction = optional<int>();
161 _default_notify = false;
162 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
163 _notification[i] = false;
165 _barco_username = optional<string>();
166 _barco_password = optional<string>();
167 _christie_username = optional<string>();
168 _christie_password = optional<string>();
169 _gdc_username = optional<string>();
170 _gdc_password = optional<string>();
171 _player_mode = PLAYER_MODE_WINDOW;
173 _video_view_type = VIDEO_VIEW_SIMPLE;
174 _respect_kdm_validity_periods = true;
175 _player_debug_log_file = boost::none;
176 _player_content_directory = boost::none;
177 _player_playlist_directory = boost::none;
178 _player_kdm_directory = boost::none;
179 _audio_mapping = boost::none;
180 _custom_languages.clear ();
181 _add_files_path = boost::none;
182 _use_isdcf_name_by_default = true;
183 _write_kdms_to_disk = true;
186 _allowed_dcp_frame_rates.clear ();
187 _allowed_dcp_frame_rates.push_back (24);
188 _allowed_dcp_frame_rates.push_back (25);
189 _allowed_dcp_frame_rates.push_back (30);
190 _allowed_dcp_frame_rates.push_back (48);
191 _allowed_dcp_frame_rates.push_back (50);
192 _allowed_dcp_frame_rates.push_back (60);
194 set_kdm_email_to_default ();
195 set_notification_email_to_default ();
196 set_cover_sheet_to_default ();
200 Config::restore_defaults ()
202 Config::instance()->set_defaults ();
203 Config::instance()->changed ();
206 shared_ptr<dcp::CertificateChain>
207 Config::create_certificate_chain ()
209 return make_shared<dcp::CertificateChain> (
211 CERTIFICATE_VALIDITY_PERIOD,
214 ".dcpomatic.smpte-430-2.ROOT",
215 ".dcpomatic.smpte-430-2.INTERMEDIATE",
216 "CS.dcpomatic.smpte-430-2.LEAF"
223 using namespace boost::filesystem;
225 auto copy_adding_number = [](path const& path_to_copy) {
227 auto add_number = [](path const& path, int number) {
228 return String::compose("%1.%2", path, number);
232 while (n < 100 && exists(add_number(path_to_copy, n))) {
235 boost::system::error_code ec;
236 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
239 /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
240 * to write over. This is more intended for the situation where we have a corrupted config.xml,
241 * and decide to overwrite it with a new one (possibly losing important details in the corrupted
242 * file). But we might as well back up the other files while we're about it.
245 /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
246 * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
248 copy_adding_number (config_write_file());
250 /* These do not use State::write_path, so whatever path is in the Config we will copy
253 copy_adding_number (_cinemas_file);
254 copy_adding_number (_dkdm_recipients_file);
261 cxml::Document f ("Config");
262 f.read_file (config_read_file());
264 auto version = f.optional_number_child<int> ("Version");
265 if (version && *version < _current_version) {
266 /* Back up the old config before we re-write it in a back-incompatible way */
270 if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
271 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
273 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
274 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
277 _default_directory = f.optional_string_child ("DefaultDirectory");
278 if (_default_directory && _default_directory->empty ()) {
279 /* We used to store an empty value for this to mean "none set" */
280 _default_directory = boost::optional<boost::filesystem::path> ();
283 auto b = f.optional_number_child<int> ("ServerPort");
285 b = f.optional_number_child<int> ("ServerPortBase");
287 _server_port_base = b.get ();
289 auto u = f.optional_bool_child ("UseAnyServers");
290 _use_any_servers = u.get_value_or (true);
292 for (auto i: f.node_children("Server")) {
293 if (i->node_children("HostName").size() == 1) {
294 _servers.push_back (i->string_child ("HostName"));
296 _servers.push_back (i->content ());
300 _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
301 _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
302 _tms_ip = f.string_child ("TMSIP");
303 _tms_path = f.string_child ("TMSPath");
304 _tms_user = f.string_child ("TMSUser");
305 _tms_password = f.string_child ("TMSPassword");
307 _language = f.optional_string_child ("Language");
309 auto c = f.optional_string_child ("DefaultContainer");
311 _default_container = Ratio::from_id (c.get ());
314 if (_default_container && !_default_container->used_for_container()) {
315 Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)"));
316 _default_container = Ratio::from_id ("185");
319 _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
320 _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
322 if (f.optional_string_child ("DCPMetadataIssuer")) {
323 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
324 } else if (f.optional_string_child ("DCPIssuer")) {
325 _dcp_issuer = f.string_child ("DCPIssuer");
328 auto up = f.optional_bool_child("UploadAfterMakeDCP");
330 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
332 _upload_after_make_dcp = up.get_value_or (false);
333 _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
334 _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
335 _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
336 _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
337 _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
339 _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
340 _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
341 _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
342 _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
344 for (auto const& i: f.node_children("DefaultMetadata")) {
345 _default_metadata[i->string_attribute("key")] = i->content();
348 _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
350 /* Read any cinemas that are still lying around in the config file
351 * from an old version.
355 _mail_server = f.string_child ("MailServer");
356 _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
359 /* Make sure this matches the code in write_config */
360 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
361 if (protocol == "Auto") {
362 _mail_protocol = EmailProtocol::AUTO;
363 } else if (protocol == "Plain") {
364 _mail_protocol = EmailProtocol::PLAIN;
365 } else if (protocol == "STARTTLS") {
366 _mail_protocol = EmailProtocol::STARTTLS;
367 } else if (protocol == "SSL") {
368 _mail_protocol = EmailProtocol::SSL;
372 _mail_user = f.optional_string_child("MailUser").get_value_or ("");
373 _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
375 _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
376 _kdm_from = f.string_child ("KDMFrom");
377 for (auto i: f.node_children("KDMCC")) {
378 if (!i->content().empty()) {
379 _kdm_cc.push_back (i->content ());
382 _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
383 _kdm_email = f.string_child ("KDMEmail");
385 _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
386 _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
387 _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
388 for (auto i: f.node_children("NotificationCC")) {
389 if (!i->content().empty()) {
390 _notification_cc.push_back (i->content ());
393 _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
394 if (f.optional_string_child("NotificationEmail")) {
395 _notification_email = f.string_child("NotificationEmail");
398 _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
399 _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
401 _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
402 _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
403 _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
404 _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
406 _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
407 _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
408 _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
409 #ifdef DCPOMATIC_WINDOWS
410 _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
413 for (auto i: f.node_children("History")) {
414 _history.push_back (i->content ());
417 for (auto i: f.node_children("PlayerHistory")) {
418 _player_history.push_back (i->content ());
421 auto signer = f.optional_node_child ("Signer");
423 auto c = make_shared<dcp::CertificateChain>();
424 /* Read the signing certificates and private key in from the config file */
425 for (auto i: signer->node_children ("Certificate")) {
426 c->add (dcp::Certificate (i->content ()));
428 c->set_key (signer->string_child ("PrivateKey"));
431 /* Make a new set of signing certificates and key */
432 _signer_chain = create_certificate_chain ();
435 auto decryption = f.optional_node_child ("Decryption");
437 auto c = make_shared<dcp::CertificateChain>();
438 for (auto i: decryption->node_children ("Certificate")) {
439 c->add (dcp::Certificate (i->content ()));
441 c->set_key (decryption->string_child ("PrivateKey"));
442 _decryption_chain = c;
444 _decryption_chain = create_certificate_chain ();
447 /* These must be done before we call Bad as that might set one
450 for (auto i: f.node_children("Nagged")) {
451 auto const id = i->number_attribute<int>("Id");
452 if (id >= 0 && id < NAG_COUNT) {
453 _nagged[id] = raw_convert<int>(i->content());
457 auto bad = check_certificates ();
459 auto const remake = Bad(*bad);
460 if (remake && *remake) {
462 case BAD_SIGNER_UTF8_STRINGS:
463 case BAD_SIGNER_INCONSISTENT:
464 case BAD_SIGNER_VALIDITY_TOO_LONG:
465 _signer_chain = create_certificate_chain ();
467 case BAD_DECRYPTION_INCONSISTENT:
468 _decryption_chain = create_certificate_chain ();
474 if (f.optional_node_child("DKDMGroup")) {
475 /* New-style: all DKDMs in a group */
476 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
478 /* Old-style: one or more DKDM nodes */
479 _dkdms.reset (new DKDMGroup ("root"));
480 for (auto i: f.node_children("DKDM")) {
481 _dkdms->add (DKDMBase::read (i));
484 _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
485 _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
486 _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
487 _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
488 _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
489 _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
490 _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
491 _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
492 _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
493 _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
494 /* The variable was renamed but not the XML tag */
495 _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
496 _sound_output = f.optional_string_child("PreviewSoundOutput");
497 if (f.optional_string_child("CoverSheet")) {
498 _cover_sheet = f.optional_string_child("CoverSheet").get();
500 _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
501 if (f.optional_string_child("LastKDMWriteType")) {
502 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
503 _last_kdm_write_type = KDM_WRITE_FLAT;
504 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
505 _last_kdm_write_type = KDM_WRITE_FOLDER;
506 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
507 _last_kdm_write_type = KDM_WRITE_ZIP;
510 if (f.optional_string_child("LastDKDMWriteType")) {
511 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
512 _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
513 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
514 _last_dkdm_write_type = DKDM_WRITE_FILE;
517 _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
518 _decode_reduction = f.optional_number_child<int>("DecodeReduction");
519 _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
521 for (auto i: f.node_children("Notification")) {
522 int const id = i->number_attribute<int>("Id");
523 if (id >= 0 && id < NOTIFICATION_COUNT) {
524 _notification[id] = raw_convert<int>(i->content());
528 _barco_username = f.optional_string_child("BarcoUsername");
529 _barco_password = f.optional_string_child("BarcoPassword");
530 _christie_username = f.optional_string_child("ChristieUsername");
531 _christie_password = f.optional_string_child("ChristiePassword");
532 _gdc_username = f.optional_string_child("GDCUsername");
533 _gdc_password = f.optional_string_child("GDCPassword");
535 auto pm = f.optional_string_child("PlayerMode");
536 if (pm && *pm == "window") {
537 _player_mode = PLAYER_MODE_WINDOW;
538 } else if (pm && *pm == "full") {
539 _player_mode = PLAYER_MODE_FULL;
540 } else if (pm && *pm == "dual") {
541 _player_mode = PLAYER_MODE_DUAL;
544 _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
545 auto vc = f.optional_string_child("VideoViewType");
546 if (vc && *vc == "opengl") {
547 _video_view_type = VIDEO_VIEW_OPENGL;
548 } else if (vc && *vc == "simple") {
549 _video_view_type = VIDEO_VIEW_SIMPLE;
551 _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
552 _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
553 _player_content_directory = f.optional_string_child("PlayerContentDirectory");
554 _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
555 _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
557 if (f.optional_node_child("AudioMapping")) {
558 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
561 for (auto i: f.node_children("CustomLanguage")) {
563 /* This will fail if it's called before dcp::init() as it won't recognise the
564 * tag. That's OK because the Config will be reloaded again later.
566 _custom_languages.push_back (dcp::LanguageTag(i->content()));
567 } catch (std::runtime_error& e) {}
570 _add_files_path = f.optional_string_child("AddFilesPath");
571 _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
572 _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
573 _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
575 if (boost::filesystem::exists (_cinemas_file)) {
576 cxml::Document f ("Cinemas");
577 f.read_file (_cinemas_file);
581 if (boost::filesystem::exists (_dkdm_recipients_file)) {
582 cxml::Document f ("DKDMRecipients");
583 f.read_file (_dkdm_recipients_file);
584 read_dkdm_recipients (f);
588 if (have_existing("config.xml") || have_existing("cinemas.xml") || have_existing("dkdm_recipients.xml")) {
590 /* We have a config file but it didn't load */
594 /* Make a new set of signing certificates and key */
595 _signer_chain = create_certificate_chain ();
596 /* And similar for decryption of KDMs */
597 _decryption_chain = create_certificate_chain ();
601 /** @return Singleton instance */
605 if (_instance == 0) {
606 _instance = new Config;
613 /** Write our configuration to disk */
615 Config::write () const
619 write_dkdm_recipients ();
623 Config::write_config () const
626 auto root = doc.create_root_node ("Config");
628 /* [XML] Version The version number of the configuration file format. */
629 root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
630 /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
631 root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
632 /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
633 root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
634 if (_default_directory) {
635 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
636 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
638 /* [XML] ServerPortBase Port number to use for frame encoding requests. <code>ServerPortBase</code> + 1 and
639 <code>ServerPortBase</code> + 2 are used for querying servers. <code>ServerPortBase</code> + 3 is used
640 by the batch converter to listen for job requests.
642 root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
643 /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
644 root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
646 for (auto i: _servers) {
647 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
650 root->add_child("Server")->add_child_text (i);
653 /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
654 is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
656 root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
657 /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
658 root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
659 /* [XML] TMSIP IP address of TMS. */
660 root->add_child("TMSIP")->add_child_text (_tms_ip);
661 /* [XML] TMSPath Path on the TMS to copy files to. */
662 root->add_child("TMSPath")->add_child_text (_tms_path);
663 /* [XML] TMSUser Username to log into the TMS with. */
664 root->add_child("TMSUser")->add_child_text (_tms_user);
665 /* [XML] TMSPassword Password to log into the TMS with. */
666 root->add_child("TMSPassword")->add_child_text (_tms_password);
668 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
669 root->add_child("Language")->add_child_text (_language.get());
671 if (_default_container) {
672 /* [XML:opt] DefaultContainer ID of default container
673 to use when creating new films (<code>185</code>,<code>239</code> or
676 root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
678 if (_default_dcp_content_type) {
679 /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
680 <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
681 <code>PSA</code> or <code>ADV</code>). */
682 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
684 /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
685 root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
686 /* [XML] DCPIssuer Issuer text to write into CPL files. */
687 root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
688 /* [XML] DCPCreator Creator text to write into CPL files. */
689 root->add_child("DCPCreator")->add_child_text (_dcp_creator);
690 /* [XML] Company name to write into MXF files. */
691 root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
692 /* [XML] Product name to write into MXF files. */
693 root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
694 /* [XML] Product version to write into MXF files. */
695 root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
696 /* [XML] Comment to write into JPEG2000 data. */
697 root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
698 /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
699 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
701 /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
702 root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
703 /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
704 root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
705 /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
706 root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
707 /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
708 root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
709 for (auto const& i: _default_metadata) {
710 auto c = root->add_child("DefaultMetadata");
711 c->set_attribute("key", i.first);
712 c->add_child_text(i.second);
714 if (_default_kdm_directory) {
715 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
716 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
718 /* [XML] MailServer Hostname of SMTP server to use. */
719 root->add_child("MailServer")->add_child_text (_mail_server);
720 /* [XML] MailPort Port number to use on SMTP server. */
721 root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
722 /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
723 switch (_mail_protocol) {
724 case EmailProtocol::AUTO:
725 root->add_child("MailProtocol")->add_child_text("Auto");
727 case EmailProtocol::PLAIN:
728 root->add_child("MailProtocol")->add_child_text("Plain");
730 case EmailProtocol::STARTTLS:
731 root->add_child("MailProtocol")->add_child_text("STARTTLS");
733 case EmailProtocol::SSL:
734 root->add_child("MailProtocol")->add_child_text("SSL");
737 /* [XML] MailUser Username to use on SMTP server. */
738 root->add_child("MailUser")->add_child_text (_mail_user);
739 /* [XML] MailPassword Password to use on SMTP server. */
740 root->add_child("MailPassword")->add_child_text (_mail_password);
742 /* [XML] KDMSubject Subject to use for KDM emails. */
743 root->add_child("KDMSubject")->add_child_text (_kdm_subject);
744 /* [XML] KDMFrom From address to use for KDM emails. */
745 root->add_child("KDMFrom")->add_child_text (_kdm_from);
746 for (auto i: _kdm_cc) {
747 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
748 root->add_child("KDMCC")->add_child_text (i);
750 /* [XML] KDMBCC BCC address to use for KDM emails. */
751 root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
752 /* [XML] KDMEmail Text of KDM email. */
753 root->add_child("KDMEmail")->add_child_text (_kdm_email);
755 /* [XML] NotificationSubject Subject to use for notification emails. */
756 root->add_child("NotificationSubject")->add_child_text (_notification_subject);
757 /* [XML] NotificationFrom From address to use for notification emails. */
758 root->add_child("NotificationFrom")->add_child_text (_notification_from);
759 /* [XML] NotificationFrom To address to use for notification emails. */
760 root->add_child("NotificationTo")->add_child_text (_notification_to);
761 for (auto i: _notification_cc) {
762 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
763 root->add_child("NotificationCC")->add_child_text (i);
765 /* [XML] NotificationBCC BCC address to use for notification emails. */
766 root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
767 /* [XML] NotificationEmail Text of notification email. */
768 root->add_child("NotificationEmail")->add_child_text (_notification_email);
770 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
771 root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
772 /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
773 root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
775 /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
776 root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
777 /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
778 root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
779 /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
780 root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
781 /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
782 root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
783 /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
784 to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
785 to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
786 related to the player, 1024 debug information related to audio analyses.
788 root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
789 /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
790 root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
791 /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
792 root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
793 #ifdef DCPOMATIC_WINDOWS
794 if (_win32_console) {
795 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
796 * We only write this if it's true, which is a bit of a hack to allow unit tests to work
797 * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
799 root->add_child("Win32Console")->add_child_text ("1");
803 /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs. Should contain <code><Certificate></code>
804 tags in order and a <code><PrivateKey></code> tag all containing PEM-encoded certificates or private keys as appropriate.
806 auto signer = root->add_child ("Signer");
807 DCPOMATIC_ASSERT (_signer_chain);
808 for (auto const& i: _signer_chain->unordered()) {
809 signer->add_child("Certificate")->add_child_text (i.certificate (true));
811 signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
813 /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
814 auto decryption = root->add_child ("Decryption");
815 DCPOMATIC_ASSERT (_decryption_chain);
816 for (auto const& i: _decryption_chain->unordered()) {
817 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
819 decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
821 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
824 for (auto i: _history) {
825 root->add_child("History")->add_child_text (i.string ());
828 /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
831 for (auto i: _player_history) {
832 root->add_child("PlayerHistory")->add_child_text (i.string ());
835 /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code><DKDMGroup></code>
836 or <code><DKDM></code> tags.
838 /* [XML] DKDM A DKDM as XML */
839 _dkdms->as_xml (root);
841 /* [XML] CinemasFile Filename of cinemas list file. */
842 root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
843 /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
844 root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
845 /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
846 root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
847 /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
848 root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
849 /* [XML] KDMFilenameFormat Format for KDM filenames. */
850 root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
851 /* [XML] KDMFilenameFormat Format for DKDM filenames. */
852 root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
853 /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
854 root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
855 /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
856 root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
857 /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
858 root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
859 /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
860 root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
861 /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
862 for (int i = 0; i < NAG_COUNT; ++i) {
863 xmlpp::Element* e = root->add_child ("Nagged");
864 e->set_attribute ("Id", raw_convert<string>(i));
865 e->add_child_text (_nagged[i] ? "1" : "0");
867 /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
868 root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
870 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
871 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
873 /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
874 root->add_child("CoverSheet")->add_child_text (_cover_sheet);
875 if (_last_player_load_directory) {
876 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
878 /* [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. */
879 if (_last_kdm_write_type) {
880 switch (_last_kdm_write_type.get()) {
882 root->add_child("LastKDMWriteType")->add_child_text("flat");
884 case KDM_WRITE_FOLDER:
885 root->add_child("LastKDMWriteType")->add_child_text("folder");
888 root->add_child("LastKDMWriteType")->add_child_text("zip");
892 /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
893 if (_last_dkdm_write_type) {
894 switch (_last_dkdm_write_type.get()) {
895 case DKDM_WRITE_INTERNAL:
896 root->add_child("LastDKDMWriteType")->add_child_text("internal");
898 case DKDM_WRITE_FILE:
899 root->add_child("LastDKDMWriteType")->add_child_text("file");
903 /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
904 frames to be held in memory at once.
906 root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
908 /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
909 if (_decode_reduction) {
910 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
913 /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
914 root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
916 /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
917 for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
918 xmlpp::Element* e = root->add_child ("Notification");
919 e->set_attribute ("Id", raw_convert<string>(i));
920 e->add_child_text (_notification[i] ? "1" : "0");
923 if (_barco_username) {
924 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
925 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
927 if (_barco_password) {
928 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
929 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
932 if (_christie_username) {
933 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
934 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
936 if (_christie_password) {
937 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
938 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
942 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
943 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
946 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
947 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
950 /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
951 with controls on another monitor.
953 switch (_player_mode) {
954 case PLAYER_MODE_WINDOW:
955 root->add_child("PlayerMode")->add_child_text("window");
957 case PLAYER_MODE_FULL:
958 root->add_child("PlayerMode")->add_child_text("full");
960 case PLAYER_MODE_DUAL:
961 root->add_child("PlayerMode")->add_child_text("dual");
965 /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
966 root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
967 switch (_video_view_type) {
968 case VIDEO_VIEW_SIMPLE:
969 root->add_child("VideoViewType")->add_child_text("simple");
971 case VIDEO_VIEW_OPENGL:
972 root->add_child("VideoViewType")->add_child_text("opengl");
975 /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
976 root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
977 if (_player_debug_log_file) {
978 /* [XML] PlayerLogFile Filename to use for player debug logs. */
979 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
981 if (_player_content_directory) {
982 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
983 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
985 if (_player_playlist_directory) {
986 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
987 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
989 if (_player_kdm_directory) {
990 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
991 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
993 if (_audio_mapping) {
994 _audio_mapping->as_xml (root->add_child("AudioMapping"));
996 for (auto const& i: _custom_languages) {
997 root->add_child("CustomLanguage")->add_child_text(i.to_string());
999 if (_add_files_path) {
1000 /* [XML] AddFilesPath The default path that will be offered in the picker when adding files to a film. */
1001 root->add_child("AddFilesPath")->add_child_text(_add_files_path->string());
1003 root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1004 root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0");
1005 root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0");
1007 auto target = config_write_file();
1010 auto const s = doc.write_to_string_formatted ();
1011 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1012 auto f = fopen_boost (tmp, "w");
1014 throw FileError (_("Could not open file for writing"), tmp);
1016 checked_fwrite (s.c_str(), s.bytes(), f, tmp);
1018 boost::filesystem::remove (target);
1019 boost::filesystem::rename (tmp, target);
1020 } catch (xmlpp::exception& e) {
1021 string s = e.what ();
1023 throw FileError (s, target);
1030 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1032 xmlpp::Document doc;
1033 auto root = doc.create_root_node (root_node);
1034 root->add_child("Version")->add_child_text(version);
1036 for (auto i: things) {
1037 i->as_xml (root->add_child(node));
1041 doc.write_to_file_formatted (file.string() + ".tmp");
1042 boost::filesystem::remove (file);
1043 boost::filesystem::rename (file.string() + ".tmp", file);
1044 } catch (xmlpp::exception& e) {
1045 string s = e.what ();
1047 throw FileError (s, file);
1053 Config::write_cinemas () const
1055 write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1060 Config::write_dkdm_recipients () const
1062 write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1066 boost::filesystem::path
1067 Config::default_directory_or (boost::filesystem::path a) const
1069 return directory_or (_default_directory, a);
1072 boost::filesystem::path
1073 Config::default_kdm_directory_or (boost::filesystem::path a) const
1075 return directory_or (_default_kdm_directory, a);
1078 boost::filesystem::path
1079 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1085 boost::system::error_code ec;
1086 auto const e = boost::filesystem::exists (*dir, ec);
1102 Config::changed (Property what)
1108 Config::set_kdm_email_to_default ()
1110 _kdm_subject = _("KDM delivery: $CPL_NAME");
1113 "Dear Projectionist\n\n"
1114 "Please find attached KDMs for $CPL_NAME.\n\n"
1115 "Cinema: $CINEMA_NAME\n"
1116 "Screen(s): $SCREENS\n\n"
1117 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1118 "Best regards,\nDCP-o-matic"
1123 Config::set_notification_email_to_default ()
1125 _notification_subject = _("DCP-o-matic notification");
1127 _notification_email = _(
1128 "$JOB_NAME: $JOB_STATUS"
1133 Config::reset_kdm_email ()
1135 set_kdm_email_to_default ();
1140 Config::reset_notification_email ()
1142 set_notification_email_to_default ();
1147 Config::set_cover_sheet_to_default ()
1152 "Format: $CONTAINER\n"
1154 "Audio Language: $AUDIO_LANGUAGE\n"
1155 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1162 Config::add_to_history (boost::filesystem::path p)
1164 add_to_history_internal (_history, p);
1167 /** Remove non-existant items from the history */
1169 Config::clean_history ()
1171 clean_history_internal (_history);
1175 Config::add_to_player_history (boost::filesystem::path p)
1177 add_to_history_internal (_player_history, p);
1180 /** Remove non-existant items from the player history */
1182 Config::clean_player_history ()
1184 clean_history_internal (_player_history);
1188 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1190 /* Remove existing instances of this path in the history */
1191 h.erase (remove (h.begin(), h.end(), p), h.end ());
1193 h.insert (h.begin (), p);
1194 if (h.size() > HISTORY_SIZE) {
1202 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1208 if (boost::filesystem::is_directory(i)) {
1212 /* We couldn't find out if it's a directory for some reason; just ignore it */
1219 Config::have_existing (string file)
1221 return boost::filesystem::exists (read_path(file));
1226 Config::read_cinemas (cxml::Document const & f)
1229 for (auto i: f.node_children("Cinema")) {
1230 /* Slightly grotty two-part construction of Cinema here so that we can use
1233 auto cinema = make_shared<Cinema>(i);
1234 cinema->read_screens (i);
1235 _cinemas.push_back (cinema);
1240 Config::set_cinemas_file (boost::filesystem::path file)
1242 if (file == _cinemas_file) {
1246 _cinemas_file = file;
1248 if (boost::filesystem::exists (_cinemas_file)) {
1249 /* Existing file; read it in */
1250 cxml::Document f ("Cinemas");
1251 f.read_file (_cinemas_file);
1260 Config::read_dkdm_recipients (cxml::Document const & f)
1262 _dkdm_recipients.clear ();
1263 for (auto i: f.node_children("DKDMRecipient")) {
1264 _dkdm_recipients.push_back (shared_ptr<DKDMRecipient>(new DKDMRecipient(i)));
1270 Config::save_template (shared_ptr<const Film> film, string name) const
1272 film->write_template (template_write_path(name));
1277 Config::templates () const
1279 if (!boost::filesystem::exists(read_path("templates"))) {
1284 for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
1285 n.push_back (i.path().filename().string());
1291 Config::existing_template (string name) const
1293 return boost::filesystem::exists (template_read_path(name));
1297 boost::filesystem::path
1298 Config::template_read_path (string name) const
1300 return read_path("templates") / tidy_for_filename (name);
1304 boost::filesystem::path
1305 Config::template_write_path (string name) const
1307 return write_path("templates") / tidy_for_filename (name);
1312 Config::rename_template (string old_name, string new_name) const
1314 boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
1318 Config::delete_template (string name) const
1320 boost::filesystem::remove (template_write_path(name));
1323 /** @return Path to the config.xml containing the actual settings, following a link if required */
1324 boost::filesystem::path
1325 config_file (boost::filesystem::path main)
1327 cxml::Document f ("Config");
1328 if (!boost::filesystem::exists (main)) {
1329 /* It doesn't exist, so there can't be any links; just return it */
1333 /* See if there's a link */
1336 auto link = f.optional_string_child("Link");
1340 } catch (xmlpp::exception& e) {
1341 /* There as a problem reading the main configuration file,
1342 so there can't be a link.
1350 boost::filesystem::path
1351 Config::config_read_file ()
1353 return config_file (read_path("config.xml"));
1357 boost::filesystem::path
1358 Config::config_write_file ()
1360 return config_file (write_path("config.xml"));
1365 Config::reset_cover_sheet ()
1367 set_cover_sheet_to_default ();
1372 Config::link (boost::filesystem::path new_file) const
1374 xmlpp::Document doc;
1375 doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1377 doc.write_to_file_formatted(write_path("config.xml").string());
1378 } catch (xmlpp::exception& e) {
1379 string s = e.what ();
1381 throw FileError (s, write_path("config.xml"));
1386 Config::copy_and_link (boost::filesystem::path new_file) const
1389 boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1394 Config::have_write_permission () const
1396 auto f = fopen_boost (config_write_file(), "r+");
1405 /** @param output_channels Number of output channels in use.
1406 * @return Audio mapping for this output channel count (may be a default).
1409 Config::audio_mapping (int output_channels)
1411 if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1412 /* Set up a default */
1413 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1414 if (output_channels == 2) {
1415 /* Special case for stereo output.
1416 Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1417 Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1419 _audio_mapping->set (dcp::Channel::LEFT, 0, 1 / sqrt(2)); // L -> Lt
1420 _audio_mapping->set (dcp::Channel::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
1421 _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0); // C -> Lt
1422 _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0); // C -> Rt
1423 _audio_mapping->set (dcp::Channel::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
1424 _audio_mapping->set (dcp::Channel::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
1425 _audio_mapping->set (dcp::Channel::LS, 0, 1 / sqrt(2)); // Ls -> Lt
1426 _audio_mapping->set (dcp::Channel::RS, 1, 1 / sqrt(2)); // Rs -> Rt
1429 for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1430 _audio_mapping->set (i, i, 1);
1435 return *_audio_mapping;
1439 Config::set_audio_mapping (AudioMapping m)
1442 changed (AUDIO_MAPPING);
1446 Config::set_audio_mapping_to_default ()
1448 DCPOMATIC_ASSERT (_audio_mapping);
1449 auto const ch = _audio_mapping->output_channels ();
1450 _audio_mapping = boost::none;
1451 _audio_mapping = audio_mapping (ch);
1452 changed (AUDIO_MAPPING);
1457 Config::add_custom_language (dcp::LanguageTag tag)
1459 if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1460 _custom_languages.push_back (tag);
1466 optional<Config::BadReason>
1467 Config::check_certificates () const
1469 optional<BadReason> bad;
1471 for (auto const& i: _signer_chain->unordered()) {
1472 if (i.has_utf8_strings()) {
1473 bad = BAD_SIGNER_UTF8_STRINGS;
1475 if ((i.not_after().year() - i.not_before().year()) > 15) {
1476 bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1480 if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1481 bad = BAD_SIGNER_INCONSISTENT;
1484 if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1485 bad = BAD_DECRYPTION_INCONSISTENT;
1493 save_all_config_as_zip (boost::filesystem::path zip_file)
1495 Zipper zipper (zip_file);
1497 auto config = Config::instance();
1498 zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1499 if (boost::filesystem::exists(config->cinemas_file())) {
1500 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1502 if (boost::filesystem::exists(config->dkdm_recipients_file())) {
1503 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));