diff options
-rw-r--r-- | deadbeef.h | 2 | ||||
-rw-r--r-- | junklib.c | 72 | ||||
-rw-r--r-- | junklib.h | 6 | ||||
-rw-r--r-- | plugins.c | 2 | ||||
-rw-r--r-- | plugins/mpgmad/mpgmad.c | 190 |
5 files changed, 246 insertions, 26 deletions
@@ -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); @@ -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); @@ -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); @@ -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[] = { |