Add --remove-all-tags-except to metaflac

This commit is contained in:
Martijn van Beurden 2022-11-08 13:23:00 +01:00 committed by GitHub
parent a87e6ba5f4
commit 20d8b0f50c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 173 additions and 1 deletions

View File

@ -118,6 +118,10 @@ modification time is set to the current time):
**\--remove-all-tags**
: Remove all tags, leaving only the vendor string.
**\--remove-all-tags-except=NAME1\[=NAME2\[=...\]\]**
: Remove all tags, except the vendor string and the tag names
specified. Tag names must be separated by an = character.
**\--set-tag=field**
: Add a tag. The field must comply with the Vorbis comment spec, of the
form "NAME=VALUE". If there is currently no tag block, one will be

View File

@ -360,6 +360,7 @@ FLAC__bool do_shorthand_operation(const char *filename, FLAC__bool prefix_with_f
case OP__SHOW_VC_VENDOR:
case OP__SHOW_VC_FIELD:
case OP__REMOVE_VC_ALL:
case OP__REMOVE_VC_ALL_EXCEPT:
case OP__REMOVE_VC_FIELD:
case OP__REMOVE_VC_FIRSTFIELD:
case OP__SET_VC_FIELD:

View File

@ -33,6 +33,7 @@
#include "share/compat.h"
static FLAC__bool remove_vc_all(const char *filename, FLAC__StreamMetadata *block, FLAC__bool *needs_write);
static FLAC__bool remove_vc_all_except(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write);
static FLAC__bool remove_vc_field(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write);
static FLAC__bool remove_vc_firstfield(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write);
static FLAC__bool set_vc_field(const char *filename, FLAC__StreamMetadata *block, const Argument_VcField *field, FLAC__bool *needs_write, FLAC__bool raw);
@ -90,6 +91,9 @@ FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__bo
case OP__REMOVE_VC_ALL:
ok = remove_vc_all(filename, block, needs_write);
break;
case OP__REMOVE_VC_ALL_EXCEPT:
ok = remove_vc_all_except(filename, block, operation->argument.vc_field_name.value, needs_write);
break;
case OP__REMOVE_VC_FIELD:
ok = remove_vc_field(filename, block, operation->argument.vc_field_name.value, needs_write);
break;
@ -144,6 +148,51 @@ FLAC__bool remove_vc_all(const char *filename, FLAC__StreamMetadata *block, FLAC
return true;
}
FLAC__bool remove_vc_all_except(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write)
{
char * field_names[200];
uint32_t field_name_length, i;
int j, num_field_names;
FLAC__ASSERT(0 != block);
FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
FLAC__ASSERT(0 != needs_write);
field_name_length = strlen(field_name);
field_names[0] = (void *)field_name;
for(num_field_names = 1; num_field_names < 200; num_field_names++) {
char * separator = strchr(field_names[num_field_names-1], '=');
if(separator == 0 || separator >= field_name + field_name_length)
break;
field_names[num_field_names] = separator+1;
}
if(num_field_names > 200) {
flac_fprintf(stderr, "%s: ERROR: too many field names\n", filename);
return false;
}
for(i = 0; i < block->data.vorbis_comment.num_comments; ) {
int field_name_found = false;
for(j = 0; j < num_field_names; j++) {
const uint32_t length = (j == (num_field_names - 1))?(uint32_t)strlen(field_names[j]):(uint32_t)(strchr(field_names[j],'=')-field_names[j]);
if(FLAC__metadata_object_vorbiscomment_entry_matches(block->data.vorbis_comment.comments[i], field_names[j], length)) {
field_name_found = true;
break;
}
}
if(!field_name_found) {
FLAC__metadata_object_vorbiscomment_delete_comment(block, i);
*needs_write = true;
}
else
i++;
}
return true;
}
FLAC__bool remove_vc_field(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write)
{
int n;

View File

@ -67,6 +67,7 @@ struct share__option long_options_[] = {
{ "show-vendor-tag", 0, 0, 0 },
{ "show-tag", 1, 0, 0 },
{ "remove-all-tags", 0, 0, 0 },
{ "remove-all-tags-except", 1, 0, 0 },
{ "remove-tag", 1, 0, 0 },
{ "remove-first-tag", 1, 0, 0 },
{ "set-tag", 1, 0, 0 },
@ -114,6 +115,7 @@ static FLAC__bool parse_uint32(const char *src, FLAC__uint32 *dest);
static FLAC__bool parse_uint64(const char *src, FLAC__uint64 *dest);
static FLAC__bool parse_string(const char *src, char **dest);
static FLAC__bool parse_vorbis_comment_field_name(const char *field_ref, char **name, const char **violation);
static FLAC__bool parse_vorbis_comment_field_names(const char *field_ref, char **names, const char **violation);
static FLAC__bool parse_add_seekpoint(const char *in, char **out, const char **violation);
static FLAC__bool parse_add_padding(const char *in, unsigned *out);
static FLAC__bool parse_block_number(const char *in, Argument_BlockNumber *out);
@ -269,6 +271,7 @@ void free_options(CommandLineOptions *options)
case OP__SHOW_VC_FIELD:
case OP__REMOVE_VC_FIELD:
case OP__REMOVE_VC_FIRSTFIELD:
case OP__REMOVE_VC_ALL_EXCEPT:
if(0 != op->argument.vc_field_name.value)
free(op->argument.vc_field_name.value);
break;
@ -496,6 +499,16 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi
else if(0 == strcmp(opt, "remove-all-tags")) {
(void) append_shorthand_operation(options, OP__REMOVE_VC_ALL);
}
else if(0 == strcmp(opt, "remove-all-tags-except")) {
const char *violation;
op = append_shorthand_operation(options, OP__REMOVE_VC_ALL_EXCEPT);
FLAC__ASSERT(0 != option_argument);
if(!parse_vorbis_comment_field_names(option_argument, &(op->argument.vc_field_name.value), &violation)) {
FLAC__ASSERT(0 != violation);
flac_fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n %s\n", opt, option_argument, violation);
ok = false;
}
}
else if(0 == strcmp(opt, "remove-tag")) {
const char *violation;
op = append_shorthand_operation(options, OP__REMOVE_VC_FIELD);
@ -886,6 +899,29 @@ FLAC__bool parse_vorbis_comment_field_name(const char *field_ref, char **name, c
return true;
}
FLAC__bool parse_vorbis_comment_field_names(const char *field_ref, char **names, const char **violation)
{
static const char * const violations[] = {
"field name contains invalid character"
};
char *q, *s;
s = local_strdup(field_ref);
for(q = s; *q; q++) {
if(*q < 0x20 || *q > 0x7d) {
free(s);
*violation = violations[0];
return false;
}
}
*names = s;
return true;
}
FLAC__bool parse_add_seekpoint(const char *in, char **out, const char **violation)
{
static const char *garbled_ = "garbled specification";

View File

@ -54,6 +54,7 @@ typedef enum {
OP__SHOW_VC_VENDOR,
OP__SHOW_VC_FIELD,
OP__REMOVE_VC_ALL,
OP__REMOVE_VC_ALL_EXCEPT,
OP__REMOVE_VC_FIELD,
OP__REMOVE_VC_FIRSTFIELD,
OP__SET_VC_FIELD,

View File

@ -127,6 +127,9 @@ int long_usage(const char *message, ...)
fprintf(out, "--remove-tag=NAME Remove all tags whose field name is 'NAME'.\n");
fprintf(out, "--remove-first-tag=NAME Remove first tag whose field name is 'NAME'.\n");
fprintf(out, "--remove-all-tags Remove all tags, leaving only the vendor string.\n");
fprintf(out, "--remove-all-tags-except=NAME1[=NAME2[=...]] Remove all tags, except the vendor");
fprintf(out, " string and the tag names specified. Tag names must be");
fprintf(out, " separated by an = character.");
fprintf(out, "--set-tag=FIELD Add a tag. The FIELD must comply with the Vorbis comment\n");
fprintf(out, " spec, of the form \"NAME=VALUE\". If there is currently\n");
fprintf(out, " no tag block, one will be created.\n");

View File

@ -79,7 +79,8 @@ EXTRA_DIST = \
case59-expect.meta \
case60-expect.meta \
case61-expect.meta \
case62-expect.meta
case62-expect.meta \
case63-expect.meta
clean-local:
-rm -f out.*

View File

@ -0,0 +1,74 @@
METADATA block #0
type: 0 (STREAMINFO)
is last: false
length: XXX
sample_rate: 8000 Hz
channels: 1
bits-per-sample: 8
total samples: 80000
MD5 signature: a042237c5493fdb9656b94a83608d11a
METADATA block #1
type: 3 (SEEKTABLE)
is last: false
length: XXX
seek points: 1
point 0: sample_number=0
METADATA block #2
type: 4 (VORBIS_COMMENT)
is last: false
length: XXX
comments: 3
comment[0]: TITLE=Tittle
comment[1]: artist=Fartist
comment[2]: artist=artits
METADATA block #3
type: 5 (CUESHEET)
is last: false
length: XXX
media catalog number: 1234567890123
lead-in: 0
is CD: false
number of tracks: 2
track[0]
offset: 0
number: 1
ISRC:
type: AUDIO
pre-emphasis: false
number of index points: 1
index[0]
offset: 0
number: 1
track[1]
offset: 80000
number: 255 (LEAD-OUT)
METADATA block #4
type: 6 (PICTURE)
is last: false
length: XXX
type: 1 (32x32 pixels 'file icon' (PNG only))
MIME type: image/png
description: standard_icon
width: 32
height: 32
depth: 24
colors: 0 (unindexed)
data length: XXX
data:
METADATA block #5
type: 6 (PICTURE)
is last: false
length: XXX
type: 2 (Other file icon)
MIME type: image/png
description: icon
width: 64
height: 64
depth: 24
colors: 0 (unindexed)
data length: XXX
data:
METADATA block #6
type: 1 (PADDING)
is last: true
length: XXX

View File

@ -369,6 +369,9 @@ metaflac_test case61 "--import-picture-from" "--list"
run_metaflac --import-picture-from="2|image/png|icon|64x64x24|${top_srcdir}/test/pictures/1.png" $flacfile
check_flac
metaflac_test case62 "--import-picture-from" "--list"
run_metaflac --remove-all-tags-except=artist=title $flacfile
check_flac
metaflac_test case63 "--remove-all-tags-except=artist=title" "--list"
# UNKNOWN blocks
echo $ECHO_N "Testing FLAC file with unknown metadata... " $ECHO_C