From: Carl Hetherington Date: Thu, 10 Oct 2013 11:14:52 +0000 (+0100) Subject: Various improvements to dcpomatic_kdm. X-Git-Tag: v2.0.48~1311 X-Git-Url: https://git.carlh.net/gitweb/?a=commitdiff_plain;h=acb1ace5884337757102bc05074b6b37f04c49cd;p=dcpomatic.git Various improvements to dcpomatic_kdm. --- diff --git a/ChangeLog b/ChangeLog index f60dcbcc4..eaaefd1ed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2013-10-10 Carl Hetherington + * Various improvements to dcpomatic_kdm. + * libdcp fix to incorrect signature digests. 2013-10-09 Carl Hetherington diff --git a/src/lib/kdm.cc b/src/lib/kdm.cc new file mode 100644 index 000000000..3503306e7 --- /dev/null +++ b/src/lib/kdm.cc @@ -0,0 +1,209 @@ +/* + Copyright (C) 2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include "kdm.h" +#include "cinema.h" +#include "exceptions.h" +#include "util.h" +#include "film.h" +#include "config.h" + +using std::list; +using std::string; +using boost::shared_ptr; + +struct ScreenKDM +{ + ScreenKDM (shared_ptr s, libdcp::KDM k) + : screen (s) + , kdm (k) + {} + + shared_ptr screen; + libdcp::KDM kdm; +}; + +struct CinemaKDMs +{ + shared_ptr cinema; + list screen_kdms; + + void make_zip_file (boost::filesystem::path zip_file) const + { + int error; + struct zip* zip = zip_open (zip_file.string().c_str(), ZIP_CREATE | ZIP_EXCL, &error); + if (!zip) { + if (error == ZIP_ER_EXISTS) { + throw FileError ("ZIP file already exists", zip_file); + } + throw FileError ("could not create ZIP file", zip_file); + } + + list > kdm_strings; + + for (list::const_iterator i = screen_kdms.begin(); i != screen_kdms.end(); ++i) { + shared_ptr kdm (new string (i->kdm.as_xml ())); + kdm_strings.push_back (kdm); + + struct zip_source* source = zip_source_buffer (zip, kdm->c_str(), kdm->length(), 0); + if (!source) { + throw StringError ("could not create ZIP source"); + } + + string const name = tidy_for_filename (i->screen->cinema->name) + "_" + + tidy_for_filename (i->screen->name) + ".kdm.xml"; + + if (zip_add (zip, name.c_str(), source) == -1) { + throw StringError ("failed to add KDM to ZIP archive"); + } + } + + if (zip_close (zip) == -1) { + throw StringError ("failed to close ZIP archive"); + } + } +}; + +/* Not complete but sufficient for our purposes (we're using + ScreenKDM in a list where all the screens will be unique). +*/ +bool +operator== (ScreenKDM const & a, ScreenKDM const & b) +{ + return a.screen == b.screen; +} + +static list +make_screen_kdms (shared_ptr film, list > screens, boost::posix_time::ptime from, boost::posix_time::ptime to) +{ + list kdms = film->make_kdms (screens, from, to); + + list screen_kdms; + + list >::iterator i = screens.begin (); + list::iterator j = kdms.begin (); + while (i != screens.end() && j != kdms.end ()) { + screen_kdms.push_back (ScreenKDM (*i, *j)); + ++i; + ++j; + } + + return screen_kdms; +} + +static list +make_cinema_kdms (shared_ptr film, list > screens, boost::posix_time::ptime from, boost::posix_time::ptime to) +{ + list screen_kdms = make_screen_kdms (film, screens, from, to); + list cinema_kdms; + + while (!screen_kdms.empty ()) { + + /* Get all the screens from a single cinema */ + + CinemaKDMs ck; + + list::iterator i = screen_kdms.begin (); + ck.cinema = i->screen->cinema; + ck.screen_kdms.push_back (*i); + list::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::iterator j = i; + ++i; + screen_kdms.remove (*j); + } else { + ++i; + } + } + + cinema_kdms.push_back (ck); + } + + return cinema_kdms; +} + +void +write_kdm_files ( + shared_ptr film, list > screens, boost::posix_time::ptime from, boost::posix_time::ptime to, boost::filesystem::path directory + ) +{ + list screen_kdms = make_screen_kdms (film, screens, from, to); + + /* Write KDMs to the specified directory */ + for (list::iterator i = screen_kdms.begin(); i != screen_kdms.end(); ++i) { + boost::filesystem::path out = directory; + out /= tidy_for_filename (i->screen->cinema->name) + "_" + tidy_for_filename (i->screen->name) + ".kdm.xml"; + i->kdm.as_xml (out); + } +} + +void +write_kdm_zip_files ( + shared_ptr film, list > screens, boost::posix_time::ptime from, boost::posix_time::ptime to, boost::filesystem::path directory + ) +{ + list cinema_kdms = make_cinema_kdms (film, screens, from, to); + + for (list::const_iterator i = cinema_kdms.begin(); i != cinema_kdms.end(); ++i) { + boost::filesystem::path path = directory; + path /= tidy_for_filename (i->cinema->name) + ".zip"; + i->make_zip_file (path); + } +} + +void +email_kdms (shared_ptr film, list > screens, boost::posix_time::ptime from, boost::posix_time::ptime to) +{ + list cinema_kdms = make_cinema_kdms (film, screens, from, to); + + for (list::const_iterator i = cinema_kdms.begin(); i != cinema_kdms.end(); ++i) { + + boost::filesystem::path zip_file = boost::filesystem::temp_directory_path (); + zip_file /= boost::filesystem::unique_path().string() + ".zip"; + i->make_zip_file (zip_file); + + /* Send email */ + + quickmail_initialize (); + quickmail mail = quickmail_create (Config::instance()->kdm_from().c_str(), "KDM delivery"); + quickmail_add_to (mail, i->cinema->email.c_str ()); + + string body = Config::instance()->kdm_email().c_str(); + boost::algorithm::replace_all (body, "$DCP_NAME", film->dcp_name ()); + + quickmail_set_body (mail, body.c_str()); + quickmail_add_attachment_file (mail, zip_file.string().c_str()); + char const* error = quickmail_send (mail, Config::instance()->mail_server().c_str(), 25, "", ""); + if (error) { + quickmail_destroy (mail); + throw StringError (String::compose ("Failed to send KDM email (%1)", error)); + } + quickmail_destroy (mail); + } +} diff --git a/src/lib/kdm.h b/src/lib/kdm.h new file mode 100644 index 000000000..f79656b93 --- /dev/null +++ b/src/lib/kdm.h @@ -0,0 +1,48 @@ +/* + Copyright (C) 2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +class Screen; +class Film; + +extern void write_kdm_files ( + boost::shared_ptr film, + std::list > screens, + boost::posix_time::ptime from, + boost::posix_time::ptime to, + boost::filesystem::path directory + ); + +extern void write_kdm_zip_files ( + boost::shared_ptr film, + std::list > screens, + boost::posix_time::ptime from, + boost::posix_time::ptime to, + boost::filesystem::path directory + ); + +extern void email_kdms ( + boost::shared_ptr film, + std::list > screens, + boost::posix_time::ptime from, + boost::posix_time::ptime to + ); + diff --git a/src/lib/wscript b/src/lib/wscript index 8c02ff158..e27cf3cc7 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -32,6 +32,7 @@ sources = """ image.cc job.cc job_manager.cc + kdm.cc log.cc moving_image_content.cc moving_image_decoder.cc @@ -74,7 +75,7 @@ def build(bld): AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2 SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XML++ - CURL + CURL ZIP QUICKMAIL """ obj.source = sources + ' version.cc' diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index 7646d5ebe..874f19d2a 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2013 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,8 +29,6 @@ #include #include #include -#include -#include #include "wx/film_viewer.h" #include "wx/film_editor.h" #include "wx/job_manager_view.h" @@ -52,6 +50,7 @@ #include "lib/transcode_job.h" #include "lib/exceptions.h" #include "lib/cinema.h" +#include "lib/kdm.h" using std::cout; using std::string; @@ -226,25 +225,6 @@ setup_menu (wxMenuBar* m) m->Append (help, _("&Help")); } -struct ScreenKDM -{ - ScreenKDM (shared_ptr s, libdcp::KDM k) - : screen (s) - , kdm (k) - {} - - shared_ptr screen; - libdcp::KDM kdm; -}; - -/* Not complete but sufficient for our purposes (we're using - ScreenKDM in a list where all the screens will be unique). -*/ -bool operator== (ScreenKDM const & a, ScreenKDM const & b) -{ - return a.screen == b.screen; -} - class Frame : public wxFrame { public: @@ -446,112 +426,13 @@ private: d->Destroy (); return; } - + try { - list > screens = d->screens (); - list kdms = film->make_kdms ( - screens, - d->from (), - d->until () - ); - - list screen_kdms; - - list >::iterator i = screens.begin (); - list::iterator j = kdms.begin (); - while (i != screens.end() && j != kdms.end ()) { - screen_kdms.push_back (ScreenKDM (*i, *j)); - ++i; - ++j; - } - if (d->write_to ()) { - /* Write KDMs to the specified directory */ - for (list::iterator i = screen_kdms.begin(); i != screen_kdms.end(); ++i) { - boost::filesystem::path out = d->directory (); - out /= tidy_for_filename (i->screen->cinema->name) + "_" + tidy_for_filename (i->screen->name) + ".kdm.xml"; - i->kdm.as_xml (out); - } + write_kdm_files (film, d->screens (), d->from (), d->until (), d->directory ()); } else { - while (!screen_kdms.empty ()) { - - /* Get all the screens from a single cinema */ - - shared_ptr cinema; - list cinema_screen_kdms; - - list::iterator i = screen_kdms.begin (); - cinema = i->screen->cinema; - cinema_screen_kdms.push_back (*i); - list::iterator j = i; - ++i; - screen_kdms.remove (*j); - - while (i != screen_kdms.end ()) { - if (i->screen->cinema == cinema) { - cinema_screen_kdms.push_back (*i); - list::iterator j = i; - ++i; - screen_kdms.remove (*j); - } else { - ++i; - } - } - - /* Make a ZIP file of this cinema's KDMs */ - - boost::filesystem::path zip_file = boost::filesystem::temp_directory_path (); - zip_file /= boost::filesystem::unique_path().string() + ".zip"; - struct zip* zip = zip_open (zip_file.string().c_str(), ZIP_CREATE | ZIP_EXCL, 0); - if (!zip) { - throw FileError ("could not create ZIP file", zip_file); - } - - list > kdm_strings; - - for (list::const_iterator i = cinema_screen_kdms.begin(); i != cinema_screen_kdms.end(); ++i) { - shared_ptr kdm (new string (i->kdm.as_xml ())); - kdm_strings.push_back (kdm); - - struct zip_source* source = zip_source_buffer (zip, kdm->c_str(), kdm->length(), 0); - if (!source) { - throw StringError ("could not create ZIP source"); - } - - string const name = tidy_for_filename (i->screen->cinema->name) + "_" + - tidy_for_filename (i->screen->name) + ".kdm.xml"; - - if (zip_add (zip, name.c_str(), source) == -1) { - throw StringError ("failed to add KDM to ZIP archive"); - } - } - - if (zip_close (zip) == -1) { - throw StringError ("failed to close ZIP archive"); - } - - /* Send email */ - - quickmail_initialize (); - quickmail mail = quickmail_create (Config::instance()->kdm_from().c_str(), "KDM delivery"); - quickmail_add_to (mail, cinema->email.c_str ()); - - string body = Config::instance()->kdm_email().c_str(); - boost::algorithm::replace_all (body, "$DCP_NAME", film->dcp_name ()); - - quickmail_set_body (mail, body.c_str()); - quickmail_add_attachment_file (mail, zip_file.string().c_str()); - char const* error = quickmail_send (mail, Config::instance()->mail_server().c_str(), 25, "", ""); - if (error) { - quickmail_destroy (mail); - throw StringError (String::compose ("Failed to send KDM email (%1)", error)); - } - quickmail_destroy (mail); - - film->log()->log (String::compose ("Send KDM email to %1", cinema->email)); - } + email_kdms (film, d->screens (), d->from (), d->until ()); } - } catch (KDMError& e) { error_dialog (this, e.what ()); } diff --git a/src/tools/dcpomatic_kdm.cc b/src/tools/dcpomatic_kdm.cc index 294248697..fb2243464 100644 --- a/src/tools/dcpomatic_kdm.cc +++ b/src/tools/dcpomatic_kdm.cc @@ -20,28 +20,40 @@ #include #include #include "lib/film.h" +#include "lib/cinema.h" +#include "lib/kdm.h" +#include "lib/config.h" +#include "lib/exceptions.h" using std::string; +using std::cout; using std::cerr; +using std::list; using boost::shared_ptr; static void help (string n) { - cerr << "Syntax: " << n << " [OPTION] \n" + cerr << "Syntax: " << n << " [OPTION] []\n" << " -h, --help show this help\n" - << " -o, --output output filename\n" + << " -o, --output output file or directory\n" << " -f, --valid-from valid from time (e.g. \"2013-09-28 01:41:51\")\n" << " -t, --valid-to valid to time (e.g. \"2014-09-28 01:41:51\")\n" - << " -c, --certificate file containing projector certificate\n"; + << " -c, --certificate file containing projector certificate\n" + << " -z, --zip ZIP each cinema's KDMs into its own file\n" + << " --cinema specify a cinema, either by name or email address\n" + << " --cinemas list known cinemas from the DCP-o-matic settings\n"; } int main (int argc, char* argv[]) { - string output; - string valid_from; - string valid_to; + boost::filesystem::path output; + boost::posix_time::ptime valid_from; + boost::posix_time::ptime valid_to; string certificate_file; + bool zip = false; + string cinema_name; + bool cinemas = false; int option_index = 0; while (1) { @@ -51,10 +63,13 @@ int main (int argc, char* argv[]) { "valid-from", required_argument, 0, 'f'}, { "valid-to", required_argument, 0, 't'}, { "certificate", required_argument, 0, 'c' }, + { "cinema", required_argument, 0, 'A' }, + { "cinemas", no_argument, 0, 'B' }, + { "zip", no_argument, 0, 'z' }, { 0, 0, 0, 0 } }; - int c = getopt_long (argc, argv, "ho:f:t:c:", long_options, &option_index); + int c = getopt_long (argc, argv, "ho:f:t:c:A:Bz", long_options, &option_index); if (c == -1) { break; @@ -68,15 +83,32 @@ int main (int argc, char* argv[]) output = optarg; break; case 'f': - valid_from = optarg; + valid_from = boost::posix_time::time_from_string (optarg); break; case 't': - valid_to = optarg; + valid_to = boost::posix_time::time_from_string (optarg); break; case 'c': certificate_file = optarg; break; + case 'A': + cinema_name = optarg; + break; + case 'B': + cinemas = true; + break; + case 'z': + zip = true; + break; + } + } + + if (cinemas) { + list > cinemas = Config::instance()->cinemas (); + for (list >::const_iterator i = cinemas.begin(); i != cinemas.end(); ++i) { + cout << (*i)->name << " (" << (*i)->email << ")\n"; } + exit (EXIT_SUCCESS); } if (optind >= argc) { @@ -84,6 +116,11 @@ int main (int argc, char* argv[]) exit (EXIT_FAILURE); } + if (cinema_name.empty() && certificate_file.empty()) { + cerr << argv[0] << ": you must specify either a cinema, a screen or a certificate file\n"; + exit (EXIT_FAILURE); + } + string const film_dir = argv[optind]; dcpomatic_setup (); @@ -97,12 +134,34 @@ int main (int argc, char* argv[]) exit (EXIT_FAILURE); } - cerr << "reading " << certificate_file << "\n"; - shared_ptr certificate (new libdcp::Certificate (boost::filesystem::path (certificate_file))); - libdcp::KDM kdm = film->make_kdm ( - certificate, boost::posix_time::time_from_string (valid_from), boost::posix_time::time_from_string (valid_to) - ); + if (cinema_name.empty ()) { + shared_ptr certificate (new libdcp::Certificate (boost::filesystem::path (certificate_file))); + libdcp::KDM kdm = film->make_kdm (certificate, valid_from, valid_to); + kdm.as_xml (output); + } else { + + list > cinemas = Config::instance()->cinemas (); + list >::const_iterator i = cinemas.begin(); + while (i != cinemas.end() && (*i)->name != cinema_name && (*i)->email != cinema_name) { + ++i; + } + + if (i == cinemas.end ()) { + cerr << argv[0] << ": could not find cinema \"" << cinema_name << "\"\n"; + exit (EXIT_FAILURE); + } + + try { + if (zip) { + write_kdm_zip_files (film, (*i)->screens(), valid_from, valid_to, output); + } else { + write_kdm_files (film, (*i)->screens(), valid_from, valid_to, output); + } + } catch (FileError& e) { + cerr << argv[0] << ": " << e.what() << " (" << e.file().string() << ")\n"; + exit (EXIT_FAILURE); + } + } - kdm.as_xml (output); return 0; } diff --git a/src/tools/wscript b/src/tools/wscript index b68926830..957547d39 100644 --- a/src/tools/wscript +++ b/src/tools/wscript @@ -15,7 +15,7 @@ def build(bld): if not bld.env.DISABLE_GUI: for t in ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server']: obj = bld(features = 'cxx cxxprogram') - obj.uselib = 'DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML WXWIDGETS ZIP QUICKMAIL' + obj.uselib = 'DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML WXWIDGETS' if bld.env.STATIC: obj.uselib += ' GTK' obj.includes = ['..']