mkvmerge: output warnings/errors as JSON in JSON identification mode

Implements #1537.
This commit is contained in:
Moritz Bunkus 2015-12-16 21:58:51 +01:00
parent de4734f962
commit a3a25225c6
9 changed files with 403 additions and 10 deletions

View File

@ -1,5 +1,10 @@
2015-12-16 Moritz Bunkus <moritz@bunkus.org>
* mkvmerge: enhancement: if JSON identification mode is active
then warnings and errors will be output as JSON as well. They're
output as arrays of strings as the keys "warnings" and "errors" of
the main JSON object. Implements #1537.
* all: reversion of a change: several ISO 639-2 codes of languages
that are very old and not spoken anymore have been re-added
(e.g. "English, Middle (1100-1500)") due to feedback from users

View File

@ -0,0 +1,345 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://mkvtoolnix.download/doc/mkvmerge-identification-output-schema-v2.json",
"title": "mkvmerge identification output",
"description": "The JSON output produced by mkvmerge's file identification mode",
"type": "object",
"properties": {
"attachments": {
"description": "an array describing the attachments found if any",
"type": "array",
"items": {
"type": "object",
"properties": {
"content_type": {
"type": "string",
"minLength": 1
},
"description": {
"type": "string"
},
"file_name": {
"type": "string"
},
"id": {
"type": "integer",
"minimum": 0
},
"size": {
"type": "integer",
"minimum": 0
},
"properties": {
"type": "object",
"properties": {
"uid": {
"type": "integer",
"minimum": 0
}
},
"additionalProperties": false
},
"type": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"file_name",
"id",
"properties",
"size"
]
}
},
"chapters": {
"type": "array",
"items": {
"type": "object",
"properties": {
"num_entries": {
"type": "integer"
}
},
"additionalProperties": false,
"required": [
"num_entries"
]
}
},
"container": {
"description": "information about the identified container",
"type": "object",
"properties": {
"properties": {
"description": "additional properties for the container varying by container format",
"type": "object",
"properties": {
"container_type": {
"description": "A unique number identifying the container type that's supposed to stay constant over all future releases of MKVToolNix",
"type": "integer",
"minLength": 1
},
"duration": {
"description": "The file's/segment's duration in nanoseconds",
"type": "integer",
"minimum": 0
},
"is_providing_timecodes": {
"description": "States whether or not the container has timestamps for the packets (e.g. Matroska, MP4) or not (e.g. SRT, MP3)",
"type": "boolean"
},
"segment_uid": {
"description": "A hexadecimal string of the segment's UID (only for Matroska files)",
"type": "string",
"minLength": 32,
"maxLength": 32
},
"next_segment_uid": {
"description": "A hexadecimal string of the next segment's UID (only for Matroska files)",
"type": "string",
"minLength": 32,
"maxLength": 32
},
"previous_segment_uid": {
"description": "A hexadecimal string of the previous segment's UID (only for Matroska files)",
"type": "string",
"minLength": 32,
"maxLength": 32
},
"other_file": {
"description": "The file name of an additional file processed as well",
"type": "string",
"minLength": 1
},
"playlist": {
"description": "States whether or not the identified file is a playlist (e.g. MPLS) referring to several other files",
"type": "boolean"
},
"playlist_chapters": {
"description": "The number of chapters in a playlist if it is a one",
"type": "integer",
"minimum": 0
},
"playlist_duration": {
"description": "The total duration in nanoseconds of all files referenced by the playlist if it is a one",
"type": "integer",
"minimum": 0
},
"playlist_size": {
"description": "The total size in bytes of all files referenced by the playlist if it is a one",
"type": "integer",
"minimum": 0
},
"title": {
"type": "string"
}
},
"additionalProperties": false
},
"recognized": {
"description": "States whether or not mkvmerge knows about the format",
"type": "boolean"
},
"supported": {
"description": "States whether or not mkvmerge can read the format",
"type": "boolean"
},
"type": {
"description": "A human-readable description/name for the container format",
"type": "string",
"minLength": 1
}
},
"additionalProperties": false,
"required": [
"recognized",
"supported"
]
},
"errors": {
"type": "array",
"items": {
"type": "string"
}
},
"file_name": {
"description": "the identified file's name",
"type": "string",
"minLength": 1
},
"global_tags": {
"type": "array",
"items": {
}
},
"identification_format_version": {
"description": "The output format's version",
"type": "integer",
"minimum": 1
},
"track_tags": {
"type": "array",
"items": {
"type": "object",
"properties": {
"num_entries": {
"type": "integer"
},
"track_id": {
"type": "integer"
}
},
"additionalProperties": false,
"required": [
"num_entries",
"track_id"
]
}
},
"tracks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"codec": {
"type": "string",
"minLength": 1
},
"id": {
"type": "integer",
"minLength": 0
},
"type": {
"type": "string"
},
"properties": {
"type": "object",
"properties": {
"aac_is_sbr": {
"type": "string",
"enum": [
"true",
"false",
"unknown"
]
},
"audio_bits_per_sample": {
"type": "integer",
"minimum": 0
},
"audio_channels": {
"type": "integer",
"minimum": 0
},
"audio_sampling_frequency": {
"type": "integer",
"minimum": 0
},
"codec_id": {
"type": "string"
},
"codec_private_data": {
"type": "string"
},
"codec_private_length": {
"type": "integer",
"minimum": 0
},
"content_encoding_algorithms": {
"type": "string",
"minLength": 1
},
"default_duration": {
"type": "integer",
"minimum": 0
},
"default_track": {
"type": "boolean"
},
"display_dimensions": {
"type": "string",
"pattern": "^[0-9]+x[0-9]+$"
},
"enabled_track": {
"type": "boolean"
},
"forced_track": {
"type": "boolean"
},
"language": {
"type": "string"
},
"number": {
"type": "integer",
"minimum": 0
},
"packetizer": {
"type": "string",
"minLength": 1
},
"pixel_dimensions": {
"type": "string",
"pattern": "^[0-9]+x[0-9]+$"
},
"stereo_mode": {
"type": "integer",
"minimum": 0
},
"stream_id": {
"type": "string",
"pattern": "^[0-9a-f]{2}$"
},
"sub_stream_id": {
"type": "string",
"pattern": "^[0-9a-f]{2}$"
},
"tag_artist": {
"type": "string"
},
"tag_bitsps": {
"type": "string"
},
"tag_bps": {
"type": "string"
},
"tag_fps": {
"type": "string"
},
"tag_title": {
"type": "string"
},
"text_subtitles": {
"type": "boolean"
},
"track_name": {
"type": "string"
},
"ts_pid": {
"type": "integer",
"minimum": 0
},
"uid": {
"type": "integer",
"minimum": 0
}
},
"additionalProperties": false
}
},
"additionalProperties": false,
"required": [
"codec",
"id",
"type"
]
}
},
"warnings": {
"type": "array",
"items": {
"type": "string"
}
}
}
}

