summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2025-08-22 23:50:28 +0200
committerCarl Hetherington <cth@carlh.net>2025-08-25 11:22:27 +0200
commit6ea9b75c5fc4736f19a424796a3d2c7550fb16dc (patch)
treefc826b31fc5bc5edab88ff1b4c5759e95afd4cef
parentb02325ff69686381f40fee39df35666e58df367e (diff)
Add cover sheet magic strings for marker positions (#3076).
-rw-r--r--doc/manual/dcpomatic.xml79
-rw-r--r--src/lib/cover_sheet.cc46
-rw-r--r--test/cover_sheet_test.cc68
m---------test/data0
-rw-r--r--test/wscript1
5 files changed, 194 insertions, 0 deletions
diff --git a/doc/manual/dcpomatic.xml b/doc/manual/dcpomatic.xml
index f74299ac9..975d8bb7c 100644
--- a/doc/manual/dcpomatic.xml
+++ b/doc/manual/dcpomatic.xml
@@ -3325,6 +3325,85 @@ DCP that has been made:
</table>
<para>
+You can also insert the timecode of any of the DCP's markers using <code>$</code> with the marker's name:
+</para>
+
+<table>
+<tgroup cols='2' align='left' colsep='1' rowsep='1'>
+<tbody>
+<row>
+<entry><code>$FFOC</code></entry><entry>First frame of content</entry>
+</row>
+<row>
+<entry><code>$LFOC</code></entry><entry>Last frame of content</entry>
+</row>
+<row>
+<entry><code>$FFTC</code></entry><entry>First frame of title credits</entry>
+</row>
+<row>
+<entry><code>$LFTC</code></entry><entry>Last frame of title credits</entry>
+</row>
+<row>
+<entry><code>$FFOI</code></entry><entry>First frame of intermission</entry>
+</row>
+<row>
+<entry><code>$LFOI</code></entry><entry>Last frame of intermission</entry>
+</row>
+<row>
+<entry><code>$FFEC</code></entry><entry>First frame of end credits</entry>
+</row>
+<row>
+<entry><code>$LFEC</code></entry><entry>Last frame of end credits</entry>
+</row>
+<row>
+<entry><code>$FFMC</code></entry><entry>First frame of moving credits</entry>
+</row>
+<row>
+<entry><code>$LFMC</code></entry><entry>Last frame of moving credits</entry>
+</row>
+<row>
+<entry><code>$FFOB</code></entry><entry>First frame of ratings band</entry>
+</row>
+<row>
+<entry><code>$LFOB</code></entry><entry>Last frame of ratings band</entry>
+</row>
+</tbody>
+</tgroup>
+</table>
+
+<para>
+These magic strings will be replaced with &lsquo;Unknown&rsquo; if the marker is not defined.
+Alternatively, adding <code>_LINE</code> to a marker magic string will make the
+timecode appear if the marker is set, otherwise the whole line containing the magic string
+will be removed.
+</para>
+
+<para>
+For example, a cover sheet defined as
+</para>
+
+<para>
+<programlisting>
+Hello: $FFMC
+Goodbye: $FFMC_LINE
+</programlisting>
+</para>
+
+<para>
+will be written as
+</para>
+
+<para>
+<programlisting>
+Hello: Unknown
+</programlisting>
+</para>
+
+<para>
+if the <code>FFMC</code> marker is undefined.
+</para>
+
+<para>
Clicking <guilabel>Reset to default text</guilabel> will replace the current cover sheet with DCP-o-matic's default.
</para>
diff --git a/src/lib/cover_sheet.cc b/src/lib/cover_sheet.cc
index 52296fac7..b0450dbd0 100644
--- a/src/lib/cover_sheet.cc
+++ b/src/lib/cover_sheet.cc
@@ -33,6 +33,7 @@
using std::shared_ptr;
using std::string;
+using std::vector;
void
@@ -105,6 +106,51 @@ dcpomatic::write_cover_sheet(shared_ptr<const Film> film, boost::filesystem::pat
boost::algorithm::replace_all(text, "$LENGTH", length);
+ auto const markers = film->markers();
+
+ auto marker = [&markers, &text, film](dcp::Marker marker) {
+ auto iter = markers.find(marker);
+ auto const tag = "$" + marker_to_string(marker);
+ auto const tag_line = tag + "_LINE";
+
+ if (iter != markers.end()) {
+ auto const timecode = time_to_hmsf(iter->second, film->video_frame_rate());
+ boost::algorithm::replace_all(text, tag_line, timecode);
+ boost::algorithm::replace_all(text, tag, timecode);
+ } else {
+ vector<string> before_lines;
+ vector<string> after_lines;
+ boost::algorithm::split(before_lines, text, boost::is_any_of("\n"));
+ if (!before_lines.empty()) {
+ before_lines.pop_back();
+ }
+ for (auto& line: before_lines) {
+ if (line.find(tag_line) == std::string::npos) {
+ after_lines.push_back(line);
+ }
+ }
+ text.clear();
+ for (auto const& line: after_lines) {
+ text += line + "\n";
+ }
+
+ boost::algorithm::replace_all(text, tag, _("Unknown"));
+ }
+ };
+
+ marker(dcp::Marker::FFOC);
+ marker(dcp::Marker::LFOC);
+ marker(dcp::Marker::FFTC);
+ marker(dcp::Marker::LFTC);
+ marker(dcp::Marker::FFOI);
+ marker(dcp::Marker::LFOI);
+ marker(dcp::Marker::FFEC);
+ marker(dcp::Marker::LFEC);
+ marker(dcp::Marker::FFMC);
+ marker(dcp::Marker::LFMC);
+ marker(dcp::Marker::FFOB);
+ marker(dcp::Marker::LFOB);
+
file.checked_write(text.c_str(), text.length());
}
diff --git a/test/cover_sheet_test.cc b/test/cover_sheet_test.cc
new file mode 100644
index 000000000..484b99ec8
--- /dev/null
+++ b/test/cover_sheet_test.cc
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 2025 Carl Hetherington <cth@carlh.net>
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "lib/config.h"
+#include "lib/cover_sheet.h"
+#include "lib/dcpomatic_time.h"
+#include "lib/film.h"
+#include "test.h"
+#include <boost/test/unit_test.hpp>
+
+
+BOOST_AUTO_TEST_CASE(cover_sheet_test)
+{
+ ConfigRestorer cr;
+
+ auto film = new_test_film("cover_sheet_test");
+ film->set_video_frame_rate(24);
+
+ film->set_marker(dcp::Marker::FFOC, dcpomatic::DCPTime::from_frames(24 * 6 + 9, 24));
+ film->set_marker(dcp::Marker::LFOC, dcpomatic::DCPTime::from_frames(24 * 42 + 15, 24));
+ film->set_marker(dcp::Marker::FFTC, dcpomatic::DCPTime::from_frames(24 * 95 + 4, 24));
+ film->set_marker(dcp::Marker::LFTC, dcpomatic::DCPTime::from_frames(24 * 106 + 1, 24));
+ film->set_marker(dcp::Marker::FFOI, dcpomatic::DCPTime::from_frames(24 * 112 + 0, 24));
+ film->set_marker(dcp::Marker::LFOI, dcpomatic::DCPTime::from_frames(24 * 142 + 6, 24));
+ film->set_marker(dcp::Marker::FFEC, dcpomatic::DCPTime::from_frames(24 * 216 + 23, 24));
+ film->set_marker(dcp::Marker::LFEC, dcpomatic::DCPTime::from_frames(24 * 242 + 21, 24));
+ film->set_marker(dcp::Marker::FFMC, dcpomatic::DCPTime::from_frames(24 * 250 + 23, 24));
+ film->set_marker(dcp::Marker::LFMC, dcpomatic::DCPTime::from_frames(24 * 251 + 21, 24));
+
+ Config::instance()->set_cover_sheet(
+ "First frame of content: $FFOC\n"
+ "Last frame of content: $LFOC\n"
+ "First frame of title credits: $FFTC\n"
+ "Last frame of title credits: $LFTC\n"
+ "First frame of intermission: $FFOI\n"
+ "Last frame of intermission: $LFOI\n"
+ "First frame of end credits: $FFEC\n"
+ "Last frame of end credits: $LFEC\n"
+ "First frame of moving credits: $FFMC\n"
+ "Last frame of moving credits: $LFMC\n"
+ "First frame of ratings band: $FFOB\n"
+ "Last frame of ratings band: $LFOB\n"
+ "First frame of ratings band (to remove): $FFOB_LINE\n"
+ "Last frame of ratings band (to remove): $LFOB_LINE\n"
+ );
+
+ dcpomatic::write_cover_sheet(film, "test/data/dcp_digest_test_dcp", "build/test/cover_sheet.txt");
+ check_text_file("test/data/cover_sheet.txt", "build/test/cover_sheet.txt");
+}
+
diff --git a/test/data b/test/data
-Subproject d1292b1ce9a2ee42320e7d3b94e94ea1e6101db
+Subproject 0e08d315725cb18acd04880eb6c7f463f003873
diff --git a/test/wscript b/test/wscript
index d9529d0b8..4bd2c8891 100644
--- a/test/wscript
+++ b/test/wscript
@@ -72,6 +72,7 @@ def build(bld):
config_test.cc
content_test.cc
copy_dcp_details_to_film_test.cc
+ cover_sheet_test.cc
cpl_hash_test.cc
cpl_metadata_test.cc
create_cli_test.cc