mirror of https://github.com/xiph/flac
Add --remove-all-tags-except to metaflac
This commit is contained in:
parent
a87e6ba5f4
commit
20d8b0f50c
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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.*
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue