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"
32 #include <dcp/filesystem.h>
40 using std::shared_ptr;
43 using boost::optional;
48 list<KDMWithMetadataPtr> kdms,
49 boost::filesystem::path directory,
50 dcp::NameFormat name_format,
51 std::function<bool (boost::filesystem::path)> confirm_overwrite
56 if (directory == "-") {
57 /* Write KDMs to the stdout */
59 cout << i->kdm_as_xml ();
66 if (!dcp::filesystem::exists(directory)) {
67 dcp::filesystem::create_directories(directory);
70 /* Write KDMs to the specified directory */
72 auto out = directory / careful_string_filter(name_format.get(i->name_values(), ".xml"));
73 if (!dcp::filesystem::exists(out) || confirm_overwrite(out)) {
84 KDMWithMetadata::get (char k) const
86 auto i = _name_values.find (k);
87 if (i == _name_values.end()) {
96 make_zip_file (list<KDMWithMetadataPtr> kdms, boost::filesystem::path zip_file, dcp::NameFormat name_format)
98 Zipper zipper (zip_file);
101 auto const name = careful_string_filter(name_format.get(i->name_values(), ".xml"));
102 zipper.add (name, i->kdm_as_xml());
109 /** Collect a list of KDMWithMetadatas into a list of lists so that
110 * each list contains the KDMs for one list.
112 list<list<KDMWithMetadataPtr>>
113 collect (list<KDMWithMetadataPtr> kdms)
115 list<list<KDMWithMetadataPtr>> grouped;
119 auto j = grouped.begin ();
121 while (j != grouped.end()) {
122 if (j->front()->group() == i->group()) {
129 if (j == grouped.end()) {
130 grouped.push_back (list<KDMWithMetadataPtr>());
131 grouped.back().push_back (i);
139 /** Write one directory per list into another directory */
142 list<list<KDMWithMetadataPtr>> kdms,
143 boost::filesystem::path directory,
144 dcp::NameFormat container_name_format,
145 dcp::NameFormat filename_format,
146 function<bool (boost::filesystem::path)> confirm_overwrite
151 for (auto const& kdm: kdms) {
152 auto path = directory;
153 path /= container_name_format.get(kdm.front()->name_values(), "", "s");
154 if (!dcp::filesystem::exists(path) || confirm_overwrite(path)) {
155 dcp::filesystem::create_directories(path);
156 write_files(kdm, path, filename_format, confirm_overwrite);
157 written += kdm.size();
165 /** Write one ZIP file per cinema into a directory */
168 list<list<KDMWithMetadataPtr>> kdms,
169 boost::filesystem::path directory,
170 dcp::NameFormat container_name_format,
171 dcp::NameFormat filename_format,
172 function<bool (boost::filesystem::path)> confirm_overwrite
177 for (auto const& kdm: kdms) {
178 auto path = directory;
179 path /= container_name_format.get(kdm.front()->name_values(), ".zip", "s");
180 if (!dcp::filesystem::exists(path) || confirm_overwrite(path)) {
181 if (dcp::filesystem::exists(path)) {
182 /* Creating a new zip file over an existing one is an error */
183 dcp::filesystem::remove(path);
185 make_zip_file(kdm, path, filename_format);
186 written += kdm.size();
194 /** Email one ZIP file per cinema to the cinema.
195 * @param kdms KDMs to email.
196 * @param container_name_format Format of folder / ZIP to use.
197 * @param filename_format Format of filenames to use.
198 * @param name_values Values to substitute into \p container_name_format and \p filename_format.
199 * @param cpl_name Name of the CPL that the KDMs are for.
203 list<list<KDMWithMetadataPtr>> kdms,
204 dcp::NameFormat container_name_format,
205 dcp::NameFormat filename_format,
207 vector<string> extra_addresses
210 auto config = Config::instance ();
212 if (config->mail_server().empty()) {
213 throw MissingConfigurationError(_("No outgoing mail server configured in the Email tab of preferences"));
216 if (config->kdm_from().empty()) {
217 throw MissingConfigurationError(_("No from address configured in the KDM Email tab of preferences"));
220 for (auto const& kdms_for_cinema: kdms) {
222 auto first = kdms_for_cinema.front();
224 auto zip_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
225 dcp::filesystem::create_directories(zip_file);
226 zip_file /= container_name_format.get(first->name_values(), ".zip");
227 make_zip_file (kdms_for_cinema, zip_file, filename_format);
229 auto substitute_variables = [cpl_name, first](string target) {
230 boost::algorithm::replace_all(target, "$CPL_NAME", cpl_name);
231 boost::algorithm::replace_all(target, "$START_TIME", first->get('b').get_value_or(""));
232 boost::algorithm::replace_all(target, "$END_TIME", first->get('e').get_value_or(""));
233 boost::algorithm::replace_all(target, "$CINEMA_NAME", first->get('c').get_value_or(""));
234 boost::algorithm::replace_all(target, "$CINEMA_SHORT_NAME", first->get('c').get_value_or("").substr(0, 14));
238 auto subject = substitute_variables(config->kdm_subject());
239 auto body = substitute_variables(config->kdm_email());
242 for (auto kdm: kdms_for_cinema) {
243 auto screen_name = kdm->get('s');
245 screens += *screen_name + ", ";
248 boost::algorithm::replace_all (body, "$SCREENS", screens.substr (0, screens.length() - 2));
250 auto emails = first->emails();
251 std::copy(extra_addresses.begin(), extra_addresses.end(), std::back_inserter(emails));
252 if (emails.empty()) {
256 Emailer email (config->kdm_from(), { emails.front() }, subject, body);
258 /* Use CC for the second and subsequent email addresses, so we seem less spammy (#2310) */
259 for (auto cc = std::next(emails.begin()); cc != emails.end(); ++cc) {
263 for (auto cc: config->kdm_cc()) {
266 if (!config->kdm_bcc().empty()) {
267 email.add_bcc (config->kdm_bcc());
270 email.add_attachment (zip_file, container_name_format.get(first->name_values(), ".zip"), "application/zip");
272 auto log_details = [](Emailer& email) {
273 dcpomatic_log->log("Email content follows", LogEntry::TYPE_DEBUG_EMAIL);
274 dcpomatic_log->log(email.email(), LogEntry::TYPE_DEBUG_EMAIL);
275 dcpomatic_log->log("Email session follows", LogEntry::TYPE_DEBUG_EMAIL);
276 dcpomatic_log->log(email.notes(), LogEntry::TYPE_DEBUG_EMAIL);
280 email.send (config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
282 dcp::filesystem::remove(zip_file);
289 dcp::filesystem::remove(zip_file);