summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-03-31 13:53:25 +0200
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-03-31 13:53:25 +0200
commitd4f7499e49b40a449d04d771e1a360ac6b263648 (patch)
tree4907278a3d6823e51f8c2bd4b49f1c897082d41c
parentd528a3051f1f4f1fc9e24fb5442158b28341f750 (diff)
cleaned up id3v2 parser;
added support for multiple text frames of same type; improved (?) cp1251 detection
-rw-r--r--deadbeef.h1
-rw-r--r--junklib.c486
-rw-r--r--playlist.c25
-rw-r--r--playlist.h3
-rw-r--r--plugins.c1
5 files changed, 206 insertions, 310 deletions
diff --git a/deadbeef.h b/deadbeef.h
index d7434d02..61f5f0b9 100644
--- a/deadbeef.h
+++ b/deadbeef.h
@@ -380,6 +380,7 @@ typedef struct {
void (*pl_search_process) (const char *text);
// metainfo
void (*pl_add_meta) (DB_playItem_t *it, const char *key, const char *value);
+ void (*pl_append_meta) (DB_playItem_t *it, const char *key, const char *value);
// must be used from within explicit pl_lock/unlock block
const char *(*pl_find_meta) (DB_playItem_t *it, const char *key);
void (*pl_replace_meta) (DB_playItem_t *it, const char *key, const char *value);
diff --git a/junklib.c b/junklib.c
index 811df9c3..7ebadb90 100644
--- a/junklib.c
+++ b/junklib.c
@@ -33,6 +33,8 @@
#pragma GCC optimize("O0")
+#define MAX_TEXT_FRAME_SIZE 1024
+
#define UTF8 "utf-8"
#define trace(...) { fprintf(stderr, __VA_ARGS__); }
@@ -256,21 +258,29 @@ static int
can_be_russian (const signed char *str) {
int latin = 0;
int rus = 0;
+ int rus_in_row = 0;
+ int max_rus_row = 0;
for (; *str; str++) {
if ((*str >= 'A' && *str <= 'Z')
|| *str >= 'a' && *str <= 'z') {
+ if (rus_in_row > max_rus_row) {
+ max_rus_row = rus_in_row;
+ }
+ rus_in_row = 0;
latin++;
}
else if (*str < 0) {
+ rus_in_row++;
rus++;
}
}
- if (rus > latin/2) {
+ if (rus > latin/2 || (max_rus_row > 4)) {
return 1;
}
return 0;
}
+#if 0
static char *
convstr_id3v2_2to3 (const unsigned char* str, int sz) {
static char out[2048];
@@ -322,16 +332,17 @@ convstr_id3v2_2to3 (const unsigned char* str, int sz) {
}
return strdup (ret);
}
+#endif
static char *
-convstr_id3v2_4 (const unsigned char* str, int sz) {
+convstr_id3v2 (int version, const unsigned char* str, int sz) {
static char out[2048];
const char *enc = "iso8859-1";
char *ret = out;
// hack to add limited cp1251 recoding support
- if (*str == 3) {
+ if (version == 4 && *str == 3) {
// utf8
trace ("utf8\n");
strncpy (out, str+1, 2047);
@@ -339,14 +350,32 @@ convstr_id3v2_4 (const unsigned char* str, int sz) {
out[min (sz, 2047)] = 0;
return strdup (out);
}
- else if (*str == 1) {
- trace ("utf16\n");
- enc = "UTF-16";
- }
- else if (*str == 2) {
+ else if (version == 4 && *str == 2) {
trace ("utf16be\n");
enc = "UTF-16BE";
}
+ else if (*str == 1) {
+ if (version < 4) {
+ if (str[1] == 0xff && str[2] == 0xfe) {
+ enc = "UCS-2LE";
+ str += 2;
+ sz -= 2;
+ }
+ else if (str[2] == 0xff && str[1] == 0xfe) {
+ enc = "UCS-2BE";
+ str += 2;
+ sz -= 2;
+ }
+ else {
+ trace ("invalid ucs-2 signature %x %x\n", (int)str[1], (int)str[2]);
+ return NULL;
+ }
+ }
+ else {
+ trace ("utf16\n");
+ enc = "UTF-16";
+ }
+ }
#if 0
// NOTE: some dumb taggers put non-iso8859-1 text with enc=0
else if (*str == 0) {
@@ -530,13 +559,7 @@ junk_read_id3v1 (playItem_t *it, DB_FILE *fp) {
pl_add_meta (it, "track", s);
}
- char new_tags[100] = "";
- const char *tags = pl_find_meta (it, "tags");
- if (tags) {
- strcpy (new_tags, tags);
- }
- strcat (new_tags, "ID3v1 ");
- pl_replace_meta (it, "tags", new_tags);
+ pl_append_meta (it, "tags", "ID3v1");
// FIXME: that should be accounted for
// if (it->endoffset < 128) {
// it->endoffset = 128;
@@ -583,13 +606,8 @@ junk_read_ape (playItem_t *it, DB_FILE *fp) {
uint32_t flags = extract_i32_le (&header[20]);
trace ("APEv%d, size=%d, items=%d, flags=%x\n", version, size, numitems, flags);
- char new_tags[100] = "";
- const char *tags = pl_find_meta (it, "tags");
- if (tags) {
- strcpy (new_tags, tags);
- }
- strcat (new_tags, "APEv2 ");
- pl_replace_meta (it, "tags", new_tags);
+ pl_append_meta (it, "tags", "APEv2");
+
// now seek to beginning of the tag (exluding header)
if (deadbeef->fseek (fp, -size, SEEK_CUR) == -1) {
trace ("failed to seek to tag start (-%d)\n", size);
@@ -950,6 +968,7 @@ junk_id3v2_add_text_frame_24 (DB_id3v2_tag_t *tag, const char *frame_id, const c
return f;
}
+
// [+] TODO: remove all unsync bytes during conversion, where appropriate
// TODO: some non-Txxx frames might still need charset conversion
// TODO: 2.4 TDTG frame (tagging time) should not be converted, but might be useful to create it
@@ -1078,7 +1097,7 @@ junk_id3v2_convert_24_to_23 (DB_id3v2_tag_t *tag24, DB_id3v2_tag_t *tag23) {
unsync = 1;
}
id3v2_string_read (4, str, f24->size, unsync, f24->data);
- char *decoded = convstr_id3v2_4 (str, f24->size);
+ char *decoded = convstr_id3v2 (4, str, f24->size);
if (!decoded) {
trace ("junk_id3v2_convert_24_to_23: failed to decode text frame %s\n", f24->id);
continue; // failed, discard it
@@ -1268,7 +1287,7 @@ junk_id3v2_convert_23_to_24 (DB_id3v2_tag_t *tag23, DB_id3v2_tag_t *tag24) {
unsync = 1;
}
id3v2_string_read (4, str, f23->size, unsync, f23->data);
- char *decoded = convstr_id3v2_2to3 (str, f23->size);
+ char *decoded = convstr_id3v2 (3, str, f23->size);
if (!decoded) {
trace ("junk_id3v2_convert_23_to_24: failed to decode text frame %s\n", f23->id);
continue; // failed, discard it
@@ -1470,7 +1489,7 @@ junk_id3v2_convert_22_to_24 (DB_id3v2_tag_t *tag22, DB_id3v2_tag_t *tag24) {
unsync = 1;
}
id3v2_string_read (4, str, f22->size, unsync, f22->data);
- char *decoded = convstr_id3v2_2to3 (str, f22->size);
+ char *decoded = convstr_id3v2 (2, str, f22->size);
if (!decoded) {
trace ("junk_id3v2_convert_23_to_24: failed to decode text frame %s\n", f22->id);
continue; // failed, discard it
@@ -1690,6 +1709,18 @@ junklib_id3v2_sync_frame (uint8_t *data, int size) {
return written;
}
+char *
+junk_append_meta (const char *old, const char *new) {
+ int sz = strlen (old) + strlen (new) + 2;
+ char *appended = malloc (sz);
+ if (!appended) {
+ trace ("junk_append_meta: failed to allocate %d bytes\n");
+ return NULL;
+ }
+ snprintf (appended, sz, "%s\n%s", old, new);
+ return appended;
+}
+
int
junk_read_id3v2_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
DB_id3v2_frame_t *tail = NULL;
@@ -1776,13 +1807,6 @@ junk_read_id3v2_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
readptr += 4; // skip crc
#endif
}
- char * (*convstr)(const unsigned char *, int);
- if (version_major == 3) {
- convstr = convstr_id3v2_2to3;
- }
- else {
- convstr = convstr_id3v2_4;
- }
char *artist = NULL;
char *album = NULL;
char *band = NULL;
@@ -1925,112 +1949,39 @@ junk_read_id3v2_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
sz--;
}
}
- if (!strcmp (frameid, "TPE1")) {
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- artist = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TPE2")) {
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- band = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TRCK")) {
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
-
- track = convstr (str, sz);
- if (track) {
- char *slash = strchr (track, '/');
- if (slash) {
- // split into track/number
- *slash = 0;
- slash++;
- numtracks = strdup (slash);
+
+ // parse basic 2.3/2.4 text frames
+ const char *text_frames[] = { "TPE1", "TPE2", "TPOS", "TIT2", "TALB", "TCOP", "TCON", "TENC", "TPE3", "TCOM", "TRCK", "TYER", "TDRC", NULL };
+ char **text_holders[] = { &artist, &band, &disc, &title, &album, &copyright, &genre, &vendor, &performer, &composer, &track, version_major == 3 ? &year : NULL, version_major == 4 ? &year : NULL, };
+ int f = 0;
+ for (f = 0; text_frames[f]; f++) {
+ if (!strcmp (frameid, text_frames[f])) {
+ if (sz > MAX_TEXT_FRAME_SIZE) {
+ trace ("frame %s is too big, discard\n", frameid);
+ break;
}
+ char str[sz + 2];
+ id3v2_string_read (version_major, str, sz, unsync, readptr);
+ char *text = convstr_id3v2 (version_major, str, sz);
+ if (text && text_holders[f]) {
+ if (*text_holders[f]) {
+ // append
+ char *new = junk_append_meta (*text_holders[f], text);
+ if (new) {
+ free (*text_holders[f]);
+ *text_holders[f] = new;
+ }
+ free (text);
+ }
+ else {
+ *text_holders[f] = text;
+ }
+ }
+ break;
}
}
- else if (!strcmp (frameid, "TPOS")) {
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- disc = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TIT2")) {
- trace ("parsing TIT2...\n");
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- trace ("parsing TIT2....\n");
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- title = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TALB")) {
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- album = convstr (str, sz);
- }
- else if (version_major == 3 && !strcmp (frameid, "TYER")) { // this frame is 2.3-only
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- year = convstr (str, sz);
- }
- else if (version_major == 4 && !strcmp (frameid, "TDRC")) { // this frame is 2.3-only
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- year = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TCOP")) {
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- copyright = convstr (str, sz);
- trace ("TCOP: %s\n", copyright);
- }
- else if (!strcmp (frameid, "TCON")) {
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- genre = convstr (str, sz);
- trace ("TCON: %s\n", genre);
- }
- else if (!strcmp (frameid, "COMM")) {
+ if (!strcmp (frameid, "COMM")) {
if (sz < 4) {
trace ("COMM frame is too short, skipped\n");
readptr += sz; // bad tag
@@ -2052,7 +2003,7 @@ junk_read_id3v2_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
char str[s + 3];
id3v2_string_read (version_major, &str[1], s, unsync, descr+dlen);
str[0] = enc;
- char *text = convstr (str, s+1);
+ char *text = convstr_id3v2 (version_major, str, s+1);
int len = (comment ? (strlen (comment) + 1) : 0) + strlen (descr) + strlen (text) + 3;
char *newcomment = malloc (len);
@@ -2069,36 +2020,6 @@ junk_read_id3v2_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
comment = newcomment;
trace ("COMM text: %s\n", text);
}
- else if (!strcmp (frameid, "TENC")) {
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- vendor = convstr (str, sz);
- trace ("TENC: %s\n", vendor);
- }
- else if (!strcmp (frameid, "TPE3")) {
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- performer = convstr (str, sz);
- trace ("TPE3: %s\n", performer);
- }
- else if (!strcmp (frameid, "TCOM")) {
- if (sz > 1000) {
- err = 1;
- break; // too large
- }
- char str[sz+2];
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- composer = convstr (str, sz);
- trace ("TCOM: %s\n", composer);
- }
else if (!strcmp (frameid, "TXXX")) {
if (sz < 2) {
trace ("TXXX frame is too short, skipped\n");
@@ -2180,144 +2101,40 @@ junk_read_id3v2_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
frm->size = sz;
}
// trace ("found id3v2.2 frame: %s, size=%d\n", frameid, sz);
- if (!strcmp (frameid, "TEN")) {
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- vendor = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TT2")) {
- if (sz > 1000) {
- readptr += sz;
- continue;
- }
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- title = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TAL")) {
- if (sz > 1000) {
- readptr += sz;
- continue;
- }
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- album = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TP1")) {
- if (sz > 1000) {
- readptr += sz;
- continue;
- }
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- artist = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TP2")) {
- if (sz > 1000) {
- readptr += sz;
- continue;
- }
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- band = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TP3")) {
- if (sz > 1000) {
- readptr += sz;
- continue;
- }
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- performer = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TCM")) {
- if (sz > 1000) {
- readptr += sz;
- continue;
- }
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- composer = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TPA")) {
- if (sz > 1000) {
- readptr += sz;
- continue;
- }
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- disc = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TRK")) {
- if (sz > 1000) {
- readptr += sz;
- continue;
- }
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- track = convstr (str, sz);
- if (track) {
- char *slash = strchr (track, '/');
- if (slash) {
- // split into track/number
- *slash = 0;
- slash++;
- numtracks = strdup (slash);
+
+ // parse basic 2.2 text frames
+ const char *text_frames[] = { "TEN", "TT2", "TAL", "TP1", "TP2", "TP3", "TCM", "TPA", "TRK", "TYE", "TCR", "TCO", NULL };
+ char **text_holders[] = { &vendor, &title, &album, &artist, &band, &performer, &composer, &disc, &track, &year, &copyright, &genre, };
+ int f = 0;
+ for (f = 0; text_frames[f]; f++) {
+ if (!strcmp (frameid, text_frames[f])) {
+ if (sz > MAX_TEXT_FRAME_SIZE) {
+ trace ("frame %s is too big, discard\n", frameid);
+ break;
}
+ char str[sz + 2];
+ id3v2_string_read (version_major, str, sz, unsync, readptr);
+ str[sz] = 0;
+ char *text = convstr_id3v2 (version_major, str, sz);
+ if (text && text_holders[f]) {
+ if (*text_holders[f]) {
+ // append
+ char *new = junk_append_meta (*text_holders[f], text);
+ if (new) {
+ free (*text_holders[f]);
+ *text_holders[f] = new;
+ }
+ free (text);
+ }
+ else {
+ *text_holders[f] = text;
+ }
+ }
+ break;
}
}
- else if (!strcmp (frameid, "TYE")) {
- if (sz > 1000) {
- readptr += sz;
- continue;
- }
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- year = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TCR")) {
- if (sz > 1000) {
- readptr += sz;
- continue;
- }
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- copyright = convstr (str, sz);
- }
- else if (!strcmp (frameid, "TCO")) {
- if (sz > 1000) {
- readptr += sz;
- continue;
- }
- char str[sz+2];
- //memcpy (str, readptr, sz);
- id3v2_string_read (version_major, &str[0], sz, unsync, readptr);
- str[sz] = 0;
- genre = convstr (str, sz);
- }
- else if (!strcmp (frameid, "COM")) {
+
+ if (!strcmp (frameid, "COM")) {
if (sz > 1000) {
readptr += sz;
continue;
@@ -2338,7 +2155,7 @@ junk_read_id3v2_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
char str[s + 3];
id3v2_string_read (version_major, &str[1], s, unsync, descr+dlen);
str[0] = enc;
- char *text = convstr (str, s+1);
+ char *text = convstr_id3v2 (version_major, str, s+1);
int len = (comment ? (strlen (comment) + 1) : 0) + strlen (descr) + strlen (text) + 3;
char *newcomment = malloc (len);
@@ -2355,6 +2172,50 @@ junk_read_id3v2_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
comment = newcomment;
trace ("COM text: %s\n", text);
}
+ else if (!strcmp (frameid, "TXX")) {
+ if (sz < 2) {
+ trace ("TXX frame is too short, skipped\n");
+ readptr += sz; // bad tag
+ continue;
+ }
+ uint8_t *p = readptr;
+ uint8_t encoding = *p;
+ p++;
+ uint8_t *desc = p;
+ int desc_sz = 0;
+ while (*p && p - readptr < sz) {
+ p++;
+ desc_sz++;
+ }
+ p++;
+ if (p - readptr >= sz) {
+ trace ("bad TXXX frame, skipped\n");
+ readptr += sz; // bad tag
+ continue;
+ }
+ char desc_s[desc_sz+2];
+ id3v2_string_read (version_major, desc_s, desc_sz, unsync, desc);
+ //trace ("desc=%s\n", desc_s);
+ char value_s[readptr+sz-p+2];
+ id3v2_string_read (version_major, value_s, readptr+sz-p, unsync, p);
+ //trace ("value=%s\n", value_s);
+ if (!strcasecmp (desc_s, "replaygain_album_gain")) {
+ it->replaygain_album_gain = atof (value_s);
+ trace ("%s=%s (%f)\n", desc_s, value_s, it->replaygain_album_gain);
+ }
+ else if (!strcasecmp (desc_s, "replaygain_album_peak")) {
+ it->replaygain_album_peak = atof (value_s);
+ trace ("%s=%s (%f)\n", desc_s, value_s, it->replaygain_album_peak);
+ }
+ else if (!strcasecmp (desc_s, "replaygain_track_gain")) {
+ it->replaygain_track_gain = atof (value_s);
+ trace ("%s=%s (%f)\n", desc_s, value_s, it->replaygain_track_gain);
+ }
+ else if (!strcasecmp (desc_s, "replaygain_track_peak")) {
+ it->replaygain_track_peak = atof (value_s);
+ trace ("%s=%s (%f)\n", desc_s, value_s, it->replaygain_track_peak);
+ }
+ }
readptr += sz;
}
else {
@@ -2383,6 +2244,15 @@ junk_read_id3v2_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
free (composer);
}
if (track) {
+ if (!numtracks) {
+ char *slash = strchr (track, '/');
+ if (slash) {
+ // split into track/number
+ *slash = 0;
+ slash++;
+ numtracks = strdup (slash);
+ }
+ }
pl_add_meta (it, "track", track);
free (track);
}
@@ -2466,21 +2336,17 @@ junk_read_id3v2_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
pl_add_meta (it, "disc", disc);
free (disc);
}
- char new_tags[100] = "";
- const char *tags = pl_find_meta (it, "tags");
- if (tags) {
- strcpy (new_tags, tags);
- }
+
if (version_major == 2) {
- strcat (new_tags, "ID3v2.2 ");
+ pl_append_meta (it, "tags", "ID3v2.2");
}
else if (version_major == 3) {
- strcat (new_tags, "ID3v2.3 ");
+ pl_append_meta (it, "tags", "ID3v2.3");
}
else if (version_major == 4) {
- strcat (new_tags, "ID3v2.4 ");
+ pl_append_meta (it, "tags", "ID3v2.4");
}
- pl_replace_meta (it, "tags", new_tags);
+
if (!title) {
pl_add_meta (it, "title", NULL);
}
diff --git a/playlist.c b/playlist.c
index ebeb1a14..6ad528a0 100644
--- a/playlist.c
+++ b/playlist.c
@@ -1441,6 +1441,20 @@ pl_add_meta (playItem_t *it, const char *key, const char *value) {
}
void
+pl_append_meta (playItem_t *it, const char *key, const char *value) {
+ const char *old = pl_find_meta (it, key);
+ if (!old) {
+ pl_add_meta (it, key, value);
+ }
+ else {
+ int sz = strlen (old) + strlen (value) + 2;
+ char str[sz];
+ snprintf (str, sz, "%s\n%s", old, value);
+ pl_replace_meta (it, key, str);
+ }
+}
+
+void
pl_replace_meta (playItem_t *it, const char *key, const char *value) {
LOCK;
// check if it's already set
@@ -2135,6 +2149,8 @@ pl_format_title (playItem_t *it, int idx, char *s, int size, int id, const char
const char *copyright = NULL;
const char *filename = NULL;
+ char *ss = s;
+
LOCK;
if (id != -1) {
const char *text = NULL;
@@ -2279,6 +2295,15 @@ pl_format_title (playItem_t *it, int idx, char *s, int size, int id, const char
*s = 0;
UNLOCK;
+
+ // replace all \n with ;
+ while (*ss) {
+ if (*ss == '\n') {
+ *ss = ';';
+ }
+ ss++;
+ }
+
return size - n - 1;
}
diff --git a/playlist.h b/playlist.h
index d1023295..9651e148 100644
--- a/playlist.h
+++ b/playlist.h
@@ -178,6 +178,9 @@ pl_insert_cue (playItem_t *after, playItem_t *origin, int numsamples, int sample
void
pl_add_meta (playItem_t *it, const char *key, const char *value);
+void
+pl_append_meta (playItem_t *it, const char *key, const char *value);
+
// must be used in explicit pl_lock/unlock block
// that makes it possible to avoid copying metadata on every access
const char *
diff --git a/plugins.c b/plugins.c
index 48f7195b..86faf747 100644
--- a/plugins.c
+++ b/plugins.c
@@ -158,6 +158,7 @@ static DB_functions_t deadbeef_api = {
.pl_search_process = pl_search_process,
// metainfo
.pl_add_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_add_meta,
+ .pl_append_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_append_meta,
.pl_find_meta = (const char *(*) (DB_playItem_t *, const char *))pl_find_meta,
.pl_replace_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_replace_meta,
.pl_delete_all_meta = (void (*) (DB_playItem_t *it))pl_delete_all_meta,