[easytag/wip/dsf-support: 1/2] Write ID3v2.3 tags to DSF files
- From: David King <davidk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [easytag/wip/dsf-support: 1/2] Write ID3v2.3 tags to DSF files
- Date: Wed, 19 Nov 2014 18:48:02 +0000 (UTC)
commit e75fd2eb72ebfba19c4c7091e9eefcabd9e57847
Author: David King <amigadave amigadave com>
Date: Thu Nov 13 16:59:45 2014 +0000
Write ID3v2.3 tags to DSF files
https://bugzilla.gnome.org/show_bug.cgi?id=708368
src/tags/dsf_tag.c | 449 ++++++++++++++++++++++++++++++-----------
src/tags/id3_tag.c | 235 ++++++++++-----------
src/tags/id3_tag.h | 6 +
src/tags/id3lib/c_wrapper.cpp | 24 +++
src/tags/id3lib/id3_bugfix.h | 2 +
5 files changed, 473 insertions(+), 243 deletions(-)
---
diff --git a/src/tags/dsf_tag.c b/src/tags/dsf_tag.c
index c50fb2b..30b327c 100644
--- a/src/tags/dsf_tag.c
+++ b/src/tags/dsf_tag.c
@@ -33,6 +33,11 @@
#include <id3tag.h>
#include "id3_tag.h"
+#ifdef ENABLE_ID3LIB
+#include <id3.h>
+#include "id3lib/id3_bugfix.h"
+#endif
+
#define DSF_METADATA_CHUNK_OFFSET 20
/* Read the little-endian 64-bit integer from the next 8 characters. */
@@ -183,7 +188,6 @@ et_dsf_tag_read_file_tag (GFile *file,
unsigned version = id3_tag_version (tag);
#ifdef ENABLE_ID3LIB
-#if 0 /* Disable tag downgrading from 2.4 until id3lib render is supported. */
/* Besides upgrading old tags, downgrade ID3v2.4 to ID3v2.3 */
if (g_settings_get_boolean (MainSettings, "id3v2-version-4"))
{
@@ -195,7 +199,6 @@ et_dsf_tag_read_file_tag (GFile *file,
| (ID3_TAG_VERSION_MAJOR (version) == 4));
}
#else
-#endif
update = (ID3_TAG_VERSION_MAJOR (version) < 4);
#endif
}
@@ -232,167 +235,200 @@ err:
return FALSE;
}
-gboolean
-et_dsf_tag_write_file_tag (const ET_File *ETFile,
- GError **error)
+#ifdef ENABLE_ID3LIB
+
+static gboolean
+et_dsf_tag_write_id3v23 (const File_Tag *FileTag,
+ const guchar *id3_buffer,
+ long tagsize,
+ GOutputStream *ostream,
+ GError **error)
{
- const File_Tag *FileTag;
- const gchar *filename;
- GFile *file;
- GFileIOStream *iostream;
- GInputStream *istream;
- GOutputStream *ostream;
- guchar header[DSF_HEADER_LENGTH];
- gsize bytes_read;
- guint64 size;
- guint64 id3_offset;
GSeekable *seekable;
- guchar id3_query[ID3_TAG_QUERYSIZE];
- long tagsize;
- struct id3_tag *tag;
+ goffset id3_offset;
+ ID3Tag *tag;
gboolean strip_tags = TRUE;
- long new_tagsize;
+ size_t new_tagsize;
guchar *new_tag_buffer;
gsize bytes_written;
- g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
- FileTag = (File_Tag *)ETFile->FileTag->data;
- filename = ((File_Name *)ETFile->FileNameCur->data)->value;
-
- file = g_file_new_for_path (filename);
- iostream = g_file_open_readwrite (file, NULL, error);
- g_object_unref (file);
-
- if (iostream == NULL)
- {
- return FALSE;
- }
+ /* TODO: Call id3tag_check_if_id3lib_is_buggy(). */
- istream = g_io_stream_get_input_stream (G_IO_STREAM (iostream));
+ seekable = G_SEEKABLE (ostream);
+ /* Offset of the ID3v2 tag is at the current stream position. */
+ id3_offset = g_seekable_tell (seekable);
- /* Read the complete header from the file. */
- if (!g_input_stream_read_all (istream, &header, DSF_HEADER_LENGTH,
- &bytes_read, NULL, error))
+ if ((tag = ID3Tag_New ()) == NULL)
{
- g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %d "
- "bytes were read", bytes_read, DSF_HEADER_LENGTH);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM, "%s",
+ g_strerror (ENOMEM));
goto err;
}
- if (memcmp (&header, DSF_HEADER_MAGIC, 4) != 0)
+ /* Copy the header out to a separate buffer, as id3lib expects the header
+ * and remainder of the tag to be processed separately. */
+ if (tagsize > ID3_TAGHEADERSIZE)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
- _("Not a DSF file"));
- goto err;
- }
-
- if (memcmp (&header[28], DSF_HEADER_FORMAT_MAGIC, 4) != 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
- _("Not a DSF file"));
- goto err;
- }
+ const guchar id3_header[ID3_TAGHEADERSIZE];
+ ID3_Err id3_err;
- /* 12 (8 bytes) total file size. */
- size = guint64_from_bytes (&header[12]);
+ memcpy (&id3_header, id3_buffer, ID3_TAGHEADERSIZE);
- /* 20 (8 bytes) metadata chunk offset, or 0 if no tag is present. */
- id3_offset = guint64_from_bytes (&header[DSF_METADATA_CHUNK_OFFSET]);
-
- if (id3_offset >= size)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
- _("Invalid DSF header"));
- goto err;
+ if ((id3_err = ID3Tag_Parse (tag, id3_header,
+ &id3_buffer[ID3_TAGHEADERSIZE]))
+ != ID3E_NoError)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+ Id3tag_Get_Error_Message (id3_err));
+ ID3Tag_Delete (tag);
+ goto err;
+ }
}
- seekable = G_SEEKABLE (istream);
-
- if (!g_seekable_can_seek (seekable))
- {
- g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_BADF, "%s",
- g_strerror (EBADF));
- goto err;
- }
+ ID3Tag_SetPadding (tag, TRUE);
- if (id3_offset == 0)
- {
- /* No ID3v2 tag. */
- id3_offset = size;
+ /* Fill the tag with the new values. */
+ et_id3tag_set_id3tag_from_file_tag (FileTag, tag, &strip_tags);
- tag = id3_tag_new ();
- tagsize = 0;
- }
- else
+ /* Truncate the file and update the metadata offset if the tags should be
+ * stripped. */
+ if (strip_tags)
{
- guchar *id3_buffer;
+ guchar metadata_offset[8] = { 0, };
- /* Read existing ID3v2 tag. */
+ ID3Tag_Delete (tag);
- /* Seek to the start of the metadata chunk. */
- if (!g_seekable_seek (seekable, id3_offset, G_SEEK_SET, NULL, error))
+ if (!g_seekable_can_truncate (seekable))
{
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_BADF, "%s",
+ g_strerror (EBADF));
goto err;
}
- if (!g_input_stream_read_all (istream, id3_query, ID3_TAG_QUERYSIZE,
- &bytes_read, NULL, error))
+ if (!g_seekable_truncate (seekable, id3_offset, NULL, error))
{
goto err;
}
- else if (bytes_read != ID3_TAG_QUERYSIZE)
+
+ if (!g_seekable_seek (seekable, DSF_METADATA_CHUNK_OFFSET, G_SEEK_SET,
+ NULL, error))
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
- _("Error reading tags from file"));
goto err;
}
- if ((tagsize = id3_tag_query ((id3_byte_t const *)id3_query,
- ID3_TAG_QUERYSIZE)) <= ID3_TAG_QUERYSIZE)
+ if (!g_output_stream_write_all (ostream, metadata_offset, 8,
+ &bytes_written, NULL, error))
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
- _("Error reading tags from file"));
+ goto err;
+ }
+ else if (bytes_written != 8)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+ _("Error writing tags to file"));
goto err;
}
- id3_buffer = g_malloc (tagsize);
- /* Copy the query buffer. */
- memcpy (id3_buffer, id3_query, ID3_TAG_QUERYSIZE);
+ return TRUE;
+ }
- if (!g_input_stream_read_all (istream, &id3_buffer[ID3_TAG_QUERYSIZE],
- tagsize - ID3_TAG_QUERYSIZE, &bytes_read,
- NULL, error))
+ new_tagsize = ID3Tag_Size (tag);
+
+ /* FIXME: This is probably a bug in id3lib, as a size of 0 should only be
+ * returned if there are no valid frames, and frames were added earlier in
+ * et_id3tag_set_id3tag_from_file_tag(). */
+ if (new_tagsize == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+ _("Error writing tags to file"));
+ ID3Tag_Delete (tag);
+ goto err;
+ }
+
+ /* FIXME: The return vlaue of ID3Tag_Size() should include the header and
+ * tag size, so this looks like an overallocation. However, Valgrind
+ * complains about reading into unallocated memory otherwise. */
+ new_tag_buffer = g_malloc0 (new_tagsize + ID3_TAGHEADERSIZE);
+ /* The call to ID3Tag_Size() gives an overestimate, but ID3Tag_Render()
+ * returns the real size of the tag. */
+ new_tagsize = ID3Tag_Render (tag, new_tag_buffer, ID3TT_ID3V2);
+
+ ID3Tag_Delete (tag);
+
+ /* Only truncate the file if the new tag is smaller than the old one. */
+ if (new_tagsize < tagsize)
+ {
+ if (!g_seekable_can_truncate (seekable))
{
- g_free (id3_buffer);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_BADF, "%s",
+ g_strerror (EBADF));
+ g_free (new_tag_buffer);
goto err;
}
- else if (bytes_read != tagsize - ID3_TAG_QUERYSIZE)
+
+ if (!g_seekable_truncate (seekable, id3_offset + new_tagsize, NULL,
+ error))
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
- _("Error reading tags from file"));
- g_free (id3_buffer);
+ g_free (new_tag_buffer);
goto err;
}
+ }
+
+ if (!g_output_stream_write_all (ostream, new_tag_buffer, new_tagsize,
+ &bytes_written, NULL, error))
+ {
+ g_free (new_tag_buffer);
+ goto err;
+ }
+ else if (bytes_written != new_tagsize)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+ _("Error writing tags to file"));
+ g_free (new_tag_buffer);
+ goto err;
+ }
+ g_free (new_tag_buffer);
+ return TRUE;
+
+err:
+ return FALSE;
+}
+
+#endif
+
+static gboolean
+et_dsf_tag_write_id3v24 (const File_Tag *FileTag,
+ const guchar *id3_buffer,
+ long tagsize,
+ GOutputStream *ostream,
+ GError **error)
+{
+ struct id3_tag *tag;
+ GSeekable *seekable;
+ gboolean strip_tags = TRUE;
+ gsize bytes_written;
+ guchar *new_tag_buffer;
+ long new_tagsize;
+ goffset id3_offset;
+
+ if (tagsize == 0)
+ {
+ /* No ID3v2 tag. */
+ tag = id3_tag_new ();
+ }
+ else
+ {
/* Create an empty tag if there was a problem reading the existing
* tag. */
if (!(tag = id3_tag_parse ((id3_byte_t const *)id3_buffer, tagsize)))
{
tag = id3_tag_new ();
}
-
- g_free (id3_buffer);
-
- /* TODO: Check for an empty tag? */
}
- /* No reading from this point forward. */
- g_input_stream_close (istream, NULL, NULL);
-
- ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
seekable = G_SEEKABLE (ostream);
+ /* Offset of the ID3v2 tag is at the current stream position. */
+ id3_offset = g_seekable_tell (seekable);
/* Set padding. */
if ((tag->paddedsize < 1024) || ((tag->paddedsize > 4096)
@@ -419,18 +455,9 @@ et_dsf_tag_write_file_tag (const ET_File *ETFile,
id3_tag_options (tag, ID3_TAG_OPTION_COMPRESSION, ~0);
}
- /* Seek to the start of the metadata chunk. */
- if (!g_seekable_seek (seekable, id3_offset, G_SEEK_SET, NULL, error))
- {
- goto err;
- }
-
/* Only set the IDv2 tag. */
et_id3tag_set_id3_tag_from_file_tag (FileTag, NULL, tag, &strip_tags);
- /* FIXME; For ID3v2.3 support, add ID3Tag_Render() to id3lib c_wrapper.cpp:
- * http://sourceforge.net/p/id3lib/patches/75/ */
-
/* Truncate the file and update the metadata offset if the tags should be
* stripped. */
if (strip_tags)
@@ -469,7 +496,6 @@ et_dsf_tag_write_file_tag (const ET_File *ETFile,
goto err;
}
- g_object_unref (iostream);
return TRUE;
}
@@ -521,6 +547,191 @@ et_dsf_tag_write_file_tag (const ET_File *ETFile,
}
g_free (new_tag_buffer);
+ return TRUE;
+
+err:
+ return FALSE;
+}
+
+gboolean
+et_dsf_tag_write_file_tag (const ET_File *ETFile,
+ GError **error)
+{
+ const File_Tag *FileTag;
+ const gchar *filename;
+ GFile *file;
+ GFileIOStream *iostream;
+ GInputStream *istream;
+ GOutputStream *ostream;
+ guchar header[DSF_HEADER_LENGTH];
+ gsize bytes_read;
+ guint64 size;
+ guint64 id3_offset;
+ GSeekable *seekable;
+ guchar id3_query[ID3_TAG_QUERYSIZE];
+ guchar *id3_buffer;
+ long tagsize;
+
+ g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ FileTag = (File_Tag *)ETFile->FileTag->data;
+ filename = ((File_Name *)ETFile->FileNameCur->data)->value;
+
+ file = g_file_new_for_path (filename);
+ iostream = g_file_open_readwrite (file, NULL, error);
+ g_object_unref (file);
+
+ if (!iostream)
+ {
+ return FALSE;
+ }
+
+ istream = g_io_stream_get_input_stream (G_IO_STREAM (iostream));
+
+ /* Read the complete header from the file. */
+ if (!g_input_stream_read_all (istream, &header, DSF_HEADER_LENGTH,
+ &bytes_read, NULL, error))
+ {
+ g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %d "
+ "bytes were read", bytes_read, DSF_HEADER_LENGTH);
+ goto err;
+ }
+
+ if (memcmp (&header, "DSD ", 4) != 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+ _("Not a DSF file"));
+ goto err;
+ }
+
+ if (memcmp (&header[28], "fmt ", 4) != 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+ _("Not a DSF file"));
+ goto err;
+ }
+
+ /* 12 (8 bytes) total file size. */
+ size = guint64_from_bytes (&header[12]);
+
+ /* 20 (8 bytes) metadata chunk offset, or 0 if no tag is present. */
+ id3_offset = guint64_from_bytes (&header[20]);
+
+ if (id3_offset >= size)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+ _("Invalid DSF header"));
+ goto err;
+ }
+
+ seekable = G_SEEKABLE (istream);
+
+ if (!g_seekable_can_seek (seekable))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_BADF, "%s",
+ g_strerror (EBADF));
+ goto err;
+ }
+
+ if (id3_offset == 0)
+ {
+ /* No ID3v2 tag. */
+ id3_offset = size;
+ id3_buffer = NULL;
+ tagsize = 0;
+ }
+ else
+ {
+
+ /* Read in the existing ID3v2 tag, using libid3tag to determine the
+ * size. */
+
+ /* Seek to the start of the metadata chunk. */
+ if (!g_seekable_seek (seekable, id3_offset, G_SEEK_SET, NULL, error))
+ {
+ goto err;
+ }
+
+ if (!g_input_stream_read_all (istream, id3_query, ID3_TAG_QUERYSIZE,
+ &bytes_read, NULL, error))
+ {
+ goto err;
+ }
+ else if (bytes_read != ID3_TAG_QUERYSIZE)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
+ _("Error reading tags from file"));
+ goto err;
+ }
+
+ if ((tagsize = id3_tag_query ((id3_byte_t const *)id3_query,
+ ID3_TAG_QUERYSIZE)) <= ID3_TAG_QUERYSIZE)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
+ _("Error reading tags from file"));
+ goto err;
+ }
+
+ /* FIXME: id3lib seems to read past the end of a buffer that is exactly
+ * the right size, so over-allocate. */
+ id3_buffer = g_malloc0 (tagsize + ID3_TAG_QUERYSIZE);
+ /* Copy the query buffer. */
+ memcpy (id3_buffer, id3_query, ID3_TAG_QUERYSIZE);
+
+ if (!g_input_stream_read_all (istream, &id3_buffer[ID3_TAG_QUERYSIZE],
+ tagsize - ID3_TAG_QUERYSIZE, &bytes_read,
+ NULL, error))
+ {
+ g_free (id3_buffer);
+ goto err;
+ }
+ else if (bytes_read != tagsize - ID3_TAG_QUERYSIZE)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
+ _("Error reading tags from file"));
+ g_free (id3_buffer);
+ goto err;
+ }
+ }
+
+ /* No reading from this point forward. */
+ g_input_stream_close (istream, NULL, NULL);
+
+ ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
+ seekable = G_SEEKABLE (ostream);
+
+ /* Seek to the start of the metadata chunk. */
+ if (!g_seekable_seek (seekable, id3_offset, G_SEEK_SET, NULL, error))
+ {
+ g_free (id3_buffer);
+ goto err;
+ }
+
+#ifdef ENABLE_ID3LIB
+ if (g_settings_get_boolean (MainSettings, "id3v2-version-4"))
+ {
+#endif
+ if (!et_dsf_tag_write_id3v24 (FileTag, id3_buffer, tagsize, ostream,
+ error))
+ {
+ g_free (id3_buffer);
+ goto err;
+ }
+#ifdef ENABLE_ID3LIB
+ }
+ else
+ {
+ if (!et_dsf_tag_write_id3v23 (FileTag, id3_buffer, tagsize, ostream,
+ error))
+ {
+ g_free (id3_buffer);
+ goto err;
+ }
+ }
+#endif
+
+ g_free (id3_buffer);
g_object_unref (iostream);
return TRUE;
diff --git a/src/tags/id3_tag.c b/src/tags/id3_tag.c
index a61f938..11191dc 100644
--- a/src/tags/id3_tag.c
+++ b/src/tags/id3_tag.c
@@ -57,7 +57,6 @@
/**************
* Prototypes *
**************/
-static gchar *Id3tag_Get_Error_Message (ID3_Err error);
static void Id3tag_Prepare_ID3v1 (ID3Tag *id3_tag);
static gchar *Id3tag_Rules_For_ISO_Fields (const gchar *string,
const gchar *from_codeset,
@@ -130,114 +129,16 @@ et_id3tag_get_tpos_from_file_tag (const File_Tag *FileTag)
return g_string_free (gstring, FALSE);
}
-/*
- * Write the ID3 tags to the file. Returns TRUE on success, else 0.
- */
-static gboolean
-id3tag_write_file_v23tag (const ET_File *ETFile,
- GError **error)
+void
+et_id3tag_set_id3tag_from_file_tag (const File_Tag *FileTag,
+ ID3Tag *id3_tag,
+ gboolean *strip_tags)
{
- const File_Tag *FileTag;
- const gchar *filename;
- const gchar *filename_utf8;
- gchar *basename_utf8;
- GFile *file;
- ID3Tag *id3_tag = NULL;
- ID3_Err error_strip_id3v1 = ID3E_NoError;
- ID3_Err error_strip_id3v2 = ID3E_NoError;
- ID3_Err error_update_id3v1 = ID3E_NoError;
- ID3_Err error_update_id3v2 = ID3E_NoError;
- gboolean success = TRUE;
- gint number_of_frames;
- gboolean has_title = FALSE;
- gboolean has_artist = FALSE;
- gboolean has_album_artist= FALSE;
- gboolean has_album = FALSE;
- gboolean has_disc_number = FALSE;
- gboolean has_year = FALSE;
- gboolean has_track = FALSE;
- gboolean has_genre = FALSE;
- gboolean has_comment = FALSE;
- gboolean has_composer = FALSE;
- gboolean has_orig_artist = FALSE;
- gboolean has_copyright = FALSE;
- gboolean has_url = FALSE;
- gboolean has_encoded_by = FALSE;
- gboolean has_picture = FALSE;
- //gboolean has_song_len = FALSE;
- static gboolean flag_first_check = TRUE;
- static gboolean flag_id3lib_bugged = TRUE;
-
ID3Frame *id3_frame;
ID3Field *id3_field;
- //gchar *string;
gchar *string1;
Picture *pic;
- g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
- // When writing the first MP3 file, we check if the version of id3lib of the
- // system doesn't contain a bug when writting Unicode tags
- if (flag_first_check
- && g_settings_get_boolean (MainSettings, "id3v2-enable-unicode"))
- {
- flag_first_check = FALSE;
- flag_id3lib_bugged = id3tag_check_if_id3lib_is_buggy (NULL);
- }
-
- FileTag = (File_Tag *)ETFile->FileTag->data;
- filename = ((File_Name *)ETFile->FileNameCur->data)->value;
- filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
-
- file = g_file_new_for_path (filename);
-
- /* FIXME: Handle this in the caller instead. */
- /* This is a protection against a bug in id3lib that enters an infinite
- * loop with corrupted MP3 files (files containing only zeroes) */
- if (et_id3tag_check_if_file_is_corrupted (file, error))
- {
- GtkWidget *msgdialog;
- gchar *basename;
- gchar *basename_utf8;
-
- basename = g_file_get_basename (file);
- basename_utf8 = filename_to_display (basename);
-
- msgdialog = gtk_message_dialog_new (GTK_WINDOW (MainWindow),
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- _("As the following corrupted file ā%sā will cause an error in
id3lib, it will not be processed"),
- basename_utf8);
- gtk_window_set_title (GTK_WINDOW (msgdialog), _("Corrupted file"));
-
- gtk_dialog_run (GTK_DIALOG (msgdialog));
- gtk_widget_destroy (msgdialog);
- g_free (basename);
- g_free (basename_utf8);
- g_object_unref (file);
- return FALSE;
- }
-
- /* We get again the tag from the file to keep also unused data (by EasyTAG), then
- * we replace the changed data */
- if ((id3_tag = ID3Tag_New ()) == NULL)
- {
- g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM, "%s",
- g_strerror (ENOMEM));
- g_object_unref (file);
- return FALSE;
- }
-
- basename_utf8 = g_path_get_basename(filename_utf8);
-
- ID3Tag_Link(id3_tag,filename);
-
- /* Set padding when tag was changed, for faster writing */
- ID3Tag_SetPadding(id3_tag,TRUE);
-
-
/*********
* Title *
*********/
@@ -250,7 +151,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
id3_frame = ID3Frame_NewID(ID3FID_TITLE);
ID3Tag_AttachFrame(id3_tag,id3_frame);
Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->title);
- has_title = TRUE;
+ *strip_tags = FALSE;
}
@@ -264,7 +165,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
id3_frame = ID3Frame_NewID(ID3FID_LEADARTIST);
ID3Tag_AttachFrame(id3_tag,id3_frame);
Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->artist);
- has_artist = TRUE;
+ *strip_tags = FALSE;
}
/****************
@@ -277,7 +178,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
id3_frame = ID3Frame_NewID(ID3FID_BAND);
ID3Tag_AttachFrame(id3_tag,id3_frame);
Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->album_artist);
- has_album_artist = TRUE;
+ *strip_tags = FALSE;
}
/*********
@@ -290,7 +191,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
id3_frame = ID3Frame_NewID(ID3FID_ALBUM);
ID3Tag_AttachFrame(id3_tag,id3_frame);
Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->album);
- has_album = TRUE;
+ *strip_tags = FALSE;
}
@@ -306,7 +207,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
string1 = et_id3tag_get_tpos_from_file_tag (FileTag);
Id3tag_Set_Field (id3_frame, ID3FN_TEXT, string1);
g_free (string1);
- has_disc_number = TRUE;
+ *strip_tags = FALSE;
}
@@ -320,7 +221,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
id3_frame = ID3Frame_NewID(ID3FID_YEAR);
ID3Tag_AttachFrame(id3_tag,id3_frame);
Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->year);
- has_year = TRUE;
+ *strip_tags = FALSE;
}
@@ -341,7 +242,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
Id3tag_Set_Field(id3_frame, ID3FN_TEXT, string1);
g_free(string1);
- has_track = TRUE;
+ *strip_tags = FALSE;
}
@@ -376,7 +277,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
Id3tag_Set_Field(id3_frame, ID3FN_TEXT, genre_string_tmp);
g_free(genre_string_tmp);
- has_genre = TRUE;
+ *strip_tags = FALSE;
}
@@ -394,7 +295,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
// Disabled as when using unicode, the comment field stay in ISO.
//Id3tag_Set_Field(id3_frame, ID3FN_DESCRIPTION, "ID3v1 Comment");
//Id3tag_Set_Field(id3_frame, ID3FN_LANGUAGE, "XXX");
- has_comment = TRUE;
+ *strip_tags = FALSE;
}
@@ -408,7 +309,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
id3_frame = ID3Frame_NewID(ID3FID_COMPOSER);
ID3Tag_AttachFrame(id3_tag,id3_frame);
Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->composer);
- has_composer = TRUE;
+ *strip_tags = FALSE;
}
@@ -422,7 +323,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
id3_frame = ID3Frame_NewID(ID3FID_ORIGARTIST);
ID3Tag_AttachFrame(id3_tag,id3_frame);
Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->orig_artist);
- has_orig_artist = TRUE;
+ *strip_tags = FALSE;
}
@@ -436,7 +337,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
id3_frame = ID3Frame_NewID(ID3FID_COPYRIGHT);
ID3Tag_AttachFrame(id3_tag,id3_frame);
Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->copyright);
- has_copyright = TRUE;
+ *strip_tags = FALSE;
}
@@ -450,7 +351,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
id3_frame = ID3Frame_NewID(ID3FID_WWWUSER);
ID3Tag_AttachFrame(id3_tag,id3_frame);
Id3tag_Set_Field(id3_frame, ID3FN_URL, FileTag->url);
- has_composer = TRUE;
+ *strip_tags = FALSE;
}
@@ -464,7 +365,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
id3_frame = ID3Frame_NewID(ID3FID_ENCODEDBY);
ID3Tag_AttachFrame(id3_tag,id3_frame);
Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->encoded_by);
- has_encoded_by = TRUE;
+ *strip_tags = FALSE;
}
@@ -474,8 +375,6 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_PICTURE)) )
ID3Tag_RemoveFrame(id3_tag,id3_frame);
- has_picture = FALSE;
-
for (pic = FileTag->picture; pic != NULL; pic = pic->next)
{
Picture_Format format = Picture_Format_From_Data(pic);
@@ -527,7 +426,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_DATA)))
ID3Field_SetBINARY(id3_field, pic->data, pic->size);
- has_picture = TRUE;
+ *strip_tags = FALSE;
}
@@ -549,7 +448,96 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
g_free(string);
has_song_len = TRUE;
}*/
+}
+/*
+ * Write the ID3 tags to the file. Returns TRUE on success, else 0.
+ */
+static gboolean
+id3tag_write_file_v23tag (const ET_File *ETFile,
+ GError **error)
+{
+ const File_Tag *FileTag;
+ const gchar *filename;
+ const gchar *filename_utf8;
+ gchar *basename_utf8;
+ GFile *file;
+ ID3Tag *id3_tag = NULL;
+ ID3_Err error_strip_id3v1 = ID3E_NoError;
+ ID3_Err error_strip_id3v2 = ID3E_NoError;
+ ID3_Err error_update_id3v1 = ID3E_NoError;
+ ID3_Err error_update_id3v2 = ID3E_NoError;
+ gboolean success = TRUE;
+ gint number_of_frames;
+ gboolean strip_tags = TRUE;
+ //gboolean has_song_len = FALSE;
+ static gboolean flag_first_check = TRUE;
+ static gboolean flag_id3lib_bugged = TRUE;
+
+ g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ // When writing the first MP3 file, we check if the version of id3lib of the
+ // system doesn't contain a bug when writting Unicode tags
+ if (flag_first_check
+ && g_settings_get_boolean (MainSettings, "id3v2-enable-unicode"))
+ {
+ flag_first_check = FALSE;
+ flag_id3lib_bugged = id3tag_check_if_id3lib_is_buggy (NULL);
+ }
+
+ FileTag = (File_Tag *)ETFile->FileTag->data;
+ filename = ((File_Name *)ETFile->FileNameCur->data)->value;
+ filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
+
+ file = g_file_new_for_path (filename);
+
+ /* FIXME: Handle this in the caller instead. */
+ /* This is a protection against a bug in id3lib that enters an infinite
+ * loop with corrupted MP3 files (files containing only zeroes) */
+ if (et_id3tag_check_if_file_is_corrupted (file, error))
+ {
+ GtkWidget *msgdialog;
+ gchar *basename;
+ gchar *basename_utf8;
+
+ basename = g_file_get_basename (file);
+ basename_utf8 = filename_to_display (basename);
+
+ msgdialog = gtk_message_dialog_new (GTK_WINDOW (MainWindow),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("As the following corrupted file ā%sā will cause an error in
id3lib, it will not be processed"),
+ basename_utf8);
+ gtk_window_set_title (GTK_WINDOW (msgdialog), _("Corrupted file"));
+
+ gtk_dialog_run (GTK_DIALOG (msgdialog));
+ gtk_widget_destroy (msgdialog);
+ g_free (basename);
+ g_free (basename_utf8);
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ /* We get again the tag from the file to keep also unused data (by EasyTAG), then
+ * we replace the changed data */
+ if ((id3_tag = ID3Tag_New ()) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM, "%s",
+ g_strerror (ENOMEM));
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ basename_utf8 = g_path_get_basename(filename_utf8);
+
+ ID3Tag_Link(id3_tag,filename);
+
+ /* Set padding when tag was changed, for faster writing */
+ ID3Tag_SetPadding(id3_tag,TRUE);
+
+ et_id3tag_set_id3tag_from_file_tag (FileTag, id3_tag, &strip_tags);
/******************************
* Delete an APE tag if found *
@@ -583,9 +571,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
* is set to TRUE, we strip the ID3v1.x and ID3v2 tags. Else, write ID3v2
* and/or ID3v1. */
if (g_settings_get_boolean (MainSettings, "id3-strip-empty")
- && !has_title && !has_artist && !has_album_artist && !has_album && !has_year &&
!has_track
- && !has_genre && !has_composer && !has_orig_artist && !has_copyright && !has_url
- && !has_encoded_by && !has_picture && !has_comment && !has_disc_number)//&& !has_song_len )
+ && strip_tags)
{
error_strip_id3v1 = ID3Tag_Strip(id3_tag,ID3TT_ID3V1);
error_strip_id3v2 = ID3Tag_Strip(id3_tag,ID3TT_ID3V2);
@@ -743,7 +729,8 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
}
-gchar *Id3tag_Get_Error_Message(ID3_Err error)
+gchar *
+Id3tag_Get_Error_Message (ID3_Err error)
{
switch (error)
{
diff --git a/src/tags/id3_tag.h b/src/tags/id3_tag.h
index 342d22e..a63e423 100644
--- a/src/tags/id3_tag.h
+++ b/src/tags/id3_tag.h
@@ -38,10 +38,16 @@ guchar Id3tag_String_To_Genre (const gchar *genre);
gchar *et_id3tag_get_tpos_from_file_tag (const File_Tag *file_tag);
+
#ifdef ENABLE_MP3
#include <id3tag.h>
gboolean et_id3tag_fill_file_tag_from_id3tag (struct id3_tag *tag, File_Tag *file_tag);
void et_id3tag_set_id3_tag_from_file_tag (const File_Tag *FileTag, struct id3_tag *v1tag, struct id3_tag
*v2tag, gboolean *strip_tags);
+#ifdef ENABLE_ID3LIB
+#include <id3.h>
+gchar * Id3tag_Get_Error_Message (ID3_Err error);
+void et_id3tag_set_id3tag_from_file_tag (const File_Tag *FileTag, ID3Tag *id3_tag, gboolean *strip_tags);
+#endif /* ENABLE_ID3LIB */
#endif /* ENABLE_MP3 */
G_END_DECLS
diff --git a/src/tags/id3lib/c_wrapper.cpp b/src/tags/id3lib/c_wrapper.cpp
index 7f63406..2c92755 100644
--- a/src/tags/id3lib/c_wrapper.cpp
+++ b/src/tags/id3lib/c_wrapper.cpp
@@ -119,6 +119,30 @@ extern "C"
return headerInfo;
}
+ /* Filed as a patch against id3lib:
+ * http://sourceforge.net/p/id3lib/patches/75/ */
+ ID3_C_EXPORT size_t CCONV
+ ID3Tag_Render(const ID3Tag *tag, uchar *buffer, ID3_TagType tt)
+ {
+ size_t size = 0;
+ if (tag)
+ {
+ ID3_CATCH(size = reinterpret_cast<const ID3_Tag *>(tag)->Render(buffer, tt));
+ }
+ return size;
+ }
+
+ ID3_C_EXPORT size_t CCONV
+ ID3Tag_Size(const ID3Tag *tag)
+ {
+ size_t size = 0;
+ if (tag)
+ {
+ ID3_CATCH(size = reinterpret_cast<const ID3_Tag *>(tag)->Size());
+ }
+ return size;
+ }
+
// Call with :
// Mp3_Headerinfo* headerInfo = malloc(sizeof(Mp3_Headerinfo));
// ID3Tag_GetMp3HeaderInfo(tag, headerInfo);
diff --git a/src/tags/id3lib/id3_bugfix.h b/src/tags/id3lib/id3_bugfix.h
index 14e97a9..8a281d6 100644
--- a/src/tags/id3lib/id3_bugfix.h
+++ b/src/tags/id3lib/id3_bugfix.h
@@ -42,6 +42,8 @@ extern "C"
ID3_C_EXPORT ID3_TextEnc CCONV ID3Field_GetEncoding (const ID3Field *field);
ID3_C_EXPORT bool CCONV ID3Field_IsEncodable (const ID3Field *field);
ID3_C_EXPORT ID3_FieldType CCONV ID3Field_GetType (const ID3Field *field);
+ ID3_C_EXPORT size_t CCONV ID3Tag_Render (const ID3Tag *tag, uchar *buffer,
ID3_TagType tt);
+ ID3_C_EXPORT size_t CCONV ID3Tag_Size (const ID3Tag *tag);
//ID3_C_EXPORT ID3_FieldID CCONV ID3Field_GetID (const ID3Field *field);
ID3_C_EXPORT const Mp3_Headerinfo* CCONV ID3Tag_GetMp3HeaderInfo (ID3Tag *tag);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]