commit 64f2b51e0e1be5503d83784fc04467f6403bf195
Author: phantomjinx <p.g.richard...@phantomjinx.co.uk>
Date:   Tue Apr 10 23:52:39 2012 +0100

    Support for writing mp4 tags using atomic parsley
    
    * AtomicParsleyBridge
     * Include read / write lyric support for the first time
     * Include write metadata function
    
    * m4afile.[ch]
     * Mirror the function calls in mp4file.c
     * Could we make these common somehow??

 libs/atomic-parsley/AtomicParsley.cpp       |   38 ++--
 libs/atomic-parsley/AtomicParsleyBridge.cpp |  385 +++++++++++++++++++++++++--
 libs/atomic-parsley/AtomicParsleyBridge.h   |   66 ++---
 plugins/filetype_m4a/Makefile.am            |    2 +-
 plugins/filetype_m4a/m4afile.c              |   20 ++-
 plugins/filetype_m4a/m4afile.h              |    3 +
 plugins/filetype_m4a/plugin.c               |    6 +-
 plugins/filetype_mp4/Makefile.am            |    2 +-
 plugins/filetype_mp4/mp4file.c              |   21 ++-
 plugins/filetype_mp4/mp4file.h              |    4 +-
 plugins/filetype_mp4/plugin.c               |    6 +-
 11 files changed, 456 insertions(+), 97 deletions(-)