View File

@ -33,6 +33,44 @@ charset_converter_cptr g_cc_stdio = charset_converter_cptr(new charset_converter
std::shared_ptr<mm_io_c> g_mm_stdio = std::shared_ptr<mm_io_c>(new mm_stdio_c);
static mxmsg_handler_t s_mxmsg_info_handler, s_mxmsg_warning_handler, s_mxmsg_error_handler;
static std::vector<std::string> s_warnings_emitted, s_errors_emitted;
static nlohmann::json
to_json_array(std::vector<std::string> const &messages) {
auto result = nlohmann::json::array();
for (auto const &message : messages)
result.push_back(message);
return result;
}
void
display_json_output(nlohmann::json json) {
json["warnings"] = to_json_array(s_warnings_emitted);
json["errors"] = to_json_array(s_errors_emitted);
mxinfo(boost::format("%1%\n") % json.dump(2));
}
static void
json_warning_error_handler(unsigned int level,
std::string const &message) {
if (MXMSG_WARNING == level)
s_warnings_emitted.push_back(message);
else {
s_errors_emitted.push_back(message);
display_json_output(nlohmann::json{});
mxexit(2);
}
}
void
redirect_warnings_and_errors_to_json() {
set_mxmsg_handler(MXMSG_WARNING, json_warning_error_handler);
set_mxmsg_handler(MXMSG_ERROR, json_warning_error_handler);
}
void
redirect_stdio(const mm_io_cptr &stdio) {

View File

@ -22,6 +22,7 @@
#include "common/locale.h"
#include "common/mm_io.h"
#include "nlohmann-json/src/json.hpp"
using namespace libebml;
@ -36,6 +37,9 @@ extern std::shared_ptr<mm_io_c> g_mm_stdio;
void redirect_stdio(const mm_io_cptr &new_stdio);
bool stdio_redirected();
void redirect_warnings_and_errors_to_json();
void display_json_output(nlohmann::json json);
void init_common_output(bool no_charset_detection);
void set_cc_stdio(const std::string &charset);

View File

@ -415,7 +415,7 @@ generic_reader_c::display_identification_results_as_json() {
};
auto json = nlohmann::json{
{ "identification_format_version", 1 },
{ "identification_format_version", 2 },
{ "file_name", m_ti.m_fname },
{ "tracks", nlohmann::json::array() },
{ "attachments", nlohmann::json::array() },
@ -465,7 +465,7 @@ generic_reader_c::display_identification_results_as_json() {
};
}
mxinfo(boost::format("%1%\n") % json.dump(2));
display_json_output(json);
}
std::string

View File

@ -33,7 +33,7 @@ static void
output_container_unsupported_json(std::string const &filename,
translatable_string_c const &info) {
auto json = nlohmann::json{
{ "identification_format_version", 1 },
{ "identification_format_version", 2 },
{ "file_name", filename },
{ "container", {
{ "recognized", true },
@ -42,7 +42,7 @@ output_container_unsupported_json(std::string const &filename,
} },
};
mxinfo(boost::format("%1%\n") % json.dump(2));
display_json_output(json);
mxexit(0);
}

View File

@ -369,7 +369,7 @@ list_file_types() {
static void
display_unsupported_file_type_json(filelist_t const &file) {
auto json = nlohmann::json{
{ "identification_format_version", 1 },
{ "identification_format_version", 2 },
{ "file_name", file.name },
{ "container", {
{ "recognized", false },
@ -377,7 +377,7 @@ display_unsupported_file_type_json(filelist_t const &file) {
} },
};
mxinfo(boost::format("%1%\n") % json.dump(2));
display_json_output(json);
mxexit(0);
}
@ -1843,10 +1843,11 @@ parse_arg_identification_format(std::vector<std::string>::const_iterator &sit,
else if (next_arg == "gui")
g_identification_output_format = identification_output_format_e::gui;
else if (next_arg == "json")
else if (next_arg == "json") {
g_identification_output_format = identification_output_format_e::json;
redirect_warnings_and_errors_to_json();
else
} else
mxerror(boost::format(Y("Invalid identification format in '%1% %2%'.\n")) % *sit % *(sit + 1));
++sit;

View File

@ -357,6 +357,6 @@ T_508splitting_by_parts_with_segment_linking:existence0-true-true-true-existence
T_509rerender_track_headers_chapters_attachments:1ad646e49e231108ed8b5d1c6dce8b1c:passed:20151115-230226:0.287840782
T_510propedit_add_attachments_without_meta_seek_present:770103c238a0f502c9ec55f0599d8544:passed:20151121-101043:0.070892905
T_511propedit_ensure_seek_head_exists_at_front:20f53afd94e39f5bbf3f1091eefbe31d:passed:20151129-194025:0.152563199
T_512json_identification:231941d7da79ca0eab3f8f5cd2a66e3c-ok-793233ac68c8c0af280898ec33aa6e05-ok-06dde7e7bcbec8fc9cd8f101662906dd-ok-8797f2adcd279428afa4834d93f0be34-ok-90de565f88767c95673e7b2d5ecf85d9-ok-d9c71aaad86e27979d1b5bf71cb1143f-ok-acddfd1324aa62e187f8d4023f24ec2a-ok-6c20b604bd1652766e442bf52f284a18-ok-cdf24911aad494277fd2a032affdf498-ok-dd2d4b55d2fe3f56c697371325b7d8e9-ok-7dc0464e838aa187128964387b9adcba-ok-6d365dd230156e30bfe3d60a3c6c24dc-ok-7fada0edcbd9818093ba57b78ae5706a-ok-ca5e66fad9ed5af2d672c4432cbded48-ok-b859b2500847aca0ac91918fdc8eae70-ok:passed:20151207-223859:6.280036064
T_512json_identification:dc56910afee27e5f42414fde294a262c-ok-4d5b44ce8fea381a4de100ed77ee77bc-ok-4ce52c415319a3c9ace3394b251bfa04-ok-b31447af73fb7453a6801f6817a5f904-ok-d9eb73880861a423ee0d33364685d0a5-ok-4147bc09272d6a650f3ebac47008129a-ok-966a3a948e86b73f25d0cbd20e659dda-ok-5105d97f6ca79db07caab7bb66f12ef4-ok-76d5c58b6fc06efcfea66e447ad8bf44-ok-bb52b7e2c30f3741c92e5152bef8ea8a-ok-8dd46981cf9e1787ea682fe03aba4d92-ok-ebaf88003f5d09295d18e255edf689be-ok-c56940a2497513380531e693ec06b76c-ok-ee1ae1f2602ebaef4e83772ab5e39804-ok-617e011e200630baff85bc674b2a1292-ok:passed:20151207-223859:6.280036064
T_513vp9_10bit_key_frame_detection:9eab6e85ec792dcf670873d70a87f6ea:passed:20151208-224613:0.267556245
T_514remove_track_statistics_tags_during_remux:022578a22c45c06ab23dc453df71f7c0-afe190e36be530592fe3b83fb28d3e69-a7f246fe02132a1fb9cd3d7d0f85f180:passed:20151215-134129:1.426290351

View File

@ -302,7 +302,7 @@ class SimpleTest
json_store = JsonSchema::DocumentStore.new
parser = JsonSchema::Parser.new
expander = JsonSchema::ReferenceExpander.new
schema = parser.parse JSON.load(File.read("../doc/json-schema/mkvmerge-identification-output-schema-v1.json"))
schema = parser.parse JSON.load(File.read("../doc/json-schema/mkvmerge-identification-output-schema-v2.json"))
expander.expand(schema, store: json_store)
json_store.add_schema schema