diff options
| -rw-r--r-- | src/json_verify_report.cc | 146 | ||||
| -rw-r--r-- | src/json_verify_report.h | 41 | ||||
| -rw-r--r-- | src/wscript | 4 | ||||
| -rw-r--r-- | tools/dcpverify.cc | 16 | ||||
| -rw-r--r-- | wscript | 3 |
5 files changed, 207 insertions, 3 deletions
diff --git a/src/json_verify_report.cc b/src/json_verify_report.cc new file mode 100644 index 00000000..01d30647 --- /dev/null +++ b/src/json_verify_report.cc @@ -0,0 +1,146 @@ +/* + Copyright (C) 2025 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 "cpl.h" +#include "dcp.h" +#include "reel.h" +#include "reel_picture_asset.h" +#include "reel_sound_asset.h" +#include "reel_text_asset.h" +#include "verify.h" +#include "json_verify_report.h" +#include <nlohmann/json.hpp> +#include <fmt/format.h> +#include <fstream> + + +using std::shared_ptr; +using std::string; +using std::vector; +using boost::optional; +using namespace dcp; + + +void +dcp::make_json_verify_report(vector<dcp::VerificationResult> const& results, boost::filesystem::path file) +{ + auto reel_asset_details = [](shared_ptr<dcp::ReelAsset> asset) { + nlohmann::json details; + details["id"] = asset->id(); + details["intrinsic_duration"] = asset->intrinsic_duration(); + details["entry_point"] = asset->entry_point().get_value_or(0); + details["duration"] = asset->duration().get_value_or(0); + if (asset->annotation_text()) { + details["annotation_text"] = *asset->annotation_text(); + } + return details; + }; + + auto get_notes = [](dcp::VerificationResult const& result, optional<string> cpl_id) { + nlohmann::json json = nlohmann::json::array(); + + for (auto note: result.notes) { + if (note.cpl_id() == cpl_id) { + switch (note.type()) { + case dcp::VerificationNote::Type::OK: + json.push_back({ {"type", "ok"}, {"message", dcp::note_to_string(note)} }); + break; + case dcp::VerificationNote::Type::WARNING: + json.push_back({ {"type", "warning"}, {"message", dcp::note_to_string(note)} }); + break; + case dcp::VerificationNote::Type::ERROR: + json.push_back({ {"type", "error"}, {"message", dcp::note_to_string(note)} }); + break; + case dcp::VerificationNote::Type::BV21_ERROR: + json.push_back({ {"type", "bv21_error"}, {"message", dcp::note_to_string(note)} }); + break; + } + } + } + + return json; + }; + + nlohmann::json all_json = nlohmann::json::array(); + + for (auto const& result: results) { + for (auto dcp: result.dcps) { + nlohmann::json dcp_json; + dcp_json["cpls"] = nlohmann::json::array(); + for (auto cpl: dcp->cpls()) { + nlohmann::json cpl_json; + cpl_json["reels"] = nlohmann::json::array(); + if (cpl->annotation_text()) { + cpl_json["annotation_text"] = *cpl->annotation_text(); + } + cpl_json["id"] = cpl->id(); + int reel_index = 1; + for (auto reel: cpl->reels()) { + nlohmann::json reel_json; + reel_json["index"] = reel_index++; + if (auto pic = reel->main_picture()) { + auto pic_json = reel_asset_details(pic); + pic_json["frame_rate"] = pic->frame_rate().numerator; + pic_json["screen_aspect_ratio"] = { + { "width", pic->screen_aspect_ratio().numerator }, + { "height", pic->screen_aspect_ratio().denominator } + }; + reel_json["main_picture"] = pic_json; + } + if (auto sound = reel->main_sound()) { + reel_json["main_sound"] = reel_asset_details(sound); + } + if (auto sub = reel->main_subtitle()) { + auto sub_json = reel_asset_details(sub); + if (sub->language()) { + sub_json["language"] = *sub->language(); + } + } + cpl_json["reels"].push_back(reel_json); + } + cpl_json["notes"] = get_notes(result, cpl->id()); + dcp_json["cpls"].push_back(cpl_json); + } + + if (std::count_if(result.notes.begin(), result.notes.end(), [](VerificationNote const& note) { return !note.cpl_id(); }) > 0) { + dcp_json["notes"] = get_notes(result, {}); + } + + all_json.push_back(dcp_json); + } + } + + std::ofstream out(file.string().c_str()); + out << all_json.dump(4) << "\n"; +} diff --git a/src/json_verify_report.h b/src/json_verify_report.h new file mode 100644 index 00000000..f36e170d --- /dev/null +++ b/src/json_verify_report.h @@ -0,0 +1,41 @@ +/* + 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/>. + +*/ + + +#ifndef DCP_JSON_VERIFY_REPORT_H +#define DCP_JSON_VERIFY_REPORT_H + + +#include "verify.h" +#include <boost/filesystem.hpp> +#include <vector> + + +namespace dcp { + + +extern void make_json_verify_report(std::vector<dcp::VerificationResult> const& results, boost::filesystem::path file); + + +} + + +#endif + diff --git a/src/wscript b/src/wscript index 6e83fe6a..064bd5a4 100644 --- a/src/wscript +++ b/src/wscript @@ -270,6 +270,10 @@ def build(bld): headers += "pdf_formatter.h " uselib += ' HARU' + if bld.env.LIBDCP_HAVE_JSON: + source += "json_verify_report.cc " + headers += "json_verify_report.h " + # Main library if bld.env.STATIC: obj = bld(features='cxx cxxstlib') diff --git a/tools/dcpverify.cc b/tools/dcpverify.cc index ed1b2a8c..6728bfcf 100644 --- a/tools/dcpverify.cc +++ b/tools/dcpverify.cc @@ -37,6 +37,9 @@ #include "filesystem.h" #include "formatted_verify_report.h" #include "html_formatter.h" +#ifdef LIBDCP_HAVE_JSON +#include "json_verify_report.h" +#endif #ifdef LIBDCP_HAVE_HARU #include "pdf_formatter.h" #endif @@ -76,11 +79,14 @@ help (string n) << " --asset-hash-check-maximum-size <size-in-MB> only check hashes for assets smaller than this size (in MB)\n" << " --no-picture-details-check don't check details of picture assets (J2K bitstream etc.)\n" << " -o <filename> write report to filename " + " (.txt, .htm, .html" #ifdef LIBDCP_HAVE_HARU - " (.txt, .htm, .html or .pdf)\n" -#else - " (.txt, .htm or .html)\n" + ", .pdf" +#endif +#ifdef LIBDCP_HAVE_JSON + ", .json" #endif + ")\n" << " -q, --quiet don't report progress\n"; } @@ -244,6 +250,10 @@ main (int argc, char* argv[]) dcp::PDFFormatter formatter(*report_filename); dcp::make_formatted_verify_report({ result }, formatter); #endif +#ifdef LIBDCP_HAVE_JSON + } else if (report_filename->extension() == ".json") { + dcp::make_json_verify_report({ result }, *report_filename); +#endif } else { dcp::TextFormatter formatter(*report_filename); dcp::make_formatted_verify_report({ result }, formatter); @@ -285,6 +285,9 @@ def configure(conf): if haru: conf.env.append_value('LIBDCP_HAVE_HARU', '1') + if conf.check_cxx(header_name="nlohmann/json.hpp", uselib_store='JSON', define_name='LIBDCP_HAVE_JSON', mandatory=False): + conf.env.append_value('LIBDCP_HAVE_JSON', '1') + if not conf.env.DISABLE_TESTS: conf.recurse('test') if conf.options.enable_gcov: |
