770dd78fa75c93bf9f37df5e2329f15dbf17ca56
[libdcp.git] / tools / dcpverify.cc
1 /*
2     Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp 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.
10
11     libdcp 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.
15
16     You should have received a copy of the GNU General Public License
17     along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34
35 #include "common.h"
36 #include "compose.hpp"
37 #include "raw_convert.h"
38 #include "verify.h"
39 #include "version.h"
40 #include <boost/bind/bind.hpp>
41 #include <boost/filesystem.hpp>
42 #include <boost/optional.hpp>
43 #include <getopt.h>
44 #include <iostream>
45 #include <cstdlib>
46
47
48 using std::cerr;
49 using std::cout;
50 using std::list;
51 using std::string;
52 using std::vector;
53 using boost::bind;
54 using boost::optional;
55 #if BOOST_VERSION >= 106100
56 using namespace boost::placeholders;
57 #endif
58
59
60 static void
61 help (string n)
62 {
63         cerr << "Syntax: " << n << " [OPTION] <DCP>\n"
64              << "  -V, --version                                show libdcp version\n"
65              << "  -h, --help                                   show this help\n"
66              << "  --ignore-missing-assets                      don't give errors about missing assets\n"
67              << "  --ignore-bv21-smpte                          don't give the SMPTE Bv2.1 error about a DCP not being SMPTE\n"
68              << "  --no-asset-hash-check                        don't check asset hashes\n"
69              << "  --asset-hash-check-maximum-size <size-in-MB> only check hashes for assets smaller than this size (in MB)\n"
70              << "  -q, --quiet                                  don't report progress\n";
71 }
72
73
74 int
75 main (int argc, char* argv[])
76 {
77         dcp::init ();
78
79         bool ignore_missing_assets = false;
80         bool ignore_bv21_smpte = false;
81         bool quiet = false;
82
83         dcp::VerificationOptions verification_options;
84
85         int option_index = 0;
86         while (true) {
87                 static struct option long_options[] = {
88                         { "version", no_argument, 0, 'V' },
89                         { "help", no_argument, 0, 'h' },
90                         { "ignore-missing-assets", no_argument, 0, 'A' },
91                         { "ignore-bv21-smpte", no_argument, 0, 'B' },
92                         { "no-asset-hash-check", no_argument, 0, 'C' },
93                         { "asset-hash-check-maximum-size", required_argument, 0, 'D' },
94                         { "quiet", no_argument, 0, 'q' },
95                         { 0, 0, 0, 0 }
96                 };
97
98                 int c = getopt_long (argc, argv, "VhABCD:q", long_options, &option_index);
99
100                 if (c == -1) {
101                         break;
102                 } else if (c == '?' || c == ':') {
103                         exit(EXIT_FAILURE);
104                 }
105
106                 switch (c) {
107                 case 'V':
108                         cout << "dcpverify version " << dcp::version << "\n";
109                         exit (EXIT_SUCCESS);
110                 case 'h':
111                         help (argv[0]);
112                         exit (EXIT_SUCCESS);
113                 case 'A':
114                         ignore_missing_assets = true;
115                         break;
116                 case 'B':
117                         ignore_bv21_smpte = true;
118                         break;
119                 case 'C':
120                         verification_options.check_asset_hashes = false;
121                         break;
122                 case 'D':
123                         verification_options.maximum_asset_size_for_hash_check = dcp::raw_convert<int>(optarg) * 1000000LL;
124                         break;
125                 case 'q':
126                         quiet = true;
127                         break;
128                 }
129         }
130
131         if (argc <= optind) {
132                 help (argv[0]);
133                 exit (EXIT_FAILURE);
134         }
135
136         if (!boost::filesystem::exists (argv[optind])) {
137                 cerr << argv[0] << ": DCP " << argv[optind] << " not found.\n";
138                 exit (EXIT_FAILURE);
139         }
140
141         auto stage = [quiet](string s, optional<boost::filesystem::path> path) {
142                 if (quiet) {
143                         return;
144                 }
145
146                 if (path) {
147                         cout << s << ": " << path->string() << "\n";
148                 } else {
149                         cout << s << "\n";
150                 }
151         };
152
153         auto progress = [quiet](float amount) {
154                 if (quiet) {
155                         return;
156                 }
157                 int const width = 60;
158                 int const index = std::rint(amount * width);
159                 cout << "[";
160                 for (int i = 0; i < width; ++i) {
161                         if (i < index) {
162                                 std::cout << "=";
163                         } else if (i == index) {
164                                 std::cout << ">";
165                         } else {
166                                 std::cout << " ";
167                         }
168                 }
169                 cout << "] " << std::rint(amount * 100) << "%\r";
170                 cout.flush();
171         };
172
173         vector<boost::filesystem::path> directories;
174         directories.push_back (argv[optind]);
175         auto notes = dcp::verify(directories, stage, progress, verification_options);
176         dcp::filter_notes (notes, ignore_missing_assets);
177
178         if (!quiet) {
179                 cout << "\n";
180         }
181
182         bool failed = false;
183         bool bv21_failed = false;
184         bool warned = false;
185         for (auto i: notes) {
186                 if (ignore_bv21_smpte && i.code() == dcp::VerificationNote::Code::INVALID_STANDARD) {
187                         continue;
188                 }
189                 switch (i.type()) {
190                 case dcp::VerificationNote::Type::ERROR:
191                         cout << "Error: " << note_to_string(i) << "\n";
192                         failed = true;
193                         break;
194                 case dcp::VerificationNote::Type::BV21_ERROR:
195                         cout << "Bv2.1 error: " << note_to_string(i) << "\n";
196                         bv21_failed = true;
197                         break;
198                 case dcp::VerificationNote::Type::WARNING:
199                         cout << "Warning: " << note_to_string(i) << "\n";
200                         warned = true;
201                         break;
202                 }
203         }
204
205         if (!failed && !quiet) {
206                 if (bv21_failed && warned) {
207                         cout << "\nDCP verified OK (but with Bv2.1 errors and warnings).\n";
208                 } else if (bv21_failed) {
209                         cout << "\nDCP verified OK (but with Bv2.1 errors).\n";
210                 } else if (warned) {
211                         cout << "\nDCP verified OK (but with warnings).\n";
212                 } else {
213                         cout << "DCP verified OK.\n";
214                 }
215         }
216
217         exit (failed ? EXIT_FAILURE : EXIT_SUCCESS);
218 }