Emit no audio from DCPs if none is mapped
[dcpomatic.git] / src / lib / kdm_cli.cc
index a76155a2c8d8a4bd6ad17bbe9f508e5fb7561c59..2d3a021b5f5709368c2c46854ed561dbd24c8579 100644 (file)
@@ -27,7 +27,7 @@
 #include "cinema.h"
 #include "config.h"
 #include "dkdm_wrapper.h"
-#include "emailer.h"
+#include "email.h"
 #include "exceptions.h"
 #include "film.h"
 #include "kdm_with_metadata.h"
@@ -35,6 +35,7 @@
 #include <dcp/certificate.h>
 #include <dcp/decrypted_kdm.h>
 #include <dcp/encrypted_kdm.h>
+#include <dcp/filesystem.h>
 #include <getopt.h>
 
 
@@ -58,22 +59,23 @@ help (std::function<void (string)> out)
 {
        out (String::compose("Syntax: %1 [OPTION] <FILM|CPL-ID|DKDM>", program_name));
        out ("  -h, --help                               show this help");
-       out ("  -o, --output                             output file or directory");
-       out ("  -K, --filename-format                    filename format for KDMs");
-       out ("  -Z, --container-name-format              filename format for ZIP containers");
-       out ("  -f, --valid-from                         valid from time (in local time zone of the cinema) (e.g. \"2013-09-28 01:41:51\") or \"now\"");
-       out ("  -t, --valid-to                           valid to time (in local time zone of the cinema) (e.g. \"2014-09-28 01:41:51\")");
-       out ("  -d, --valid-duration                     valid duration (e.g. \"1 day\", \"4 hours\", \"2 weeks\")");
-       out ("  -F, --formulation                        modified-transitional-1, multiple-modified-transitional-1, dci-any or dci-specific [default modified-transitional-1]");
+       out ("  -o, --output <path>                      output file or directory");
+       out ("  -K, --filename-format <format>           filename format for KDMs");
+       out ("  -Z, --container-name-format <format>     filename format for ZIP containers");
+       out ("  -f, --valid-from <time>                  valid from time (in local time zone of the cinema) (e.g. \"2013-09-28 01:41:51\") or \"now\"");
+       out ("  -t, --valid-to <time>                    valid to time (in local time zone of the cinema) (e.g. \"2014-09-28 01:41:51\")");
+       out ("  -d, --valid-duration <duration>          valid duration (e.g. \"1 day\", \"4 hours\", \"2 weeks\")");
+       out ("  -F, --formulation <formulation>          modified-transitional-1, multiple-modified-transitional-1, dci-any or dci-specific [default modified-transitional-1]");
        out ("  -p, --disable-forensic-marking-picture   disable forensic marking of pictures essences");
        out ("  -a, --disable-forensic-marking-audio     disable forensic marking of audio essences (optionally above a given channel, e.g 12)");
        out ("  -e, --email                              email KDMs to cinemas");
        out ("  -z, --zip                                ZIP each cinema's KDMs into its own file");
        out ("  -v, --verbose                            be verbose");
-       out ("  -c, --cinema                             cinema name (when using -C) or name/email (to filter cinemas)");
-       out ("  -S, --screen                             screen name (when using -C) or screen name (to filter screens when using -c)");
-       out ("  -C, --certificate                        file containing projector certificate");
-       out ("  -T, --trusted-device                     file containing a trusted device's certificate");
+       out ("  -c, --cinema <name|email>                cinema name (when using -C) or name/email (to filter cinemas)");
+       out ("  -S, --screen <name>                      screen name (when using -C) or screen name (to filter screens when using -c)");
+       out ("  -C, --certificate <file>                 file containing projector certificate");
+       out ("  -T, --trusted-device <file>              file containing a trusted device's certificate");
+       out ("      --cinemas-file <file>                use the given file as a list of cinemas instead of the current configuration");
        out ("      --list-cinemas                       list known cinemas from the DCP-o-matic settings");
        out ("      --list-dkdm-cpls                     list CPLs for which DCP-o-matic has DKDMs");
        out ("");
@@ -203,7 +205,7 @@ find_cinema (string cinema_name)
 static
 void
 from_film (
-       list<shared_ptr<Screen>> screens,
+       vector<shared_ptr<Screen>> screens,
        boost::filesystem::path film_dir,
        bool verbose,
        boost::filesystem::path output,
@@ -240,14 +242,39 @@ from_film (
 
        auto cpl = cpls.front().cpl_file;
 
+       std::vector<KDMCertificatePeriod> period_checks;
+
        try {
                list<KDMWithMetadataPtr> kdms;
                for (auto i: screens) {
-                       auto p = kdm_for_screen (film, cpl, i, valid_from, valid_to, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio);
+                       std::function<dcp::DecryptedKDM (dcp::LocalTime, dcp::LocalTime)> make_kdm = [film, cpl](dcp::LocalTime begin, dcp::LocalTime end) {
+                               return film->make_kdm(cpl, begin, end);
+                       };
+                       auto p = kdm_for_screen(make_kdm, i, valid_from, valid_to, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio, period_checks);
                        if (p) {
                                kdms.push_back (p);
                        }
                }
+
+
+               if (find_if(
+                       period_checks.begin(),
+                       period_checks.end(),
+                       [](KDMCertificatePeriod const& p) { return p.overlap == KDMCertificateOverlap::KDM_OUTSIDE_CERTIFICATE; }
+                  ) != period_checks.end()) {
+                       throw KDMCLIError(
+                               "Some KDMs would have validity periods which are completely outside the recipient certificate periods.  Such KDMs are very unlikely to work, so will not be created."
+                               );
+               }
+
+               if (find_if(
+                       period_checks.begin(),
+                       period_checks.end(),
+                       [](KDMCertificatePeriod const& p) { return p.overlap == KDMCertificateOverlap::KDM_OVERLAPS_CERTIFICATE; }
+                  ) != period_checks.end()) {
+                       out("For some of these KDMs the recipient certificate's validity period will not cover the whole of the KDM validity period.  This might cause problems with the KDMs.");
+               }
+
                write_files (kdms, zip, output, container_name_format, filename_format, verbose, out);
                if (email) {
                        send_emails ({kdms}, container_name_format, filename_format, film->dcp_name(), {});
@@ -329,7 +356,7 @@ kdm_from_dkdm (
 static
 void
 from_dkdm (
-       list<shared_ptr<Screen>> screens,
+       vector<shared_ptr<Screen>> screens,
        dcp::DecryptedKDM dkdm,
        bool verbose,
        boost::filesystem::path output,
@@ -379,7 +406,7 @@ from_dkdm (
                        name_values['e'] = end.date() + " " + end.time_of_day(true, false);
                        name_values['i'] = kdm.cpl_id();
 
-                       kdms.push_back (make_shared<KDMWithMetadata>(name_values, i->cinema.get(), i->cinema ? i->cinema->emails : list<string>(), kdm));
+                       kdms.push_back(make_shared<KDMWithMetadata>(name_values, i->cinema.get(), i->cinema ? i->cinema->emails : vector<string>(), kdm));
                }
                write_files (kdms, zip, output, container_name_format, filename_format, verbose, out);
                if (email) {
@@ -417,14 +444,14 @@ optional<string>
 kdm_cli (int argc, char* argv[], std::function<void (string)> out)
 try
 {
-       boost::filesystem::path output = boost::filesystem::current_path();
+       boost::filesystem::path output = dcp::filesystem::current_path();
        auto container_name_format = Config::instance()->kdm_container_name_format();
        auto filename_format = Config::instance()->kdm_filename_format();
        optional<string> cinema_name;
        shared_ptr<Cinema> cinema;
        optional<boost::filesystem::path> certificate;
        optional<string> screen;
-       list<shared_ptr<Screen>> screens;
+       vector<shared_ptr<Screen>> screens;
        optional<dcp::EncryptedKDM> dkdm;
        optional<boost::posix_time::ptime> valid_from;
        optional<boost::posix_time::ptime> valid_to;
@@ -437,6 +464,7 @@ try
        bool disable_forensic_marking_picture = false;
        optional<int> disable_forensic_marking_audio;
        bool email = false;
+       optional<boost::filesystem::path> cinemas_file;
 
        program_name = argv[0];
 
@@ -465,10 +493,11 @@ try
                        { "trusted-device", required_argument, 0, 'T' },
                        { "list-cinemas", no_argument, 0, 'B' },
                        { "list-dkdm-cpls", no_argument, 0, 'D' },
+                       { "cinemas-file", required_argument, 0, 'E' },
                        { 0, 0, 0, 0 }
                };
 
-               int c = getopt_long (argc, argv, "ho:K:Z:f:t:d:F:pae::zvc:S:C:T:BD", long_options, &option_index);
+               int c = getopt_long (argc, argv, "ho:K:Z:f:t:d:F:pae::zvc:S:C:T:BDE:", long_options, &option_index);
 
                if (c == -1) {
                        break;
@@ -477,7 +506,7 @@ try
                switch (c) {
                case 'h':
                        help (out);
-                       exit (EXIT_SUCCESS);
+                       return {};
                case 'o':
                        output = optarg;
                        break;
@@ -535,7 +564,7 @@ try
                           (for lookup) and by creating a Cinema which the next Screen will be added to.
                        */
                        cinema_name = optarg;
-                       cinema = make_shared<Cinema>(optarg, list<string>(), "", 0, 0);
+                       cinema = make_shared<Cinema>(optarg, vector<string>(), "", 0, 0);
                        break;
                case 'S':
                        /* Similarly, this could be the name of a new (temporary) screen or the name of a screen
@@ -558,9 +587,16 @@ try
                case 'D':
                        list_dkdm_cpls = true;
                        break;
+               case 'E':
+                       cinemas_file = optarg;
+                       break;
                }
        }
 
+       if (cinemas_file) {
+               Config::instance()->set_cinemas_file(*cinemas_file);
+       }
+
        if (certificate) {
                /* Make a new screen and add it to the current cinema */
                dcp::CertificateChain chain(dcp::file_to_string(*certificate));
@@ -574,14 +610,14 @@ try
        if (list_cinemas) {
                auto cinemas = Config::instance()->cinemas ();
                for (auto i: cinemas) {
-                       out (String::compose("%1 (%2)", i->name, Emailer::address_list (i->emails)));
+                       out (String::compose("%1 (%2)", i->name, Email::address_list(i->emails)));
                }
-               exit (EXIT_SUCCESS);
+               return {};
        }
 
        if (list_dkdm_cpls) {
                dump_dkdm_group (Config::instance()->dkdms(), 0, out);
-               exit (EXIT_SUCCESS);
+               return {};
        }
 
        if (!duration_string && !valid_to) {
@@ -616,7 +652,7 @@ try
        }
 
        string const thing = argv[optind];
-       if (boost::filesystem::is_directory(thing) && boost::filesystem::is_regular_file(boost::filesystem::path(thing) / "metadata.xml")) {
+       if (dcp::filesystem::is_directory(thing) && dcp::filesystem::is_regular_file(boost::filesystem::path(thing) / "metadata.xml")) {
                from_film (
                        screens,
                        thing,
@@ -634,7 +670,7 @@ try
                        out
                        );
        } else {
-               if (boost::filesystem::is_regular_file(thing)) {
+               if (dcp::filesystem::is_regular_file(thing)) {
                        dkdm = dcp::EncryptedKDM (dcp::file_to_string (thing));
                } else {
                        dkdm = find_dkdm (thing);