1b9bab80d9c294948f41799ad056830f7a4eca3a
[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.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
56
57 static void
58 help (string n)
59 {
60         cerr << "Syntax: " << n << " [OPTION] <DCP>\n"
61              << "  -V, --version                                show libdcp version\n"
62              << "  -h, --help                                   show this help\n"
63              << "  --ignore-missing-assets                      don't give errors about missing assets\n"
64              << "  --ignore-bv21-smpte                          don't give the SMPTE Bv2.1 error about a DCP not being SMPTE\n"
65              << "  --no-asset-hash-check                        don't check asset hashes\n"
66              << "  --asset-hash-check-maximum-size <size-in-MB> only check hashes for assets smaller than this size (in MB)\n"
67              << "  -q, --quiet                                  don't report progress\n";
68 }
69
70
71 int
72 main (int argc, char* argv[])
73 {
74         dcp::init ();
75
76         bool ignore_missing_assets = false;
77         bool ignore_bv21_smpte = false;
78         bool quiet = false;
79
80         dcp::VerificationOptions verification_options;
81
82         int option_index = 0;
83         while (true) {
84                 static struct option long_options[] = {
85                         { "version", no_argument, 0, 'V' },
86                         { "help", no_argument, 0, 'h' },
87                         { "ignore-missing-assets", no_argument, 0, 'A' },
88                         { "ignore-bv21-smpte", no_argument, 0, 'B' },
89                         { "no-asset-hash-check", no_argument, 0, 'C' },
90                         { "asset-hash-check-maximum-size", required_argument, 0, 'D' },
91                         { "quiet", no_argument, 0, 'q' },
92                         { 0, 0, 0, 0 }
93                 };
94
95                 int c = getopt_long (argc, argv, "VhABCD:q", long_options, &option_index);
96
97                 if (c == -1) {
98                         break;
99                 } else if (c == '?' || c == ':') {
100                         exit(EXIT_FAILURE);
101                 }
102
103                 switch (c) {
104                 case 'V':
105                         cout << "dcpverify version " << dcp::version << "\n";
106                         exit (EXIT_SUCCESS);
107                 case 'h':
108                         help (argv[0]);
109                         exit (EXIT_SUCCESS);
110                 case 'A':
111                         ignore_missing_assets = true;
112                         break;
113                 case 'B':
114                         ignore_bv21_smpte = true;
115                         break;
116                 case 'C':
117                         verification_options.check_asset_hashes = false;
118                         break;
119                 case 'D':
120                         verification_options.maximum_asset_size_for_hash_check = dcp::raw_convert<int>(optarg) * 1000000LL;
121                         break;
122                 case 'q':
123                         quiet = true;
124                         break;
125                 }
126         }
127
128         if (argc <= optind) {
129                 help (argv[0]);
130                 exit (EXIT_FAILURE);
131         }
132
133         if (!boost::filesystem::exists (argv[optind])) {
134                 cerr << argv[0] << ": DCP " << argv[optind] << " not found.\n";
135                 exit (EXIT_FAILURE);
136         }
137
138         auto stage = [quiet](string s, optional<boost::filesystem::path> path) {
139                 if (quiet) {
140                         return;
141                 }
142
143                 if (path) {
144                         cout << s << ": " << path->string() << "\n";
145                 } else {
146                         cout << s << "\n";
147                 }
148         };
149
150         auto progress = [quiet](float amount) {
151                 if (quiet) {
152                         return;
153                 }
154                 int const width = 60;
155                 int const index = std::rint(amount * width);
156                 cout << "[";
157                 for (int i = 0; i < width; ++i) {
158                         if (i < index) {
159                                 std::cout << "=";
160                         } else if (i == index) {
161                                 std::cout << ">";
162                         } else {
163                                 std::cout << " ";
164                         }
165                 }
166                 cout << "] " << std::rint(amount * 100) << "%\r";
167                 cout.flush();
168         };
169
170         vector<boost::filesystem::path> directories;
171         directories.push_back (argv[optind]);
172         auto notes = dcp::verify(directories, stage, progress, verification_options);
173         dcp::filter_notes (notes, ignore_missing_assets);
174
175         if (!quiet) {
176                 cout << "\n";
177         }
178
179         bool failed = false;
180         bool bv21_failed = false;
181         bool warned = false;
182         for (auto i: notes) {
183                 if (ignore_bv21_smpte && i.code() == dcp::VerificationNote::Code::INVALID_STANDARD) {
184                         continue;
185                 }
186                 switch (i.type()) {
187                 case dcp::VerificationNote::Type::ERROR:
188                         cout << "Error: " << note_to_string(i) << "\n";
189                         failed = true;
190                         break;
191                 case dcp::VerificationNote::Type::BV21_ERROR:
192                         cout << "Bv2.1 error: " << note_to_string(i) << "\n";
193                         bv21_failed = true;
194                         break;
195                 case dcp::VerificationNote::Type::WARNING:
196                         cout << "Warning: " << note_to_string(i) << "\n";
197                         warned = true;
198                         break;
199                 }
200         }
201
202         if (!failed && !quiet) {
203                 if (bv21_failed && warned) {
204                         cout << "\nDCP verified OK (but with Bv2.1 errors and warnings).\n";
205                 } else if (bv21_failed) {
206                         cout << "\nDCP verified OK (but with Bv2.1 errors).\n";
207                 } else if (warned) {
208                         cout << "\nDCP verified OK (but with warnings).\n";
209                 } else {
210                         cout << "DCP verified OK.\n";
211                 }
212         }
213
214         exit (failed ? EXIT_FAILURE : EXIT_SUCCESS);
215 }