commit 3d6b05ecd6f3ec0b8cfe14ce4f40bca486a74bda Author: Joel Smith <jsf-lists.gtk...@jk1.net> Date: Wed Apr 14 21:58:19 2010 -0600
Support for reading PlayCounts.plist for play counts, stats and bookmarks. iPhone/iPod Touch 3.x use this file, so without this support, bookmarks are lost during sync. src/itdb_itunesdb.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/itdb_private.h | 1 + 2 files changed, 116 insertions(+), 5 deletions(-) --- diff --git a/src/itdb_itunesdb.c b/src/itdb_itunesdb.c index f6fa52c..60bec2e 100644 --- a/src/itdb_itunesdb.c +++ b/src/itdb_itunesdb.c @@ -111,6 +111,7 @@ #include "itdb_device.h" #include "itdb_private.h" #include "itdb_zlib.h" +#include "itdb_plist.h" #include <errno.h> #include <fcntl.h> @@ -905,7 +906,7 @@ static void playcounts_free (FImport *fimp) } -/* called by init_playcounts */ +/* called by playcounts_init */ static gboolean playcounts_read (FImport *fimp, FContents *cts) { GList* playcounts = NULL; @@ -1016,7 +1017,7 @@ static gboolean playcounts_read (FImport *fimp, FContents *cts) } -/* called by init_playcounts */ +/* called by playcounts_init */ static gboolean itunesstats_read (FImport *fimp, FContents *cts) { GList* playcounts = NULL; @@ -1118,6 +1119,85 @@ static gboolean itunesstats_read (FImport *fimp, FContents *cts) return TRUE; } +static gint64 playcounts_plist_get_gint64 (GHashTable *track_dict, + const char *key) +{ + GValue *value; + + value = g_hash_table_lookup (track_dict, key); + if (value && G_VALUE_HOLDS (value, G_TYPE_INT64)) { + return g_value_get_int64 (value); + } + + return 0; +} + +/* called by playcounts_init */ +static gboolean playcounts_plist_read (FImport *fimp, GValue *plist_data) +{ + GHashTable *playcounts; + struct playcount *playcount; + GHashTable *pc_dict, *track_dict; + GValue *to_parse; + GValueArray *array; + gint i; + guint32 mac_time; + guint64 *dbid; + + g_return_val_if_fail (G_VALUE_HOLDS (plist_data, G_TYPE_HASH_TABLE), FALSE); + pc_dict = g_value_get_boxed (plist_data); + + to_parse = g_hash_table_lookup (pc_dict, "tracks"); + if (to_parse == NULL) { + return FALSE; + } + if (!G_VALUE_HOLDS (to_parse, G_TYPE_VALUE_ARRAY)) { + return FALSE; + } + + playcounts = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_free); + + array = (GValueArray*)g_value_get_boxed (to_parse); + for (i = 0; i < array->n_values; i++) { + if (!G_VALUE_HOLDS (g_value_array_get_nth (array, i), G_TYPE_HASH_TABLE)) { + continue; + } + + track_dict = g_value_get_boxed (g_value_array_get_nth (array, i)); + if (track_dict == NULL) + continue; + + to_parse = g_hash_table_lookup (track_dict, "persistentID"); + if (!to_parse) + continue; + + dbid = g_new0 (guint64, 1); + if (!G_VALUE_HOLDS (to_parse, G_TYPE_INT64)) + continue; + *dbid = g_value_get_int64 (to_parse); + playcount = g_new0 (struct playcount, 1); + g_hash_table_insert (playcounts, dbid, playcount); + + playcount->bookmark_time = playcounts_plist_get_gint64 (track_dict, "bookmarkTimeInMS"); + playcount->playcount = playcounts_plist_get_gint64 (track_dict, "playCount"); + mac_time = playcounts_plist_get_gint64 (track_dict, "playMacOSDate"); + playcount->time_played = device_time_mac_to_time_t (fimp->itdb->device, mac_time); + playcount->skipcount = playcounts_plist_get_gint64 (track_dict, "skipCount"); + mac_time = playcounts_plist_get_gint64 (track_dict, "skipMacOSDate"); + playcount->last_skipped = device_time_mac_to_time_t (fimp->itdb->device, mac_time); + playcount->rating = playcounts_plist_get_gint64 (track_dict, "userRating"); + if (!playcount->rating) + playcount->rating = NO_PLAYCOUNT; + + to_parse = g_hash_table_lookup (track_dict, "playedState"); + if (to_parse && G_VALUE_HOLDS (to_parse, G_TYPE_BOOLEAN)) { + ; /* What do we do with this? */ + } + } + fimp->pcounts2 = playcounts; + return TRUE; +} + /* Read the Play Count file (formed by adding "Play Counts" to the * directory component of fimp->itdb->itdb_filename) and set up the * GList *playcounts. If no Play Count file is present, attempt to @@ -1131,10 +1211,12 @@ static gboolean playcounts_init (FImport *fimp) { const gchar *plc[] = {"Play Counts", NULL}; const gchar *ist[] = {"iTunesStats", NULL}; - gchar *plcname, *dirname, *istname; + const gchar *plcpl[] = {"PlayCounts.plist", NULL}; + gchar *plcname, *dirname, *istname, *plcplname; gboolean result=TRUE; struct stat filestat; FContents *cts; + GValue *plist_data; g_return_val_if_fail (fimp, FALSE); g_return_val_if_fail (!fimp->error, FALSE); @@ -1146,6 +1228,7 @@ static gboolean playcounts_init (FImport *fimp) plcname = itdb_resolve_path (dirname, plc); istname = itdb_resolve_path (dirname, ist); + plcplname = itdb_resolve_path (dirname, plcpl); g_free (dirname); @@ -1188,9 +1271,28 @@ static gboolean playcounts_init (FImport *fimp) } } } + else if (plcplname) + { + /* skip if PlayCounts.plist file has zero-length */ + stat (plcplname, &filestat); + if (filestat.st_size > 0) + { + plist_data = itdb_plist_parse_from_file (plcplname, &fimp->error); + if (plist_data) + { + result = playcounts_plist_read (fimp, plist_data); + g_value_unset (plist_data); + } + else + { + result = FALSE; + } + } + } g_free (plcname); g_free (istname); + g_free (plcplname); return result; } @@ -1206,6 +1308,7 @@ static void itdb_free_fimp (FImport *fimp) g_list_free (fimp->pos_glist); g_list_free (fimp->tracks); playcounts_free (fimp); + g_hash_table_destroy (fimp->pcounts2); g_free (fimp); } } @@ -2295,6 +2398,7 @@ static glong get_mhit (FImport *fimp, glong mhit_seek) guint32 i, mhod_nums; FContents *cts; glong seek = mhit_seek; + gboolean free_playcount; #if ITUNESDB_DEBUG fprintf(stderr, "get_mhit seek: %x\n", (int)seek); @@ -2566,6 +2670,12 @@ static glong get_mhit (FImport *fimp, glong mhit_seek) } playcount = playcount_take_next (fimp); + free_playcount = TRUE; + if (!playcount && fimp->pcounts2) + { + playcount = g_hash_table_lookup (fimp->pcounts2, &track->dbid); + free_playcount = FALSE; + } if (playcount) { if (playcount->rating != NO_PLAYCOUNT) @@ -2592,8 +2702,8 @@ static glong get_mhit (FImport *fimp, glong mhit_seek) track->skipcount += playcount->skipcount; track->recent_skipcount = playcount->skipcount; - - g_free (playcount); + if (free_playcount) + g_free (playcount); } fimp->tracks = g_list_prepend(fimp->tracks, track); return seek; diff --git a/src/itdb_private.h b/src/itdb_private.h index d2e6744..596c670 100644 --- a/src/itdb_private.h +++ b/src/itdb_private.h @@ -91,6 +91,7 @@ typedef struct GList *pos_glist; /* temporary list to store position indicators */ GList *tracks; /* temporary list to store tracks */ GList *playcounts; /* contents of Play Counts file */ + GHashTable *pcounts2;/* contents of the PlayCounts.plist file */ GTree *idtree; /* temporary tree with track id tree */ GError *error; /* where to report errors to */ } FImport; ------------------------------------------------------------------------------ Start uncovering the many advantages of virtual appliances and start using them to simplify application deployment and accelerate your shift to cloud computing. http://p.sf.net/sfu/novell-sfdev2dev _______________________________________________ gtkpod-cvs2 mailing list gtkpod-cvs2@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/gtkpod-cvs2