mirror of
https://github.com/fmang/opustags.git
synced 2025-01-15 12:43:17 +01:00
Add option --output-cover
This commit is contained in:
parent
66fb3574a1
commit
ec68f5c0e9
10
opustags.1
10
opustags.1
@ -106,6 +106,16 @@ Edit tags interactively by spawning the program specified by the EDITOR
|
||||
environment variable. The allowed format is the same as \fB--set-all\fP.
|
||||
If TERM and VISUAL are set, VISUAL takes precedence over EDITOR.
|
||||
.TP
|
||||
.B \-\-output-cover \fIFILE\fP
|
||||
Save the cover art of the input file at the specified location.
|
||||
If the input file does not contain any cover art, this option has no effect.
|
||||
To allow overwriting the target location, specify \fB--overwrite\fP.
|
||||
In the case of multiple pictures embedded in the Opus tags, only the first one is saved.
|
||||
Note that the since the image format is not fixed, you should consider an extension-less file name
|
||||
and rely on the magic number to deduce the type. opustags does not add or check the target file’s
|
||||
extension.
|
||||
You can specify \fB-\fP for standard output, in which case the regular output will be suppressed.
|
||||
.TP
|
||||
.B \-\-raw
|
||||
OpusTags metadata should always be encoded in UTF-8, as per RFC 7845. However, some files may be
|
||||
corrupted or possibly even contain intentional binary data. In that case, --raw lets you edit that
|
||||
|
47
src/cli.cc
47
src/cli.cc
@ -36,6 +36,7 @@ Options:
|
||||
-s, --set FIELD=VALUE replace a comment
|
||||
-S, --set-all import comments from standard input
|
||||
-e, --edit edit tags interactively in VISUAL/EDITOR
|
||||
--output-cover FILE extract and save the cover art, if any
|
||||
--raw disable encoding conversion
|
||||
|
||||
See the man page for extensive documentation.
|
||||
@ -52,6 +53,7 @@ static struct option getopt_options[] = {
|
||||
{"delete-all", no_argument, 0, 'D'},
|
||||
{"set-all", no_argument, 0, 'S'},
|
||||
{"edit", no_argument, 0, 'e'},
|
||||
{"output-cover", required_argument, 0, 'c'},
|
||||
{"raw", no_argument, 0, 'r'},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
@ -107,6 +109,11 @@ ot::options ot::parse_options(int argc, char** argv, FILE* comments_input)
|
||||
case 'e':
|
||||
opt.edit_interactively = true;
|
||||
break;
|
||||
case 'c':
|
||||
if (opt.cover_out)
|
||||
throw status {st::bad_arguments, "Cannot specify --output-cover more than once."};
|
||||
opt.cover_out = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
opt.raw = true;
|
||||
break;
|
||||
@ -160,6 +167,12 @@ ot::options ot::parse_options(int argc, char** argv, FILE* comments_input)
|
||||
if (opt.edit_interactively && (opt.delete_all || !opt.to_add.empty() || !opt.to_delete.empty()))
|
||||
throw status {st::bad_arguments, "Cannot mix --edit with -adDsS."};
|
||||
|
||||
if (opt.cover_out == "-" && opt.path_out == "-")
|
||||
throw status {st::bad_arguments, "Cannot specify standard output for both --output and --output-cover."};
|
||||
|
||||
if (opt.cover_out && opt.paths_in.size() > 1)
|
||||
throw status {st::bad_arguments, "Cannot use --output-cover with multiple input files."};
|
||||
|
||||
if (set_all) {
|
||||
// Read comments from stdin and prepend them to opt.to_add.
|
||||
std::list<std::string> comments = read_comments(comments_input, opt.raw);
|
||||
@ -385,6 +398,35 @@ static void edit_tags_interactively(ot::opus_tags& tags, const std::optional<std
|
||||
remove(tags_path.c_str());
|
||||
}
|
||||
|
||||
static void output_cover(const ot::opus_tags& tags, const ot::options &opt)
|
||||
{
|
||||
std::optional<ot::picture> cover = extract_cover(tags);
|
||||
if (!cover) {
|
||||
fputs("warning: no cover found.\n", stderr);
|
||||
return;
|
||||
}
|
||||
|
||||
ot::file output;
|
||||
if (opt.cover_out == "-") {
|
||||
output = stdout;
|
||||
} else {
|
||||
struct stat output_info;
|
||||
if (stat(opt.cover_out->c_str(), &output_info) == 0) {
|
||||
if (S_ISREG(output_info.st_mode) && !opt.overwrite)
|
||||
throw ot::status {ot::st::error, "'" + opt.cover_out.value() + "' already exists. Use -y to overwrite."};
|
||||
} else if (errno != ENOENT) {
|
||||
throw ot::status {ot::st::error, "Could not identify '" + opt.cover_out.value() + "': " + strerror(errno)};
|
||||
}
|
||||
|
||||
output = fopen(opt.cover_out->c_str(), "w");
|
||||
if (output == nullptr)
|
||||
throw ot::status {ot::st::standard_error, "Could not open '" + opt.cover_out.value() + "' for writing: " + strerror(errno)};
|
||||
}
|
||||
|
||||
if (fwrite(cover->picture_data.data(), 1, cover->picture_data.size(), output.get()) < cover->picture_data.size())
|
||||
throw ot::status {ot::st::standard_error, "fwrite error: "s + strerror(errno)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Main loop of opustags. Read the packets from the reader, and forwards them to the writer.
|
||||
* Transform the OpusTags packet on the fly.
|
||||
@ -420,6 +462,8 @@ static void process(ot::ogg_reader& reader, ot::ogg_writer* writer, const ot::op
|
||||
} else if (reader.absolute_page_no == 1) { // Comment header
|
||||
ot::opus_tags tags;
|
||||
reader.process_header_packet([&tags](ogg_packet& p) { tags = ot::parse_tags(p); });
|
||||
if (opt.cover_out)
|
||||
output_cover(tags, opt);
|
||||
edit_tags(tags, opt);
|
||||
if (writer) {
|
||||
if (opt.edit_interactively) {
|
||||
@ -430,7 +474,8 @@ static void process(ot::ogg_reader& reader, ot::ogg_writer* writer, const ot::op
|
||||
writer->write_header_packet(serialno, pageno, packet);
|
||||
pageno_offset = writer->next_page_no - 1 - reader.absolute_page_no;
|
||||
} else {
|
||||
ot::print_comments(tags.comments, stdout, opt.raw);
|
||||
if (opt.cover_out != "-")
|
||||
ot::print_comments(tags.comments, stdout, opt.raw);
|
||||
break;
|
||||
}
|
||||
} else if (writer) {
|
||||
|
@ -504,6 +504,14 @@ struct options {
|
||||
* Options: --add, --set, --set-all
|
||||
*/
|
||||
std::list<std::string> to_add;
|
||||
/**
|
||||
* If set, the input file’s cover art is exported to the specified file. - for stdout. Does
|
||||
* not overwrite the file if it already exists unless -y is specified. Does nothing if the
|
||||
* input file does not contain a cover art.
|
||||
*
|
||||
* Option: --output-cover
|
||||
*/
|
||||
std::optional<std::string> cover_out;
|
||||
/**
|
||||
* Disable encoding conversions. OpusTags are specified to always be encoded as UTF-8, but
|
||||
* if for some reason a specific file contains binary tags that someone would like to
|
||||
|
6
t/cli.cc
6
t/cli.cc
@ -180,6 +180,12 @@ void check_bad_arguments()
|
||||
error_case({"opustags", "--edit", "x", "-i", "-d", "X"}, "Cannot mix --edit with -adDsS.", "mixing -e and -d");
|
||||
error_case({"opustags", "--edit", "x", "-i", "-D"}, "Cannot mix --edit with -adDsS.", "mixing -e and -D");
|
||||
error_case({"opustags", "--edit", "x", "-i", "-S"}, "Cannot mix --edit with -adDsS.", "mixing -e and -S");
|
||||
error_case({"opustags", "--output-cover", "x", "--output-cover", "y"},
|
||||
"Cannot specify --output-cover more than once.", "multiple --output-cover");
|
||||
error_case({"opustags", "x", "-o", "-", "--output-cover", "-"},
|
||||
"Cannot specify standard output for both --output and --output-cover.", "-o and --output-cover conflict");
|
||||
error_case({"opustags", "-i", "x", "y", "--output-cover", "z"},
|
||||
"Cannot use --output-cover with multiple input files.", "--output-cover with multiple input");
|
||||
error_case({"opustags", "-d", "\xFF", "x"},
|
||||
"Could not encode argument into UTF-8:",
|
||||
"-d with binary data");
|
||||
|
@ -72,6 +72,7 @@ Options:
|
||||
-s, --set FIELD=VALUE replace a comment
|
||||
-S, --set-all import comments from standard input
|
||||
-e, --edit edit tags interactively in VISUAL/EDITOR
|
||||
--output-cover FILE extract and save the cover art, if any
|
||||
--raw disable encoding conversion
|
||||
|
||||
See the man page for extensive documentation.
|
||||
|
Loading…
x
Reference in New Issue
Block a user