2 Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
25 #include "dcpomatic_log.h"
27 #include "kdm_with_metadata.h"
39 using std::shared_ptr;
42 using boost::optional;
47 list<KDMWithMetadataPtr> kdms,
48 boost::filesystem::path directory,
49 dcp::NameFormat name_format,
50 std::function<bool (boost::filesystem::path)> confirm_overwrite
55 if (directory == "-") {
56 /* Write KDMs to the stdout */
58 cout << i->kdm_as_xml ();
65 if (!boost::filesystem::exists (directory)) {
66 boost::filesystem::create_directories (directory);
69 /* Write KDMs to the specified directory */
71 auto out = dcp::fix_long_path(directory / careful_string_filter(name_format.get(i->name_values(), ".xml")));
72 if (!boost::filesystem::exists (out) || confirm_overwrite (out)) {
83 KDMWithMetadata::get (char k) const
85 auto i = _name_values.find (k);
86 if (i == _name_values.end()) {
95 make_zip_file (list<KDMWithMetadataPtr> kdms, boost::filesystem::path zip_file, dcp::NameFormat name_format)
97 Zipper zipper (zip_file);
100 auto const name = careful_string_filter(name_format.get(i->name_values(), ".xml"));
101 zipper.add (name, i->kdm_as_xml());
108 /** Collect a list of KDMWithMetadatas into a list of lists so that
109 * each list contains the KDMs for one list.
111 list<list<KDMWithMetadataPtr>>
112 collect (list<KDMWithMetadataPtr> kdms)
114 list<list<KDMWithMetadataPtr>> grouped;
118 auto j = grouped.begin ();
120 while (j != grouped.end()) {
121 if (j->front()->group() == i->group()) {
128 if (j == grouped.end()) {
129 grouped.push_back (list<KDMWithMetadataPtr>());
130 grouped.back().push_back (i);
138 /** Write one directory per list into another directory */
141 list<list<KDMWithMetadataPtr>> kdms,
142 boost::filesystem::path directory,
143 dcp::NameFormat container_name_format,
144 dcp::NameFormat filename_format,
145 function<bool (boost::filesystem::path)> confirm_overwrite
150 for (auto const& kdm: kdms) {
151 auto path = directory;
152 path /= container_name_format.get(kdm.front()->name_values(), "", "s");
153 if (!boost::filesystem::exists (path) || confirm_overwrite (path)) {
154 boost::filesystem::create_directories (path);
155 write_files(kdm, path, filename_format, confirm_overwrite);
156 written += kdm.size();
164 /** Write one ZIP file per cinema into a directory */
167 list<list<KDMWithMetadataPtr>> kdms,
168 boost::filesystem::path directory,
169 dcp::NameFormat container_name_format,
170 dcp::NameFormat filename_format,
171 function<bool (boost::filesystem::path)> confirm_overwrite
176 for (auto const& kdm: kdms) {
177 auto path = directory;
178 path /= container_name_format.get(kdm.front()->name_values(), ".zip", "s");
179 if (!boost::filesystem::exists (path) || confirm_overwrite (path)) {
180 if (boost::filesystem::exists (path)) {
181 /* Creating a new zip file over an existing one is an error */
182 boost::filesystem::remove (path);
184 make_zip_file(kdm, path, filename_format);
185 written += kdm.size();
193 /** Email one ZIP file per cinema to the cinema.
194 * @param kdms KDMs to email.
195 * @param container_name_format Format of folder / ZIP to use.
196 * @param filename_format Format of filenames to use.
197 * @param name_values Values to substitute into \p container_name_format and \p filename_format.
198 * @param cpl_name Name of the CPL that the KDMs are for.
202 list<list<KDMWithMetadataPtr>> kdms,
203 dcp::NameFormat container_name_format,
204 dcp::NameFormat filename_format,
206 vector<string> extra_addresses
209 auto config = Config::instance ();
211 if (config->mail_server().empty()) {
212 throw NetworkError (_("No mail server configured in preferences"));
215 if (config->kdm_from().empty()) {
216 throw NetworkError(_("No KDM from address configured in preferences"));
219 for (auto const& kdms_for_cinema: kdms) {
221 auto first = kdms_for_cinema.front();
223 if (first->emails().empty()) {
227 auto zip_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
228 boost::filesystem::create_directories (zip_file);
229 zip_file /= container_name_format.get(first->name_values(), ".zip");
230 make_zip_file (kdms_for_cinema, zip_file, filename_format);
232 auto substitute_variables = [cpl_name, first](string target) {
233 boost::algorithm::replace_all(target, "$CPL_NAME", cpl_name);
234 boost::algorithm::replace_all(target, "$START_TIME", first->get('b').get_value_or(""));
235 boost::algorithm::replace_all(target, "$END_TIME", first->get('e').get_value_or(""));
236 boost::algorithm::replace_all(target, "$CINEMA_NAME", first->get('c').get_value_or(""));
237 boost::algorithm::replace_all(target, "$CINEMA_SHORT_NAME", first->get('c').get_value_or("").substr(0, 14));
241 auto subject = substitute_variables(config->kdm_subject());
242 auto body = substitute_variables(config->kdm_email());
245 for (auto kdm: kdms_for_cinema) {
246 auto screen_name = kdm->get('s');
248 screens += *screen_name + ", ";
251 boost::algorithm::replace_all (body, "$SCREENS", screens.substr (0, screens.length() - 2));
253 auto emails = first->emails();
254 Emailer email (config->kdm_from(), { emails.front() }, subject, body);
256 /* Use CC for the second and subsequent email addresses, so we seem less spammy (#2310) */
257 for (auto cc = std::next(emails.begin()); cc != emails.end(); ++cc) {
261 for (auto cc: config->kdm_cc()) {
264 if (!config->kdm_bcc().empty()) {
265 email.add_bcc (config->kdm_bcc());
268 email.add_attachment (zip_file, container_name_format.get(first->name_values(), ".zip"), "application/zip");
270 auto log_details = [](Emailer& email) {
271 dcpomatic_log->log("Email content follows", LogEntry::TYPE_DEBUG_EMAIL);
272 dcpomatic_log->log(email.email(), LogEntry::TYPE_DEBUG_EMAIL);
273 dcpomatic_log->log("Email session follows", LogEntry::TYPE_DEBUG_EMAIL);
274 dcpomatic_log->log(email.notes(), LogEntry::TYPE_DEBUG_EMAIL);
278 email.send (config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
280 boost::filesystem::remove (zip_file);
287 for (auto extra: extra_addresses) {
288 Emailer email (config->kdm_from(), { extra }, subject, body);
289 email.add_attachment (zip_file, container_name_format.get(first->name_values(), ".zip"), "application/zip");
292 email.send (config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
294 boost::filesystem::remove (zip_file);
302 boost::filesystem::remove (zip_file);