From 736b3a068ba5a402b541d32f270669e6e1a4e5c4 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 29 Jul 2016 09:57:58 +0100 Subject: [PATCH] Allow configuration of KDM filename format. --- ChangeLog | 4 ++ src/lib/cinema_kdms.cc | 61 ++++++++++++++------- src/lib/cinema_kdms.h | 18 ++++--- src/lib/config.cc | 4 ++ src/lib/config.h | 12 ++++- src/lib/kdm_name_format.cc | 33 ++++++++++++ src/lib/kdm_name_format.h | 33 ++++++++++++ src/lib/name_format.cc | 98 ++++++++++++++++++++++++++++++++++ src/lib/name_format.h | 78 +++++++++++++++++++++++++++ src/lib/screen_kdm.cc | 12 ++--- src/lib/screen_kdm.h | 8 +-- src/lib/send_kdm_email_job.cc | 19 ++++--- src/lib/send_kdm_email_job.h | 13 +++-- src/lib/wscript | 2 + src/tools/dcpomatic.cc | 18 ++++--- src/tools/dcpomatic_kdm.cc | 13 +++-- src/tools/dcpomatic_kdm_cli.cc | 14 ++++- src/wx/kdm_dialog.cc | 14 +++++ src/wx/kdm_dialog.h | 4 ++ src/wx/kdm_output_panel.cc | 35 ++++++++++++ src/wx/kdm_output_panel.h | 5 ++ src/wx/name_format_editor.h | 91 +++++++++++++++++++++++++++++++ 22 files changed, 523 insertions(+), 66 deletions(-) create mode 100644 src/lib/kdm_name_format.cc create mode 100644 src/lib/kdm_name_format.h create mode 100644 src/lib/name_format.cc create mode 100644 src/lib/name_format.h create mode 100644 src/wx/name_format_editor.h diff --git a/ChangeLog b/ChangeLog index 9a1015d76..c5b9cc56e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2016-07-29 c.hetherington + + * Allow configuration of KDM filename format. + 2016-07-28 Carl Hetherington * Version 2.9.7 released. diff --git a/src/lib/cinema_kdms.cc b/src/lib/cinema_kdms.cc index f76c96283..a91fc1c1e 100644 --- a/src/lib/cinema_kdms.cc +++ b/src/lib/cinema_kdms.cc @@ -27,6 +27,7 @@ #include "emailer.h" #include "compose.hpp" #include "log.h" +#include "kdm_name_format.h" #include #include @@ -39,7 +40,7 @@ using std::runtime_error; using boost::shared_ptr; void -CinemaKDMs::make_zip_file (string film_name, boost::filesystem::path zip_file) const +CinemaKDMs::make_zip_file (boost::filesystem::path zip_file, KDMNameFormat name_format, NameFormat::Map name_values) const { int error; struct zip* zip = zip_open (zip_file.string().c_str(), ZIP_CREATE | ZIP_EXCL, &error); @@ -52,6 +53,8 @@ CinemaKDMs::make_zip_file (string film_name, boost::filesystem::path zip_file) c list > kdm_strings; + name_values["cinema"] = cinema->name; + BOOST_FOREACH (ScreenKDM const & i, screen_kdms) { shared_ptr kdm (new string (i.kdm.as_xml ())); kdm_strings.push_back (kdm); @@ -61,7 +64,9 @@ CinemaKDMs::make_zip_file (string film_name, boost::filesystem::path zip_file) c throw runtime_error ("could not create ZIP source"); } - if (zip_add (zip, i.filename(film_name).c_str(), source) == -1) { + name_values["screen"] = i.screen->name; + string const name = name_format.get(name_values) + ".xml"; + if (zip_add (zip, name.c_str(), source) == -1) { throw runtime_error ("failed to add KDM to ZIP archive"); } } @@ -71,6 +76,9 @@ CinemaKDMs::make_zip_file (string film_name, boost::filesystem::path zip_file) c } } +/** Collect a list of ScreenKDMs into a list of CinemaKDMs so that each + * CinemaKDM contains the KDMs for its cinema. + */ list CinemaKDMs::collect (list screen_kdms) { @@ -106,21 +114,36 @@ CinemaKDMs::collect (list screen_kdms) return cinema_kdms; } +/** Write one ZIP file per cinema into a directory */ void -CinemaKDMs::write_zip_files (string film_name, list cinema_kdms, boost::filesystem::path directory) +CinemaKDMs::write_zip_files ( + list cinema_kdms, + boost::filesystem::path directory, + KDMNameFormat name_format, + NameFormat::Map name_values + ) { + /* No specific screen */ + name_values["screen"] = ""; + BOOST_FOREACH (CinemaKDMs const & i, cinema_kdms) { boost::filesystem::path path = directory; - path /= tidy_for_filename (i.cinema->name) + ".zip"; - i.make_zip_file (film_name, path); + name_values["cinema"] = i.cinema->name; + path /= name_format.get(name_values) + ".zip"; + i.make_zip_file (path, name_format, name_values); } } -/** @param log Log to write email session transcript to, or 0 */ -/* XXX: should probably get from/to from the KDMs themselves */ +/** Email one ZIP file per cinema to the cinema. + * @param log Log to write email session transcript to, or 0. + */ void CinemaKDMs::email ( - string film_name, string cpl_name, list cinema_kdms, dcp::LocalTime from, dcp::LocalTime to, shared_ptr log + list cinema_kdms, + KDMNameFormat name_format, + NameFormat::Map name_values, + string cpl_name, + shared_ptr log ) { Config* config = Config::instance (); @@ -129,26 +152,27 @@ CinemaKDMs::email ( throw NetworkError (_("No mail server configured in preferences")); } + /* No specific screen */ + name_values["screen"] = ""; + BOOST_FOREACH (CinemaKDMs const & i, cinema_kdms) { + name_values["cinema"] = i.cinema->name; + boost::filesystem::path zip_file = boost::filesystem::temp_directory_path (); zip_file /= boost::filesystem::unique_path().string() + ".zip"; - i.make_zip_file (film_name, zip_file); + i.make_zip_file (zip_file, name_format, name_values); string subject = config->kdm_subject(); - locked_stringstream start; - start << from.date() << " " << from.time_of_day(); - locked_stringstream end; - end << to.date() << " " << to.time_of_day(); boost::algorithm::replace_all (subject, "$CPL_NAME", cpl_name); - boost::algorithm::replace_all (subject, "$START_TIME", start.str ()); - boost::algorithm::replace_all (subject, "$END_TIME", end.str ()); + boost::algorithm::replace_all (subject, "$START_TIME", name_values["from"]); + boost::algorithm::replace_all (subject, "$END_TIME", name_values["to"]); boost::algorithm::replace_all (subject, "$CINEMA_NAME", i.cinema->name); string body = config->kdm_email().c_str(); boost::algorithm::replace_all (body, "$CPL_NAME", cpl_name); - boost::algorithm::replace_all (body, "$START_TIME", start.str ()); - boost::algorithm::replace_all (body, "$END_TIME", end.str ()); + boost::algorithm::replace_all (body, "$START_TIME", name_values["from"]); + boost::algorithm::replace_all (body, "$END_TIME", name_values["to"]); boost::algorithm::replace_all (body, "$CINEMA_NAME", i.cinema->name); locked_stringstream screens; @@ -166,8 +190,7 @@ CinemaKDMs::email ( email.add_bcc (config->kdm_bcc ()); } - string const name = tidy_for_filename(i.cinema->name) + "_" + tidy_for_filename(film_name) + ".zip"; - email.add_attachment (zip_file, name, "application/zip"); + email.add_attachment (zip_file, name_format.get(name_values) + ".zip", "application/zip"); Config* c = Config::instance (); diff --git a/src/lib/cinema_kdms.h b/src/lib/cinema_kdms.h index e7efcb985..a9ab4867e 100644 --- a/src/lib/cinema_kdms.h +++ b/src/lib/cinema_kdms.h @@ -27,16 +27,22 @@ class Log; class CinemaKDMs { public: - void make_zip_file (std::string film_name, boost::filesystem::path zip_file) const; + void make_zip_file (boost::filesystem::path zip_file, KDMNameFormat name_format, NameFormat::Map name_values) const; static std::list collect (std::list kdms); - static void write_zip_files (std::string film_name, std::list cinema_kdms, boost::filesystem::path directory); + + static void write_zip_files ( + std::list cinema_kdms, + boost::filesystem::path directory, + KDMNameFormat name_format, + NameFormat::Map name_values + ); + static void email ( - std::string film_name, - std::string cpl_name, std::list cinema_kdms, - dcp::LocalTime from, - dcp::LocalTime to, + KDMNameFormat name_format, + NameFormat::Map name_values, + std::string cpl_name, boost::shared_ptr log ); diff --git a/src/lib/config.cc b/src/lib/config.cc index c6c6cac8b..a5a42436b 100644 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@ -30,6 +30,7 @@ #include "util.h" #include "cross.h" #include "raw_convert.h" +#include "kdm_name_format.h" #include #include #include @@ -108,6 +109,7 @@ Config::set_defaults () #endif _cinemas_file = path ("cinemas.xml"); _show_hints_before_make_dcp = true; + _kdm_filename_format = KDMNameFormat ("KDM %f %c %s"); _allowed_dcp_frame_rates.clear (); _allowed_dcp_frame_rates.push_back (24); @@ -290,6 +292,7 @@ try _cinemas_file = f.optional_string_child("CinemasFile").get_value_or (path ("cinemas.xml").string ()); _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true); + _kdm_filename_format = KDMNameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s")); /* Replace any cinemas from config.xml with those from the configured file */ if (boost::filesystem::exists (_cinemas_file)) { @@ -447,6 +450,7 @@ Config::write_config_xml () const root->add_child("CinemasFile")->add_child_text (_cinemas_file.string()); root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0"); + root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ()); try { doc.write_to_file_formatted (path("config.xml").string ()); diff --git a/src/lib/config.h b/src/lib/config.h index 61c6bfa69..d89adf491 100644 --- a/src/lib/config.h +++ b/src/lib/config.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2015 Carl Hetherington + Copyright (C) 2012-2016 Carl Hetherington This file is part of DCP-o-matic. @@ -26,6 +26,7 @@ #define DCPOMATIC_CONFIG_H #include "isdcf_metadata.h" +#include "kdm_name_format.h" #include "types.h" #include #include @@ -266,6 +267,10 @@ public: return _show_hints_before_make_dcp; } + KDMNameFormat kdm_filename_format () const { + return _kdm_filename_format; + } + /** @param n New number of local encoding threads */ void set_num_local_encoding_threads (int n) { maybe_set (_num_local_encoding_threads, n); @@ -474,6 +479,10 @@ public: maybe_set (_show_hints_before_make_dcp, s); } + void set_kdm_filename_format (KDMNameFormat n) { + maybe_set (_kdm_filename_format, n); + } + void clear_history () { _history.clear (); changed (); @@ -585,6 +594,7 @@ private: std::vector _dkdms; boost::filesystem::path _cinemas_file; bool _show_hints_before_make_dcp; + KDMNameFormat _kdm_filename_format; /** Singleton instance, or 0 */ static Config* _instance; diff --git a/src/lib/kdm_name_format.cc b/src/lib/kdm_name_format.cc new file mode 100644 index 000000000..fe4a3fc62 --- /dev/null +++ b/src/lib/kdm_name_format.cc @@ -0,0 +1,33 @@ +/* + Copyright (C) 2016 Carl Hetherington + + 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 . + +*/ + +#include "kdm_name_format.h" + +using std::string; + +KDMNameFormat::KDMNameFormat (string specification) + : NameFormat (specification) +{ + add ("film_name", 'f', "film name"); + add ("cinema", 'c', "cinema"); + add ("screen", 's', "screen"); + add ("from", 'b', "from date/time"); + add ("to", 'e', "to date/time"); +} diff --git a/src/lib/kdm_name_format.h b/src/lib/kdm_name_format.h new file mode 100644 index 000000000..fad5f7265 --- /dev/null +++ b/src/lib/kdm_name_format.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2016 Carl Hetherington + + 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 . + +*/ + +#ifndef DCPOMATIC_KDM_NAME_FORMAT +#define DCPOMATIC_KDM_NAME_FORMAT + +#include "name_format.h" + +class KDMNameFormat : public NameFormat +{ +public: + KDMNameFormat () {} + KDMNameFormat (std::string specification); +}; + +#endif diff --git a/src/lib/name_format.cc b/src/lib/name_format.cc new file mode 100644 index 000000000..07909c5fb --- /dev/null +++ b/src/lib/name_format.cc @@ -0,0 +1,98 @@ +/* + Copyright (C) 2016 Carl Hetherington + + 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 . + +*/ + +#include "name_format.h" +#include +#include + +using std::string; +using std::map; +using boost::optional; + +static char +filter (char c) +{ + if (c == '/' || c == ':') { + c = '-'; + } else if (c == ' ') { + c = '_'; + } + + return c; +} + +static string +filter (string c) +{ + string o; + + for (size_t i = 0; i < c.length(); ++i) { + o += filter (c[i]); + } + + return o; +} + +void +NameFormat::add (string name, char placeholder, string title) +{ + _components.push_back (Component (name, placeholder, title)); +} + +optional +NameFormat::component_by_placeholder (char p) const +{ + BOOST_FOREACH (Component const & i, _components) { + if (i.placeholder == p) { + return i; + } + } + + return optional (); +} + +string +NameFormat::get (Map values) const +{ + string result; + for (size_t i = 0; i < _specification.length(); ++i) { + bool done = false; + if (_specification[i] == '%' && (i < _specification.length() - 1)) { + optional c = component_by_placeholder (_specification[i + 1]); + if (c) { + result += filter (values[c->name]); + ++i; + done = true; + } + } + + if (!done) { + result += filter (_specification[i]); + } + } + + return result; +} + +bool +operator== (NameFormat const & a, NameFormat const & b) +{ + return a.specification() == b.specification(); +} diff --git a/src/lib/name_format.h b/src/lib/name_format.h new file mode 100644 index 000000000..7e06dc0a8 --- /dev/null +++ b/src/lib/name_format.h @@ -0,0 +1,78 @@ +/* + Copyright (C) 2016 Carl Hetherington + + 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 . + +*/ + +#ifndef DCPOMATIC_NAME_FORMAT +#define DCPOMATIC_NAME_FORMAT + +#include +#include +#include + +class NameFormat +{ +public: + struct Component + { + Component (std::string name_, char placeholder_, std::string title_) + : name (name_) + , placeholder (placeholder_) + , title (title_) + {} + + std::string name; + char placeholder; + std::string title; + }; + + std::list components () const { + return _components; + } + + std::string specification () const { + return _specification; + } + + void set_specification (std::string specification) { + _specification = specification; + } + + typedef std::map Map; + + std::string get (Map) const; + +protected: + NameFormat () {} + + NameFormat (std::string specification) + : _specification (specification) + {} + + void add (std::string name, char placeholder, std::string title); + +private: + boost::optional component_by_placeholder (char p) const; + + std::list _components; + std::string _specification; +}; + +extern bool operator== (NameFormat const & a, NameFormat const & b); + +#endif diff --git a/src/lib/screen_kdm.cc b/src/lib/screen_kdm.cc index 5cdf1c77b..52590bc55 100644 --- a/src/lib/screen_kdm.cc +++ b/src/lib/screen_kdm.cc @@ -34,18 +34,14 @@ operator== (ScreenKDM const & a, ScreenKDM const & b) return a.screen == b.screen && a.kdm == b.kdm; } -string -ScreenKDM::filename (string film_name) const -{ - return tidy_for_filename (film_name) + "_" + tidy_for_filename (screen->cinema->name) + "_" + tidy_for_filename (screen->name) + ".kdm.xml"; -} - void -ScreenKDM::write_files (string film_name, list screen_kdms, boost::filesystem::path directory) +ScreenKDM::write_files (list screen_kdms, boost::filesystem::path directory, KDMNameFormat name_format, NameFormat::Map name_values) { /* Write KDMs to the specified directory */ BOOST_FOREACH (ScreenKDM const & i, screen_kdms) { - boost::filesystem::path out = directory / i.filename(film_name); + name_values["cinema"] = i.screen->cinema->name; + name_values["screen"] = i.screen->name; + boost::filesystem::path out = directory / (name_format.get(name_values) + ".xml"); i.kdm.as_xml (out); } } diff --git a/src/lib/screen_kdm.h b/src/lib/screen_kdm.h index 7ec8e9cd9..2e015e63e 100644 --- a/src/lib/screen_kdm.h +++ b/src/lib/screen_kdm.h @@ -21,6 +21,7 @@ #ifndef DCPOMATIC_SCREEN_KDM_H #define DCPOMATIC_SCREEN_KDM_H +#include "kdm_name_format.h" #include #include @@ -35,9 +36,10 @@ public: , kdm (k) {} - std::string filename (std::string film_name) const; - - static void write_files (std::string film_name, std::list screen_kdms, boost::filesystem::path directory); + static void write_files ( + std::list screen_kdms, boost::filesystem::path directory, + KDMNameFormat name_format, NameFormat::Map name_values + ); boost::shared_ptr screen; dcp::EncryptedKDM kdm; diff --git a/src/lib/send_kdm_email_job.cc b/src/lib/send_kdm_email_job.cc index 82fb55d80..4834657ed 100644 --- a/src/lib/send_kdm_email_job.cc +++ b/src/lib/send_kdm_email_job.cc @@ -32,18 +32,16 @@ using boost::shared_ptr; /** @param log Log to write to, or 0 */ SendKDMEmailJob::SendKDMEmailJob ( - string film_name, - string cpl_name, - boost::posix_time::ptime from, - boost::posix_time::ptime to, list cinema_kdms, + KDMNameFormat name_format, + NameFormat::Map name_values, + string cpl_name, shared_ptr log ) : Job (shared_ptr()) - , _film_name (film_name) + , _name_format (name_format) + , _name_values (name_values) , _cpl_name (cpl_name) - , _from (from) - , _to (to) , _cinema_kdms (cinema_kdms) , _log (log) { @@ -53,11 +51,12 @@ SendKDMEmailJob::SendKDMEmailJob ( string SendKDMEmailJob::name () const { - if (_film_name.empty ()) { + NameFormat::Map::const_iterator i = _name_values.find ("film_name"); + if (i == _name_values.end() || i->second.empty ()) { return _("Email KDMs"); } - return String::compose (_("Email KDMs for %1"), _film_name); + return String::compose (_("Email KDMs for %1"), i->second); } string @@ -70,7 +69,7 @@ void SendKDMEmailJob::run () { set_progress_unknown (); - CinemaKDMs::email (_film_name, _cpl_name, _cinema_kdms, dcp::LocalTime (_from), dcp::LocalTime (_to), _log); + CinemaKDMs::email (_cinema_kdms, _name_format, _name_values, _cpl_name, _log); 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 cb968301f..e16716a66 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_name_format.h" #include #include @@ -30,11 +31,10 @@ class SendKDMEmailJob : public Job { public: SendKDMEmailJob ( - std::string film_name, - std::string cpl_name, - boost::posix_time::ptime from, - boost::posix_time::ptime to, std::list cinema_kdms, + KDMNameFormat name_format, + NameFormat::Map name_values, + std::string cpl_name, boost::shared_ptr log ); @@ -43,10 +43,9 @@ public: void run (); private: - std::string _film_name; + KDMNameFormat _name_format; + NameFormat::Map _name_values; std::string _cpl_name; - boost::posix_time::ptime _from; - boost::posix_time::ptime _to; std::list _cinema_kdms; boost::shared_ptr _log; }; diff --git a/src/lib/wscript b/src/lib/wscript index 9883f31d6..84843992b 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -94,10 +94,12 @@ sources = """ job.cc job_manager.cc json_server.cc + kdm_name_format.cc log.cc log_entry.cc magick_image_proxy.cc mid_side_decoder.cc + name_format.cc overlaps.cc player.cc player_subtitles.cc diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index ac5b8a544..e29d41db7 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -492,20 +492,26 @@ private: try { list screen_kdms = _film->make_kdms (d->screens(), d->cpl(), d->from(), d->until(), d->formulation()); + + NameFormat::Map name_values; + name_values["film_name"] = _film->name(); + name_values["from"] = dcp::LocalTime(d->from()).date() + " " + dcp::LocalTime(d->from()).time_of_day(); + name_values["to"] = dcp::LocalTime(d->until()).date() + " " + dcp::LocalTime(d->until()).time_of_day(); + if (d->write_to ()) { ScreenKDM::write_files ( - _film->name(), screen_kdms, - d->directory() + d->directory(), + d->name_format(), + name_values ); } else { JobManager::instance()->add ( shared_ptr (new SendKDMEmailJob ( - _film->name(), - _film->dcp_name(), - d->from(), - d->until(), CinemaKDMs::collect (screen_kdms), + d->name_format(), + name_values, + _film->dcp_name(), _film->log() )) ); diff --git a/src/tools/dcpomatic_kdm.cc b/src/tools/dcpomatic_kdm.cc index c686a974d..a0a803737 100644 --- a/src/tools/dcpomatic_kdm.cc +++ b/src/tools/dcpomatic_kdm.cc @@ -288,8 +288,13 @@ private: screen_kdms.push_back (ScreenKDM (i, kdm.encrypt (signer, i->recipient.get(), i->trusted_devices, _output->formulation()))); } + NameFormat::Map name_values; + name_values["film_name"] = decrypted.content_title_text(); + name_values["from"] = dcp::LocalTime(_timing->from()).date() + " " + dcp::LocalTime(_timing->from()).time_of_day(); + name_values["to"] = dcp::LocalTime(_timing->until()).date() + " " + dcp::LocalTime(_timing->until()).time_of_day(); + if (_output->write_to()) { - ScreenKDM::write_files (decrypted.content_title_text(), screen_kdms, _output->directory()); + ScreenKDM::write_files (screen_kdms, _output->directory(), _output->name_format(), name_values); /* XXX: proper plural form support in wxWidgets? */ wxString s = screen_kdms.size() == 1 ? _("%d KDM written to %s") : _("%d KDMs written to %s"); message_dialog ( @@ -302,10 +307,10 @@ private: film_name = decrypted.content_title_text (); } shared_ptr job (new SendKDMEmailJob ( - film_name, - decrypted.content_title_text(), - _timing->from(), _timing->until(), CinemaKDMs::collect (screen_kdms), + _output->name_format(), + name_values, + decrypted.content_title_text(), shared_ptr () )); diff --git a/src/tools/dcpomatic_kdm_cli.cc b/src/tools/dcpomatic_kdm_cli.cc index 4c527ee0c..ad5ee1c7b 100644 --- a/src/tools/dcpomatic_kdm_cli.cc +++ b/src/tools/dcpomatic_kdm_cli.cc @@ -284,19 +284,29 @@ int main (int argc, char* argv[]) output = "."; } + NameFormat::Map values; + values["film_name"] = film->name(); + values["from"] = dcp::LocalTime(valid_from.get()).date() + " " + dcp::LocalTime(valid_from.get()).time_of_day(); + values["to"] = dcp::LocalTime(valid_to.get()).date() + " " + dcp::LocalTime(valid_to.get()).time_of_day(); + try { list screen_kdms = film->make_kdms ( (*i)->screens(), cpl, valid_from.get(), valid_to.get(), formulation ); if (zip) { - CinemaKDMs::write_zip_files (film->name(), CinemaKDMs::collect (screen_kdms), output); + CinemaKDMs::write_zip_files ( + CinemaKDMs::collect (screen_kdms), + output, + Config::instance()->kdm_filename_format(), + values + ); if (verbose) { cout << "Wrote ZIP files to " << output << "\n"; } } else { - ScreenKDM::write_files (film->name(), screen_kdms, output); + ScreenKDM::write_files (screen_kdms, output, Config::instance()->kdm_filename_format(), values); if (verbose) { cout << "Wrote KDM files to " << output << "\n"; diff --git a/src/wx/kdm_dialog.cc b/src/wx/kdm_dialog.cc index 7f3f4b07b..ee164518a 100644 --- a/src/wx/kdm_dialog.cc +++ b/src/wx/kdm_dialog.cc @@ -161,3 +161,17 @@ KDMDialog::formulation () const { return _output->formulation (); } + +KDMNameFormat +KDMDialog::name_format () const +{ + return _output->name_format (); +} + +int +KDMDialog::ShowModal () +{ + int const r = wxDialog::ShowModal (); + _output->save_kdm_name_format (); + return r; +} diff --git a/src/wx/kdm_dialog.h b/src/wx/kdm_dialog.h index e0111b6c5..7d64fea77 100644 --- a/src/wx/kdm_dialog.h +++ b/src/wx/kdm_dialog.h @@ -19,6 +19,7 @@ */ #include "wx_util.h" +#include "lib/kdm_name_format.h" #include #include #include @@ -50,6 +51,9 @@ public: boost::filesystem::path directory () const; bool write_to () const; dcp::Formulation formulation () const; + KDMNameFormat name_format () const; + + int ShowModal (); private: void setup_sensitivity (); diff --git a/src/wx/kdm_output_panel.cc b/src/wx/kdm_output_panel.cc index 901832684..49e561c65 100644 --- a/src/wx/kdm_output_panel.cc +++ b/src/wx/kdm_output_panel.cc @@ -18,8 +18,10 @@ */ +#include "lib/config.h" #include "kdm_output_panel.h" #include "wx_util.h" +#include "name_format_editor.h" #include #ifdef DCPOMATIC_USE_OWN_PICKER #include "dir_picker_ctrl.h" @@ -43,6 +45,27 @@ KDMOutputPanel::KDMOutputPanel (wxWindow* parent, bool interop) table->Add (_type, 1, wxEXPAND); _type->SetSelection (0); + { + int flags = wxALIGN_TOP | wxTOP; + wxString t = _("Filename format"); +#ifdef __WXOSX__ + flags |= wxALIGN_RIGHT; + t += wxT (":"); +#endif + wxStaticText* m = new wxStaticText (this, wxID_ANY, t); + table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP); + } + + _filename_format = new NameFormatEditor (this, Config::instance()->kdm_filename_format()); + NameFormat::Map ex; + ex["film_name"] = "Bambi"; + ex["cinema"] = "Lumière"; + ex["screen"] = "Screen 1"; + ex["from"] = "2012/03/15 12:30"; + ex["to"] = "2012/03/22 02:30"; + _filename_format->set_example (ex); + table->Add (_filename_format->panel(), 1, wxEXPAND); + _write_to = new wxRadioButton (this, wxID_ANY, _("Write to")); table->Add (_write_to, 1, wxEXPAND); @@ -91,3 +114,15 @@ KDMOutputPanel::formulation () const { return (dcp::Formulation) reinterpret_cast (_type->GetClientData (_type->GetSelection())); } + +void +KDMOutputPanel::save_kdm_name_format () const +{ + Config::instance()->set_kdm_filename_format (name_format ()); +} + +KDMNameFormat +KDMOutputPanel::name_format () const +{ + return _filename_format->get (); +} diff --git a/src/wx/kdm_output_panel.h b/src/wx/kdm_output_panel.h index 0ca55e325..002b189f5 100644 --- a/src/wx/kdm_output_panel.h +++ b/src/wx/kdm_output_panel.h @@ -19,6 +19,8 @@ */ #include "wx_util.h" +#include "name_format_editor.h" +#include "lib/kdm_name_format.h" #include #include #include @@ -34,11 +36,14 @@ public: boost::filesystem::path directory () const; bool write_to () const; dcp::Formulation formulation () const; + KDMNameFormat name_format () const; void setup_sensitivity (); + void save_kdm_name_format () const; private: wxChoice* _type; + NameFormatEditor* _filename_format; wxRadioButton* _write_to; #ifdef DCPOMATIC_USE_OWN_PICKER DirPickerCtrl* _folder; diff --git a/src/wx/name_format_editor.h b/src/wx/name_format_editor.h new file mode 100644 index 000000000..ee88d46b3 --- /dev/null +++ b/src/wx/name_format_editor.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 2016 Carl Hetherington + + 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 . + +*/ + +#ifndef DCPOMATIC_NAME_FORMAT_EDITOR_H +#define DCPOMATIC_NAME_FORMAT_EDITOR_H + +#include "lib/name_format.h" +#include "lib/compose.hpp" +#include +#include + +template +class NameFormatEditor +{ +public: + NameFormatEditor (wxWindow* parent, T name) + : _panel (new wxPanel (parent)) + , _example (new wxStaticText (_panel, wxID_ANY, "")) + , _sizer (new wxBoxSizer (wxVERTICAL)) + , _specification (new wxTextCtrl (_panel, wxID_ANY, "")) + , _name (name) + { + _sizer->Add (_specification, 0, wxEXPAND, DCPOMATIC_SIZER_Y_GAP); + _sizer->Add (_example, 0, wxBOTTOM, DCPOMATIC_SIZER_Y_GAP); + _panel->SetSizer (_sizer); + + BOOST_FOREACH (NameFormat::Component c, name.components ()) { + wxStaticText* t = new wxStaticText (_panel, wxID_ANY, std_to_wx (String::compose ("%%%1 %2", c.placeholder, c.title))); + _sizer->Add (t); + wxFont font = t->GetFont(); + font.SetStyle (wxFONTSTYLE_ITALIC); + font.SetPointSize (font.GetPointSize() - 1); + t->SetFont (font); + t->SetForegroundColour (wxColour (0, 0, 204)); + } + + _specification->SetValue (std_to_wx (_name.specification ())); + _specification->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&NameFormatEditor::update_example, this)); + + update_example (); + } + + wxPanel* panel () const + { + return _panel; + } + + void set_example (NameFormat::Map v) { + _example_values = v; + update_example (); + } + + T get () const { + return _name; + } + +private: + + virtual void update_example () + { + _name.set_specification (wx_to_std (_specification->GetValue ())); + _example->SetLabel (wxString::Format (_("e.g. %s"), _name.get (_example_values))); + } + + wxPanel* _panel; + wxStaticText* _example; + wxSizer* _sizer; + wxTextCtrl* _specification; + + T _name; + NameFormat::Map _example_values; +}; + +#endif -- 2.30.2