diff options
| author | Carl Hetherington <cth@carlh.net> | 2020-05-07 00:02:26 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2020-05-07 00:02:26 +0200 |
| commit | 47e29203daec51d313ed8ab8ef759752bce18d45 (patch) | |
| tree | 2b34f05ffe9ad73c49225e82d91a8dede9f3d6de | |
| parent | e81c5eb9e8ff875240dde9fdaaab0a46f99af615 (diff) | |
| parent | 581797d640af1572f884ddf4395924894b745b3a (diff) | |
Add a new "Add DKDM" dialogue (#1637).
The basic motivation here is to avoid having to tell people to
"just" create a cinema with a screen in it just to be able to make
a DKDM. Here you can just have a recipient, with emails etc.
and make DKDMs for them. I hope this makes things clearer from the
user POV even if it does muddy the waters a bit with respect to
DKDMs just being KDMs (really).
33 files changed, 1064 insertions, 614 deletions
diff --git a/src/lib/cinema_kdms.h b/src/lib/cinema_kdms.h deleted file mode 100644 index 2b82cdab6..000000000 --- a/src/lib/cinema_kdms.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright (C) 2013-2015 Carl Hetherington <cth@carlh.net> - - This file is part of DCP-o-matic. - - DCP-o-matic is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - DCP-o-matic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "screen_kdm.h" - -class Cinema; -class Job; -class Log; - -class CinemaKDMs -{ -public: - void make_zip_file (boost::filesystem::path zip_file, dcp::NameFormat name_format, dcp::NameFormat::Map name_values) const; - - static std::list<CinemaKDMs> collect (std::list<boost::shared_ptr<ScreenKDM> > kdms); - - static int write_directories ( - std::list<CinemaKDMs> cinema_kdms, - boost::filesystem::path directory, - dcp::NameFormat container_name_format, - dcp::NameFormat filename_format, - dcp::NameFormat::Map name_values, - boost::function<bool (boost::filesystem::path)> confirm_overwrite - ); - - static int write_zip_files ( - std::list<CinemaKDMs> cinema_kdms, - boost::filesystem::path directory, - dcp::NameFormat container_name_format, - dcp::NameFormat filename_format, - dcp::NameFormat::Map name_values, - boost::function<bool (boost::filesystem::path)> confirm_overwrite - ); - - static void email ( - std::list<CinemaKDMs> cinema_kdms, - dcp::NameFormat container_name_format, - dcp::NameFormat filename_format, - dcp::NameFormat::Map name_values, - std::string cpl_name - ); - - boost::shared_ptr<Cinema> cinema; - std::list<boost::shared_ptr<ScreenKDM> > screen_kdms; -}; diff --git a/src/lib/config.cc b/src/lib/config.cc index 7364a122c..ebb6ece93 100644 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -32,6 +32,7 @@ #include "dkdm_wrapper.h" #include "compose.hpp" #include "crypto.h" +#include "dkdm_recipient.h" #include <dcp/raw_convert.h> #include <dcp/name_format.h> #include <dcp/certificate_chain.h> @@ -127,10 +128,12 @@ Config::set_defaults () _win32_console = false; #endif _cinemas_file = path ("cinemas.xml"); + _dkdm_recipients_file = path ("dkdm_recipients.xml"); _show_hints_before_make_dcp = true; _confirm_kdm_email = true; _kdm_container_name_format = dcp::NameFormat ("KDM %f %c"); _kdm_filename_format = dcp::NameFormat ("KDM %f %c %s"); + _dkdm_filename_format = dcp::NameFormat ("DKDM %f %c %s"); _dcp_metadata_filename_format = dcp::NameFormat ("%t"); _dcp_asset_filename_format = dcp::NameFormat ("%t"); _jump_to_selected = true; @@ -232,6 +235,7 @@ Config::backup () boost::filesystem::copy_file(path("config.xml", false), path(String::compose("config.xml.%1", n), false)); boost::filesystem::copy_file(path("cinemas.xml", false), path(String::compose("cinemas.xml.%1", n), false)); + boost::filesystem::copy_file(path("dkdm_recipients.xml", false), path(String::compose("dkdm_recipients.xml.%1", n), false)); } catch (...) {} } @@ -334,7 +338,9 @@ try _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false); _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory"); - /* Load any cinemas from config.xml */ + /* Read any cinemas that are still lying around in the config file + * from an old version. + */ read_cinemas (f); _mail_server = f.string_child ("MailServer"); @@ -513,10 +519,12 @@ try } } _cinemas_file = f.optional_string_child("CinemasFile").get_value_or (path ("cinemas.xml").string ()); + _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or (path("dkdm_recipients.xml").string()); _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true); _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true); _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c")); _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s")); + _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s")); _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t")); _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t")); _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true); @@ -608,12 +616,17 @@ try _player_lock_file = f.optional_string_child("PlayerLockFile"); #endif - /* Replace any cinemas from config.xml with those from the configured file */ if (boost::filesystem::exists (_cinemas_file)) { cxml::Document f ("Cinemas"); f.read_file (_cinemas_file); read_cinemas (f); } + + if (boost::filesystem::exists (_dkdm_recipients_file)) { + cxml::Document f ("DKDMRecipients"); + f.read_file (_dkdm_recipients_file); + read_dkdm_recipients (f); + } } catch (...) { if (have_existing ("config.xml")) { @@ -647,6 +660,7 @@ Config::write () const { write_config (); write_cinemas (); + write_dkdm_recipients (); } void @@ -883,12 +897,16 @@ Config::write_config () const /* [XML] CinemasFile Filename of cinemas list file. */ root->add_child("CinemasFile")->add_child_text (_cinemas_file.string()); + /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */ + root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string()); /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */ root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0"); /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */ root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0"); /* [XML] KDMFilenameFormat Format for KDM filenames. */ root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ()); + /* [XML] KDMFilenameFormat Format for DKDM filenames. */ + root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification()); /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */ root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ()); /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */ @@ -1080,28 +1098,45 @@ Config::write_config () const } } + +template <class T> void -Config::write_cinemas () const +write_file (string root_node, string node, string version, list<shared_ptr<T> > things, boost::filesystem::path file) { xmlpp::Document doc; - xmlpp::Element* root = doc.create_root_node ("Cinemas"); - root->add_child("Version")->add_child_text ("1"); + xmlpp::Element* root = doc.create_root_node (root_node); + root->add_child("Version")->add_child_text(version); - BOOST_FOREACH (shared_ptr<Cinema> i, _cinemas) { - i->as_xml (root->add_child ("Cinema")); + BOOST_FOREACH (shared_ptr<T> i, things) { + i->as_xml (root->add_child(node)); } try { - doc.write_to_file_formatted (_cinemas_file.string() + ".tmp"); - boost::filesystem::remove (_cinemas_file); - boost::filesystem::rename (_cinemas_file.string() + ".tmp", _cinemas_file); + doc.write_to_file_formatted (file.string() + ".tmp"); + boost::filesystem::remove (file); + boost::filesystem::rename (file.string() + ".tmp", file); } catch (xmlpp::exception& e) { string s = e.what (); trim (s); - throw FileError (s, _cinemas_file); + throw FileError (s, file); } } + +void +Config::write_cinemas () const +{ + write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file); +} + + +void +Config::write_dkdm_recipients () const +{ + write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file); +} + + boost::filesystem::path Config::default_directory_or (boost::filesystem::path a) const { @@ -1293,6 +1328,37 @@ Config::set_cinemas_file (boost::filesystem::path file) changed (OTHER); } + +void +Config::read_dkdm_recipients (cxml::Document const & f) +{ + _dkdm_recipients.clear (); + list<cxml::NodePtr> cin = f.node_children ("DKDMRecipient"); + BOOST_FOREACH (cxml::ConstNodePtr i, f.node_children("DKDMRecipient")) { + _dkdm_recipients.push_back (shared_ptr<DKDMRecipient>(new DKDMRecipient(i))); + } +} + +void +Config::set_dkdm_recipients_file (boost::filesystem::path file) +{ + if (file == _dkdm_recipients_file) { + return; + } + + _dkdm_recipients_file = file; + + if (boost::filesystem::exists (_dkdm_recipients_file)) { + /* Existing file; read it in */ + cxml::Document f ("DKDMRecipients"); + f.read_file (_dkdm_recipients_file); + read_dkdm_recipients (f); + } + + changed (OTHER); +} + + void Config::save_template (shared_ptr<const Film> film, string name) const { diff --git a/src/lib/config.h b/src/lib/config.h index 749407403..f54ca3814 100644 --- a/src/lib/config.h +++ b/src/lib/config.h @@ -44,6 +44,7 @@ class Ratio; class Cinema; class Film; class DKDMGroup; +class DKDMRecipient; /** @class Config * @brief A singleton class holding configuration. @@ -76,6 +77,7 @@ public: USE_ANY_SERVERS, SERVERS, CINEMAS, + DKDM_RECIPIENTS, SOUND, SOUND_OUTPUT, INTERFACE_COMPLEXITY, @@ -148,6 +150,10 @@ public: return _cinemas; } + std::list<boost::shared_ptr<DKDMRecipient> > dkdm_recipients () const { + return _dkdm_recipients; + } + std::list<int> allowed_dcp_frame_rates () const { return _allowed_dcp_frame_rates; } @@ -342,6 +348,10 @@ public: return _cinemas_file; } + boost::filesystem::path dkdm_recipients_file () const { + return _dkdm_recipients_file; + } + bool show_hints_before_make_dcp () const { return _show_hints_before_make_dcp; } @@ -358,6 +368,10 @@ public: return _kdm_filename_format; } + dcp::NameFormat dkdm_filename_format () const { + return _dkdm_filename_format; + } + dcp::NameFormat dcp_metadata_filename_format () const { return _dcp_metadata_filename_format; } @@ -617,6 +631,16 @@ public: changed (CINEMAS); } + void add_dkdm_recipient (boost::shared_ptr<DKDMRecipient> c) { + _dkdm_recipients.push_back (c); + changed (DKDM_RECIPIENTS); + } + + void remove_dkdm_recipient (boost::shared_ptr<DKDMRecipient> c) { + _dkdm_recipients.remove (c); + changed (DKDM_RECIPIENTS); + } + void set_allowed_dcp_frame_rates (std::list<int> const & r) { maybe_set (_allowed_dcp_frame_rates, r); } @@ -814,6 +838,8 @@ public: void set_cinemas_file (boost::filesystem::path file); + void set_dkdm_recipients_file (boost::filesystem::path file); + void set_show_hints_before_make_dcp (bool s) { maybe_set (_show_hints_before_make_dcp, s); } @@ -859,6 +885,10 @@ public: maybe_set (_kdm_filename_format, n); } + void set_dkdm_filename_format (dcp::NameFormat n) { + maybe_set (_dkdm_filename_format, n); + } + void set_dcp_metadata_filename_format (dcp::NameFormat n) { maybe_set (_dcp_metadata_filename_format, n); } @@ -1111,6 +1141,7 @@ public: void write () const; void write_config () const; void write_cinemas () const; + void write_dkdm_recipients () const; void link (boost::filesystem::path new_file) const; void copy_and_link (boost::filesystem::path new_file) const; bool have_write_permission () const; @@ -1136,6 +1167,7 @@ private: void set_notification_email_to_default (); void set_cover_sheet_to_default (); void read_cinemas (cxml::Document const & f); + void read_dkdm_recipients (cxml::Document const & f); boost::shared_ptr<dcp::CertificateChain> create_certificate_chain (); boost::filesystem::path directory_or (boost::optional<boost::filesystem::path> dir, boost::filesystem::path a) const; void add_to_history_internal (std::vector<boost::filesystem::path>& h, boost::filesystem::path p); @@ -1215,6 +1247,7 @@ private: boost::optional<boost::filesystem::path> _default_kdm_directory; bool _default_upload_after_make_dcp; std::list<boost::shared_ptr<Cinema> > _cinemas; + std::list<boost::shared_ptr<DKDMRecipient> > _dkdm_recipients; std::string _mail_server; int _mail_port; EmailProtocol _mail_protocol; @@ -1257,9 +1290,11 @@ private: std::vector<boost::filesystem::path> _player_history; boost::shared_ptr<DKDMGroup> _dkdms; boost::filesystem::path _cinemas_file; + boost::filesystem::path _dkdm_recipients_file; bool _show_hints_before_make_dcp; bool _confirm_kdm_email; dcp::NameFormat _kdm_filename_format; + dcp::NameFormat _dkdm_filename_format; dcp::NameFormat _kdm_container_name_format; dcp::NameFormat _dcp_metadata_filename_format; dcp::NameFormat _dcp_asset_filename_format; diff --git a/src/lib/film.cc b/src/lib/film.cc index a24e9aa30..b233e5ee6 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -50,7 +50,7 @@ #include "text_content.h" #include "ffmpeg_content.h" #include "dcp_content.h" -#include "screen_kdm.h" +#include "kdm_with_metadata.h" #include "cinema.h" #include "change_signaller.h" #include "check_content_change_job.h" @@ -1502,47 +1502,6 @@ Film::make_kdm ( ).encrypt (signer, recipient, trusted_devices, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio); } -/** @param screens Screens to make KDMs for. - * @param cpl_file Path to CPL to make KDMs for. - * @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema. - * @param until KDM to time expressed as a local time in the time zone of the Screen's Cinema. - * @param formulation KDM formulation to use. - * @param disable_forensic_marking_picture true to disable forensic marking of picture. - * @param disable_forensic_marking_audio if not set, don't disable forensic marking of audio. If set to 0, - * disable all forensic marking; if set above 0, disable forensic marking above that channel. - */ -list<shared_ptr<ScreenKDM> > -Film::make_kdms ( - list<shared_ptr<Screen> > screens, - boost::filesystem::path cpl_file, - boost::posix_time::ptime from, - boost::posix_time::ptime until, - dcp::Formulation formulation, - bool disable_forensic_marking_picture, - optional<int> disable_forensic_marking_audio - ) const -{ - list<shared_ptr<ScreenKDM> > kdms; - - BOOST_FOREACH (shared_ptr<Screen> i, screens) { - if (i->recipient) { - dcp::EncryptedKDM const kdm = make_kdm ( - i->recipient.get(), - i->trusted_device_thumbprints(), - cpl_file, - dcp::LocalTime (from, i->cinema ? i->cinema->utc_offset_hour() : 0, i->cinema ? i->cinema->utc_offset_minute() : 0), - dcp::LocalTime (until, i->cinema ? i->cinema->utc_offset_hour() : 0, i->cinema ? i->cinema->utc_offset_minute() : 0), - formulation, - disable_forensic_marking_picture, - disable_forensic_marking_audio - ); - - kdms.push_back (shared_ptr<ScreenKDM>(new DCPScreenKDM(i, kdm))); - } - } - - return kdms; -} /** @return The approximate disk space required to encode a DCP of this film with the * current settings, in bytes. diff --git a/src/lib/film.h b/src/lib/film.h index 6cce07c17..40d366f8f 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -60,7 +60,6 @@ class AudioProcessor; class AudioMapping; class Ratio; class Job; -class ScreenKDM; class Film; struct isdcf_name_test; @@ -169,16 +168,6 @@ public: boost::optional<int> disable_forensic_marking_audio ) const; - std::list<boost::shared_ptr<ScreenKDM> > make_kdms ( - std::list<boost::shared_ptr<dcpomatic::Screen> > screens, - boost::filesystem::path cpl_file, - boost::posix_time::ptime from, - boost::posix_time::ptime until, - dcp::Formulation formulation, - bool disable_forensic_marking_picture, - boost::optional<int> disable_forensic_marking_audio - ) const; - int state_version () const { return _state_version; } diff --git a/src/lib/kdm_recipient.cc b/src/lib/kdm_recipient.cc new file mode 100644 index 000000000..d05192ac6 --- /dev/null +++ b/src/lib/kdm_recipient.cc @@ -0,0 +1,45 @@ +/* + Copyright (C) 2020 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "kdm_recipient.h" + +KDMRecipient::KDMRecipient (cxml::ConstNodePtr node) + : name (node->string_child("Name")) + , notes (node->optional_string_child("Notes").get_value_or ("")) +{ + if (node->optional_string_child("Certificate")) { + recipient = dcp::Certificate (node->string_child("Certificate")); + } else if (node->optional_string_child("Recipient")) { + recipient = dcp::Certificate (node->string_child("Recipient")); + } +} + + +void +KDMRecipient::as_xml (xmlpp::Element* parent) const +{ + parent->add_child("Name")->add_child_text(name); + if (recipient) { + parent->add_child("Recipient")->add_child_text(recipient->certificate(true)); + } + + parent->add_child("Notes")->add_child_text(notes); +} + diff --git a/src/lib/kdm_recipient.h b/src/lib/kdm_recipient.h new file mode 100644 index 000000000..c0533daeb --- /dev/null +++ b/src/lib/kdm_recipient.h @@ -0,0 +1,48 @@ +/* + Copyright (C) 2020 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#ifndef DCPOMATIC_KDM_RECIPIENT_H +#define DCPOMATIC_KDM_RECIPIENT_H + +#include <dcp/certificate.h> +#include <libcxml/cxml.h> +#include <libxml++/libxml++.h> +#include <boost/optional.hpp> +#include <string> + +class KDMRecipient +{ +public: + KDMRecipient (std::string const& name_, std::string const& notes_, boost::optional<dcp::Certificate> recipient_) + : name (name_) + , notes (notes_) + , recipient (recipient_) + {} + + explicit KDMRecipient (cxml::ConstNodePtr); + + virtual void as_xml (xmlpp::Element *) const; + + std::string name; + std::string notes; + boost::optional<dcp::Certificate> recipient; +}; + +#endif diff --git a/src/lib/cinema_kdms.cc b/src/lib/kdm_with_metadata.cc index 3af1e0d84..0ef1b8f38 100644 --- a/src/lib/cinema_kdms.cc +++ b/src/lib/kdm_with_metadata.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2015 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -18,159 +18,187 @@ */ -#include "exceptions.h" -#include "cinema_kdms.h" +#include "kdm_with_metadata.h" #include "cinema.h" #include "screen.h" -#include "config.h" #include "util.h" -#include "emailer.h" -#include "compose.hpp" -#include "log.h" #include "zipper.h" +#include "config.h" #include "dcpomatic_log.h" +#include "emailer.h" #include <boost/foreach.hpp> +#include <boost/function.hpp> +#include <boost/function.hpp> #include "i18n.h" -using std::list; -using std::cout; using std::string; -using std::runtime_error; +using std::cout; +using std::list; using boost::shared_ptr; +using boost::optional; using boost::function; +int +write_files ( + list<KDMWithMetadataPtr> kdms, + boost::filesystem::path directory, + dcp::NameFormat name_format, + boost::function<bool (boost::filesystem::path)> confirm_overwrite + ) +{ + int written = 0; + + if (directory == "-") { + /* Write KDMs to the stdout */ + BOOST_FOREACH (KDMWithMetadataPtr i, kdms) { + cout << i->kdm_as_xml (); + ++written; + } + + return written; + } + + if (!boost::filesystem::exists (directory)) { + boost::filesystem::create_directories (directory); + } + + /* Write KDMs to the specified directory */ + BOOST_FOREACH (KDMWithMetadataPtr i, kdms) { + boost::filesystem::path out = directory / careful_string_filter(name_format.get(i->name_values(), ".xml")); + if (!boost::filesystem::exists (out) || confirm_overwrite (out)) { + i->kdm_as_xml (out); + ++written; + } + } + + return written; +} + + +optional<string> +KDMWithMetadata::get (char k) const +{ + dcp::NameFormat::Map::const_iterator i = _name_values.find (k); + if (i == _name_values.end()) { + return optional<string>(); + } + + return i->second; +} + + void -CinemaKDMs::make_zip_file (boost::filesystem::path zip_file, dcp::NameFormat name_format, dcp::NameFormat::Map name_values) const +make_zip_file (list<KDMWithMetadataPtr> kdms, boost::filesystem::path zip_file, dcp::NameFormat name_format) { Zipper zipper (zip_file); - name_values['c'] = cinema->name; - - BOOST_FOREACH (shared_ptr<ScreenKDM> i, screen_kdms) { - name_values['s'] = i->screen->name; - name_values['i'] = i->kdm_id (); - string const name = careful_string_filter(name_format.get(name_values, ".xml")); + BOOST_FOREACH (KDMWithMetadataPtr i, kdms) { + string const name = careful_string_filter(name_format.get(i->name_values(), ".xml")); zipper.add (name, i->kdm_as_xml()); } zipper.close (); } -/** Collect a list of ScreenKDMs into a list of CinemaKDMs so that each - * CinemaKDM contains the KDMs for its cinema. + +/** Collect a list of KDMWithMetadatas into a list of lists so that + * each list contains the KDMs for one list. */ -list<CinemaKDMs> -CinemaKDMs::collect (list<shared_ptr<ScreenKDM> > screen_kdms) +list<list<KDMWithMetadataPtr> > +collect (list<KDMWithMetadataPtr> kdms) { - list<CinemaKDMs> cinema_kdms; + list<list<KDMWithMetadataPtr> > grouped; - while (!screen_kdms.empty ()) { + BOOST_FOREACH (KDMWithMetadataPtr i, kdms) { - /* Get all the screens from a single cinema */ + list<list<KDMWithMetadataPtr> >::iterator j = grouped.begin (); - CinemaKDMs ck; - - list<shared_ptr<ScreenKDM> >::iterator i = screen_kdms.begin (); - ck.cinema = (*i)->screen->cinema; - ck.screen_kdms.push_back (*i); - list<shared_ptr<ScreenKDM> >::iterator j = i; - ++i; - screen_kdms.remove (*j); - - while (i != screen_kdms.end ()) { - if ((*i)->screen->cinema == ck.cinema) { - ck.screen_kdms.push_back (*i); - list<shared_ptr<ScreenKDM> >::iterator j = i; - ++i; - screen_kdms.remove (*j); - } else { - ++i; + while (j != grouped.end()) { + if (j->front()->group() == i->group()) { + j->push_back (i); + break; } + ++j; } - cinema_kdms.push_back (ck); + if (j == grouped.end()) { + grouped.push_back (list<KDMWithMetadataPtr>()); + grouped.back().push_back (i); + } } - return cinema_kdms; + return grouped; } -/** Write one directory per cinema into another directory */ + +/** Write one directory per list into another directory */ int -CinemaKDMs::write_directories ( - list<CinemaKDMs> cinema_kdms, +write_directories ( + list<list<KDMWithMetadataPtr> > kdms, boost::filesystem::path directory, dcp::NameFormat container_name_format, dcp::NameFormat filename_format, - dcp::NameFormat::Map name_values, function<bool (boost::filesystem::path)> confirm_overwrite ) { - /* No specific screen */ - name_values['s'] = ""; - int written = 0; - BOOST_FOREACH (CinemaKDMs const & i, cinema_kdms) { + BOOST_FOREACH (list<KDMWithMetadataPtr> const & i, kdms) { boost::filesystem::path path = directory; - name_values['c'] = i.cinema->name; - path /= container_name_format.get(name_values, ""); + path /= container_name_format.get(i.front()->name_values(), "", "s"); if (!boost::filesystem::exists (path) || confirm_overwrite (path)) { boost::filesystem::create_directories (path); - ScreenKDM::write_files (i.screen_kdms, path, filename_format, name_values, confirm_overwrite); + write_files (i, path, filename_format, confirm_overwrite); } - written += i.screen_kdms.size(); + written += i.size(); } return written; } + /** Write one ZIP file per cinema into a directory */ int -CinemaKDMs::write_zip_files ( - list<CinemaKDMs> cinema_kdms, +write_zip_files ( + list<list<KDMWithMetadataPtr> > kdms, boost::filesystem::path directory, dcp::NameFormat container_name_format, dcp::NameFormat filename_format, - dcp::NameFormat::Map name_values, function<bool (boost::filesystem::path)> confirm_overwrite ) { - /* No specific screen */ - name_values['s'] = ""; - int written = 0; - BOOST_FOREACH (CinemaKDMs const & i, cinema_kdms) { + BOOST_FOREACH (list<KDMWithMetadataPtr> const & i, kdms) { boost::filesystem::path path = directory; - name_values['c'] = i.cinema->name; - path /= container_name_format.get(name_values, ".zip"); + path /= container_name_format.get(i.front()->name_values(), ".zip", "s"); if (!boost::filesystem::exists (path) || confirm_overwrite (path)) { if (boost::filesystem::exists (path)) { /* Creating a new zip file over an existing one is an error */ boost::filesystem::remove (path); } - i.make_zip_file (path, filename_format, name_values); - written += i.screen_kdms.size(); + make_zip_file (i, path, filename_format); + written += i.size(); } } return written; } + /** Email one ZIP file per cinema to the cinema. - * @param cinema_kdms KDMS to email. + * @param kdms KDMs to email. * @param container_name_format Format of folder / ZIP to use. * @param filename_format Format of filenames to use. * @param name_values Values to substitute into \p container_name_format and \p filename_format. * @param cpl_name Name of the CPL that the KDMs are for. */ void -CinemaKDMs::email ( - list<CinemaKDMs> cinema_kdms, +email ( + list<list<KDMWithMetadataPtr> > kdms, dcp::NameFormat container_name_format, dcp::NameFormat filename_format, - dcp::NameFormat::Map name_values, string cpl_name ) { @@ -180,41 +208,39 @@ CinemaKDMs::email ( throw NetworkError (_("No mail server configured in preferences")); } - /* No specific screen */ - name_values['s'] = ""; - - BOOST_FOREACH (CinemaKDMs const & i, cinema_kdms) { + BOOST_FOREACH (list<KDMWithMetadataPtr> const & i, kdms) { - if (i.cinema->emails.empty()) { + if (i.front()->emails().empty()) { continue; } - name_values['c'] = i.cinema->name; - boost::filesystem::path zip_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); boost::filesystem::create_directories (zip_file); - zip_file /= container_name_format.get(name_values, ".zip"); - i.make_zip_file (zip_file, filename_format, name_values); + zip_file /= container_name_format.get(i.front()->name_values(), ".zip"); + make_zip_file (i, zip_file, filename_format); string subject = config->kdm_subject(); boost::algorithm::replace_all (subject, "$CPL_NAME", cpl_name); - boost::algorithm::replace_all (subject, "$START_TIME", name_values['b']); - boost::algorithm::replace_all (subject, "$END_TIME", name_values['e']); - boost::algorithm::replace_all (subject, "$CINEMA_NAME", i.cinema->name); + boost::algorithm::replace_all (subject, "$START_TIME", i.front()->get('b').get_value_or("")); + boost::algorithm::replace_all (subject, "$END_TIME", i.front()->get('e').get_value_or("")); + boost::algorithm::replace_all (subject, "$CINEMA_NAME", i.front()->get('c').get_value_or("")); string body = config->kdm_email().c_str(); boost::algorithm::replace_all (body, "$CPL_NAME", cpl_name); - boost::algorithm::replace_all (body, "$START_TIME", name_values['b']); - boost::algorithm::replace_all (body, "$END_TIME", name_values['e']); - boost::algorithm::replace_all (body, "$CINEMA_NAME", i.cinema->name); + boost::algorithm::replace_all (body, "$START_TIME", i.front()->get('b').get_value_or("")); + boost::algorithm::replace_all (body, "$END_TIME", i.front()->get('e').get_value_or("")); + boost::algorithm::replace_all (body, "$CINEMA_NAME", i.front()->get('c').get_value_or("")); string screens; - BOOST_FOREACH (shared_ptr<ScreenKDM> j, i.screen_kdms) { - screens += j->screen->name + ", "; + BOOST_FOREACH (KDMWithMetadataPtr j, i) { + optional<string> screen_name = j->get('n'); + if (screen_name) { + screens += *screen_name + ", "; + } } boost::algorithm::replace_all (body, "$SCREENS", screens.substr (0, screens.length() - 2)); - Emailer email (config->kdm_from(), i.cinema->emails, subject, body); + Emailer email (config->kdm_from(), i.front()->emails(), subject, body); BOOST_FOREACH (string i, config->kdm_cc()) { email.add_cc (i); @@ -223,7 +249,7 @@ CinemaKDMs::email ( email.add_bcc (config->kdm_bcc ()); } - email.add_attachment (zip_file, container_name_format.get(name_values, ".zip"), "application/zip"); + email.add_attachment (zip_file, container_name_format.get(i.front()->name_values(), ".zip"), "application/zip"); Config* c = Config::instance (); diff --git a/src/lib/kdm_with_metadata.h b/src/lib/kdm_with_metadata.h new file mode 100644 index 000000000..b6bec1c4c --- /dev/null +++ b/src/lib/kdm_with_metadata.h @@ -0,0 +1,135 @@ +/* + Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#ifndef DCPOMATIC_KDM_WITH_METADATA_H +#define DCPOMATIC_KDM_WITH_METADATA_H + +#ifdef DCPOMATIC_VARIANT_SWAROOP +#include "encrypted_ecinema_kdm.h" +#endif +#include <dcp/encrypted_kdm.h> +#include <dcp/name_format.h> +#include <boost/shared_ptr.hpp> + +class Cinema; + +class KDMWithMetadata +{ +public: + KDMWithMetadata (dcp::NameFormat::Map const& name_values, void const* group, std::list<std::string> emails) + : _name_values (name_values) + , _group (group) + , _emails (emails) + {} + + virtual ~KDMWithMetadata () {} + + virtual std::string kdm_as_xml () const = 0; + virtual void kdm_as_xml (boost::filesystem::path out) const = 0; + + dcp::NameFormat::Map const& name_values () const { + return _name_values; + } + + boost::optional<std::string> get (char k) const; + + void const* group () const { + return _group; + } + + std::list<std::string> emails () const { + return _emails; + } + +private: + dcp::NameFormat::Map _name_values; + void const* _group; + std::list<std::string> _emails; +}; + + +typedef boost::shared_ptr<KDMWithMetadata> KDMWithMetadataPtr; + + +int write_files ( + std::list<KDMWithMetadataPtr> screen_kdms, boost::filesystem::path directory, + dcp::NameFormat name_format, boost::function<bool (boost::filesystem::path)> confirm_overwrite + ); + + +void make_zip_file (std::list<KDMWithMetadataPtr> kdms, boost::filesystem::path zip_file, dcp::NameFormat name_format); + + +std::list<std::list<KDMWithMetadataPtr> > collect (std::list<KDMWithMetadataPtr> kdms); + + +int write_directories ( + std::list<std::list<KDMWithMetadataPtr> > kdms, + boost::filesystem::path directory, + dcp::NameFormat container_name_format, + dcp::NameFormat filename_format, + boost::function<bool (boost::filesystem::path)> confirm_overwrite + ); + + +int write_zip_files ( + std::list<std::list<KDMWithMetadataPtr> > kdms, + boost::filesystem::path directory, + dcp::NameFormat container_name_format, + dcp::NameFormat filename_format, + boost::function<bool (boost::filesystem::path)> confirm_overwrite + ); + + +void email ( + std::list<std::list<KDMWithMetadataPtr> > kdms, + dcp::NameFormat container_name_format, + dcp::NameFormat filename_format, + std::string cpl_name + ); + + +template <class T> +class SpecialKDMWithMetadata : public KDMWithMetadata +{ +public: + SpecialKDMWithMetadata (dcp::NameFormat::Map const& name_values, void const* group, std::list<std::string> emails, T k) + : KDMWithMetadata (name_values, group, emails) + , kdm (k) + {} + + std::string kdm_as_xml () const { + return kdm.as_xml (); + } + + void kdm_as_xml (boost::filesystem::path out) const { + return kdm.as_xml (out); + } + + T kdm; +}; + +typedef SpecialKDMWithMetadata<dcp::EncryptedKDM> DCPKDMWithMetadata; +#ifdef DCPOMATIC_VARIANT_SWAROOP +typedef SpecialKDMWithMetadata<EncryptedECinemaKDM> ECinemaKDMWithMetadata; +#endif + +#endif + diff --git a/src/lib/screen.cc b/src/lib/screen.cc index fe62a5e6c..61a27f2bc 100644 --- a/src/lib/screen.cc +++ b/src/lib/screen.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2020 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -19,24 +19,24 @@ */ #include "screen.h" +#include "kdm_with_metadata.h" +#include "film.h" +#include "cinema.h" #include <libxml++/libxml++.h> #include <boost/foreach.hpp> #include <boost/algorithm/string.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> using std::string; using std::vector; +using std::list; +using boost::shared_ptr; +using boost::optional; using namespace dcpomatic; Screen::Screen (cxml::ConstNodePtr node) - : name (node->string_child("Name")) - , notes (node->optional_string_child("Notes").get_value_or ("")) + : KDMRecipient (node) { - if (node->optional_string_child ("Certificate")) { - recipient = dcp::Certificate (node->string_child ("Certificate")); - } else if (node->optional_string_child ("Recipient")) { - recipient = dcp::Certificate (node->string_child ("Recipient")); - } - BOOST_FOREACH (cxml::ConstNodePtr i, node->node_children ("TrustedDevice")) { if (boost::algorithm::starts_with(i->content(), "-----BEGIN CERTIFICATE-----")) { trusted_devices.push_back (TrustedDevice(dcp::Certificate(i->content()))); @@ -49,13 +49,7 @@ Screen::Screen (cxml::ConstNodePtr node) void Screen::as_xml (xmlpp::Element* parent) const { - parent->add_child("Name")->add_child_text (name); - if (recipient) { - parent->add_child("Recipient")->add_child_text (recipient->certificate (true)); - } - - parent->add_child("Notes")->add_child_text (notes); - + KDMRecipient::as_xml (parent); BOOST_FOREACH (TrustedDevice i, trusted_devices) { parent->add_child("TrustedDevice")->add_child_text(i.as_string()); } @@ -71,34 +65,48 @@ Screen::trusted_device_thumbprints () const return t; } -TrustedDevice::TrustedDevice (string thumbprint) - : _thumbprint (thumbprint) -{ - -} - -TrustedDevice::TrustedDevice (dcp::Certificate certificate) - : _certificate (certificate) -{ - -} -string -TrustedDevice::as_string () const +KDMWithMetadataPtr +kdm_for_screen ( + shared_ptr<const Film> film, + boost::filesystem::path cpl, + shared_ptr<const dcpomatic::Screen> screen, + boost::posix_time::ptime valid_from, + boost::posix_time::ptime valid_to, + dcp::Formulation formulation, + bool disable_forensic_marking_picture, + optional<int> disable_forensic_marking_audio + ) { - if (_certificate) { - return _certificate->certificate(true); + if (!screen->recipient) { + return KDMWithMetadataPtr(); } - return *_thumbprint; -} - -string -TrustedDevice::thumbprint () const -{ - if (_certificate) { - return _certificate->thumbprint (); + shared_ptr<const Cinema> cinema = screen->cinema; + dcp::LocalTime const begin(valid_from, cinema ? cinema->utc_offset_hour() : 0, cinema ? cinema->utc_offset_minute() : 0); + dcp::LocalTime const end (valid_to, cinema ? cinema->utc_offset_hour() : 0, cinema ? cinema->utc_offset_minute() : 0); + + dcp::EncryptedKDM const kdm = film->make_kdm ( + screen->recipient.get(), + screen->trusted_device_thumbprints(), + cpl, + begin, + end, + formulation, + disable_forensic_marking_picture, + disable_forensic_marking_audio + ); + + dcp::NameFormat::Map name_values; + if (cinema) { + name_values['c'] = cinema->name; } + name_values['s'] = screen->name; + name_values['f'] = film->name(); + name_values['b'] = begin.date() + " " + begin.time_of_day(true, false); + name_values['e'] = end.date() + " " + end.time_of_day(true, false); + name_values['i'] = kdm.cpl_id(); - return *_thumbprint; + return KDMWithMetadataPtr(new DCPKDMWithMetadata(name_values, cinema.get(), cinema ? cinema->emails : list<string>(), kdm)); } + diff --git a/src/lib/screen.h b/src/lib/screen.h index 40990b684..013afff85 100644 --- a/src/lib/screen.h +++ b/src/lib/screen.h @@ -21,30 +21,16 @@ #ifndef DCPOMATIC_SCREEN_H #define DCPOMATIC_SCREEN_H +#include "kdm_with_metadata.h" +#include "kdm_recipient.h" +#include "trusted_device.h" #include <dcp/certificate.h> #include <libcxml/cxml.h> #include <boost/optional.hpp> #include <string> class Cinema; - -class TrustedDevice -{ -public: - explicit TrustedDevice (std::string); - explicit TrustedDevice (dcp::Certificate); - - boost::optional<dcp::Certificate> certificate () const { - return _certificate; - } - - std::string thumbprint () const; - std::string as_string () const; - -private: - boost::optional<dcp::Certificate> _certificate; - boost::optional<std::string> _thumbprint; -}; +class Film; namespace dcpomatic { @@ -55,14 +41,12 @@ namespace dcpomatic { * `recipient' (i.e. the mediablock) and the certificates/thumbprints * of any trusted devices. */ -class Screen +class Screen : public KDMRecipient { public: - Screen (std::string const & na, std::string const & no, boost::optional<dcp::Certificate> rec, std::vector<TrustedDevice> td) - : name (na) - , notes (no) - , recipient (rec) - , trusted_devices (td) + Screen (std::string const & name_, std::string const & notes_, boost::optional<dcp::Certificate> recipient_, std::vector<TrustedDevice> trusted_devices_) + : KDMRecipient (name_, notes_, recipient_) + , trusted_devices (trusted_devices_) {} explicit Screen (cxml::ConstNodePtr); @@ -71,12 +55,22 @@ public: std::vector<std::string> trusted_device_thumbprints () const; boost::shared_ptr<Cinema> cinema; - std::string name; - std::string notes; - boost::optional<dcp::Certificate> recipient; std::vector<TrustedDevice> trusted_devices; }; } +KDMWithMetadataPtr +kdm_for_screen ( + boost::shared_ptr<const Film> film, + boost::filesystem::path cpl, + boost::shared_ptr<const dcpomatic::Screen> screen, + boost::posix_time::ptime valid_from, + boost::posix_time::ptime valid_to, + dcp::Formulation formulation, + bool disable_forensic_marking_picture, + boost::optional<int> disable_forensic_marking_audio + ); + + #endif diff --git a/src/lib/screen_kdm.cc b/src/lib/screen_kdm.cc deleted file mode 100644 index f9a3fa36e..000000000 --- a/src/lib/screen_kdm.cc +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net> - - This file is part of DCP-o-matic. - - DCP-o-matic is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - DCP-o-matic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "screen_kdm.h" -#include "cinema.h" -#include "screen.h" -#include "util.h" -#include <boost/foreach.hpp> - -using std::string; -using std::cout; -using std::list; -using boost::shared_ptr; - -int -ScreenKDM::write_files ( - list<shared_ptr<ScreenKDM> > screen_kdms, - boost::filesystem::path directory, - dcp::NameFormat name_format, - dcp::NameFormat::Map name_values, - boost::function<bool (boost::filesystem::path)> confirm_overwrite - ) -{ - int written = 0; - - if (directory == "-") { - /* Write KDMs to the stdout */ - BOOST_FOREACH (shared_ptr<ScreenKDM> i, screen_kdms) { - cout << i->kdm_as_xml (); - ++written; - } - - return written; - } - - if (!boost::filesystem::exists (directory)) { - boost::filesystem::create_directories (directory); - } - - /* Write KDMs to the specified directory */ - BOOST_FOREACH (shared_ptr<ScreenKDM> i, screen_kdms) { - name_values['c'] = i->screen->cinema ? i->screen->cinema->name : ""; - name_values['s'] = i->screen->name; - name_values['i'] = i->kdm_id (); - boost::filesystem::path out = directory / careful_string_filter(name_format.get(name_values, ".xml")); - if (!boost::filesystem::exists (out) || confirm_overwrite (out)) { - i->kdm_as_xml (out); - ++written; - } - } - - return written; -} diff --git a/src/lib/screen_kdm.h b/src/lib/screen_kdm.h deleted file mode 100644 index a1e36245c..000000000 --- a/src/lib/screen_kdm.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net> - - This file is part of DCP-o-matic. - - DCP-o-matic is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - DCP-o-matic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#ifndef DCPOMATIC_SCREEN_KDM_H -#define DCPOMATIC_SCREEN_KDM_H - -#ifdef DCPOMATIC_VARIANT_SWAROOP -#include "encrypted_ecinema_kdm.h" -#endif -#include <dcp/encrypted_kdm.h> -#include <dcp/name_format.h> -#include <boost/shared_ptr.hpp> - -namespace dcpomatic { - class Screen; -} - -/** Simple class to collect a screen and an encrypted KDM */ -class ScreenKDM -{ -public: - ScreenKDM (boost::shared_ptr<dcpomatic::Screen> s) - : screen (s) - {} - - virtual ~ScreenKDM () {} - - virtual std::string kdm_as_xml () const = 0; - virtual void kdm_as_xml (boost::filesystem::path out) const = 0; - virtual std::string kdm_id () const = 0; - - static int write_files ( - std::list<boost::shared_ptr<ScreenKDM> > screen_kdms, boost::filesystem::path directory, - dcp::NameFormat name_format, dcp::NameFormat::Map name_values, - boost::function<bool (boost::filesystem::path)> confirm_overwrite - ); - - boost::shared_ptr<dcpomatic::Screen> screen; -}; - -class DCPScreenKDM : public ScreenKDM -{ -public: - DCPScreenKDM (boost::shared_ptr<dcpomatic::Screen> s, dcp::EncryptedKDM k) - : ScreenKDM (s) - , kdm (k) - {} - - std::string kdm_as_xml () const { - return kdm.as_xml (); - } - - void kdm_as_xml (boost::filesystem::path out) const { - return kdm.as_xml (out); - } - - std::string kdm_id () const { - return kdm.cpl_id (); - } - - dcp::EncryptedKDM kdm; -}; - -#ifdef DCPOMATIC_VARIANT_SWAROOP -class ECinemaScreenKDM : public ScreenKDM -{ -public: - ECinemaScreenKDM (boost::shared_ptr<dcpomatic::Screen> s, EncryptedECinemaKDM k) - : ScreenKDM (s) - , kdm (k) - {} - - std::string kdm_as_xml () const { - return kdm.as_xml (); - } - - void kdm_as_xml (boost::filesystem::path out) const { - return kdm.as_xml (out); - } - - std::string kdm_id () const { - return kdm.id (); - } - - EncryptedECinemaKDM kdm; -}; -#endif - -#endif diff --git a/src/lib/send_kdm_email_job.cc b/src/lib/send_kdm_email_job.cc index 1b476fa63..55a171811 100644 --- a/src/lib/send_kdm_email_job.cc +++ b/src/lib/send_kdm_email_job.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2020 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -20,8 +20,8 @@ #include "send_kdm_email_job.h" #include "compose.hpp" +#include "kdm_with_metadata.h" #include "film.h" -#include "cinema_kdms.h" #include <list> #include "i18n.h" @@ -29,26 +29,43 @@ using std::string; using std::list; using boost::shared_ptr; +using boost::optional; -/** @param cinema_kdms KDMs to email. +SendKDMEmailJob::SendKDMEmailJob ( + list<KDMWithMetadataPtr> kdms, + dcp::NameFormat container_name_format, + dcp::NameFormat filename_format, + string cpl_name + ) + : Job (shared_ptr<Film>()) + , _container_name_format (container_name_format) + , _filename_format (filename_format) + , _cpl_name (cpl_name) +{ + BOOST_FOREACH (KDMWithMetadataPtr i, kdms) { + list<KDMWithMetadataPtr> s; + s.push_back (i); + _kdms.push_back (s); + } +} + +/** @param kdms KDMs to email. * @param container_name_format Format to ues for folders / ZIP files. * @param filename_format Format to use for filenames. * @param name_values Values to substitute into \p container_name_format and \p filename_format. * @param cpl_name Name of the CPL that the KDMs are for. */ SendKDMEmailJob::SendKDMEmailJob ( - list<CinemaKDMs> cinema_kdms, + list<list<KDMWithMetadataPtr> > kdms, dcp::NameFormat container_name_format, dcp::NameFormat filename_format, - dcp::NameFormat::Map name_values, string cpl_name ) : Job (shared_ptr<Film>()) , _container_name_format (container_name_format) , _filename_format (filename_format) - , _name_values (name_values) , _cpl_name (cpl_name) - , _cinema_kdms (cinema_kdms) + , _kdms (kdms) { } @@ -61,12 +78,12 @@ SendKDMEmailJob::~SendKDMEmailJob () string SendKDMEmailJob::name () const { - dcp::NameFormat::Map::const_iterator i = _name_values.find ('f'); - if (i == _name_values.end() || i->second.empty ()) { + optional<string> f = _kdms.front().front()->get('f'); + if (!f || f->empty()) { return _("Email KDMs"); } - return String::compose (_("Email KDMs for %1"), i->second); + return String::compose (_("Email KDMs for %2"), *f); } string @@ -79,7 +96,7 @@ void SendKDMEmailJob::run () { set_progress_unknown (); - CinemaKDMs::email (_cinema_kdms, _container_name_format, _filename_format, _name_values, _cpl_name); + email (_kdms, _container_name_format, _filename_format, _cpl_name); set_progress (1); set_state (FINISHED_OK); } diff --git a/src/lib/send_kdm_email_job.h b/src/lib/send_kdm_email_job.h index a7196fe15..fa409edaa 100644 --- a/src/lib/send_kdm_email_job.h +++ b/src/lib/send_kdm_email_job.h @@ -19,6 +19,7 @@ */ #include "job.h" +#include "kdm_with_metadata.h" #include <dcp/types.h> #include <dcp/name_format.h> #include <boost/filesystem.hpp> @@ -27,19 +28,25 @@ namespace dcpomatic { class Screen; } -class CinemaKDMs; class Log; class SendKDMEmailJob : public Job { public: SendKDMEmailJob ( - std::list<CinemaKDMs> cinema_kdms, + std::list<KDMWithMetadataPtr> kdms, dcp::NameFormat container_name_format, dcp::NameFormat filename_format, - dcp::NameFormat::Map name_values, std::string cpl_name ); + + SendKDMEmailJob ( + std::list<std::list<KDMWithMetadataPtr> > kdms, + dcp::NameFormat container_name_format, + dcp::NameFormat filename_format, + std::string cpl_name + ); + ~SendKDMEmailJob (); std::string name () const; @@ -49,7 +56,6 @@ public: private: dcp::NameFormat _container_name_format; dcp::NameFormat _filename_format; - dcp::NameFormat::Map _name_values; std::string _cpl_name; - std::list<CinemaKDMs> _cinema_kdms; + std::list<std::list<KDMWithMetadataPtr> > _kdms; }; diff --git a/src/lib/trusted_device.cc b/src/lib/trusted_device.cc new file mode 100644 index 000000000..5849b8fd2 --- /dev/null +++ b/src/lib/trusted_device.cc @@ -0,0 +1,56 @@ +/* + Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "trusted_device.h" + +using std::string; + +TrustedDevice::TrustedDevice (string thumbprint) + : _thumbprint (thumbprint) +{ + +} + +TrustedDevice::TrustedDevice (dcp::Certificate certificate) + : _certificate (certificate) +{ + +} + +string +TrustedDevice::as_string () const +{ + if (_certificate) { + return _certificate->certificate(true); + } + + return *_thumbprint; +} + +string +TrustedDevice::thumbprint () const +{ + if (_certificate) { + return _certificate->thumbprint (); + } + + return *_thumbprint; +} + diff --git a/src/lib/trusted_device.h b/src/lib/trusted_device.h new file mode 100644 index 000000000..7c6af3ea8 --- /dev/null +++ b/src/lib/trusted_device.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#ifndef DCPOMATIC_TRUSTED_DEVICE_H +#define DCPOMATIC_TRUSTED_DEVICE_H + +#include <dcp/certificate.h> +#include <boost/optional.hpp> +#include <string> + +class TrustedDevice +{ +public: + explicit TrustedDevice (std::string); + explicit TrustedDevice (dcp::Certificate); + + boost::optional<dcp::Certificate> certificate () const { + return _certificate; + } + + std::string thumbprint () const; + std::string as_string () const; + +private: + boost::optional<dcp::Certificate> _certificate; + boost::optional<std::string> _thumbprint; +}; + +#endif diff --git a/src/lib/wscript b/src/lib/wscript index ea52079d0..0f2a5d197 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -47,7 +47,6 @@ sources = """ checker.cc check_content_change_job.cc cinema.cc - cinema_kdms.cc cinema_sound_processor.cc colour_conversion.cc config.cc @@ -78,6 +77,7 @@ sources = """ decoder_part.cc decrypted_ecinema_kdm.cc digester.cc + dkdm_recipient.cc dkdm_wrapper.cc dolby_cp750.cc edid.cc @@ -125,6 +125,8 @@ sources = """ job_manager.cc j2k_encoder.cc json_server.cc + kdm_with_metadata.cc + kdm_recipient.cc lock_file_checker.cc log.cc log_entry.cc @@ -145,7 +147,6 @@ sources = """ scoped_temporary.cc scp_uploader.cc screen.cc - screen_kdm.cc send_kdm_email_job.cc send_notification_email_job.cc send_problem_report_job.cc @@ -163,6 +164,7 @@ sources = """ text_ring_buffers.cc timer.cc transcode_job.cc + trusted_device.cc types.cc signal_manager.cc stdout_log.cc diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index aea058d80..f69ab5a6a 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -33,6 +33,7 @@ #include "wx/recreate_chain_dialog.h" #include "wx/about_dialog.h" #include "wx/kdm_dialog.h" +#include "wx/dkdm_dialog.h" #include "wx/self_dkdm_dialog.h" #include "wx/servers_list_dialog.h" #include "wx/hints_dialog.h" @@ -65,14 +66,13 @@ #include "lib/job_manager.h" #include "lib/exceptions.h" #include "lib/cinema.h" -#include "lib/screen_kdm.h" +#include "lib/kdm_with_metadata.h" #include "lib/send_kdm_email_job.h" #include "lib/encode_server_finder.h" #include "lib/update_checker.h" #include "lib/cross.h" #include "lib/content_factory.h" #include "lib/compose.hpp" -#include "lib/cinema_kdms.h" #include "lib/dcpomatic_socket.h" #include "lib/hints.h" #include "lib/dcp_content.h" @@ -227,6 +227,7 @@ enum { ID_jobs_make_dcp, ID_jobs_make_dcp_batch, ID_jobs_make_kdms, + ID_jobs_make_dkdms, ID_jobs_make_self_dkdm, ID_jobs_export, ID_jobs_send_dcp_to_tms, @@ -262,6 +263,7 @@ public: , _servers_list_dialog (0) , _config_dialog (0) , _kdm_dialog (0) + , _dkdm_dialog (0) , _templates_dialog (0) , _file_menu (0) , _history_items (0) @@ -318,6 +320,7 @@ public: Bind (wxEVT_MENU, boost::bind (&DOMFrame::content_scale_to_fit_height, this), ID_content_scale_to_fit_height); Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_dcp, this), ID_jobs_make_dcp); Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_kdms, this), ID_jobs_make_kdms); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_dkdms, this), ID_jobs_make_dkdms); Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_dcp_batch, this), ID_jobs_make_dcp_batch); Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_self_dkdm, this), ID_jobs_make_self_dkdm); Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_export, this), ID_jobs_export); @@ -797,6 +800,21 @@ private: _kdm_dialog->Show (); } + void jobs_make_dkdms () + { + if (!_film) { + return; + } + + if (_dkdm_dialog) { + _dkdm_dialog->Destroy (); + _dkdm_dialog = 0; + } + + _dkdm_dialog = new DKDMDialog (this, _film); + _dkdm_dialog->Show (); + } + /** @return false if we succeeded, true if not */ bool send_to_other_tool (int port, function<void(boost::filesystem::path)> start, string message) { @@ -1314,6 +1332,7 @@ private: add_item (jobs_menu, _("Make DCP in &batch converter\tCtrl-B"), ID_jobs_make_dcp_batch, NEEDS_FILM | NOT_DURING_DCP_CREATION); jobs_menu->AppendSeparator (); add_item (jobs_menu, _("Make &KDMs...\tCtrl-K"), ID_jobs_make_kdms, NEEDS_FILM); + add_item (jobs_menu, _("Make &DKDMs...\tCtrl-D"), ID_jobs_make_dkdms, NEEDS_FILM); add_item (jobs_menu, _("Make DKDM for DCP-o-matic..."), ID_jobs_make_self_dkdm, NEEDS_FILM | NEEDS_ENCRYPTION); jobs_menu->AppendSeparator (); add_item (jobs_menu, _("Export...\tCtrl-E"), ID_jobs_export, NEEDS_FILM); @@ -1485,6 +1504,7 @@ private: ServersListDialog* _servers_list_dialog; wxPreferencesEditor* _config_dialog; KDMDialog* _kdm_dialog; + DKDMDialog* _dkdm_dialog; TemplatesDialog* _templates_dialog; wxMenu* _file_menu; shared_ptr<Film> _film; diff --git a/src/tools/dcpomatic_kdm.cc b/src/tools/dcpomatic_kdm.cc index ef6b783f4..117e756c7 100644 --- a/src/tools/dcpomatic_kdm.cc +++ b/src/tools/dcpomatic_kdm.cc @@ -38,9 +38,8 @@ #include "lib/util.h" #include "lib/screen.h" #include "lib/job_manager.h" -#include "lib/screen_kdm.h" +#include "lib/kdm_with_metadata.h" #include "lib/exceptions.h" -#include "lib/cinema_kdms.h" #include "lib/send_kdm_email_job.h" #include "lib/compose.hpp" #include "lib/cinema.h" @@ -303,7 +302,7 @@ private: return; } - list<shared_ptr<ScreenKDM> > screen_kdms; + list<KDMWithMetadataPtr> kdms; string title; #ifdef DCPOMATIC_VARIANT_SWAROOP @@ -318,18 +317,29 @@ private: continue; } + dcp::LocalTime begin(_timing->from(), i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()); + dcp::LocalTime end(_timing->until(), i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()); + DecryptedECinemaKDM kdm ( decrypted.id(), decrypted.name(), decrypted.key(), - dcp::LocalTime (_timing->from(), i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()), - dcp::LocalTime (_timing->until(), i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()) + begin, + end ); + dcp::NameFormat::Map name_values; + name_values['c'] = i->cinema->name; + name_values['s'] = i->name; + name_values['f'] = title; + name_values['b'] = begin.date() + " " + begin.time_of_day(true, false); + name_values['e'] = end.date() + " " + end.time_of_day(true, false); + name_values['i'] = kdm.id(); + /* Encrypt */ - screen_kdms.push_back ( - shared_ptr<ScreenKDM>( - new ECinemaScreenKDM(i, kdm.encrypt(i->recipient.get())) + kdms.push_back ( + KDMWithMetadataPtr( + new ECinemaKDMWithMetadata(name_values, i->cinema, kdm.encrypt(i->recipient.get())) ) ); } @@ -355,10 +365,13 @@ private: continue; } + dcp::LocalTime begin(_timing->from(), i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()); + dcp::LocalTime end(_timing->until(), i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()); + /* Make an empty KDM */ dcp::DecryptedKDM kdm ( - dcp::LocalTime (_timing->from(), i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()), - dcp::LocalTime (_timing->until(), i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()), + begin, + end, decrypted.annotation_text().get_value_or (""), decrypted.content_title_text(), dcp::LocalTime().as_string() @@ -369,27 +382,34 @@ private: kdm.add_key (j); } + dcp::EncryptedKDM const encrypted = kdm.encrypt( + signer, i->recipient.get(), i->trusted_device_thumbprints(), _output->formulation(), + !_output->forensic_mark_video(), _output->forensic_mark_audio() ? boost::optional<int>() : 0 + ); + + dcp::NameFormat::Map name_values; + name_values['c'] = i->cinema->name; + name_values['s'] = i->name; + name_values['f'] = title; + name_values['b'] = begin.date() + " " + begin.time_of_day(true, false); + name_values['e'] = end.date() + " " + end.time_of_day(true, false); + name_values['i'] = encrypted.cpl_id (); + /* Encrypt */ - screen_kdms.push_back ( - shared_ptr<ScreenKDM>( - new DCPScreenKDM( - i, - kdm.encrypt( - signer, i->recipient.get(), i->trusted_device_thumbprints(), _output->formulation(), - !_output->forensic_mark_video(), _output->forensic_mark_audio() ? boost::optional<int>() : 0 - ) - ) + kdms.push_back ( + KDMWithMetadataPtr( + new DCPKDMWithMetadata(name_values, i->cinema.get(), i->cinema->emails, encrypted) ) ); } } - if (screen_kdms.empty()) { + if (kdms.empty()) { return; } pair<shared_ptr<Job>, int> result = _output->make ( - screen_kdms, title, _timing, bind (&DOMFrame::confirm_overwrite, this, _1) + kdms, title, bind (&DOMFrame::confirm_overwrite, this, _1) ); if (result.first) { diff --git a/src/tools/dcpomatic_kdm_cli.cc b/src/tools/dcpomatic_kdm_cli.cc index 166b22285..adb14f49e 100644 --- a/src/tools/dcpomatic_kdm_cli.cc +++ b/src/tools/dcpomatic_kdm_cli.cc @@ -24,8 +24,7 @@ #include "lib/film.h" #include "lib/cinema.h" -#include "lib/screen_kdm.h" -#include "lib/cinema_kdms.h" +#include "lib/kdm_with_metadata.h" #include "lib/config.h" #include "lib/exceptions.h" #include "lib/emailer.h" @@ -130,22 +129,20 @@ always_overwrite () void write_files ( - list<shared_ptr<ScreenKDM> > screen_kdms, + list<KDMWithMetadataPtr> kdms, bool zip, boost::filesystem::path output, dcp::NameFormat container_name_format, dcp::NameFormat filename_format, - dcp::NameFormat::Map values, bool verbose ) { if (zip) { - int const N = CinemaKDMs::write_zip_files ( - CinemaKDMs::collect (screen_kdms), + int const N = write_zip_files ( + collect (kdms), output, container_name_format, filename_format, - values, bind (&always_overwrite) ); @@ -153,8 +150,8 @@ write_files ( cout << "Wrote " << N << " ZIP files to " << output << "\n"; } } else { - int const N = ScreenKDM::write_files ( - screen_kdms, output, filename_format, values, + int const N = write_files ( + kdms, output, filename_format, bind (&always_overwrite) ); @@ -223,17 +220,15 @@ from_film ( boost::filesystem::path cpl = cpls.front().cpl_file; - dcp::NameFormat::Map values; - values['f'] = film->name(); - values['b'] = dcp::LocalTime(valid_from).date() + " " + dcp::LocalTime(valid_from).time_of_day(true, false); - values['e'] = dcp::LocalTime(valid_to).date() + " " + dcp::LocalTime(valid_to).time_of_day(true, false); - try { - list<shared_ptr<ScreenKDM> > screen_kdms = film->make_kdms ( - screens, cpl, valid_from, valid_to, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio - ); - - write_files (screen_kdms, zip, output, container_name_format, filename_format, values, verbose); + list<KDMWithMetadataPtr> kdms; + BOOST_FOREACH (shared_ptr<Screen> i, screens) { + KDMWithMetadataPtr p = kdm_for_screen (film, cpl, i, valid_from, valid_to, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio); + if (p) { + kdms.push_back (p); + } + } + write_files (kdms, zip, output, container_name_format, filename_format, verbose); } catch (FileError& e) { cerr << program_name << ": " << e.what() << " (" << e.file().string() << ")\n"; exit (EXIT_FAILURE); @@ -325,36 +320,39 @@ from_dkdm ( ) { dcp::NameFormat::Map values; - values['f'] = dkdm.annotation_text().get_value_or(""); - values['b'] = dcp::LocalTime(valid_from).date() + " " + dcp::LocalTime(valid_from).time_of_day(true, false); - values['e'] = dcp::LocalTime(valid_to).date() + " " + dcp::LocalTime(valid_to).time_of_day(true, false); try { - list<shared_ptr<ScreenKDM> > screen_kdms; + list<KDMWithMetadataPtr> kdms; BOOST_FOREACH (shared_ptr<Screen> i, screens) { if (!i->recipient) { continue; } - screen_kdms.push_back ( - shared_ptr<ScreenKDM>( - new DCPScreenKDM( - i, - kdm_from_dkdm( + dcp::LocalTime begin(valid_from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()); + dcp::LocalTime end(valid_to, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()); + + dcp::EncryptedKDM const kdm = kdm_from_dkdm( dkdm, i->recipient.get(), i->trusted_device_thumbprints(), - dcp::LocalTime(valid_from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()), - dcp::LocalTime(valid_to, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()), + begin, + end, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio - ) - ) - ) - ); + ); + + dcp::NameFormat::Map name_values; + name_values['c'] = i->cinema->name; + name_values['s'] = i->name; + name_values['f'] = dkdm.annotation_text().get_value_or(""); + name_values['b'] = begin.date() + " " + begin.time_of_day(true, false); + name_values['e'] = end.date() + " " + end.time_of_day(true, false); + name_values['i'] = kdm.cpl_id(); + + kdms.push_back (KDMWithMetadataPtr(new DCPKDMWithMetadata(name_values, i->cinema.get(), i->cinema->emails, kdm))); } - write_files (screen_kdms, zip, output, container_name_format, filename_format, values, verbose); + write_files (kdms, zip, output, container_name_format, filename_format, verbose); } catch (FileError& e) { cerr << program_name << ": " << e.what() << " (" << e.file().string() << ")\n"; exit (EXIT_FAILURE); diff --git a/src/wx/cinema_dialog.cc b/src/wx/cinema_dialog.cc index ff5d1faf6..31e6ebe79 100644 --- a/src/wx/cinema_dialog.cc +++ b/src/wx/cinema_dialog.cc @@ -83,37 +83,8 @@ CinemaDialog::CinemaDialog (wxWindow* parent, wxString title, string name, list< overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); } - _offsets.push_back (Offset (_("UTC-11"), -11, 0)); - _offsets.push_back (Offset (_("UTC-10"), -10, 0)); - _offsets.push_back (Offset (_("UTC-9"), -9, 0)); - _offsets.push_back (Offset (_("UTC-8"), -8, 0)); - _offsets.push_back (Offset (_("UTC-7"), -7, 0)); - _offsets.push_back (Offset (_("UTC-6"), -6, 0)); - _offsets.push_back (Offset (_("UTC-5"), -5, 0)); - _offsets.push_back (Offset (_("UTC-4:30"), -4, 30)); - _offsets.push_back (Offset (_("UTC-4"), -4, 0)); - _offsets.push_back (Offset (_("UTC-3:30"), -3, 30)); - _offsets.push_back (Offset (_("UTC-3"), -3, 0)); - _offsets.push_back (Offset (_("UTC-2"), -2, 0)); - _offsets.push_back (Offset (_("UTC-1"), -1, 0)); - _offsets.push_back (Offset (_("UTC") , 0, 0)); - _offsets.push_back (Offset (_("UTC+1"), 1, 0)); - _offsets.push_back (Offset (_("UTC+2"), 2, 0)); - _offsets.push_back (Offset (_("UTC+3"), 3, 0)); - _offsets.push_back (Offset (_("UTC+4"), 4, 0)); - _offsets.push_back (Offset (_("UTC+5"), 5, 0)); - _offsets.push_back (Offset (_("UTC+5:30"), 5, 30)); - _offsets.push_back (Offset (_("UTC+6"), 6, 0)); - _offsets.push_back (Offset (_("UTC+7"), 7, 0)); - _offsets.push_back (Offset (_("UTC+8"), 8, 0)); - _offsets.push_back (Offset (_("UTC+9"), 9, 0)); - _offsets.push_back (Offset (_("UTC+9:30"), 9, 30)); - _offsets.push_back (Offset (_("UTC+10"), 10, 0)); - _offsets.push_back (Offset (_("UTC+11"), 11, 0)); - _offsets.push_back (Offset (_("UTC+12"), 12, 0)); - /* Default to UTC */ - size_t sel = 13; + size_t sel = get_offsets (_offsets); for (size_t i = 0; i < _offsets.size(); ++i) { _utc_offset->Append (_offsets[i].name); if (_offsets[i].hour == utc_offset_hour && _offsets[i].minute == utc_offset_minute) { diff --git a/src/wx/cinema_dialog.h b/src/wx/cinema_dialog.h index 9cb0a65dc..d16b0ba9c 100644 --- a/src/wx/cinema_dialog.h +++ b/src/wx/cinema_dialog.h @@ -53,19 +53,5 @@ private: EditableList<std::string, EmailDialog>* _email_list; std::vector<std::string> _emails; wxChoice* _utc_offset; - - struct Offset - { - Offset (wxString n, int h, int m) - : name (n) - , hour (h) - , minute (m) - {} - - wxString name; - int hour; - int minute; - }; - std::vector<Offset> _offsets; }; diff --git a/src/wx/confirm_kdm_email_dialog.cc b/src/wx/confirm_kdm_email_dialog.cc index eca34b5cf..df6131fe1 100644 --- a/src/wx/confirm_kdm_email_dialog.cc +++ b/src/wx/confirm_kdm_email_dialog.cc @@ -23,7 +23,6 @@ #include "static_text.h" #include "check_box.h" #include "lib/config.h" -#include "lib/cinema_kdms.h" #include <boost/foreach.hpp> using std::list; diff --git a/src/wx/kdm_dialog.cc b/src/wx/kdm_dialog.cc index 8682fe82f..d3bbf02c9 100644 --- a/src/wx/kdm_dialog.cc +++ b/src/wx/kdm_dialog.cc @@ -29,9 +29,8 @@ #include "dcpomatic_button.h" #include "lib/film.h" #include "lib/screen.h" -#include "lib/screen_kdm.h" +#include "lib/kdm_with_metadata.h" #include "lib/job_manager.h" -#include "lib/cinema_kdms.h" #include "lib/config.h" #include "lib/cinema.h" #include <libcxml/cxml.h> @@ -150,7 +149,7 @@ KDMDialog::make_clicked () shared_ptr<const Film> film = _film.lock (); DCPOMATIC_ASSERT (film); - list<shared_ptr<ScreenKDM> > screen_kdms; + list<KDMWithMetadataPtr> kdms; try { /* Start off by enabling forensic marking for all */ optional<int> for_audio; @@ -161,11 +160,13 @@ KDMDialog::make_clicked () /* Forensic mark up to this channel; disabled on channels greater than this */ for_audio = _output->forensic_mark_audio_up_to(); } - screen_kdms = film->make_kdms ( - _screens->screens(), _cpl->cpl(), _timing->from(), _timing->until(), _output->formulation(), - !_output->forensic_mark_video(), for_audio - ); + BOOST_FOREACH (shared_ptr<dcpomatic::Screen> i, _screens->screens()) { + KDMWithMetadataPtr p = kdm_for_screen (film, _cpl->cpl(), i, _timing->from(), _timing->until(), _output->formulation(), !_output->forensic_mark_video(), for_audio); + if (p) { + kdms.push_back (p); + } + } } catch (dcp::BadKDMDateError& e) { if (e.starts_too_early()) { error_dialog (this, _("The KDM start period is before (or close to) the start of the signing certificate's validity period. Use a later start time for this KDM.")); @@ -178,7 +179,7 @@ KDMDialog::make_clicked () return; } - pair<shared_ptr<Job>, int> result = _output->make (screen_kdms, film->name(), _timing, bind (&KDMDialog::confirm_overwrite, this, _1)); + pair<shared_ptr<Job>, int> result = _output->make (kdms, film->name(), bind (&KDMDialog::confirm_overwrite, this, _1)); if (result.first) { JobManager::instance()->add (result.first); } diff --git a/src/wx/kdm_output_panel.cc b/src/wx/kdm_output_panel.cc index d76a27359..a345d0e96 100644 --- a/src/wx/kdm_output_panel.cc +++ b/src/wx/kdm_output_panel.cc @@ -20,7 +20,6 @@ #include "lib/config.h" #include "lib/cinema.h" -#include "lib/cinema_kdms.h" #include "lib/send_kdm_email_job.h" #include "kdm_output_panel.h" #include "kdm_timing_panel.h" @@ -54,6 +53,7 @@ KDMOutputPanel::KDMOutputPanel (wxWindow* parent, bool interop) , _forensic_mark_audio_up_to (12) { wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, 0); + table->AddGrowableCol (1); add_label_to_sizer (table, this, _("KDM type"), true); @@ -183,10 +183,10 @@ KDMOutputPanel::kdm_write_type_changed () pair<shared_ptr<Job>, int> KDMOutputPanel::make ( - list<shared_ptr<ScreenKDM> > screen_kdms, string name, KDMTimingPanel* timing, function<bool (boost::filesystem::path)> confirm_overwrite + list<KDMWithMetadataPtr> kdms, string name, function<bool (boost::filesystem::path)> confirm_overwrite ) { - list<CinemaKDMs> const cinema_kdms = CinemaKDMs::collect (screen_kdms); + list<list<KDMWithMetadataPtr> > const cinema_kdms = collect (kdms); /* Decide whether to proceed */ @@ -200,8 +200,8 @@ KDMOutputPanel::make ( } bool cinemas_with_no_email = false; - BOOST_FOREACH (CinemaKDMs i, cinema_kdms) { - if (i.cinema->emails.empty ()) { + BOOST_FOREACH (list<KDMWithMetadataPtr> i, cinema_kdms) { + if (i.front()->emails().empty()) { cinemas_with_no_email = true; } } @@ -215,8 +215,8 @@ KDMOutputPanel::make ( if (proceed && Config::instance()->confirm_kdm_email ()) { list<string> emails; - BOOST_FOREACH (CinemaKDMs i, cinema_kdms) { - BOOST_FOREACH (string j, i.cinema->emails) { + BOOST_FOREACH (list<KDMWithMetadataPtr> const& i, cinema_kdms) { + BOOST_FOREACH (string j, i.front()->emails()) { emails.push_back (j); } } @@ -240,36 +240,29 @@ KDMOutputPanel::make ( shared_ptr<Job> job; try { - dcp::NameFormat::Map name_values; - name_values['f'] = name; - name_values['b'] = dcp::LocalTime(timing->from()).date() + " " + dcp::LocalTime(timing->from()).time_of_day(false, false); - name_values['e'] = dcp::LocalTime(timing->until()).date() + " " + dcp::LocalTime(timing->until()).time_of_day(false, false); if (_write_to->GetValue()) { if (_write_flat->GetValue()) { - written = ScreenKDM::write_files ( - screen_kdms, + written = write_files ( + kdms, directory(), _filename_format->get(), - name_values, confirm_overwrite ); } else if (_write_folder->GetValue()) { - written = CinemaKDMs::write_directories ( - CinemaKDMs::collect (screen_kdms), + written = write_directories ( + collect (kdms), directory(), _container_name_format->get(), _filename_format->get(), - name_values, confirm_overwrite ); } else if (_write_zip->GetValue()) { - written = CinemaKDMs::write_zip_files ( - CinemaKDMs::collect (screen_kdms), + written = write_zip_files ( + collect (kdms), directory(), _container_name_format->get(), _filename_format->get(), - name_values, confirm_overwrite ); } @@ -281,7 +274,6 @@ KDMOutputPanel::make ( cinema_kdms, _container_name_format->get(), _filename_format->get(), - name_values, name ) ); diff --git a/src/wx/kdm_output_panel.h b/src/wx/kdm_output_panel.h index 7b9315071..0281b26d0 100644 --- a/src/wx/kdm_output_panel.h +++ b/src/wx/kdm_output_panel.h @@ -18,7 +18,7 @@ */ -#include "lib/screen_kdm.h" +#include "lib/kdm_with_metadata.h" #include "wx_util.h" #include "name_format_editor.h" #include <dcp/types.h> @@ -52,9 +52,8 @@ public: } std::pair<boost::shared_ptr<Job>, int> make ( - std::list<boost::shared_ptr<ScreenKDM> > screen_kdms, + std::list<KDMWithMetadataPtr > screen_kdms, std::string name, - KDMTimingPanel* timing, boost::function<bool (boost::filesystem::path)> confirm_overwrite ); diff --git a/src/wx/recreate_chain_dialog.cc b/src/wx/recreate_chain_dialog.cc index e477cdc1f..ae6afdd08 100644 --- a/src/wx/recreate_chain_dialog.cc +++ b/src/wx/recreate_chain_dialog.cc @@ -23,7 +23,6 @@ #include "static_text.h" #include "check_box.h" #include "lib/config.h" -#include "lib/cinema_kdms.h" #include <boost/foreach.hpp> using std::list; diff --git a/src/wx/wscript b/src/wx/wscript index f78a8c617..22d9f0db6 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -52,12 +52,14 @@ sources = """ dcp_panel.cc dcpomatic_button.cc disk_warning_dialog.cc + dkdm_output_panel.cc drive_wipe_warning_dialog.cc email_dialog.cc image_sequence_dialog.cc isdcf_metadata_dialog.cc dcp_text_track_dialog.cc dir_picker_ctrl.cc + dkdm_dialog.cc dolby_doremi_certificate_panel.cc download_certificate_dialog.cc download_certificate_panel.cc @@ -110,6 +112,8 @@ sources = """ question_dialog.cc rating_dialog.cc qube_certificate_panel.cc + recipients_panel.cc + recipient_dialog.cc recreate_chain_dialog.cc repeat_dialog.cc report_problem_dialog.cc diff --git a/src/wx/wx_util.cc b/src/wx/wx_util.cc index 28f79431a..6eef0d147 100644 --- a/src/wx/wx_util.cc +++ b/src/wx/wx_util.cc @@ -530,3 +530,40 @@ display_progress (wxString title, wxString task) return ok; } + + +int +get_offsets (vector<Offset>& offsets) +{ + offsets.push_back (Offset(_("UTC-11"), -11, 0)); + offsets.push_back (Offset(_("UTC-10"), -10, 0)); + offsets.push_back (Offset(_("UTC-9"), -9, 0)); + offsets.push_back (Offset(_("UTC-8"), -8, 0)); + offsets.push_back (Offset(_("UTC-7"), -7, 0)); + offsets.push_back (Offset(_("UTC-6"), -6, 0)); + offsets.push_back (Offset(_("UTC-5"), -5, 0)); + offsets.push_back (Offset(_("UTC-4:30"), -4, 30)); + offsets.push_back (Offset(_("UTC-4"), -4, 0)); + offsets.push_back (Offset(_("UTC-3:30"), -3, 30)); + offsets.push_back (Offset(_("UTC-3"), -3, 0)); + offsets.push_back (Offset(_("UTC-2"), -2, 0)); + offsets.push_back (Offset(_("UTC-1"), -1, 0)); + int utc = offsets.size(); + offsets.push_back (Offset(_("UTC") , 0, 0)); + offsets.push_back (Offset(_("UTC+1"), 1, 0)); + offsets.push_back (Offset(_("UTC+2"), 2, 0)); + offsets.push_back (Offset(_("UTC+3"), 3, 0)); + offsets.push_back (Offset(_("UTC+4"), 4, 0)); + offsets.push_back (Offset(_("UTC+5"), 5, 0)); + offsets.push_back (Offset(_("UTC+5:30"), 5, 30)); + offsets.push_back (Offset(_("UTC+6"), 6, 0)); + offsets.push_back (Offset(_("UTC+7"), 7, 0)); + offsets.push_back (Offset(_("UTC+8"), 8, 0)); + offsets.push_back (Offset(_("UTC+9"), 9, 0)); + offsets.push_back (Offset(_("UTC+9:30"), 9, 30)); + offsets.push_back (Offset(_("UTC+10"), 10, 0)); + offsets.push_back (Offset(_("UTC+11"), 11, 0)); + offsets.push_back (Offset(_("UTC+12"), 12, 0)); + + return utc; +} diff --git a/src/wx/wx_util.h b/src/wx/wx_util.h index af25ce0f5..8e0befba9 100644 --- a/src/wx/wx_util.h +++ b/src/wx/wx_util.h @@ -88,6 +88,23 @@ extern double calculate_mark_interval (double start); extern bool display_progress (wxString title, wxString task); extern bool report_errors_from_last_job (wxWindow* parent); + +struct Offset +{ + Offset (wxString n, int h, int m) + : name (n) + , hour (h) + , minute (m) + {} + + wxString name; + int hour; + int minute; +}; + +extern int get_offsets (std::vector<Offset>& offsets); + + extern void checked_set (FilePickerCtrl* widget, boost::filesystem::path value); extern void checked_set (wxDirPickerCtrl* widget, boost::filesystem::path value); extern void checked_set (wxSpinCtrl* widget, int value); diff --git a/test/kdm_naming_test.cc b/test/kdm_naming_test.cc new file mode 100644 index 000000000..35188d5e6 --- /dev/null +++ b/test/kdm_naming_test.cc @@ -0,0 +1,212 @@ +/* + Copyright (C) 2020 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "lib/cinema.h" +#include "lib/screen.h" +#include "lib/config.h" +#include "lib/content_factory.h" +#include "lib/film.h" +#include "lib/kdm_with_metadata.h" +#include "test.h" +#include <boost/test/unit_test.hpp> +#include <boost/shared_ptr.hpp> + +using std::list; +using std::string; +using std::vector; +using boost::shared_ptr; +using boost::optional; +using boost::dynamic_pointer_cast; + +static +bool +confirm_overwrite (boost::filesystem::path) +{ + return true; +} + +static shared_ptr<dcpomatic::Screen> cinema_a_screen_1; +static shared_ptr<dcpomatic::Screen> cinema_a_screen_2; +static shared_ptr<dcpomatic::Screen> cinema_b_screen_x; +static shared_ptr<dcpomatic::Screen> cinema_b_screen_y; +static shared_ptr<dcpomatic::Screen> cinema_b_screen_z; + +BOOST_AUTO_TEST_CASE (single_kdm_naming_test) +{ + Config* c = Config::instance(); + + dcp::Certificate cert = c->decryption_chain()->leaf(); + + /* Cinema A: UTC +4:30 */ + shared_ptr<Cinema> cinema_a (new Cinema("Cinema A", list<string>(), "", 4, 30)); + cinema_a_screen_1.reset(new dcpomatic::Screen("Screen 1", "", cert, vector<TrustedDevice>())); + cinema_a->add_screen (cinema_a_screen_1); + cinema_a_screen_2.reset(new dcpomatic::Screen("Screen 2", "", cert, vector<TrustedDevice>())); + cinema_a->add_screen (cinema_a_screen_2); + c->add_cinema (cinema_a); + + /* Cinema B: UTC -1:00 */ + shared_ptr<Cinema> cinema_b (new Cinema("Cinema B", list<string>(), "", -1, 0)); + cinema_b_screen_x.reset(new dcpomatic::Screen("Screen X", "", cert, vector<TrustedDevice>())); + cinema_b->add_screen (cinema_b_screen_x); + cinema_b_screen_y.reset(new dcpomatic::Screen("Screen Y", "", cert, vector<TrustedDevice>())); + cinema_b->add_screen (cinema_b_screen_y); + cinema_b_screen_z.reset(new dcpomatic::Screen("Screen Z", "", cert, vector<TrustedDevice>())); + cinema_b->add_screen (cinema_b_screen_z); + c->add_cinema (cinema_a); + + /* Film */ + boost::filesystem::remove_all ("build/test/single_kdm_naming_test"); + shared_ptr<Film> film = new_test_film2 ("single_kdm_naming_test"); + film->set_name ("my_great_film"); + film->examine_and_add_content (content_factory("test/data/flat_black.png").front()); + BOOST_REQUIRE (!wait_for_jobs()); + film->set_encrypted (true); + film->make_dcp (); + BOOST_REQUIRE (!wait_for_jobs()); + film->write_metadata (); + vector<CPLSummary> cpls = film->cpls (); + BOOST_REQUIRE(cpls.size() == 1); + + dcp::LocalTime from (cert.not_before()); + from.add_months (2); + dcp::LocalTime until (cert.not_after()); + until.add_months (-2); + + string const from_string = from.date() + " " + from.time_of_day(true, false); + string const until_string = until.date() + " " + until.time_of_day(true, false); + + boost::filesystem::path cpl = cpls.front().cpl_file; + KDMWithMetadataPtr kdm = kdm_for_screen ( + film, + cpls.front().cpl_file, + cinema_a_screen_1, + boost::posix_time::time_from_string(from_string), + boost::posix_time::time_from_string(until_string), + dcp::MODIFIED_TRANSITIONAL_1, + false, + optional<int>() + ); + + list<KDMWithMetadataPtr> kdms; + kdms.push_back (kdm); + + write_files ( + kdms, + boost::filesystem::path("build/test/single_kdm_naming_test"), + dcp::NameFormat("KDM %c - %s - %f - %b - %e"), + &confirm_overwrite + ); + + string from_time = from.time_of_day (true, false); + boost::algorithm::replace_all (from_time, ":", "-"); + string until_time = until.time_of_day (true, false); + boost::algorithm::replace_all (until_time, ":", "-"); + + string const ref = String::compose("KDM_Cinema_A_-_Screen_1_-_my_great_film_-_%1_%2_-_%3_%4.xml", from.date(), from_time, until.date(), until_time); + BOOST_CHECK_MESSAGE (boost::filesystem::exists("build/test/single_kdm_naming_test/" + ref), "File " << ref << " not found"); +} + + +BOOST_AUTO_TEST_CASE (directory_kdm_naming_test, * boost::unit_test::depends_on("single_kdm_naming_test")) +{ + using boost::filesystem::path; + + dcp::Certificate cert = Config::instance()->decryption_chain()->leaf(); + + /* Film */ + boost::filesystem::remove_all ("build/test/directory_kdm_naming_test"); + shared_ptr<Film> film = new_test_film2 ("directory_kdm_naming_test"); + film->set_name ("my_great_film"); + film->examine_and_add_content (content_factory("test/data/flat_black.png").front()); + BOOST_REQUIRE (!wait_for_jobs()); + film->set_encrypted (true); + film->make_dcp (); + BOOST_REQUIRE (!wait_for_jobs()); + film->write_metadata (); + vector<CPLSummary> cpls = film->cpls (); + BOOST_REQUIRE(cpls.size() == 1); + + dcp::LocalTime from (cert.not_before()); + from.add_months (2); + dcp::LocalTime until (cert.not_after()); + until.add_months (-2); + + string const from_string = from.date() + " " + from.time_of_day(true, false); + string const until_string = until.date() + " " + until.time_of_day(true, false); + + list<shared_ptr<dcpomatic::Screen> > screens; + screens.push_back (cinema_a_screen_2); + screens.push_back (cinema_b_screen_x); + screens.push_back (cinema_a_screen_1); + screens.push_back (cinema_b_screen_z); + + path const cpl = cpls.front().cpl_file; + string const cpl_id = cpls.front().cpl_id; + + list<KDMWithMetadataPtr> kdms; + BOOST_FOREACH (shared_ptr<dcpomatic::Screen> i, screens) { + KDMWithMetadataPtr kdm = kdm_for_screen ( + film, + cpls.front().cpl_file, + i, + boost::posix_time::time_from_string(from_string), + boost::posix_time::time_from_string(until_string), + dcp::MODIFIED_TRANSITIONAL_1, + false, + optional<int>() + ); + + kdms.push_back (kdm); + } + + write_directories ( + collect(kdms), + path("build/test/directory_kdm_naming_test"), + dcp::NameFormat("%c - %s - %f - %b - %e"), + dcp::NameFormat("KDM %c - %s - %f - %b - %e - %i"), + &confirm_overwrite + ); + + string from_time = from.time_of_day (true, false); + boost::algorithm::replace_all (from_time, ":", "-"); + string until_time = until.time_of_day (true, false); + boost::algorithm::replace_all (until_time, ":", "-"); + + path const base = "build/test/directory_kdm_naming_test"; + + path dir_a = String::compose("Cinema_A_-_%s_-_my_great_film_-_%1_%2_-_%3_%4", from.date(), from_time, until.date(), until_time); + BOOST_CHECK_MESSAGE (boost::filesystem::exists(base / dir_a), "Directory " << dir_a << " not found"); + path dir_b = String::compose("Cinema_B_-_%s_-_my_great_film_-_%1_%2_-_%3_%4", from.date(), from_time, until.date(), until_time); + BOOST_CHECK_MESSAGE (boost::filesystem::exists(base / dir_b), "Directory " << dir_b << " not found"); + + path ref = String::compose("KDM_Cinema_A_-_Screen_2_-_my_great_film_-_%1_%2_-_%3_%4_-_%5.xml", from.date(), from_time, until.date(), until_time, cpl_id); + BOOST_CHECK_MESSAGE (boost::filesystem::exists(base / dir_a / ref), "File " << ref << " not found"); + + ref = String::compose("KDM_Cinema_B_-_Screen_X_-_my_great_film_-_%1_%2_-_%3_%4_-_%5.xml", from.date(), from_time, until.date(), until_time, cpl_id); + BOOST_CHECK_MESSAGE (boost::filesystem::exists(base / dir_b / ref), "File " << ref << " not found"); + + ref = String::compose("KDM_Cinema_A_-_Screen_1_-_my_great_film_-_%1_%2_-_%3_%4_-_%5.xml", from.date(), from_time, until.date(), until_time, cpl_id); + BOOST_CHECK_MESSAGE (boost::filesystem::exists(base / dir_a / ref), "File " << ref << " not found"); + + ref = String::compose("KDM_Cinema_B_-_Screen_Z_-_my_great_film_-_%1_%2_-_%3_%4_-_%5.xml", from.date(), from_time, until.date(), until_time, cpl_id); + BOOST_CHECK_MESSAGE (boost::filesystem::exists(base / dir_b / ref), "File " << ref << " not found"); +} + diff --git a/test/wscript b/test/wscript index 29f1e1db3..1c3d750c3 100644 --- a/test/wscript +++ b/test/wscript @@ -92,6 +92,7 @@ def build(bld): isdcf_name_test.cc j2k_bandwidth_test.cc job_test.cc + kdm_naming_test.cc make_black_test.cc optimise_stills_test.cc pixel_formats_test.cc |