---
diff --git a/libs/atomic-parsley/AtomicParsley.cpp 
b/libs/atomic-parsley/AtomicParsley.cpp
index 6080d9e..6b49cd1 100755
--- a/libs/atomic-parsley/AtomicParsley.cpp
+++ b/libs/atomic-parsley/AtomicParsley.cpp
@@ -318,7 +318,6 @@ void TestFileExistence(const char *filePath, bool errorOut) 
{
        a_file = APar_OpenFile(filePath, "rb");
        if( (a_file == NULL) && errorOut ){
                fprintf(stderr, "AtomicParsley error: can't open %s for 
reading: %s\n", filePath, strerror(errno));
-               exit(1);
        } else {
                fclose(a_file);
        }
@@ -348,13 +347,13 @@ int APar_TestArtworkBinaryData(const char* artworkPath) {
                        artwork_dataType = AtomFlags_Data_JPEGBinary;
                } else {
                        fprintf(stdout, "AtomicParsley error: %s\n\t image file 
is not jpg/png and cannot be embedded.\n", artworkPath);
-                       exit(1);
+                       artwork_dataType = -1;
                }
                fclose(artfile);
 
        } else {
                fprintf(stdout, "AtomicParsley error: %s\n\t image file could 
not be opened.\n", artworkPath);
-               exit(1);
+               artwork_dataType = -1;
        }
        return artwork_dataType;
 }
@@ -423,7 +422,7 @@ void APar_FreeMemory() {
        free(file_progress_buffer);
        file_progress_buffer=NULL;
 
-       if (source_file) {
+       if (source_file && file_opened) {
            fclose(source_file);
            file_opened = false;
        }
@@ -2197,7 +2196,6 @@ void APar_IdentifyBrand(char* file_brand ) {
                //what ISN'T supported
                case 0x71742020 : //'qt  '  --this is listed at mp4ra, but 
there are features of the file that aren't supported (like the 4 NULL bytes 
after the last udta child atom
                        fprintf(stdout, "AtomicParsley error: Quicktime movie 
files are not supported.\n");
-                       exit(2);
                        break;
 
                //3gp-brands are listed in 3GPP/3GPP2 specification documents, 
not all are listed at mp4ra
@@ -2245,7 +2243,6 @@ void APar_IdentifyBrand(char* file_brand ) {
                //other lesser unsupported brands; 
http://www.mp4ra.org/filetype.html like dv, mjpeg200, mp21 & ... whatever mpeg7 
brand is
                default :
                        fprintf(stdout, "AtomicParsley error: unsupported 
MPEG-4 file brand found '%s'\n", file_brand);
-                       exit(2);
                        break;
 
        }
@@ -2281,7 +2278,6 @@ uint64_t APar_64bitAtomRead(FILE *file, uint32_t 
jump_point) {
                contains_unsupported_64_bit_atom = true;
                fprintf(stdout, "You must be off your block thinking I'm going 
to tag a file that is at LEAST %llu bytes long.\n", extended_dataSize);
                fprintf(stdout, "AtomicParsley doesn't have full 64-bit 
support");
-               exit (2);
        }
        return extended_dataSize;
 }
@@ -3503,7 +3499,7 @@ void APar_MetaData_atom_QuickInit(short atom_num, const 
uint32_t atomFlags, uint
        parsedAtoms[atom_num].AtomicData = (char*)calloc(1, sizeof(char)* 
allotment+50 );
        if (parsedAtoms[atom_num].AtomicData == NULL) {
                fprintf(stdout, "AP error: there was insufficient memory 
available for allocation. Exiting.%c\n", '\a');
-               exit(1);
+               return;
        }
 
        parsedAtoms[atom_num].AtomicLength = 16 + supplemental_length; // 
4bytes atom length, 4 bytes atom length, 4 bytes version/flags, 4 bytes NULL
@@ -4408,7 +4404,7 @@ void APar_ValidateAtoms() {
 
        if (atom_number > MAX_ATOMS) {
                fprintf(stderr, "AtomicParsley error: amount of atoms exceeds 
internal limit. Aborting.\n");
-               exit(1);
+               return;
        }
 
        while (true) {
@@ -4426,7 +4422,7 @@ void APar_ValidateAtoms() {
 #else
                                fprintf(stderr, "atom %s is %u bytes long which 
is greater than the filesize of %llu\n", parsedAtoms[iter].AtomicName, 
parsedAtoms[iter].AtomicLength, (long long unsigned int)file_size);
 #endif
-                               exit(1); //its conceivable to repair such an 
off length by the surrounding atoms constrained by file_size - just not anytime 
soon; probly would catch a foobar2000 0.9 tagged file
+                               return; //its conceivable to repair such an off 
length by the surrounding atoms constrained by file_size - just not anytime 
soon; probly would catch a foobar2000 0.9 tagged file
                        }
                }
 
@@ -4440,13 +4436,13 @@ void APar_ValidateAtoms() {
 
                if (strncmp(parsedAtoms[iter].AtomicName, "mdat", 4) == 0 && 
parsedAtoms[iter].AtomicLevel != 1) {
                        fprintf(stderr, "AtomicParsley error: mdat atom was 
found at an illegal (not at top level). Aborting. %c\n", '\a');
-                       exit(1); //the error which forced this was some bad 
atom length redetermination; probably won't be fixed
+                       return; //the error which forced this was some bad atom 
length redetermination; probably won't be fixed
                }
 
                if (memcmp(parsedAtoms[iter].AtomicName, "trak", 4) == 0 && 
parsedAtoms[iter+1].NextAtomNumber != 0) { //prevent writing any malformed 
tracks
                        if (memcmp(parsedAtoms[ 
parsedAtoms[iter].NextAtomNumber ].AtomicName, "tkhd", 4) != 0) {
                                fprintf(stderr, "AtomicParsley error: incorrect 
track structure. %c\n", '\a');
-                               exit(1);
+                               return;
                        }
                }
 
@@ -4463,7 +4459,7 @@ void APar_ValidateAtoms() {
                fprintf(stderr, "AtomicParsley error: total existing atoms 
present as larger than filesize. Aborting. %c\n", '\a');
                //APar_PrintAtomicTree();
                fprintf(stdout, "%i %llu\n", percentage_difference, 
simple_tally);
-               exit(1);
+               return;
        }
 
        if (!atom_name_with_4_characters) {
@@ -5015,7 +5011,7 @@ void APar_WriteFile(const char* m4aFile, const char* 
outfile, bool rewrite_origi
 
        } else {
                fprintf(stdout, "AtomicParsley error: an error occurred while 
trying to create a temp file.\n");
-               exit(1);
+               return;
        }
 
        if (udta_dynamics.dynamic_updating) {
@@ -5027,7 +5023,7 @@ void APar_WriteFile(const char* m4aFile, const char* 
outfile, bool rewrite_origi
                if (source_file == NULL) {
                        fclose(temp_file);
                        remove(temp_file_name);
-                       exit(1);
+                       return;
                }
 
                //update moov's length
@@ -5056,7 +5052,10 @@ void APar_WriteFile(const char* m4aFile, const char* 
outfile, bool rewrite_origi
                                fwrite(file_buffer, 1, free_padding_size-8, 
source_file);
                        }
                }
+
                fclose(source_file);
+               file_opened = false;
+
                fclose(temp_file);
                remove(temp_file_name);
                fclose(temp_file);
@@ -5064,6 +5063,7 @@ void APar_WriteFile(const char* m4aFile, const char* 
outfile, bool rewrite_origi
 
        } else if (rewrite_original && !outfile) { //disable overWrite when 
writing out to a specifically named file; presumably the enumerated output file 
was meant to be the final destination
                fclose(source_file);
+               file_opened = false;
 
                if ( IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
 #if defined (_MSC_VER) /* native windows seems to require removing the file 
first; rename() on Mac OS X does the removing automatically as needed */
@@ -5114,19 +5114,19 @@ void APar_WriteFile(const char* m4aFile, const char* 
outfile, bool rewrite_origi
 
                                case ENAMETOOLONG: {
                                        fprintf (stdout, "Some or all of the 
orginal path was too long.");
-                                       exit (-1);
+                                       return;
                                }
                                case ENOENT: {
                                        fprintf (stdout, "Some part of the 
original path was missing.");
-                                       exit (-1);
+                                       return;
                                }
                                case EACCES: {
                                        fprintf (stdout, "Unable to write to a 
directory lacking write permission.");
-                                       exit (-1);
+                                       return;
                                }
                                case ENOSPC: {
                                        fprintf (stdout, "Out of space.");
-                                       exit (-1);
+                                       return;
                                }
                        }
                }
diff --git a/libs/atomic-parsley/AtomicParsleyBridge.cpp 
b/libs/atomic-parsley/AtomicParsleyBridge.cpp
index 39efc9f..9a69cfe 100644
--- a/libs/atomic-parsley/AtomicParsleyBridge.cpp
+++ b/libs/atomic-parsley/AtomicParsleyBridge.cpp
@@ -35,10 +35,40 @@
 
 extern "C" {
 #include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
 #include "libgtkpod/prefs.h"
 #include "libgtkpod/charset.h"
+#include "libgtkpod/gp_itdb.h"
+#include "libgtkpod/gp_private.h"
 }
 
+#define TITLE "\251nam"
+#define ARTIST "\251ART"
+#define ALBUM_ARTIST "aART"
+#define COMPOSER "\251wrt"
+#define COMMENT "\251cmt"
+#define YEAR "\251day"
+#define ALBUM "\251alb"
+#define TRACK_NUM_AND_TOTAL "trkn"
+#define DISK_NUM_AND_TOTAL "disk"
+#define GROUPING "\251grp"
+#define DESCRIPTION "desc"
+#define STANDARD_GENRE "gnre"
+#define CUSTOM_GENRE "\251gen"
+#define TEMPO "tmpo"
+#define LYRICS "\251lyr"
+#define KEYWORD "keyw"
+#define TV_SHOW "tvsh"
+#define TV_EPISODE "tven"
+#define TV_EPISODE_NO "tves"
+#define TV_NETWORK_NAME "tvnn"
+#define TV_SEASON_NO "tvsn"
+#define MEDIA_TYPE "stik"
+#define COMPILATION "cpil"
+#define CATEGORY "catg"
+#define PODCAST_URL "purl"
+#define ARTWORK "covr"
+
 static guint32 mediaTypeTagToMediaType(guint8 media_type) {
     switch (media_type) {
     case 0: /* Movie */
@@ -61,6 +91,26 @@ static guint32 mediaTypeTagToMediaType(guint8 media_type) {
     return 0;
 }
 
+static guint8 mediaTypeToMediaTypeTag(guint32 media_type) {
+    switch (media_type) {
+    case ITDB_MEDIATYPE_MOVIE: /* Movie */
+        return 9;
+    case ITDB_MEDIATYPE_AUDIO: /* Normal */
+        return 1;
+    case ITDB_MEDIATYPE_AUDIOBOOK: /* Audiobook */
+        return 2;
+    case ITDB_MEDIATYPE_MUSICVIDEO: /* Music Video */
+        return 6;
+    case ITDB_MEDIATYPE_TVSHOW: /* TV Show */
+        return 10;
+    case ITDB_MEDIATYPE_EPUB_BOOK: /* Booklet */
+        return 11;
+    case ITDB_MEDIATYPE_RINGTONE: /* Ringtone */
+        return 14;
+    }
+    return 0;
+}
+
 static AtomicInfo *find_atom(const char *meta) {
     char atomName[100];
 
@@ -78,14 +128,18 @@ static char* find_atom_value(const char* meta) {
     return NULL;
 }
 
-void AP_extract_metadata(const char *path, Track *track) {
+/**
+ * Open and scan the metadata of the m4a/mp4/... file
+ * and populate the given track.
+ */
+void AP_extract_metadata(const char *filePath, Track *track) {
     FILE *mp4File;
     Trackage *trackage;
     uint8_t track_cur;
     gboolean audio_or_video_found = FALSE;
 
-    APar_ScanAtoms(path, true);
-    mp4File = openSomeFile(path, true);
+    APar_ScanAtoms(filePath, true);
+    mp4File = openSomeFile(filePath, true);
 
     trackage = APar_ExtractDetails(mp4File, SHOW_TRACK_INFO);
 
@@ -111,56 +165,70 @@ void AP_extract_metadata(const char *path, Track *track) {
         char* value = NULL;
 
         // MP4 Title
-        value = find_atom_value("\251nam");
+        value = find_atom_value(TITLE);
         if (value) {
             track->title = charset_to_utf8(value);
             free(value);
         }
 
         // MP4 Artist
-        value = find_atom_value("\251ART");
+        value = find_atom_value(ARTIST);
         if (value) {
             track->artist = charset_to_utf8(value);
             free(value);
         }
 
         // MP4 Album Artist
-        value = find_atom_value("aART");
+        value = find_atom_value(ALBUM_ARTIST);
         if (value) {
             track->albumartist = charset_to_utf8(value);
             free(value);
         }
 
         // MP4 Composer
-        value = find_atom_value("\251wrt");
+        value = find_atom_value(COMPOSER);
         if (value) {
             track->composer = charset_to_utf8(value);
             free(value);
         }
 
         // MP4 Comment
-        value = find_atom_value("\251cmt");
+        value = find_atom_value(COMMENT);
         if (value) {
             track->comment = charset_to_utf8(value);
             free(value);
         }
 
+        // MP4 Description
+        value = find_atom_value(DESCRIPTION);
+        if (value) {
+            track->description = charset_to_utf8(value);
+            free(value);
+        }
+
+        // MP4 Keywords
+        value = find_atom_value(KEYWORD);
+        if (value) {
+            track->keywords = charset_to_utf8(value);
+            free(value);
+        }
+
         // MP4 Year
-        value = find_atom_value("\251day");
+        value = find_atom_value(YEAR);
         if (value) {
             track->year = atoi(value);
             free(value);
         }
 
         // MP4 Album
-        value = find_atom_value("\251alb");
+        value = find_atom_value(ALBUM);
         if (value) {
             track->album = charset_to_utf8(value);
             free(value);
         }
 
         // MP4 Track No. and Total
-        value = find_atom_value("trkn");
+        value = find_atom_value(TRACK_NUM_AND_TOTAL);
         if (value) {
             const char* delimiter = " of ";
             char *result = NULL;
@@ -176,7 +244,7 @@ void AP_extract_metadata(const char *path, Track *track) {
         }
 
         // MP4 Disk No. and Total
-        value = find_atom_value("disk");
+        value = find_atom_value(DISK_NUM_AND_TOTAL);
         if (value) {
             const char* delimiter = " of ";
             char *result = NULL;
@@ -192,7 +260,7 @@ void AP_extract_metadata(const char *path, Track *track) {
         }
 
         // MP4 Grouping
-        value = find_atom_value("\251grp");
+        value = find_atom_value(GROUPING);
         if (value) {
             track->grouping = charset_to_utf8(value);
             free(value);
@@ -200,13 +268,14 @@ void AP_extract_metadata(const char *path, Track *track) {
 
         // MP4 Genre - note: can be either a standard or custom genre
         // standard genre
-        value = find_atom_value("gnre");
+        value = find_atom_value(STANDARD_GENRE);
         if (value) {
             track->genre = charset_to_utf8(value);
             // Should not free standard genres
-        } else {
+        }
+        else {
             // custom genre
-            value = find_atom_value("\251gen");
+            value = find_atom_value(CUSTOM_GENRE);
             if (value) {
                 track->genre = charset_to_utf8(value);
                 free(value);
@@ -214,68 +283,83 @@ void AP_extract_metadata(const char *path, Track *track) {
         }
 
         // MP4 Tempo / BPM
-        value = find_atom_value("tmpo");
+        value = find_atom_value(TEMPO);
         if (value) {
             track->BPM = atoi(value);
             free(value);
         }
 
         // MP4 Lyrics
-        value = find_atom_value("\251lyr");
+        value = find_atom_value(LYRICS);
         if (value) {
             track->lyrics_flag = 0x01;
             free(value);
         }
 
         // MP4 TV Show
-        value = find_atom_value("tvsh");
+        value = find_atom_value(TV_SHOW);
         if (value) {
             track->tvshow = charset_to_utf8(value);
             free(value);
         }
 
         // MP4 TV Episode
-        value = find_atom_value("tven");
+        value = find_atom_value(TV_EPISODE);
         if (value) {
             track->tvepisode = charset_to_utf8(value);
             free(value);
         }
 
         // MP4 TV Episode No.
-        value = find_atom_value("tves");
+        value = find_atom_value(TV_EPISODE_NO);
         if (value) {
             track->episode_nr = atoi(value);
             free(value);
         }
 
         // MP4 TV Network
-        value = find_atom_value("tvnn");
+        value = find_atom_value(TV_NETWORK_NAME);
         if (value) {
             track->tvnetwork = charset_to_utf8(value);
             free(value);
         }
 
         // MP4 TV Season No.
-        value = find_atom_value("tvsn");
+        value = find_atom_value(TV_SEASON_NO);
         if (value) {
             track->season_nr = atoi(value);
             free(value);
         }
 
         // MP4 Media Type
-        value = find_atom_value("stik");
+        value = find_atom_value(MEDIA_TYPE);
         if (value) {
             track->mediatype = mediaTypeTagToMediaType(atoi(value));
             // Should not free standard media types
         }
 
         // MP4 Compilation flag
-        value = find_atom_value("cpil");
+        value = find_atom_value(COMPILATION);
         if (value) {
             track->compilation = atoi(value);
             free(value);
         }
 
+        // MP4 Category
+        value = find_atom_value(CATEGORY);
+        if (value) {
+            track->category = charset_to_utf8(value);
+            free(value);
+        }
+
+        // MP4 Podcast URL
+        value = find_atom_value(PODCAST_URL);
+        if (value) {
+            track->podcasturl = g_strdup(value);
+            free(value);
+        }
+
+
         fprintf(stdout, "Track title = %s\n", track->title);
         fprintf(stdout, "Track artist = %s\n", track->artist);
         fprintf(stdout, "Track album artist = %s\n", track->albumartist);
@@ -310,14 +394,15 @@ void AP_extract_metadata(const char *path, Track *track) {
                 tmp_file = APar_ExtractAAC_Artwork(info->AtomicNumber, 
tmp_file_prefix, 1);
                 g_free(tmp_file_prefix);
 
-                if (tmp_file && g_file_test (tmp_file, G_FILE_TEST_EXISTS)) {
+                if (tmp_file && g_file_test(tmp_file, G_FILE_TEST_EXISTS)) {
 
                     // Set the thumbnail using the tmp file
                     itdb_track_set_thumbnails(track, tmp_file);
 
                     if (track->artwork) {
                         fprintf(stdout, "Track has artwork");
-                    } else {
+                    }
+                    else {
                         fprintf(stdout, "No artwork applied to track");
                     }
 
@@ -333,3 +418,249 @@ void AP_extract_metadata(const char *path, Track *track) {
     APar_FreeMemory();
 
 }
+
+/**
+ * Read any lyrics from the given file
+ */
+char *AP_read_lyrics(const char *filePath, GError **error) {
+    APar_ScanAtoms(filePath, true);
+    openSomeFile(filePath, true);
+
+    char *value = find_atom_value(LYRICS);
+
+    fprintf(stdout, "Value of lyrics is %s\n", value);
+
+    APar_FreeMemory();
+
+    return value;
+}
+
+static void write_lyrics_internal(const char* lyrics, const char *filePath, 
GError **error) {
+    if (!lyrics || strlen(lyrics) == 0)
+        APar_RemoveAtom("moov.udta.meta.ilst.\251lyr.data", VERSIONED_ATOM, 0);
+    else {
+        short lyricsData_atom = 
APar_MetaData_atom_Init("moov.udta.meta.ilst.\251lyr.data", lyrics, 
AtomFlags_Data_Text);
+        APar_Unified_atom_Put(lyricsData_atom, lyrics, 
UTF8_iTunesStyle_Unlimited, 0, 0);
+    }
+}
+
+void AP_write_lyrics(const char *lyrics, const char *filePath, GError **error) 
{
+    APar_ScanAtoms(filePath);
+    write_lyrics_internal(lyrics, filePath, error);
+}
+
+static void set_limited_text_atom_value(const char *meta, const char *value) {
+    char atomName[100];
+
+    sprintf(atomName, "moov.udta.meta.ilst.%s.data", meta);
+
+    if (!value || strlen(value) == 0)
+        APar_RemoveAtom(atomName, VERSIONED_ATOM, 0);
+    else {
+        short atom = APar_MetaData_atom_Init(atomName, value, 
AtomFlags_Data_Text);
+        APar_Unified_atom_Put(atom, value, UTF8_iTunesStyle_256glyphLimited, 
0, 0);
+    }
+}
+
+/**
+ * Using the given track, set the metadata of the target
+ * file
+ */
+void AP_write_metadata(Track *track, const char *filePath, GError **error) {
+    ExtraTrackData *etr;
+    gchar *value;
+
+    g_return_if_fail (track);
+
+    APar_ScanAtoms(filePath);
+
+    if (metadata_style != ITUNES_STYLE) {
+        gchar *fbuf = charset_to_utf8(filePath);
+        gtkpod_log_error(error,
+                g_strdup_printf(_("ERROR %s is not itunes style."), fbuf));
+        g_free(fbuf);
+        return;
+    }
+
+    // Title
+    set_limited_text_atom_value(TITLE, track->title);
+
+    // Artist
+    set_limited_text_atom_value(ARTIST, track->artist);
+
+    // Album Artist
+    set_limited_text_atom_value(ALBUM_ARTIST, track->albumartist);
+
+    // Genre
+    APar_MetaData_atomGenre_Set(track->genre);
+
+    // Track Number and Total
+    if (track->track_nr == 0) {
+        APar_RemoveAtom("moov.udta.meta.ilst.trkn.data", VERSIONED_ATOM, 0);
+    } else {
+        gchar *track_info = g_strdup_printf("%d / %d", track->track_nr, 
track->tracks);
+        short tracknumData_atom = 
APar_MetaData_atom_Init("moov.udta.meta.ilst.trkn.data", track_info, 
AtomFlags_Data_Binary);
+        APar_Unified_atom_Put(tracknumData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, 0, 16);
+        APar_Unified_atom_Put(tracknumData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, track->track_nr, 16);
+        APar_Unified_atom_Put(tracknumData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, track->tracks, 16);
+        APar_Unified_atom_Put(tracknumData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, 0, 16);
+        g_free(track_info);
+    }
+
+    // Disk Number and Total
+    if (track->cd_nr == 0) {
+        APar_RemoveAtom("moov.udta.meta.ilst.disk.data", VERSIONED_ATOM, 0);
+    } else {
+        gchar *disk_info = g_strdup_printf("%d / %d", track->cd_nr, 
track->cds);
+        short disknumData_atom = 
APar_MetaData_atom_Init("moov.udta.meta.ilst.disk.data", disk_info, 
AtomFlags_Data_Binary);
+        APar_Unified_atom_Put(disknumData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, 0, 16);
+        APar_Unified_atom_Put(disknumData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, track->cd_nr, 16);
+        APar_Unified_atom_Put(disknumData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, track->cds, 16);
+        g_free(disk_info);
+    }
+
+    // Comment
+    set_limited_text_atom_value(COMMENT, track->comment);
+
+    // Year
+    gchar *yr = NULL;
+    if (track->year > 0)
+        yr = g_strdup_printf("%d", track->year);
+
+    set_limited_text_atom_value(YEAR, yr);
+
+    if (yr)
+        g_free(yr);
+
+    // Lyrics
+    etr = (ExtraTrackData *) track->userdata;
+    if (etr)
+        write_lyrics_internal(etr->lyrics, filePath, error);
+
+    // Composer
+    set_limited_text_atom_value(COMPOSER, track->composer);
+
+    // Grouping
+    set_limited_text_atom_value(GROUPING, track->grouping);
+
+    // Description
+    set_limited_text_atom_value(DESCRIPTION, track->description);
+
+    // TV Network
+    set_limited_text_atom_value(TV_NETWORK_NAME, track->tvnetwork);
+
+    // TV Show Name
+    set_limited_text_atom_value(TV_SHOW, track->tvshow);
+
+    // TV Episode
+    set_limited_text_atom_value(TV_EPISODE, track->tvepisode);
+
+    // Compilation
+    if (! track->compilation) {
+        APar_RemoveAtom("moov.udta.meta.ilst.cpil.data", VERSIONED_ATOM, 0);
+    } else {
+        //compilation: [0, 0, 0, 0,   boolean_value]; BUT that first uint32_t 
is already accounted for in APar_MetaData_atom_Init
+        value = g_strdup_printf("%d", track->compilation);
+        short compilationData_atom = 
APar_MetaData_atom_Init("moov.udta.meta.ilst.cpil.data", value, 
AtomFlags_Data_UInt);
+        APar_Unified_atom_Put(compilationData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 is 
compilation
+        g_free(value);
+    }
+
+    // Tempo / BPM
+    if (! track->BPM) {
+        APar_RemoveAtom("moov.udta.meta.ilst.tmpo.data", VERSIONED_ATOM, 0);
+    } else {
+        //bpm is [0, 0, 0, 0,   0, bpm_value]; BUT that first uint32_t is 
already accounted for in APar_MetaData_atom_Init
+        value = g_strdup_printf("%d", track->BPM);
+        short bpmData_atom = 
APar_MetaData_atom_Init("moov.udta.meta.ilst.tmpo.data", value, 
AtomFlags_Data_UInt);
+        APar_Unified_atom_Put(bpmData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, track->BPM, 16);
+        g_free(value);
+    }
+
+    // Media Type
+    guint8 mediaTypeTag = mediaTypeToMediaTypeTag(track->mediatype);
+    value = g_strdup_printf("%d", track->season_nr);
+    short stikData_atom = 
APar_MetaData_atom_Init("moov.udta.meta.ilst.stik.data", value, 
AtomFlags_Data_UInt);
+    APar_Unified_atom_Put(stikData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, mediaTypeTag, 8);
+    g_free(value);
+
+    // TV Season No.
+    if (track->season_nr == 0) {
+        APar_RemoveAtom("moov.udta.meta.ilst.tvsn.data", VERSIONED_ATOM, 0);
+    } else {
+        value = g_strdup_printf("%d", track->season_nr);
+        short tvseasonData_atom = 
APar_MetaData_atom_Init("moov.udta.meta.ilst.tvsn.data", value, 
AtomFlags_Data_UInt);
+        APar_Unified_atom_Put(tvseasonData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, 0, 16);
+        APar_Unified_atom_Put(tvseasonData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, track->season_nr, 16);
+        g_free(value);
+    }
+
+    // TV Episode No.
+    if(track->episode_nr == 0) {
+        APar_RemoveAtom("moov.udta.meta.ilst.tves.data", VERSIONED_ATOM, 0);
+    } else {
+        value = g_strdup_printf("%d", track->episode_nr);
+        short tvepisodenumData_atom = 
APar_MetaData_atom_Init("moov.udta.meta.ilst.tves.data", value, 
AtomFlags_Data_UInt);
+        APar_Unified_atom_Put(tvepisodenumData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, 0, 16);
+        APar_Unified_atom_Put(tvepisodenumData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, track->episode_nr, 16);
+        g_free(value);
+    }
+
+    // Keywords
+    set_limited_text_atom_value(KEYWORD, track->keywords);
+
+    // Podcast Category
+    set_limited_text_atom_value(CATEGORY, track->category);
+
+    // Podcast URL
+    if (! track->podcasturl || strlen(track->podcasturl) == 0) {
+        APar_RemoveAtom("moov.udta.meta.ilst.purl.data", VERSIONED_ATOM, 0);
+    } else {
+        short podcasturlData_atom = 
APar_MetaData_atom_Init("moov.udta.meta.ilst.purl.data", track->podcasturl, 
AtomFlags_Data_Binary);
+        APar_Unified_atom_Put(podcasturlData_atom, track->podcasturl, 
UTF8_iTunesStyle_Binary, 0, 0);
+    }
+
+    // Gapless Playback
+    if (! track->gapless_track_flag) {
+        APar_RemoveAtom("moov.udta.meta.ilst.pgap.data", VERSIONED_ATOM, 0);
+    } else {
+        value = g_strdup_printf("%d", track->gapless_track_flag);
+        short gaplessData_atom = 
APar_MetaData_atom_Init("moov.udta.meta.ilst.pgap.data", value, 
AtomFlags_Data_UInt);
+        APar_Unified_atom_Put(gaplessData_atom, NULL, 
UTF8_iTunesStyle_256glyphLimited, 1, 8); //a hard coded uint8_t of: 1 is gapl
+        g_free(value);
+    }
+
+    GdkPixbuf *pixbuf = (GdkPixbuf*) 
itdb_artwork_get_pixbuf(track->itdb->device, track->artwork, -1, -1);
+    if (! pixbuf) {
+        // Destroy any existing artwork if any
+        APar_MetaData_atomArtwork_Set("REMOVE_ALL", NULL);
+    }
+    else {
+        gchar *tmp_file = g_build_filename(g_get_tmp_dir(), "ttt.jpg", NULL);
+        GError *pixbuf_err = NULL;
+
+        gdk_pixbuf_save (pixbuf, tmp_file, "jpeg", &pixbuf_err, "quality", 
"100", NULL);
+
+        if (!pixbuf_err) {
+            APar_MetaData_atomArtwork_Set(tmp_file, NULL);
+            g_remove(tmp_file);
+        }
+        else {
+            gtkpod_log_error(error,
+                    g_strdup_printf(_("ERROR failed to change track file's 
artwork.")));
+            g_error_free(pixbuf_err);
+            return;
+        }
+
+        g_free(tmp_file);
+        g_object_unref(pixbuf);
+    }
+
+    //after all the modifications are enacted on the tree in memory
+    // THEN write out the changes
+    APar_DetermineAtomLengths();
+    openSomeFile(filePath, true);
+    APar_WriteFile(filePath, NULL, true);
+
+    APar_FreeMemory();
+}
diff --git a/libs/atomic-parsley/AtomicParsleyBridge.h 
b/libs/atomic-parsley/AtomicParsleyBridge.h
index 63ba7bc..196ad20 100644
--- a/libs/atomic-parsley/AtomicParsleyBridge.h
+++ b/libs/atomic-parsley/AtomicParsleyBridge.h
@@ -31,52 +31,38 @@
 
 #include "libgtkpod/itdb.h"
 
+/**
+ * Open and scan the metadata of the m4a/mp4/... file
+ * and populate the given track.
+ */
 #ifdef __cplusplus
 extern "C"
 #endif
-void AP_extract_metadata(const char *path, Track *track);
-
+void AP_extract_metadata(const char *filePath, Track *track);
 
-
-/*
- * Want to use:
- * APar_ExtractTrackDetails
- * APar_ExtractMovieDetails
- * APar_Extract_iods_Info
+/**
+ * Using the given track, set the metadata of the target
+ * file
  */
+#ifdef __cplusplus
+extern "C"
+#endif
+void AP_write_metadata(Track *track, const char *filePath, GError **error);
 
-//char *MP4GetMetadataName(const ca, &value);
-//MP4GetMetadataArtist(mp4File, &value) && value
-//MP4GetMetadataAlbumArtist(mp4File, &value) && value
-//MP4GetMetadataComposer(mp4File, &value) && value != NULL)
-//MP4GetMetadataComment(mp4File, &value) && value
-//MP4GetMetadataReleaseDate(mp4File, &value) && value != NULL)
-//MP4GetMetadataAlbum(mp4File, &value) && value
-//MP4GetMetadataTrack(mp4File, &numvalue, &numvalue2))
-//MP4GetMetadataDisk(mp4File, &numvalue, &numvalue2))
-//MP4GetMetadataGrouping(mp4File, &value) && value
-//MP4GetMetadataGenre(mp4File, &value) && value
-//MP4GetMetadataBPM(mp4File, &numvalue))
+/**
+ * Read any lyrics from the given file
+ */
+#ifdef __cplusplus
+extern "C"
+#endif
+char *AP_read_lyrics(const char *filePath, GError **error);
 
-//MP4GetMetadataLyrics(mp4File);
-//
-//MP4GetMetadataTVShow
-//
-//MP4TagsFetchFunc( mp4tags, mp4File);
-//mp4tags->tvShow)
-//track->tvshow = g_strdup(mp4tags->tvShow);
-//mp4tags->tvEpisodeID)
-//track->tvepisode = g_strdup(mp4tags->tvEpisodeID);
-//mp4tags->tvNetwork)
-//track->tvnetwork = g_strdup(mp4tags->tvNetwork);
-//mp4tags->tvSeason)
-//track->season_nr = *mp4tags->tvSeason;
-//mp4tags->tvEpisode)
-//track->episode_nr = *mp4tags->tvEpisode;
-//mp4tags->tvEpisode)
-//track->mediatype = mediaTypeTagToMediaType(*mp4tags->mediaType);
-//MP4TagsFreeFunc( mp4tags);
-//
-//MP4GetMetadataCoverArt(mp4File, &image_data, &image_data_len, 0))
+/**
+ * Write lyrics to the file at filePath
+ */
+#ifdef __cplusplus
+extern "C"
+#endif
+void AP_write_lyrics(const char *lyrics, const char *filePath, GError **error);
 
 #endif /* ATOMIC_PARSLEY_BRIDGE_H_ */
diff --git a/plugins/filetype_m4a/Makefile.am b/plugins/filetype_m4a/Makefile.am
index a75d807..c4bd199 100644
--- a/plugins/filetype_m4a/Makefile.am
+++ b/plugins/filetype_m4a/Makefile.am
@@ -32,7 +32,7 @@ plugin_LTLIBRARIES = libfiletype_m4a.la
 libfiletype_m4a_la_SOURCES = plugin.c plugin.h \
                                                          m4afile.c m4afile.h
 
-libfiletype_m4a_la_CFLAGS = -I $(top_srcdir)/libs/atomic-parsley
+libfiletype_m4a_la_CFLAGS = -I $(top_builddir)/libs/atomic-parsley
 libfiletype_m4a_la_LDFLAGS = $(GTKPOD_PLUGIN_LDFLAGS)
 
 # Plugin dependencies
diff --git a/plugins/filetype_m4a/m4afile.c b/plugins/filetype_m4a/m4afile.c
index 8b761f2..894589c 100644
--- a/plugins/filetype_m4a/m4afile.c
+++ b/plugins/filetype_m4a/m4afile.c
@@ -56,7 +56,7 @@ static void m4a_set_media_type(const gchar *m4aFileName, 
Track *track) {
             track->mediatype = ITDB_MEDIATYPE_AUDIOBOOK;
             track->filetype = g_strdup(_("AAC audio book file"));
         }
-        else if (g_ascii_strcasecmp(value, ".mp4") == 0) {
+        else if (g_ascii_strcasecmp(value, ".m4a") == 0) {
             track->mediatype = ITDB_MEDIATYPE_MOVIE;
             track->movie_flag = 0x01;
             track->filetype = g_strdup(_("MP4 video file"));
@@ -80,6 +80,24 @@ Track *m4a_get_file_info(const gchar *m4aFileName, GError 
**error) {
 }
 
 gboolean m4a_write_file_info(const gchar *filename, Track *track, GError 
**error) {
+    AP_write_metadata(track, filename, error);
+
+    return error ? TRUE : FALSE;
+}
+
+gboolean m4a_read_lyrics(const gchar *m4aFileName, gchar **lyrics, GError 
**error) {
+    gchar *value = AP_read_lyrics(m4aFileName, error);
+    *lyrics = value;
+    return error ? TRUE : FALSE;
+}
+
+gboolean m4a_write_lyrics(const gchar *m4aFileName, const gchar *lyrics, 
GError **error) {
+    AP_write_lyrics(lyrics, m4aFileName, error);
+
+    return error ? TRUE : FALSE;
+}
+
+gboolean m4a_read_gapless(const gchar *filename, Track *track, GError **error) 
{
     return FALSE;
 }
 
diff --git a/plugins/filetype_m4a/m4afile.h b/plugins/filetype_m4a/m4afile.h
index cd488d1..4387ac8 100644
--- a/plugins/filetype_m4a/m4afile.h
+++ b/plugins/filetype_m4a/m4afile.h
@@ -33,6 +33,9 @@
 
 Track *m4a_get_file_info (const gchar *m4aFileName, GError **error);
 gboolean m4a_write_file_info (const gchar *filename, Track *track, GError 
**error);
+gboolean m4a_read_lyrics(const gchar *filename, gchar **lyrics, GError 
**error);
+gboolean m4a_write_lyrics(const gchar *filename, const gchar *lyrics, GError 
**error);
+gboolean m4a_read_gapless(const gchar *filename, Track *track, GError **error);
 gboolean m4a_can_convert();
 gchar *m4a_get_conversion_cmd();
 gchar *m4a_get_gain_cmd();
diff --git a/plugins/filetype_m4a/plugin.c b/plugins/filetype_m4a/plugin.c
index 2d2090c..c519d64 100644
--- a/plugins/filetype_m4a/plugin.c
+++ b/plugins/filetype_m4a/plugin.c
@@ -86,9 +86,9 @@ static void m4a_filetype_iface_init(FileTypeInterface *iface) 
{
     iface->get_file_info = m4a_get_file_info;
     iface->write_file_info = m4a_write_file_info;
     iface->read_soundcheck = filetype_no_soundcheck;
-    iface->read_lyrics = filetype_no_read_lyrics;
-    iface->write_lyrics = filetype_no_write_lyrics;
-    iface->read_gapless = filetype_no_read_gapless;
+    iface->read_lyrics = m4a_read_lyrics;
+    iface->write_lyrics = m4a_write_lyrics;
+    iface->read_gapless = m4a_read_gapless;
     iface->can_convert = m4a_can_convert;
     iface->get_conversion_cmd = m4a_get_conversion_cmd;
     iface->get_gain_cmd = m4a_get_gain_cmd;
diff --git a/plugins/filetype_mp4/Makefile.am b/plugins/filetype_mp4/Makefile.am
index 114a277..ff1a1c3 100644
--- a/plugins/filetype_mp4/Makefile.am
+++ b/plugins/filetype_mp4/Makefile.am
@@ -32,7 +32,7 @@ plugin_LTLIBRARIES = libfiletype_mp4.la
 libfiletype_mp4_la_SOURCES = plugin.c plugin.h \
                                                          mp4file.c mp4file.h
 
-libfiletype_mp4_la_CFLAGS = -I $(top_srcdir)/libs/atomic-parsley
+libfiletype_mp4_la_CFLAGS = -I $(top_builddir)/libs/atomic-parsley
 libfiletype_mp4_la_LDFLAGS = $(GTKPOD_PLUGIN_LDFLAGS)
 
 # Plugin dependencies
diff --git a/plugins/filetype_mp4/mp4file.c b/plugins/filetype_mp4/mp4file.c
index b0eb448..e426515 100644
--- a/plugins/filetype_mp4/mp4file.c
+++ b/plugins/filetype_mp4/mp4file.c
@@ -152,12 +152,31 @@ Track *mp4_get_file_info(const gchar *mp4FileName, GError 
**error) {
     track = gp_track_new();
 
     mp4_set_media_type(mp4FileName, track);
-
     AP_extract_metadata(mp4FileName, track);
 
     return track;
 }
 
 gboolean mp4_write_file_info(const gchar *mp4FileName, Track *track, GError 
**error) {
+    AP_write_metadata(track, mp4FileName, error);
+
+    return error ? TRUE : FALSE;
+}
+
+gboolean mp4_read_lyrics(const gchar *mp4FileName, gchar **lyrics, GError 
**error) {
+    char *value = AP_read_lyrics(mp4FileName, error);
+
+    *lyrics = value;
+
+    return error ? TRUE : FALSE;
+}
+
+gboolean mp4_write_lyrics(const gchar *mp4FileName, const gchar *lyrics, 
GError **error) {
+    AP_write_lyrics(lyrics, mp4FileName, error);
+
+    return error ? TRUE : FALSE;
+}
+
+gboolean mp4_read_gapless(const gchar *filename, Track *track, GError **error) 
{
     return FALSE;
 }
diff --git a/plugins/filetype_mp4/mp4file.h b/plugins/filetype_mp4/mp4file.h
index bf3dcdb..1008e3c 100644
--- a/plugins/filetype_mp4/mp4file.h
+++ b/plugins/filetype_mp4/mp4file.h
@@ -34,5 +34,7 @@
 
 gboolean mp4_write_file_info (const gchar *filename, Track *track, GError 
**error);
 Track *mp4_get_file_info (const gchar *name, GError **error);
-gboolean mp4_read_soundcheck (const gchar *filename, Track *track, GError 
**error);
+gboolean mp4_read_lyrics(const gchar *filename, gchar **lyrics, GError 
**error);
+gboolean mp4_write_lyrics(const gchar *filename, const gchar *lyrics, GError 
**error);
+gboolean mp4_read_gapless(const gchar *filename, Track *track, GError **error);
 #endif
diff --git a/plugins/filetype_mp4/plugin.c b/plugins/filetype_mp4/plugin.c
index ea5f591..9b7cf4d 100644
--- a/plugins/filetype_mp4/plugin.c
+++ b/plugins/filetype_mp4/plugin.c
@@ -83,9 +83,9 @@ static void mp4_filetype_iface_init(FileTypeInterface *iface) 
{
     iface->get_file_info = mp4_get_file_info;
     iface->write_file_info = mp4_write_file_info;
     iface->read_soundcheck = filetype_no_soundcheck;
-    iface->read_lyrics = filetype_no_read_lyrics;
-    iface->write_lyrics = filetype_no_write_lyrics;
-    iface->read_gapless = filetype_no_read_gapless;
+    iface->read_lyrics = mp4_read_lyrics;
+    iface->write_lyrics = mp4_write_lyrics;
+    iface->read_gapless = mp4_read_gapless;
     iface->can_convert = filetype_no_convert;
     iface->get_conversion_cmd = filetype_no_conversion_cmd;
     iface->get_gain_cmd = filetype_no_gain_cmd;

------------------------------------------------------------------------------
For Developers, A Lot Can Happen In A Second.
Boundary is the first to Know...and Tell You.
Monitor Your Applications in Ultra-Fine Resolution. Try it FREE!
http://p.sf.net/sfu/Boundary-d2dvs2
_______________________________________________
gtkpod-cvs2 mailing list
gtkpod-cvs2@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/gtkpod-cvs2

Reply via email to