summaryrefslogtreecommitdiff
path: root/junklib.c
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-04-06 23:40:33 +0200
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-04-06 23:40:33 +0200
commit4cf0cf6185fffcd26ed4db15a832647d7409d86a (patch)
tree3d19eec29b216536ad09b2dd80b96a3dab75fc26 /junklib.c
parent9c6e2108adbcedccee5a2984b1d8ffc57e6b1087 (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.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/junklib.c b/junklib.c
index 887e243b..698ba8d5 100644
--- a/junklib.c
+++ b/junklib.c
@@ -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;
+}
+