diff options
author | Alexey Yakovenko <wakeroid@gmail.com> | 2010-04-06 23:40:33 +0200 |
---|---|---|
committer | Alexey Yakovenko <wakeroid@gmail.com> | 2010-04-06 23:40:33 +0200 |
commit | 4cf0cf6185fffcd26ed4db15a832647d7409d86a (patch) | |
tree | 3d19eec29b216536ad09b2dd80b96a3dab75fc26 /junklib.c | |
parent | 9c6e2108adbcedccee5a2984b1d8ffc57e6b1087 (diff) |
moved high level id3v2/apev2/id3v1 tag writer into junklib;
added tag writer to wavpack plugin
Diffstat (limited to 'junklib.c')
-rw-r--r-- | junklib.c | 334 |
1 files changed, 334 insertions, 0 deletions
@@ -24,6 +24,7 @@ #include <limits.h> #include <errno.h> #include <ctype.h> +#include <unistd.h> #include "playlist.h" #include "utf8.h" #include "plugins.h" @@ -2778,3 +2779,336 @@ junk_recode (const char *in, int inlen, char *out, int outlen, const char *cs) { return junk_iconv (in, inlen, out, outlen, cs, UTF8); } +int +junk_rewrite_tags (playItem_t *it, uint32_t junk_flags, int id3v2_version, const char *id3v1_encoding) { + int err = -1; + char *buffer = NULL; + DB_FILE *fp = NULL; + FILE *out = NULL; + + // get options + int strip_id3v2 = junk_flags & JUNK_STRIP_ID3V2; + int strip_id3v1 = junk_flags & JUNK_STRIP_ID3V1; + int strip_apev2 = junk_flags & JUNK_STRIP_APEV2; + int write_id3v2 = junk_flags & JUNK_WRITE_ID3V2; + int write_id3v1 = junk_flags & JUNK_WRITE_ID3V1; + int write_apev2 = junk_flags & JUNK_WRITE_APEV2; + + // find the beginning and the end of audio data + fp = deadbeef->fopen (it->fname); + if (!fp) { + return -1; + } + + 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; + uint32_t flags, numitems; + int apev2_start = deadbeef->junk_apev2_find (fp, &apev2_size, &flags, &numitems); + if (apev2_start == -1) { + 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_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[] = { + "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", + "year", NULL, "Year", + "comment", NULL, "Comment", + "copyright", NULL, "Copyright", + "cuesheet", NULL, "Cuesheet", + NULL + }; + // "TRCK" -- special case + // "TYER"/"TDRC" -- special case + + // open output file + out = NULL; + char tmppath[PATH_MAX]; + snprintf (tmppath, sizeof (tmppath), "%s.temp", 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 (apev2)); + + 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_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; + } + // 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 basic frames + for (int i = 0; md[i]; i += 3) { + if (md[i+1]) { + const char *val = 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 = pl_find_meta (it, "track"); + const char *totaltracks = pl_find_meta (it, "numtracks"); + if (track && totaltracks) { + char s[100]; + snprintf (s, sizeof (s), "%s/%s", track, totaltracks); + junk_id3v2_remove_frames (&id3v2, "TRCK"); + add_frame (&id3v2, "TRCK", s); + } + else if (track) { + junk_id3v2_remove_frames (&id3v2, "TRCK"); + add_frame (&id3v2, "TRCK", track); + } + + // add year/date + const char *year = pl_find_meta (it, "year"); + if (year) { + // FIXME: format check + if (id3v2.version[0] == 3) { + junk_id3v2_remove_frames (&id3v2, "TYER"); + add_frame (&id3v2, "TYER", year); + } + else { + junk_id3v2_remove_frames (&id3v2, "TDRC"); + add_frame (&id3v2, "TDRC", year); + } + } + + // write tag + if (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 = fsize; + if (footer > 0) { + writesize -= (fsize - footer); + } + writesize -= header; + 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); + 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; + } + if (rb == 0) { + break; // eof + } + writesize -= rb; + } + + 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 || 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 = pl_find_meta (it, md[i]); + if (val) { + junk_apev2_remove_frames (&apev2, md[i+2]); + junk_apev2_add_text_frame (&apev2, md[i+2], val); + } + } + } + + // add tracknumber/totaltracks + const char *track = pl_find_meta (it, "track"); + const char *totaltracks = pl_find_meta (it, "numtracks"); + if (track && totaltracks) { + char s[100]; + snprintf (s, sizeof (s), "%s/%s", track, totaltracks); + junk_apev2_remove_frames (&apev2, "Track"); + junk_apev2_add_text_frame (&apev2, "Track", s); + } + else if (track) { + junk_apev2_remove_frames (&apev2, "Track"); + 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 (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: + if (fp) { + deadbeef->fclose (fp); + } + if (out) { + fclose (out); + } + if (buffer) { + free (buffer); + } + if (!err) { + rename (tmppath, it->fname); + } + else { + unlink (tmppath); + } + return err; +} + |