#include <dcp/decrypted_kdm.h>
#include <dcp/encrypted_kdm.h>
#include <getopt.h>
-#include <iostream>
-using std::cerr;
-using std::cout;
using std::dynamic_pointer_cast;
using std::list;
using std::make_shared;
static void
-help ()
+help (std::function<void (string)> out)
{
- cerr << "Syntax: " << program_name << " [OPTION] <FILM|CPL-ID|DKDM>\n"
- " -h, --help show this help\n"
- " -o, --output output file or directory\n"
- " -K, --filename-format filename format for KDMs\n"
- " -Z, --container-name-format filename format for ZIP containers\n"
- " -f, --valid-from valid from time (in local time zone of the cinema) (e.g. \"2013-09-28 01:41:51\") or \"now\"\n"
- " -t, --valid-to valid to time (in local time zone of the cinema) (e.g. \"2014-09-28 01:41:51\")\n"
- " -d, --valid-duration valid duration (e.g. \"1 day\", \"4 hours\", \"2 weeks\")\n"
- " -F, --formulation modified-transitional-1, multiple-modified-transitional-1, dci-any or dci-specific [default modified-transitional-1]\n"
- " -p, --disable-forensic-marking-picture disable forensic marking of pictures essences\n"
- " -a, --disable-forensic-marking-audio disable forensic marking of audio essences (optionally above a given channel, e.g 12)\n"
- " -e, --email email KDMs to cinemas\n"
- " -z, --zip ZIP each cinema's KDMs into its own file\n"
- " -v, --verbose be verbose\n"
- " -c, --cinema specify a cinema, either by name or email address\n"
- " -S, --screen screen description\n"
- " -C, --certificate file containing projector certificate\n"
- " -T, --trusted-device file containing a trusted device's certificate\n"
- " --list-cinemas list known cinemas from the DCP-o-matic settings\n"
- " --list-dkdm-cpls list CPLs for which DCP-o-matic has DKDMs\n\n"
- "CPL-ID must be the ID of a CPL that is mentioned in DCP-o-matic's DKDM list.\n\n"
- "For example:\n\n"
- "Create KDMs for my_great_movie to play in all of Fred's Cinema's screens for the next two weeks and zip them up.\n"
- "(Fred's Cinema must have been set up in DCP-o-matic's KDM window)\n\n"
- "\t" << program_name << " -c \"Fred's Cinema\" -f now -d \"2 weeks\" -z my_great_movie\n\n";
+ 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 (" -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 (" --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 ("");
+ out ("CPL-ID must be the ID of a CPL that is mentioned in DCP-o-matic's DKDM list.");
+ out ("");
+ out ("For example:");
+ out ("");
+ out ("Create KDMs for my_great_movie to play in all of Fred's Cinema's screens for the next two weeks and zip them up.");
+ out ("(Fred's Cinema must have been set up in DCP-o-matic's KDM window)");
+ out ("");
+ out (String::compose("\t%1 -c \"Fred's Cinema\" -f now -d \"2 weeks\" -z my_great_movie", program_name));
}
-static void
-error (string m)
+class KDMCLIError : public std::runtime_error
{
- cerr << program_name << ": " << m << "\n";
- exit (EXIT_FAILURE);
-}
+public:
+ KDMCLIError (std::string message)
+ : std::runtime_error (String::compose("%1: %2", program_name, message).c_str())
+ {}
+};
static boost::posix_time::ptime
string const unit (unit_buf);
if (N == 0) {
- cerr << "Could not understand duration \"" << d << "\"\n";
- exit (EXIT_FAILURE);
+ throw KDMCLIError (String::compose("could not understand duration \"%1\"", d));
}
if (unit == "year" || unit == "years") {
return boost::posix_time::time_duration (N, 0, 0, 0);
}
- cerr << "Could not understand duration \"" << d << "\"\n";
- exit (EXIT_FAILURE);
+ throw KDMCLIError (String::compose("could not understand duration \"%1\"", d));
}
boost::filesystem::path output,
dcp::NameFormat container_name_format,
dcp::NameFormat filename_format,
- bool verbose
+ bool verbose,
+ std::function<void (string)> out
)
{
if (zip) {
);
if (verbose) {
- cout << "Wrote " << N << " ZIP files to " << output << "\n";
+ out (String::compose("Wrote %1 ZIP files to %2", N, output));
}
} else {
int const N = write_files (
);
if (verbose) {
- cout << "Wrote " << N << " KDM files to " << output << "\n";
+ out (String::compose("Wrote %1 KDM files to %2", N, output));
}
}
}
}
if (i == cinemas.end ()) {
- cerr << program_name << ": could not find cinema \"" << cinema_name << "\"\n";
- exit (EXIT_FAILURE);
+ throw KDMCLIError (String::compose("could not find cinema \"%1\"", cinema_name));
}
return *i;
bool disable_forensic_marking_picture,
optional<int> disable_forensic_marking_audio,
bool email,
- bool zip
+ bool zip,
+ std::function<void (string)> out
)
{
shared_ptr<Film> film;
film = make_shared<Film>(film_dir);
film->read_metadata ();
if (verbose) {
- cout << "Read film " << film->name () << "\n";
+ out (String::compose("Read film %1", film->name()));
}
} catch (std::exception& e) {
- cerr << program_name << ": error reading film `" << film_dir.string() << "' (" << e.what() << ")\n";
- exit (EXIT_FAILURE);
+ throw KDMCLIError (String::compose("error reading film \"%1\" (%2)", film_dir.string(), e.what()));
}
/* XXX: allow specification of this */
vector<CPLSummary> cpls = film->cpls ();
if (cpls.empty ()) {
- error ("no CPLs found in film");
+ throw KDMCLIError ("no CPLs found in film");
} else if (cpls.size() > 1) {
- error ("more than one CPL found in film");
+ throw KDMCLIError ("more than one CPL found in film");
}
auto cpl = cpls.front().cpl_file;
kdms.push_back (p);
}
}
- write_files (kdms, zip, output, container_name_format, filename_format, verbose);
+ 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());
+ send_emails ({kdms}, container_name_format, filename_format, film->dcp_name(), {});
}
} catch (FileError& e) {
- cerr << program_name << ": " << e.what() << " (" << e.file().string() << ")\n";
- exit (EXIT_FAILURE);
- } catch (KDMError& e) {
- cerr << program_name << ": " << e.what() << "\n";
- exit (EXIT_FAILURE);
- } catch (runtime_error& e) {
- cerr << program_name << ": " << e.what() << "\n";
- exit (EXIT_FAILURE);
+ throw KDMCLIError (String::compose("%1 (%2)", e.what(), e.file().string()));
}
}
}
}
- return optional<dcp::EncryptedKDM>();
+ return {};
}
/* Signer for new KDM */
auto signer = Config::instance()->signer_chain ();
if (!signer->valid ()) {
- error ("signing certificate chain is invalid.");
+ throw KDMCLIError ("signing certificate chain is invalid.");
}
/* Make a new empty KDM and add the keys from the DKDM to it */
bool disable_forensic_marking_picture,
optional<int> disable_forensic_marking_audio,
bool email,
- bool zip
+ bool zip,
+ std::function<void (string)> out
)
{
dcp::NameFormat::Map values;
continue;
}
- 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());
+ int const offset_hour = i->cinema ? i->cinema->utc_offset_hour() : 0;
+ int const offset_minute = i->cinema ? i->cinema->utc_offset_minute() : 0;
+
+ dcp::LocalTime begin(valid_from, offset_hour, offset_minute);
+ dcp::LocalTime end(valid_to, offset_hour, offset_minute);
auto const kdm = kdm_from_dkdm(
dkdm,
);
dcp::NameFormat::Map name_values;
- name_values['c'] = i->cinema->name;
+ name_values['c'] = i->cinema ? i->cinema->name : "";
name_values['s'] = i->name;
- name_values['f'] = dkdm.annotation_text().get_value_or("");
+ name_values['f'] = kdm.content_title_text();
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 (make_shared<KDMWithMetadata>(name_values, i->cinema.get(), i->cinema->emails, kdm));
+ kdms.push_back (make_shared<KDMWithMetadata>(name_values, i->cinema.get(), i->cinema ? i->cinema->emails : list<string>(), kdm));
}
- write_files (kdms, zip, output, container_name_format, filename_format, verbose);
+ write_files (kdms, zip, output, container_name_format, filename_format, verbose, out);
if (email) {
- send_emails ({kdms}, container_name_format, filename_format, dkdm.annotation_text().get_value_or(""));
+ send_emails ({kdms}, container_name_format, filename_format, dkdm.annotation_text().get_value_or(""), {});
}
} catch (FileError& e) {
- cerr << program_name << ": " << e.what() << " (" << e.file().string() << ")\n";
- exit (EXIT_FAILURE);
- } catch (KDMError& e) {
- cerr << program_name << ": " << e.what() << "\n";
- exit (EXIT_FAILURE);
- } catch (NetworkError& e) {
- cerr << program_name << ": " << e.what() << "\n";
- exit (EXIT_FAILURE);
+ throw KDMCLIError (String::compose("%1 (%2)", e.what(), e.file().string()));
}
}
static
void
-dump_dkdm_group (shared_ptr<DKDMGroup> group, int indent)
+dump_dkdm_group (shared_ptr<DKDMGroup> group, int indent, std::function<void (string)> out)
{
+ auto const indent_string = string(indent, ' ');
+
if (indent > 0) {
- for (int i = 0; i < indent; ++i) {
- cout << " ";
- }
- cout << group->name() << "\n";
+ out (indent_string + group->name());
}
for (auto i: group->children()) {
auto g = dynamic_pointer_cast<DKDMGroup>(i);
if (g) {
- dump_dkdm_group (g, indent + 2);
+ dump_dkdm_group (g, indent + 2, out);
} else {
- for (int j = 0; j < indent; ++j) {
- cout << " ";
- }
auto d = dynamic_pointer_cast<DKDM>(i);
assert(d);
- cout << d->dkdm().cpl_id() << "\n";
+ out (indent_string + d->dkdm().cpl_id());
}
}
}
-int
-kdm_cli (int argc, char* argv[])
+optional<string>
+kdm_cli (int argc, char* argv[], std::function<void (string)> out)
+try
{
- boost::filesystem::path output = ".";
+ boost::filesystem::path output = boost::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;
- string screen_description;
+ optional<boost::filesystem::path> certificate;
+ optional<string> screen;
list<shared_ptr<Screen>> screens;
optional<dcp::EncryptedKDM> dkdm;
optional<boost::posix_time::ptime> valid_from;
program_name = argv[0];
+ /* Reset getopt() so we can call this method several times in one test process */
+ optind = 1;
+
int option_index = 0;
while (true) {
static struct option long_options[] = {
switch (c) {
case 'h':
- help ();
+ help (out);
exit (EXIT_SUCCESS);
case 'o':
output = optarg;
} else if (string(optarg) == "dci-specific") {
formulation = dcp::Formulation::DCI_SPECIFIC;
} else {
- error ("unrecognised KDM formulation " + string (optarg));
+ throw KDMCLIError ("unrecognised KDM formulation " + string (optarg));
}
break;
case 'p':
cinema = make_shared<Cinema>(optarg, list<string>(), "", 0, 0);
break;
case 'S':
- screen_description = optarg;
+ /* Similarly, this could be the name of a new (temporary) screen or the name of a screen
+ * to search for.
+ */
+ screen = optarg;
break;
case 'C':
- {
- /* Make a new screen and add it to the current cinema */
- dcp::CertificateChain chain (dcp::file_to_string(optarg));
- auto screen = make_shared<Screen>(screen_description, "", chain.leaf(), vector<TrustedDevice>());
- if (cinema) {
- cinema->add_screen (screen);
- }
- screens.push_back (screen);
+ certificate = optarg;
break;
- }
case 'T':
/* A trusted device ends up in the last screen we made */
if (!screens.empty ()) {
}
}
+ if (certificate) {
+ /* Make a new screen and add it to the current cinema */
+ dcp::CertificateChain chain(dcp::file_to_string(*certificate));
+ auto screen_to_add = std::make_shared<Screen>(screen.get_value_or(""), "", chain.leaf(), boost::none, vector<TrustedDevice>());
+ if (cinema) {
+ cinema->add_screen(screen_to_add);
+ }
+ screens.push_back(screen_to_add);
+ }
+
if (list_cinemas) {
auto cinemas = Config::instance()->cinemas ();
for (auto i: cinemas) {
- cout << i->name << " (" << Emailer::address_list (i->emails) << ")\n";
+ out (String::compose("%1 (%2)", i->name, Emailer::address_list (i->emails)));
}
exit (EXIT_SUCCESS);
}
if (list_dkdm_cpls) {
- dump_dkdm_group (Config::instance()->dkdms(), 0);
+ dump_dkdm_group (Config::instance()->dkdms(), 0, out);
exit (EXIT_SUCCESS);
}
if (!duration_string && !valid_to) {
- error ("you must specify a --valid-duration or --valid-to");
+ throw KDMCLIError ("you must specify a --valid-duration or --valid-to");
}
if (!valid_from) {
- error ("you must specify --valid-from");
+ throw KDMCLIError ("you must specify --valid-from");
}
if (optind >= argc) {
- help ();
- exit (EXIT_FAILURE);
+ throw KDMCLIError ("no film, CPL ID or DKDM specified");
}
if (screens.empty()) {
if (!cinema_name) {
- error ("you must specify either a cinema or one or more screens using certificate files");
+ throw KDMCLIError ("you must specify either a cinema or one or more screens using certificate files");
}
screens = find_cinema (*cinema_name)->screens ();
+ if (screen) {
+ screens.erase(std::remove_if(screens.begin(), screens.end(), [&screen](shared_ptr<Screen> s) { return s->name != *screen; }), screens.end());
+ }
}
if (duration_string) {
valid_to = valid_from.get() + duration_from_string (*duration_string);
}
- dcpomatic_setup_path_encoding ();
- dcpomatic_setup ();
-
if (verbose) {
- cout << "Making KDMs valid from " << valid_from.get() << " to " << valid_to.get() << "\n";
+ out (String::compose("Making KDMs valid from %1 to %2", boost::posix_time::to_simple_string(valid_from.get()), boost::posix_time::to_simple_string(valid_to.get())));
}
string const thing = argv[optind];
disable_forensic_marking_picture,
disable_forensic_marking_audio,
email,
- zip
+ zip,
+ out
);
} else {
if (boost::filesystem::is_regular_file(thing)) {
}
if (!dkdm) {
- error ("could not find film or CPL ID corresponding to " + thing);
+ throw KDMCLIError ("could not find film or CPL ID corresponding to " + thing);
}
from_dkdm (
disable_forensic_marking_picture,
disable_forensic_marking_audio,
email,
- zip
+ zip,
+ out
);
}
- return 0;
+ return {};
+} catch (std::exception& e) {
+ return string(e.what());
}
+