diff options
| author | Carl Hetherington <cth@carlh.net> | 2024-04-11 10:50:51 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2024-04-15 10:59:34 +0200 |
| commit | 3ab56573dbfb395fea493096182dc14c09fb961f (patch) | |
| tree | 5c3480e6a96156b62a88335ffe5de9e8f6993739 /src | |
| parent | 0873610bb24b997545bd6d3c99a8f653ce74f805 (diff) | |
Add basic verification report.v1.9.5
Diffstat (limited to 'src')
| -rw-r--r-- | src/verify_report.cc | 144 | ||||
| -rw-r--r-- | src/verify_report.h | 237 | ||||
| -rw-r--r-- | src/wscript | 2 |
3 files changed, 383 insertions, 0 deletions
diff --git a/src/verify_report.cc b/src/verify_report.cc new file mode 100644 index 00000000..2201b8fa --- /dev/null +++ b/src/verify_report.cc @@ -0,0 +1,144 @@ +/* + Copyright (C) 2018-2021 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include "compose.hpp" +#include "cpl.h" +#include "dcp.h" +#include "file.h" +#include "reel.h" +#include "reel_picture_asset.h" +#include "reel_sound_asset.h" +#include "reel_subtitle_asset.h" +#include "verify.h" +#include "verify_report.h" + + +using std::shared_ptr; +using std::string; +using std::vector; +using boost::optional; +using namespace dcp; + + +void write_line(File& file, string format) +{ + file.puts(string(format + "\n").c_str()); +} + + +template <typename... Args> +void write_line(File& file, string format, Args... args) +{ + file.puts(String::compose(format + "\n", std::forward<Args>(args)...).c_str()); +} + + +void +dcp::verify_report(dcp::VerificationResult const& result, Formatter& formatter) +{ + auto document = formatter.document(); + auto body = formatter.body(); + + formatter.heading("DCP verification report"); + + if (result.dcps.size() > 1) { + formatter.subheading("DCPs"); + } else { + formatter.subheading("DCP"); + } + + auto reel_asset_details = [&formatter](shared_ptr<dcp::ReelAsset> asset) { + formatter.list_item(String::compose("UUID: %1", asset->id())); + formatter.list_item(String::compose("Intrinsic duration: %1", asset->intrinsic_duration())); + formatter.list_item(String::compose("Entry point: %1", asset->entry_point().get_value_or(0))); + formatter.list_item(String::compose("Duration: %1", asset->duration().get_value_or(0))); + if (asset->annotation_text()) { + formatter.list_item(String::compose("Annotation text: %1", *asset->annotation_text())); + } + }; + + auto write_notes = [&formatter](dcp::VerificationResult const& result, optional<string> cpl_id) { + for (auto note: result.notes) { + if (note.cpl_id() == cpl_id) { + auto const note_as_string = dcp::note_to_string(note, formatter.process_string(), formatter.process_filename()); + if (note.type() == dcp::VerificationNote::Type::OK) { + formatter.list_item(note_as_string, string("ok")); + } else if (note.type() == dcp::VerificationNote::Type::WARNING) { + formatter.list_item(note_as_string, string("warning")); + } else if (note.type() == dcp::VerificationNote::Type::ERROR) { + formatter.list_item(note_as_string, string("error")); + } + } + } + }; + + for (auto dcp: result.dcps) { + auto ul = formatter.unordered_list(); + for (auto cpl: dcp->cpls()) { + formatter.list_item(String::compose("CPL ID: %1", cpl->id())); + int reel_index = 1; + for (auto reel: cpl->reels()) { + formatter.list_item(String::compose("Reel: %1", reel_index++)); + auto ul2 = formatter.unordered_list(); + if (auto pic = reel->main_picture()) { + formatter.list_item("Main picture"); + auto ul3 = formatter.unordered_list(); + reel_asset_details(pic); + formatter.list_item(String::compose("Frame rate: %1", pic->frame_rate().numerator)); + formatter.list_item(String::compose("Screen aspect ratio: %1x%2", pic->screen_aspect_ratio().numerator, pic->screen_aspect_ratio().denominator)); + } + if (auto sound = reel->main_sound()) { + formatter.list_item("Main sound"); + auto ul3 = formatter.unordered_list(); + reel_asset_details(sound); + } + if (auto sub = reel->main_subtitle()) { + formatter.list_item("Main subtitle"); + auto ul3 = formatter.unordered_list(); + reel_asset_details(sub); + if (sub->language()) { + formatter.list_item(String::compose("Language: %1", *sub->language())); + } + } + } + write_notes(result, cpl->id()); + } + } + + if (std::count_if(result.notes.begin(), result.notes.end(), [](VerificationNote const& note) { return !note.cpl_id(); }) > 0) { + formatter.subheading("Report"); + write_notes(result, {}); + } +} + diff --git a/src/verify_report.h b/src/verify_report.h new file mode 100644 index 00000000..b8ca5516 --- /dev/null +++ b/src/verify_report.h @@ -0,0 +1,237 @@ +/* + Copyright (C) 2022 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 "compose.hpp" +#include "file.h" +#include "verify.h" +#include <boost/filesystem.hpp> +#include <vector> + + +namespace dcp { + + +class Formatter +{ +public: + Formatter(boost::filesystem::path file) + : _file(file, "w") + {} + + class Wrap + { + public: + Wrap() = default; + + Wrap(Formatter* formatter, std::string const& close) + : _formatter(formatter) + , _close(close) + {} + + Wrap(Formatter* formatter, std::string const& close, std::function<void ()> closer) + : _formatter(formatter) + , _close(close) + , _closer(closer) + {} + + Wrap(Wrap&& other) + { + std::swap(_formatter, other._formatter); + std::swap(_close, other._close); + std::swap(_closer, other._closer); + } + + ~Wrap() + { + if (_formatter) { + _formatter->file().puts(_close.c_str()); + } + if (_closer) { + _closer(); + } + } + + private: + Formatter* _formatter = nullptr; + std::string _close; + std::function<void ()> _closer = nullptr; + }; + + virtual Wrap document() { return {}; } + + virtual void heading(std::string const& text) = 0; + virtual void subheading(std::string const& text) = 0; + virtual Wrap body() { return {}; } + + virtual Wrap unordered_list() = 0; + virtual void list_item(std::string const& text, boost::optional<std::string> type = {}) = 0; + + virtual std::function<std::string (std::string)> process_string() = 0; + virtual std::function<std::string (std::string)> process_filename() = 0; + + dcp::File& file() { + return _file; + } + +protected: + dcp::File _file; +}; + + +class TextFormatter : public Formatter +{ +public: + TextFormatter(boost::filesystem::path file) + : Formatter(file) + {} + + void heading(std::string const& text) override { + print(text); + } + + void subheading(std::string const& text) override { + print(""); + print(text); + } + + Wrap unordered_list() override { + _indent++; + return Wrap(this, "", [this]() { _indent--; }); + } + + void list_item(std::string const& text, boost::optional<std::string> type = {}) override { + LIBDCP_UNUSED(type); + for (int i = 0; i < _indent * 2; ++i) { + _file.puts(" "); + } + _file.puts("* "); + print(text); + } + + std::function<std::string (std::string)> process_string() override { + return [](std::string s) { + return s; + }; + } + + std::function<std::string (std::string)> process_filename() override { + return [](std::string s) { + return s; + }; + } + +private: + void print(std::string const& text) { + _file.puts(text.c_str()); + _file.puts("\n"); + } + + int _indent = 0; +}; + + +class HTMLFormatter : public Formatter +{ +public: + HTMLFormatter(boost::filesystem::path file) + : Formatter(file) + {} + + void heading(std::string const& text) override { + tagged("h1", text); + } + + void subheading(std::string const& text) override { + tagged("h2", text); + } + + Wrap document() override { + auto html = wrapped("html"); + auto head = wrapped("head"); + auto style = wrapped("style"); + _file.puts("li {\n" + " margin: 2px;\n" + " padding: 2px 2px 2px 1em;\n" + "}\n" + ); + _file.puts("li.ok {\n" + " background-color: #00ff00;\n" + "}\n" + "li.warning {\n" + " background-color: #ffa500;\n" + "}\n" + "li.error {\n" + " background-color: #ff0000;\n" + "}\n" + "ul {\n" + " list-style: none;\n" + "}\n" + ); + return html; + } + + Wrap body() override { + return wrapped("body"); + } + + Wrap unordered_list() override { + return wrapped("ul"); + } + + void list_item(std::string const& text, boost::optional<std::string> type = {}) override { + if (type) { + _file.puts(dcp::String::compose("<li class=\"%1\">%2", *type, text).c_str()); + } else { + _file.puts(dcp::String::compose("<li>%1", text).c_str()); + } + } + + std::function<std::string (std::string)> process_string() override { + return [](std::string s) { + boost::replace_all(s, "<", "<"); + boost::replace_all(s, ">", ">"); + return s; + }; + } + + std::function<std::string (std::string)> process_filename() override { + return [](std::string s) { + return String::compose("<code>%1</code>", s); + }; + } + +private: + void tagged(std::string tag, std::string content) { + _file.puts(String::compose("<%1>%2</%3>\n", tag, content, tag).c_str()); + }; + + Wrap wrapped(std::string const& tag) { + _file.puts(String::compose("<%1>", tag).c_str()); + return Wrap(this, String::compose("</%1>", tag)); + }; +}; + + +extern void verify_report(dcp::VerificationResult const& result, Formatter& formatter); + + +} + diff --git a/src/wscript b/src/wscript index 29eb37ab..adaa57bb 100644 --- a/src/wscript +++ b/src/wscript @@ -125,6 +125,7 @@ def build(bld): v_align.cc verify.cc verify_j2k.cc + verify_report.cc version.cc """ @@ -232,6 +233,7 @@ def build(bld): v_align.h verify.h verify_j2k.h + verify_report.h version.h warnings.h """ |
