summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--deadbeef.h2
-rw-r--r--junklib.c72
-rw-r--r--junklib.h6
-rw-r--r--plugins.c2
-rw-r--r--plugins/mpgmad/mpgmad.c190
5 files changed, 246 insertions, 26 deletions
diff --git a/deadbeef.h b/deadbeef.h
index 484f0ef9..2c45b45f 100644
--- a/deadbeef.h
+++ b/deadbeef.h
@@ -422,6 +422,7 @@ typedef struct {
float (*volume_get_min_db) (void);
// junk reading/writing
int (*junk_id3v1_read) (DB_playItem_t *it, DB_FILE *fp);
+ int (*junk_id3v1_find) (DB_FILE *fp);
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);
@@ -433,6 +434,7 @@ 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_find) (DB_FILE *fp, int32_t *psize, uint32_t *pflags, uint32_t *pnumitems);
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 9252570f..631a2a31 100644
--- a/junklib.c
+++ b/junklib.c
@@ -573,21 +573,36 @@ junk_id3v1_read (playItem_t *it, DB_FILE *fp) {
}
int
-junk_apev2_find (FILE *fp, int32_t *psize, uint32_t *pflags, uint32_t *pnumitems) {
+junk_id3v1_find (DB_FILE *fp) {
+ uint8_t buffer[3];
+ if (deadbeef->fseek (fp, -128, SEEK_END) == -1) {
+ return -1;
+ }
+ if (deadbeef->fread (buffer, 1, 3, fp) != 3) {
+ return -1;
+ }
+ if (memcmp (buffer, "TAG", 3)) {
+ return -1; // no tag
+ }
+ return 128;
+}
+
+int
+junk_apev2_find (DB_FILE *fp, int32_t *psize, uint32_t *pflags, uint32_t *pnumitems) {
uint8_t header[32];
- if (fseek (fp, -32, SEEK_END) == -1) {
+ if (deadbeef->fseek (fp, -32, SEEK_END) == -1) {
return -1; // something bad happened
}
- if (fread (header, 1, 32, fp) != 32) {
+ if (deadbeef->fread (header, 1, 32, fp) != 32) {
return -1; // something bad happened
}
if (strncmp (header, "APETAGEX", 8)) {
// try to skip 128 bytes backwards (id3v1)
- if (fseek (fp, -128-32, SEEK_END) == -1) {
+ if (deadbeef->fseek (fp, -128-32, SEEK_END) == -1) {
return -1; // something bad happened
}
- if (fread (header, 1, 32, fp) != 32) {
+ if (deadbeef->fread (header, 1, 32, fp) != 32) {
return -1; // something bad happened
}
if (strncmp (header, "APETAGEX", 8)) {
@@ -602,30 +617,35 @@ junk_apev2_find (FILE *fp, int32_t *psize, uint32_t *pflags, uint32_t *pnumitems
trace ("APEv%d, size=%d, items=%d, flags=%x\n", version, size, numitems, flags);
- // seek to beginning of the tag
- if (fseek (fp, -size, SEEK_CUR) == -1) {
+ // size contains footer, but not header, so add header size
+ if (flags & (1<<31)) {
+ size += 32;
+ }
+
+ // seek to beginning of the tag/header
+ if (deadbeef->fseek (fp, -size, SEEK_CUR) == -1) {
trace ("failed to seek to tag start (-%d)\n", size);
return -1;
}
- *psize = size-32;
+ *psize = size;
*pflags = flags;
*pnumitems = numitems;
- return ftell (fp);
+ return deadbeef->ftell (fp);
}
int
-junk_find_id3v1 (FILE *fp) {
- if (fseek (fp, -128, SEEK_END) == -1) {
+junk_find_id3v1 (DB_FILE *fp) {
+ if (deadbeef->fseek (fp, -128, SEEK_END) == -1) {
return -1;
}
char buffer[3];
- if (fread (buffer, 1, 3, fp) != 3) {
+ if (deadbeef->fread (buffer, 1, 3, fp) != 3) {
return -1;
}
if (memcmp (buffer, "TAG", 3)) {
return -1; // no tag
}
- return ftell (fp) - 3;
+ return deadbeef->ftell (fp) - 3;
}
int
@@ -751,6 +771,13 @@ junk_apev2_read_full (playItem_t *it, DB_apev2_tag_t *tag_store, DB_FILE *fp) {
pl_add_meta (it, "album", value);
}
else if (!strcasecmp (key, "track")) {
+ char *slash = strchr (value, '/');
+ if (slash) {
+ // split into track/number
+ *slash = 0;
+ slash++;
+ pl_add_meta (it, "numtracks", slash);
+ }
pl_add_meta (it, "track", value);
}
else if (!strcasecmp (key, "year")) {
@@ -2140,7 +2167,6 @@ junk_id3v2_read_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) {
char *genre = NULL;
char *performer = NULL;
char *composer = NULL;
- char *numtracks = NULL;
char *disc = NULL;
int err = 0;
while (readptr - tag <= size - 4) {
@@ -2592,22 +2618,16 @@ junk_id3v2_read_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);
- }
+ char *slash = strchr (track, '/');
+ if (slash) {
+ // split into track/number
+ *slash = 0;
+ slash++;
+ pl_add_meta (it, "numtracks", slash);
}
pl_add_meta (it, "track", track);
free (track);
}
- if (numtracks) {
- pl_add_meta (it, "numtracks", numtracks);
- free (numtracks);
- }
if (title) {
pl_add_meta (it, "title", title);
free (title);
diff --git a/junklib.h b/junklib.h
index a3a8884c..6f745dfc 100644
--- a/junklib.h
+++ b/junklib.h
@@ -27,6 +27,9 @@ int
junk_id3v1_read (struct playItem_s *it, DB_FILE *fp);
int
+junk_id3v1_find (DB_FILE *fp);
+
+int
junk_id3v2_read_full (struct playItem_s *it, DB_id3v2_tag_t *tag, DB_FILE *fp);
int
@@ -65,6 +68,9 @@ junk_apev2_read_full (struct playItem_s *it, DB_apev2_tag_t *tag_store, DB_FILE
int
junk_apev2_read (struct playItem_s *it, DB_FILE *fp);
+int
+junk_apev2_find (DB_FILE *fp, int32_t *psize, uint32_t *pflags, uint32_t *pnumitems);
+
DB_apev2_frame_t *
junk_apev2_add_text_frame (DB_apev2_tag_t *tag, const char *frame_id, const char *value);
diff --git a/plugins.c b/plugins.c
index f28bb135..4311a2b8 100644
--- a/plugins.c
+++ b/plugins.c
@@ -180,6 +180,7 @@ static DB_functions_t deadbeef_api = {
.volume_get_min_db = volume_get_min_db,
// junk reading
.junk_id3v1_read = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_id3v1_read,
+ .junk_id3v1_find = junk_id3v1_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,
@@ -191,6 +192,7 @@ 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_find = junk_apev2_find,
.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 982ccdd0..a109985c 100644
--- a/plugins/mpgmad/mpgmad.c
+++ b/plugins/mpgmad/mpgmad.c
@@ -20,6 +20,8 @@
#include <mad.h>
#include <assert.h>
#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
#include "../../deadbeef.h"
#define trace(...) { fprintf(stderr, __VA_ARGS__); }
@@ -1168,6 +1170,11 @@ cmp3_insert (DB_playItem_t *after, const char *fname) {
int
cmp3_write_metadata (DB_playItem_t *it) {
+ int err = -1;
+ char *buffer = NULL;
+ DB_FILE *fp = NULL;
+ FILE *out = NULL;
+
// get options
int strip_id3v2 = deadbeef->conf_get_int ("mp3.strip_id3v2", 0);
int strip_id3v1 = deadbeef->conf_get_int ("mp3.strip_id3v2", 0);
@@ -1176,7 +1183,190 @@ cmp3_write_metadata (DB_playItem_t *it) {
int write_id3v1 = deadbeef->conf_get_int ("mp3.write_id3v1", 0);
int write_apev2 = deadbeef->conf_get_int ("mp3.write_apev2", 1);
int id3v2_version = deadbeef->conf_get_int ("mp3.id3v2_version", 3);
+ if (id3v2_version != 3 && id3v2_version != 4) {
+ id3v2_version = 3;
+ }
const char *id3v1_encoding = deadbeef->conf_get_str ("mp3.id3v1_encoding", "iso8859-1");
+
+ // find the beginning and the end of audio data
+ fp = deadbeef->fopen (it->fname);
+ if (!fp) {
+ return -1;
+ }
+
+ int id3v2_end = deadbeef->junk_get_leading_size (fp);
+ if (id3v2_end == -1) {
+ id3v2_end = 0;
+ }
+
+ int32_t apev2_size;
+ uint32_t flags, numitems;
+ int apev2_start = deadbeef->junk_apev2_find (fp, &apev2_size, &flags, &numitems);
+ if (apev2_start == -1) {
+ 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);
+
+ // mapping between ddb metadata names and id3v2/apev2 names
+ const char *md[] = {
+ "artist", "TPE1", "Artist",
+ "band", "TPE2", NULL,
+ "disc", "TPOS", "Media",
+ "title", "TIT2", "Title",
+ "album", "TALB", "Album",
+ "copyright", "TCOP", "Copyright",
+ "genre", "TCON", "Genre",
+ "vendor", "TENC", NULL,
+ "performer", "TPE3", NULL,
+ "composer", "TCOM", "Composer",
+ NULL
+ };
+ // "TRCK" -- special case
+ // "TYER"/"TDRC" -- special case
+
+ // open output file
+ out = NULL;
+ char tmppath[PATH_MAX];
+ snprintf (tmppath, sizeof (tmppath), "%s.temp.mp3", it->fname);
+
+ out = fopen (tmppath, "w+b");
+ trace ("will write tags into %s\n", tmppath);
+ if (!out) {
+ fprintf (stderr, "cmp3_write_metadata: failed to open temp file %s\n", tmppath);
+ goto error;
+ }
+
+ DB_id3v2_tag_t id3v2;
+ DB_apev2_tag_t apev2;
+
+ memset (&id3v2, 0, sizeof (id3v2));
+ memset (&apev2, 0, sizeof (id3v2));
+
+ if (!strip_id3v2 && !write_id3v2) {
+ id3v2_end = 0;
+ }
+ 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);
+ deadbeef->junk_id3v2_free (&id3v2);
+ memset (&id3v2, 0, sizeof (id3v2));
+ id3v2.version[0] = id3v2_version;
+ }
+ // convert to required version
+ while (id3v2.version[0] != id3v2_version) {
+ DB_id3v2_tag_t converted;
+ memset (&converted, 0, sizeof (converted));
+ if (id3v2.version[0] == 2) {
+ if (deadbeef->junk_id3v2_convert_22_to_24 (&id3v2, &converted) != 0) {
+ goto error;
+ }
+ deadbeef->junk_id3v2_free (&id3v2);
+ memcpy (&id3v2, &converted, sizeof (DB_id3v2_tag_t));
+ continue;
+ }
+ else if (id3v2.version[0] == 3) {
+ if (deadbeef->junk_id3v2_convert_23_to_24 (&id3v2, &converted) != 0) {
+ goto error;
+ }
+ deadbeef->junk_id3v2_free (&id3v2);
+ memcpy (&id3v2, &converted, sizeof (DB_id3v2_tag_t));
+ continue;
+ }
+ else if (id3v2.version[0] == 4) {
+ if (deadbeef->junk_id3v2_convert_24_to_23 (&id3v2, &converted) != 0) {
+ goto error;
+ }
+ deadbeef->junk_id3v2_free (&id3v2);
+ memcpy (&id3v2, &converted, sizeof (DB_id3v2_tag_t));
+ continue;
+ }
+ }
+
+ DB_id3v2_frame_t *(*add_frame) (DB_id3v2_tag_t *tag, const char *frame_id, const char *value);
+ if (id3v2_version == 3) {
+ add_frame = deadbeef->junk_id3v2_add_text_frame_23;
+ }
+ else {
+ add_frame = deadbeef->junk_id3v2_add_text_frame_24;
+ }
+
+ // add all known 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) {
+ add_frame (&id3v2, md[i+1], val);
+ }
+ }
+ }
+ 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);
+ add_frame (&id3v2, "TRCK", s);
+ }
+ else if (track) {
+ add_frame (&id3v2, "TRCK", track);
+ }
+
+ // write tag
+ if (deadbeef->junk_id3v2_write (out, &id3v2) != 0) {
+ trace ("cmp3_write_metadata: failed to write id3v2 tag to %s\n", it->fname)
+ goto error;
+ }
+ }
+
+ // now write audio data
+ buffer = malloc (8192);
+ deadbeef->fseek (fp, header, SEEK_SET);
+ int writesize = deadbeef->fgetlength (fp);
+ if (footer > 0) {
+ writesize -= (writesize - footer);
+ }
+ writesize -= header;
+ trace ("writesize: %d\n", writesize);
+
+ while (writesize > 0) {
+ int rb = min (8192, writesize);
+ rb = deadbeef->fread (buffer, 1, rb, fp);
+ if (rb < 0) {
+ fprintf (stderr, "junk_write_id3v2: error reading input data\n");
+ goto error;
+ }
+ if (fwrite (buffer, 1, rb, out) != rb) {
+ 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
+
+ err = 0;
+error:
+ if (fp) {
+ deadbeef->fclose (fp);
+ }
+ if (out) {
+ fclose (out);
+ }
+ if (buffer) {
+ free (buffer);
+ }
+// unlink (tmppath);
+ return err;
}
static const char *exts[] = {