summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-04-01 14:25:35 +0200
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-04-01 14:25:35 +0200
commitd556baa0cebd866a8a780aa63858fa561494b411 (patch)
tree6bdf4fa5b3e9f752583c0b02fdf1f568cd867009
parent6612a214e630e29aeb06355d158d4f5911d98f40 (diff)
mp3 id3v1/id3v2/apev2 tag editing
-rw-r--r--deadbeef.h7
-rw-r--r--junklib.c216
-rw-r--r--junklib.h9
-rw-r--r--playlist.c5
-rw-r--r--plugins.c7
-rw-r--r--plugins/mpgmad/mpgmad.c168
6 files changed, 364 insertions, 48 deletions
diff --git a/deadbeef.h b/deadbeef.h
index 2c45b45f..9294da11 100644
--- a/deadbeef.h
+++ b/deadbeef.h
@@ -423,6 +423,8 @@ typedef struct {
// junk reading/writing
int (*junk_id3v1_read) (DB_playItem_t *it, DB_FILE *fp);
int (*junk_id3v1_find) (DB_FILE *fp);
+ int (*junk_id3v1_write) (FILE *fp, DB_playItem_t *it);
+ int (*junk_id3v2_find) (DB_FILE *fp, int *psize);
int (*junk_id3v2_read) (DB_playItem_t *it, DB_FILE *fp);
int (*junk_id3v2_read_full) (DB_playItem_t *it, DB_id3v2_tag_t *tag, DB_FILE *fp);
int (*junk_id3v2_convert_24_to_23) (DB_id3v2_tag_t *tag24, DB_id3v2_tag_t *tag23);
@@ -434,7 +436,12 @@ typedef struct {
DB_id3v2_frame_t *(*junk_id3v2_add_text_frame_24) (DB_id3v2_tag_t *tag, const char *frame_id, const char *value);
int (*junk_id3v2_remove_frames) (DB_id3v2_tag_t *tag, const char *frame_id);
int (*junk_apev2_read) (DB_playItem_t *it, DB_FILE *fp);
+ int (*junk_apev2_read_full) (DB_playItem_t *it, DB_apev2_tag_t *tag_store, DB_FILE *fp);
int (*junk_apev2_find) (DB_FILE *fp, int32_t *psize, uint32_t *pflags, uint32_t *pnumitems);
+ int (*junk_apev2_remove_frames) (DB_apev2_tag_t *tag, const char *frame_id);
+ DB_apev2_frame_t * (*junk_apev2_add_text_frame) (DB_apev2_tag_t *tag, const char *frame_id, const char *value);
+ void (*junk_apev2_free) (DB_apev2_tag_t *tag);
+ int (*junk_apev2_write) (FILE *fp, DB_apev2_tag_t *tag, int write_header, int write_footer);
int (*junk_get_leading_size) (DB_FILE *fp);
int (*junk_get_leading_size_stdio) (FILE *fp);
void (*junk_copy) (DB_playItem_t *from, DB_playItem_t *first, DB_playItem_t *last);
diff --git a/junklib.c b/junklib.c
index 631a2a31..66757420 100644
--- a/junklib.c
+++ b/junklib.c
@@ -105,6 +105,37 @@ extract_f32 (unsigned char *buf) {
*x |= buf[3];
return f;
}
+
+int
+junk_iconv (const char *in, int inlen, char *out, int outlen, const char *cs_in, const char *cs_out) {
+ iconv_t cd = iconv_open (cs_out, cs_in);
+ if (cd == (iconv_t)-1) {
+ return -1;
+ }
+#ifdef __linux__
+ char *pin = (char*)in;
+#else
+ const char *pin = value;
+#endif
+
+ size_t inbytesleft = inlen;
+ size_t outbytesleft = outlen;
+
+ char *pout = out;
+ memset (out, 0, outbytesleft);
+
+ size_t res = iconv (cd, &pin, &inbytesleft, &pout, &outbytesleft);
+ int err = errno;
+ iconv_close (cd);
+
+ trace ("iconv -f %s -t %s '%s': returned %d, inbytes %d/%d, outbytes %d/%d, errno=%d\n", cs_in, cs_out, in, res, inlen, inbytesleft, outlen, outbytesleft, err);
+ if (res == -1) {
+ return -1;
+ }
+ return pout - out;
+}
+
+
static const char *junk_genretbl[] = {
"Blues",
"Classic Rock",
@@ -254,6 +285,7 @@ static const char *junk_genretbl[] = {
"Anime",
"JPop",
"SynthPop",
+ NULL
};
static int
@@ -573,6 +605,98 @@ junk_id3v1_read (playItem_t *it, DB_FILE *fp) {
}
int
+junk_id3v1_write (FILE *fp, playItem_t *it) {
+ char title[30] = "";
+ char artist[30] = "";
+ char album[30] = "";
+ char year[4] = "";
+ char comment[28] = "";
+ uint8_t genreid = 0xff;
+ uint8_t tracknum = 0;
+
+ const char *meta;
+
+#define conv(name, store) {\
+ memset (store, 0x20, sizeof (store));\
+ meta = pl_find_meta (it, name);\
+ if (meta) {\
+ char temp[1000];\
+ int l = junk_iconv (meta, strlen (meta), temp, sizeof (temp), UTF8, "ASCII");\
+ if (l == -1) {\
+ memset (store, 0, sizeof (store));\
+ }\
+ else {\
+ strncpy (store, temp, sizeof (store));\
+ }\
+ }\
+}
+
+ conv ("title", title);
+ conv ("artist", artist);
+ conv ("album", album);
+ conv ("year", year);
+ conv ("comment", comment);
+
+#undef conv
+
+ // tracknum
+ meta = pl_find_meta (it, "track");
+ if (meta) {
+ tracknum = atoi (meta);
+ }
+
+ // find genre
+ meta = pl_find_meta (it, "genre");
+ if (meta) {
+ for (int i = 0; junk_genretbl[i]; i++) {
+ if (!strcasecmp (meta, junk_genretbl[i])) {
+ genreid = i;
+ break;
+ }
+ }
+ }
+
+ if (fwrite ("TAG", 1, 3, fp) != 3) {
+ trace ("junk_id3v1_write: failed to write signature\n");
+ return -1;
+ }
+ if (fwrite (title, 1, sizeof (title), fp) != sizeof (title)) {
+ trace ("junk_id3v1_write: failed to write title\n");
+ return -1;
+ }
+ if (fwrite (artist, 1, sizeof (artist), fp) != sizeof (artist)) {
+ trace ("junk_id3v1_write: failed to write artist\n");
+ return -1;
+ }
+ if (fwrite (album, 1, sizeof (album), fp) != sizeof (album)) {
+ trace ("junk_id3v1_write: failed to write album\n");
+ return -1;
+ }
+ if (fwrite (year, 1, sizeof (year), fp) != sizeof (year)) {
+ trace ("junk_id3v1_write: failed to write year\n");
+ return -1;
+ }
+ if (fwrite (comment, 1, sizeof (comment), fp) != sizeof (comment)) {
+ trace ("junk_id3v1_write: failed to write comment\n");
+ return -1;
+ }
+ uint8_t zero = 0;
+ if (fwrite (&zero, 1, 1, fp) != 1) {
+ trace ("junk_id3v1_write: failed to write id3v1.1 marker\n");
+ return -1;
+ }
+ if (fwrite (&tracknum, 1, 1, fp) != 1) {
+ trace ("junk_id3v1_write: failed to write track\n");
+ return -1;
+ }
+ if (fwrite (&genreid, 1, 1, fp) != 1) {
+ trace ("junk_id3v1_write: failed to write genre\n");
+ return -1;
+ }
+ return 0;
+}
+
+int
junk_id3v1_find (DB_FILE *fp) {
uint8_t buffer[3];
if (deadbeef->fseek (fp, -128, SEEK_END) == -1) {
@@ -584,7 +708,7 @@ junk_id3v1_find (DB_FILE *fp) {
if (memcmp (buffer, "TAG", 3)) {
return -1; // no tag
}
- return 128;
+ return deadbeef->ftell (fp) - 3;
}
int
@@ -861,6 +985,40 @@ id3v2_string_read (int version, uint8_t *out, int sz, int unsync, const uint8_t
}
int
+junk_id3v2_find (DB_FILE *fp, int *psize) {
+ if (deadbeef->fseek (fp, 0, SEEK_SET) == -1) {
+ trace ("junk_id3v2_find: seek error\n");
+ return -1;
+ }
+ uint8_t header[10];
+ int pos = deadbeef->ftell (fp);
+ if (pos == -1) {
+ trace ("junk_id3v2_find: ftell error\n");
+ return -1;
+ }
+ if (deadbeef->fread (header, 1, 10, fp) != 10) {
+ trace ("junk_id3v2_find: read error\n");
+ return -1; // too short
+ }
+ if (strncmp (header, "ID3", 3)) {
+ return -1; // no tag
+ }
+ uint8_t flags = header[5];
+ if (flags & 15) {
+ return -1; // unsupported
+ }
+ int footerpresent = (flags & (1<<4)) ? 1 : 0;
+ // check for bad size
+ if ((header[9] & 0x80) || (header[8] & 0x80) || (header[7] & 0x80) || (header[6] & 0x80)) {
+ return -1; // bad header
+ }
+ uint32_t size = (header[9] << 0) | (header[8] << 7) | (header[7] << 14) | (header[6] << 21);
+ //trace ("junklib: leading junk size %d\n", size);
+ *psize = size + 10 + 10 * footerpresent;
+ return pos;
+}
+
+int
junk_get_leading_size_stdio (FILE *fp) {
uint8_t header[10];
int pos = ftell (fp);
@@ -913,36 +1071,6 @@ junk_get_leading_size (DB_FILE *fp) {
//trace ("junklib: leading junk size %d\n", size);
return size + 10 + 10 * footerpresent;
}
-
-int
-junk_iconv (const char *in, int inlen, char *out, int outlen, const char *cs_in, const char *cs_out) {
- iconv_t cd = iconv_open (cs_out, cs_in);
- if (cd == (iconv_t)-1) {
- return -1;
- }
-#ifdef __linux__
- char *pin = (char*)in;
-#else
- const char *pin = value;
-#endif
-
- size_t inbytesleft = inlen;
- size_t outbytesleft = outlen;
-
- char *pout = out;
- memset (out, 0, outbytesleft);
-
- size_t res = iconv (cd, &pin, &inbytesleft, &pout, &outbytesleft);
- int err = errno;
- iconv_close (cd);
-
- trace ("iconv -f %s -t %s '%s': returned %d, inbytes %d/%d, outbytes %d/%d, errno=%d\n", cs_in, cs_out, in, res, inlen, inbytesleft, outlen, outbytesleft, err);
- if (res == -1) {
- return -1;
- }
- return pout - out;
-}
-
int
junk_id3v2_unsync (uint8_t *out, int len, int maxlen) {
uint8_t buf [maxlen];
@@ -1655,6 +1783,28 @@ junk_id3v2_convert_22_to_24 (DB_id3v2_tag_t *tag22, DB_id3v2_tag_t *tag24) {
return 0;
}
+int
+junk_apev2_remove_frames (DB_apev2_tag_t *tag, const char *frame_id) {
+ DB_apev2_frame_t *prev = NULL;
+ for (DB_apev2_frame_t *f = tag->frames; f; ) {
+ DB_apev2_frame_t *next = f->next;
+ if (!strcmp (f->key, frame_id)) {
+ if (prev) {
+ prev->next = f->next;
+ }
+ else {
+ tag->frames = f->next;
+ }
+ free (f);
+ }
+ else {
+ prev = f;
+ }
+ f = next;
+ }
+ return 0;
+}
+
DB_apev2_frame_t *
junk_apev2_add_text_frame (DB_apev2_tag_t *tag, const char *frame_id, const char *value) {
DB_apev2_frame_t *tail = tag->frames;
@@ -1771,6 +1921,9 @@ junk_apev2_write (FILE *fp, DB_apev2_tag_t *tag, int write_header, int write_foo
numframes++;
f = f->next;
}
+ size += 32;
+
+ trace ("junk_apev2_write: writing apev2 tag, size=%d, numframes=%d\n", size, numframes);
if (write_header) {
@@ -1822,6 +1975,7 @@ junk_apev2_write (FILE *fp, DB_apev2_tag_t *tag, int write_header, int write_foo
trace ("junk_apev2_write_i32_le: failed to write apev2 item value\n");
goto error;
}
+ f = f->next;
}
if (write_footer) {
diff --git a/junklib.h b/junklib.h
index 6f745dfc..cde8f078 100644
--- a/junklib.h
+++ b/junklib.h
@@ -30,6 +30,12 @@ int
junk_id3v1_find (DB_FILE *fp);
int
+junk_id3v1_write (FILE *fp, struct playItem_s *it);
+
+int
+junk_id3v2_find (DB_FILE *fp, int *psize);
+
+int
junk_id3v2_read_full (struct playItem_s *it, DB_id3v2_tag_t *tag, DB_FILE *fp);
int
@@ -74,6 +80,9 @@ junk_apev2_find (DB_FILE *fp, int32_t *psize, uint32_t *pflags, uint32_t *pnumit
DB_apev2_frame_t *
junk_apev2_add_text_frame (DB_apev2_tag_t *tag, const char *frame_id, const char *value);
+int
+junk_apev2_remove_frames (DB_apev2_tag_t *tag, const char *frame_id);
+
void
junk_apev2_free (DB_apev2_tag_t *tag);
diff --git a/playlist.c b/playlist.c
index 6ad528a0..f59f0d81 100644
--- a/playlist.c
+++ b/playlist.c
@@ -2205,6 +2205,11 @@ pl_format_title (playItem_t *it, int idx, char *s, int size, int id, const char
if (text) {
strncpy (s, text, size);
UNLOCK;
+ for (ss = s; *ss; ss++) {
+ if (*ss == '\n') {
+ *ss = ';';
+ }
+ }
return strlen (s);
}
else {
diff --git a/plugins.c b/plugins.c
index 4311a2b8..02dddb88 100644
--- a/plugins.c
+++ b/plugins.c
@@ -181,6 +181,8 @@ static DB_functions_t deadbeef_api = {
// junk reading
.junk_id3v1_read = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_id3v1_read,
.junk_id3v1_find = junk_id3v1_find,
+ .junk_id3v1_write = (int (*) (FILE *, DB_playItem_t *))junk_id3v1_write,
+ .junk_id3v2_find = junk_id3v2_find,
.junk_id3v2_read = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_id3v2_read,
.junk_id3v2_read_full = (int (*)(DB_playItem_t *, DB_id3v2_tag_t *tag, DB_FILE *fp))junk_id3v2_read_full,
.junk_id3v2_convert_24_to_23 = junk_id3v2_convert_24_to_23,
@@ -192,7 +194,12 @@ static DB_functions_t deadbeef_api = {
.junk_id3v2_add_text_frame_23 = junk_id3v2_add_text_frame_23,
.junk_id3v2_add_text_frame_24 = junk_id3v2_add_text_frame_24,
.junk_apev2_read = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_apev2_read,
+ .junk_apev2_read_full = (int (*) (DB_playItem_t *it, DB_apev2_tag_t *tag_store, DB_FILE *fp))junk_apev2_read_full,
.junk_apev2_find = junk_apev2_find,
+ .junk_apev2_remove_frames = junk_apev2_remove_frames,
+ .junk_apev2_add_text_frame = junk_apev2_add_text_frame,
+ .junk_apev2_free = junk_apev2_free,
+ .junk_apev2_write = junk_apev2_write,
.junk_get_leading_size = junk_get_leading_size,
.junk_get_leading_size_stdio = junk_get_leading_size_stdio,
.junk_detect_charset = junk_detect_charset,
diff --git a/plugins/mpgmad/mpgmad.c b/plugins/mpgmad/mpgmad.c
index a109985c..d88abe6f 100644
--- a/plugins/mpgmad/mpgmad.c
+++ b/plugins/mpgmad/mpgmad.c
@@ -342,9 +342,11 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
}
valid_frames++;
+#if 0
if (nframe < 1000) {
- trace ("frame %d bitrate %d\n", nframe, bitrate);
+ trace ("frame %d bitrate %d\n", nframe, bitrate);
}
+#endif
if (sample != 0 || nframe == 0)
{
buffer->version = ver;
@@ -964,7 +966,7 @@ cmp3_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) {
static int
cmp3_read_float32 (DB_fileinfo_t *_info, char *bytes, int size) {
mpgmad_info_t *info = (mpgmad_info_t *)_info;
- trace ("cmp3_read_float32 readsize=%d, nchannels=%d\n", size, _info->channels);
+// trace ("cmp3_read_float32 readsize=%d, nchannels=%d\n", size, _info->channels);
info->buffer.readsize = size;
info->buffer.out = bytes;
cmp3_decode_float32 (info);
@@ -1194,9 +1196,11 @@ cmp3_write_metadata (DB_playItem_t *it) {
return -1;
}
- int id3v2_end = deadbeef->junk_get_leading_size (fp);
- if (id3v2_end == -1) {
- id3v2_end = 0;
+ int fsize = deadbeef->fgetlength (fp);
+ int id3v2_size = 0;
+ int id3v2_start = deadbeef->junk_id3v2_find (fp, &id3v2_size);
+ if (id3v2_start == -1) {
+ id3v2_size = -1;
}
int32_t apev2_size;
@@ -1206,13 +1210,24 @@ cmp3_write_metadata (DB_playItem_t *it) {
apev2_start = 0;
}
+ if (!strip_apev2 && !write_apev2) {
+ apev2_start = 0;
+ }
+
int id3v1_start = deadbeef->junk_id3v1_find (fp);
if (id3v1_start == -1) {
id3v1_start = 0;
}
- int header = id3v2_end;
- int footer = min (id3v1_start, apev2_start);
+ int header = id3v2_start + id3v2_size;
+ int footer = fsize;
+
+ if (id3v1_start > 0) {
+ footer = id3v1_start;
+ }
+ if (apev2_start > 0) {
+ footer = min (footer, apev2_start);
+ }
// mapping between ddb metadata names and id3v2/apev2 names
const char *md[] = {
@@ -1226,6 +1241,10 @@ cmp3_write_metadata (DB_playItem_t *it) {
"vendor", "TENC", NULL,
"performer", "TPE3", NULL,
"composer", "TCOM", "Composer",
+ "year", NULL, "Year",
+ "comment", NULL, "Comment",
+ "copyright", NULL, "Copyright",
+ "cuesheet", NULL, "Cuesheet",
NULL
};
// "TRCK" -- special case
@@ -1249,12 +1268,30 @@ cmp3_write_metadata (DB_playItem_t *it) {
memset (&id3v2, 0, sizeof (id3v2));
memset (&apev2, 0, sizeof (id3v2));
- if (!strip_id3v2 && !write_id3v2) {
- id3v2_end = 0;
+ if (!strip_id3v2 && !write_id3v2 && id3v2_size > 0) {
+ if (deadbeef->fseek (fp, id3v2_start, SEEK_SET) == -1) {
+ trace ("cmp3_write_metadata: failed to seek to original id3v2 tag position in %s\n", it->fname);
+ goto error;
+ }
+ uint8_t *buf = malloc (id3v2_size);
+ if (!buf) {
+ trace ("cmp3_write_metadata: failed to alloc %d bytes for id3v2 tag\n", id3v2_size);
+ goto error;
+ }
+ if (deadbeef->fread (buf, 1, id3v2_size, fp) != id3v2_size) {
+ trace ("cmp3_write_metadata: failed to read original id3v2 tag from %s\n", it->fname);
+ free (buf);
+ goto error;
+ }
+ if (fwrite (buf, 1, id3v2_size, out) != id3v2_size) {
+ trace ("cmp3_write_metadata: failed to copy original id3v2 tag from %s to temp file\n", it->fname);
+ free (buf);
+ goto error;
+ }
+ free (buf);
}
else if (write_id3v2) {
- if (!id3v2_end || deadbeef->junk_id3v2_read_full (NULL, &id3v2, fp) != 0) {
- trace ("failed to read id3v2 from file %s\n", it->fname);
+ if (id3v2_size <= 0 || strip_id3v2 || deadbeef->junk_id3v2_read_full (NULL, &id3v2, fp) != 0) {
deadbeef->junk_id3v2_free (&id3v2);
memset (&id3v2, 0, sizeof (id3v2));
id3v2.version[0] = id3v2_version;
@@ -1297,26 +1334,45 @@ cmp3_write_metadata (DB_playItem_t *it) {
add_frame = deadbeef->junk_id3v2_add_text_frame_24;
}
- // add all known frames
+ // add all basic frames
for (int i = 0; md[i]; i += 3) {
if (md[i+1]) {
const char *val = deadbeef->pl_find_meta (it, md[i]);
if (val) {
+ deadbeef->junk_id3v2_remove_frames (&id3v2, md[i+1]);
add_frame (&id3v2, md[i+1], val);
}
}
}
+
+ // add tracknumber/totaltracks
const char *track = deadbeef->pl_find_meta (it, "track");
const char *totaltracks = deadbeef->pl_find_meta (it, "numtracks");
if (track && totaltracks) {
char s[100];
snprintf (s, sizeof (s), "%s/%s", track, totaltracks);
+ deadbeef->junk_id3v2_remove_frames (&id3v2, "TRCK");
add_frame (&id3v2, "TRCK", s);
}
else if (track) {
+ deadbeef->junk_id3v2_remove_frames (&id3v2, "TRCK");
add_frame (&id3v2, "TRCK", track);
}
+ // add year/date
+ const char *year = deadbeef->pl_find_meta (it, "year");
+ if (year) {
+ // FIXME: format check
+ if (id3v2.version[0] == 3) {
+ deadbeef->junk_id3v2_remove_frames (&id3v2, "TYER");
+ add_frame (&id3v2, "TYER", year);
+ }
+ else {
+ deadbeef->junk_id3v2_remove_frames (&id3v2, "TDRC");
+ add_frame (&id3v2, "TDRC", year);
+ }
+ }
+
// write tag
if (deadbeef->junk_id3v2_write (out, &id3v2) != 0) {
trace ("cmp3_write_metadata: failed to write id3v2 tag to %s\n", it->fname)
@@ -1327,12 +1383,12 @@ cmp3_write_metadata (DB_playItem_t *it) {
// now write audio data
buffer = malloc (8192);
deadbeef->fseek (fp, header, SEEK_SET);
- int writesize = deadbeef->fgetlength (fp);
+ int writesize = fsize;
if (footer > 0) {
- writesize -= (writesize - footer);
+ writesize -= (fsize - footer);
}
writesize -= header;
- trace ("writesize: %d\n", writesize);
+ trace ("writesize: %d, id3v1_start: %d, apev2_start: %d, footer: %d\n", writesize, id3v1_start, apev2_start, footer);
while (writesize > 0) {
int rb = min (8192, writesize);
@@ -1345,14 +1401,92 @@ cmp3_write_metadata (DB_playItem_t *it) {
fprintf (stderr, "junk_write_id3v2: error writing output file\n");
goto error;
}
- trace ("written %d bytes\n", rb);
if (rb == 0) {
break; // eof
}
writesize -= rb;
}
- // TODO: apev2 and id3v1
+ if (!write_apev2 && !strip_apev2 && apev2_start != 0) {
+ if (deadbeef->fseek (fp, apev2_start, SEEK_SET) == -1) {
+ trace ("cmp3_write_metadata: failed to seek to original apev2 tag position in %s\n", it->fname);
+ goto error;
+ }
+ uint8_t *buf = malloc (apev2_size);
+ if (!buf) {
+ trace ("cmp3_write_metadata: failed to alloc %d bytes for apev2 tag\n", apev2_size);
+ goto error;
+ }
+ if (deadbeef->fread (buf, 1, apev2_size, fp) != apev2_size) {
+ trace ("cmp3_write_metadata: failed to read original apev2 tag from %s\n", it->fname);
+ free (buf);
+ goto error;
+ }
+ if (fwrite (buf, 1, apev2_size, out) != apev2_size) {
+ trace ("cmp3_write_metadata: failed to copy original apev2 tag from %s to temp file\n", it->fname);
+ free (buf);
+ goto error;
+ }
+ free (buf);
+ }
+ else if (write_apev2) {
+ if (!strip_apev2 || deadbeef->junk_apev2_read_full (NULL, &apev2, fp) != 0) {
+ deadbeef->junk_apev2_free (&apev2);
+ memset (&apev2, 0, sizeof (apev2));
+ }
+ // add all basic frames
+ for (int i = 0; md[i]; i += 3) {
+ if (md[i+2]) {
+ const char *val = deadbeef->pl_find_meta (it, md[i]);
+ if (val) {
+ deadbeef->junk_apev2_remove_frames (&apev2, md[i+2]);
+ deadbeef->junk_apev2_add_text_frame (&apev2, md[i+2], val);
+ }
+ }
+ }
+
+ // add tracknumber/totaltracks
+ const char *track = deadbeef->pl_find_meta (it, "track");
+ const char *totaltracks = deadbeef->pl_find_meta (it, "numtracks");
+ if (track && totaltracks) {
+ char s[100];
+ snprintf (s, sizeof (s), "%s/%s", track, totaltracks);
+ deadbeef->junk_apev2_remove_frames (&apev2, "Track");
+ deadbeef->junk_apev2_add_text_frame (&apev2, "Track", s);
+ }
+ else if (track) {
+ deadbeef->junk_apev2_remove_frames (&apev2, "Track");
+ deadbeef->junk_apev2_add_text_frame (&apev2, "Track", track);
+ }
+
+ // write tag
+ if (deadbeef->junk_apev2_write (out, &apev2, 0, 1) != 0) {
+ trace ("cmp3_write_metadata: failed to write apev2 tag to %s\n", it->fname)
+ goto error;
+ }
+ }
+
+ if (!write_id3v1 && !strip_id3v1 && id3v1_start != 0) {
+ if (deadbeef->fseek (fp, id3v1_start, SEEK_SET) == -1) {
+ trace ("cmp3_write_metadata: failed to seek to original id3v1 tag position in %s\n", it->fname);
+ goto error;
+ }
+ char buf[128];
+ if (deadbeef->fread (buf, 1, 128, fp) != 128) {
+ trace ("cmp3_write_metadata: failed to read original id3v1 tag from %s\n", it->fname);
+ goto error;
+ }
+ if (fwrite (buf, 1, 128, out) != 128) {
+ trace ("cmp3_write_metadata: failed to copy id3v1 tag from %s to temp file\n", it->fname);
+ goto error;
+ }
+ }
+ else if (write_id3v1) {
+ if (deadbeef->junk_id3v1_write (out, it) != 0) {
+ trace ("cmp3_write_metadata: failed to write id3v1 tag to %s\n", it->fname)
+ goto error;
+ }
+ }
err = 0;
error: