Skip to content

Commit

Permalink
Add option --output-cover
Browse files Browse the repository at this point in the history
  • Loading branch information
fmang committed Feb 27, 2023
1 parent 66fb357 commit ec68f5c
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 1 deletion.
10 changes: 10 additions & 0 deletions opustags.1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
47 changes: 46 additions & 1 deletion src/cli.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Usage: opustags --help
-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.
Expand All @@ -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}
};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
8 changes: 8 additions & 0 deletions src/opustags.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions t/cli.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
1 change: 1 addition & 0 deletions t/opustags.t
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit ec68f5c

Please sign in to comment.