summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJorg Schuler <jcsjcs@users.sourceforge.net>2005-09-19 09:02:52 +0000
committerJorg Schuler <jcsjcs@users.sourceforge.net>2005-09-19 09:02:52 +0000
commit4289045ca3ccab8a59754bc68ff150649c381b95 (patch)
treeca65e034f038a5881f84e5d5669646c8f1f1fb7c
parent9e39523476ce080fcf2b43f875a957827131fadb (diff)
downloadlibgpod-4289045ca3ccab8a59754bc68ff150649c381b95.tar.gz
libgpod-4289045ca3ccab8a59754bc68ff150649c381b95.tar.xz
libgpod-4289045ca3ccab8a59754bc68ff150649c381b95.zip
2005-09-17 Jorg Schuler <jcsjcs at users.sourceforge.net>
* added additional fields to Itdb_Track that are present since version 0x0c of the iTunesDB. Write long mhit version. 2005-09-17 Jorg Schuler <jcsjcs at users.sourceforge.net> * renamed fdesc to filetype git-svn-id: https://gtkpod.svn.sf.net/svnroot/gtkpod/libgpod/trunk@1092 f01d2545-417e-4e96-918e-98f8d0dbbcb6
-rw-r--r--ChangeLog12
-rw-r--r--configure.ac7
-rw-r--r--src/itdb.h107
-rw-r--r--src/itdb_itunesdb.c490
-rw-r--r--src/itdb_playlist.c4
-rw-r--r--src/itdb_track.c97
6 files changed, 551 insertions, 166 deletions
diff --git a/ChangeLog b/ChangeLog
index bc91c00..96fa166 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2005-09-17 Jorg Schuler <jcsjcs at users.sourceforge.net>
+
+ * added additional fields to Itdb_Track that are present since
+ version 0x0c of the iTunesDB. Write long mhit version.
+
+2005-09-17 Jorg Schuler <jcsjcs at users.sourceforge.net>
+
+ * renamed fdesc to filetype
+
+ * po/it.po: updated (thanks to Edward Matteucci)
+
+
2005-09-16 Jorg Schuler <jcsjcs at users.sourceforge.net>
* itdb.h/itdb_itunesdb.c: changed 'static void
diff --git a/configure.ac b/configure.ac
index 06ba65d..520ac4b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,8 +13,8 @@ AM_CONFIG_HEADER(config.h)
#
LIBGPOD_MAJOR_VERSION=0
LIBGPOD_MINOR_VERSION=1
-LIBGPOD_MICRO_VERSION=1
-LIBGPOD_INTERFACE_AGE=1
+LIBGPOD_MICRO_VERSION=2
+LIBGPOD_INTERFACE_AGE=0
# If you need a modifier for the version number.
# Normally empty, but can be used to make "fixup" releases.
LIBGPOD_EXTRAVERSION=
@@ -23,6 +23,9 @@ dnl libtool versioning from libgnome
LIBGPOD_CURRENT=`expr 100 '*' $LIBGPOD_MINOR_VERSION + $LIBGPOD_MICRO_VERSION - $LIBGPOD_INTERFACE_AGE`
LIBGPOD_BINARY_AGE=`expr 100 '*' $LIBGPOD_MINOR_VERSION + $LIBGPOD_MICRO_VERSION`
+
+LIBGPOD_BINARY_AGE=0
+
LIBGPOD_REVISION=$LIBGPOD_INTERFACE_AGE
LIBGPOD_AGE=`expr $LIBGPOD_BINARY_AGE - $LIBGPOD_INTERFACE_AGE`
LIBGPOD_VERSION=$LIBGPOD_MAJOR_VERSION.$LIBGPOD_MINOR_VERSION.$LIBGPOD_MICRO_VERSION$LIBGPOD_EXTRAVERSION
diff --git a/src/itdb.h b/src/itdb.h
index ebed05d..91f1cc8 100644
--- a/src/itdb.h
+++ b/src/itdb.h
@@ -1,4 +1,4 @@
-/* Time-stamp: <2005-09-16 23:48:44 jcs>
+/* Time-stamp: <2005-09-19 17:50:40 jcs>
|
| Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
| Part of the gtkpod project.
@@ -353,19 +353,29 @@ typedef struct
} Itdb_Playlist;
+/* some of the descriptive comments below are copied verbatim from
+ http://ipodlinux.org/ITunesDB.
+ http://ipodlinux.org/ITunesDB is the best source for information
+ about the iTunesDB and related files. */
typedef struct
{
Itdb_iTunesDB *itdb; /* pointer to iTunesDB (for convenience) */
+ gchar *title; /* title (utf8) */
+ gchar *ipod_path; /* name of file on iPod: uses ":"
+ instead of "/" */
gchar *album; /* album (utf8) */
gchar *artist; /* artist (utf8) */
- gchar *title; /* title (utf8) */
gchar *genre; /* genre (utf8) */
+ gchar *filetype; /* eg. "MP3-File"...(utf8)*/
gchar *comment; /* comment (utf8) */
+ gchar *category; /* Category for podcast */
gchar *composer; /* Composer (utf8) */
- gchar *fdesc; /* eg. "MP3-File"...(utf8)*/
gchar *grouping; /* ? (utf8) */
- gchar *ipod_path; /* name of file on iPod: uses ":"
- instead of "/" */
+ gchar *description; /* see note for MHOD_ID in itdb_itunesdb.c */
+ gchar *podcasturl; /* see note for MHOD_ID in itdb_itunesdb.c */
+ gchar *podcastrss; /* see note for MHOD_ID in itdb_itunesdb.c */
+ gchar *chapterdata; /* see note for MHOD_ID in itdb_itunesdb.c */
+ gchar *subtitle; /* see note for MHOD_ID in itdb_itunesdb.c */
guint32 id; /* unique ID of track */
gint32 size; /* size of file in bytes */
gint32 tracklen; /* Length of track in ms */
@@ -384,19 +394,96 @@ typedef struct
guint32 bookmark_time; /* bookmark set for (AudioBook) in ms */
guint32 rating; /* star rating (stars * RATING_STEP (20)) */
guint32 playcount; /* number of times track was played */
- guint32 recent_playcount; /* times track was played since last sync */
+ guint32 playcount2; /* Also stores the play count of the
+ song. Don't know if it ever differs
+ from the above value. During sync itdb
+ sets playcount2 to the same value as
+ playcount. */
+ guint32 recent_playcount; /* times track was played since last sync */
gboolean transferred; /* has file been transferred to iPod? */
gint16 BPM; /* supposed to vary the playback speed */
guint8 app_rating; /* star rating set by appl. (not iPod) */
- guint16 type;
+ guint16 type; /* CBR MP3s are type 0x100, VBR MP3s are
+ type 0x101, AAC are type 0x0 */
guint8 compilation;
guint32 starttime;
guint32 stoptime;
guint8 checked;
guint64 dbid; /* unique database ID */
-/* present in the mhit but not used by gtkpod yet */
- guint32 unk020, unk024, unk084, unk100, unk124;
- guint32 unk128, unk132, unk136, unk140, unk144, unk148, unk152;
+ guint32 drm_userid; /* Apple Store/Audible User ID (for DRM'ed
+ files only, set to 0 otherwise). */
+ guint32 visible; /* If this value is 1, the song is visible
+ on the iPod. All other values cause
+ the file to be hidden. */
+ gchar filetype_marker[4]; /* This appears to always be 0 on hard
+ drive based iPods, but for the
+ iTunesDB that is written to an iPod
+ Shuffle, iTunes 4.7.1 writes out the
+ file's type as an ANSI string(!). For
+ example, a MP3 file has a filetype of
+ 0x4d503320 -> 0x4d = 'M', 0x50 = 'P',
+ 0x33 = '3', 0x20 = <space>. (always
+ left set to 0 by itdb)*/
+ guint16 artwork_count; /* The number of album artwork items
+ associated with this song. */
+ guint32 artwork_size; /* The total size of artwork (in bytes)
+ attached to this song, when it is
+ converted to JPEG format. Observed in
+ iPodDB version 0x0b and with an iPod
+ Photo. */
+ float samplerate2; /* The sample rate of the song expressed
+ as an IEEE 32 bit floating point
+ number. It's uncertain why this is
+ here. itdb will set this when adding
+ a track */
+
+ guint16 unk060; /* unknown */
+ guint16 unk126; /* unknown, but always seems to be 0xffff for
+ MP3/AAC songs, 0x0 for uncompressed songs
+ (like WAVE format), 0x1 for Audible. itdb
+ will try to set this when adding a new track */
+ guint32 unk132; /* unknown */
+ guint32 unk140; /* date/time added to music store? definitely a
+ timestamp, always appears to be a time of
+ 0700 GMT */
+ guint32 unk144; /* unknown, but MP3 songs appear to be always
+ 0x0000000c or 0x0100000c (if played one or
+ more times in iTunes), AAC songs are always
+ 0x01000033, Audible files are 0x01000029, WAV
+ files are 0x0. itdb will attempt to set this
+ value when adding a track. */
+ guint32 unk148; /* unknown - used for Apple Store DRM songs
+ (always 0x01010100?), zero otherwise */
+ guint32 unk152; /* unknown */
+ guint32 unk156, unk160;
+ guint32 unk164; /* unknown (0x02?) in dbversion 0x0d, for
+ podcasts this seems to be set to
+ 0x01010102. The 0x10000 bit seems to control
+ whether or not iTunes remembers the last
+ played position of this song. This will work
+ on any song, not just podcasts.
+ Assumption:
+ Audiobooks also get this bit set by
+ default. Assumption2: This is really four
+ single byte flags, perhaps? Might want to try
+ find various examples and compare them. */
+ guint64 dbid2; /* not clear. if not set, itdb will set this to
+ the same value as dbid when adding a track */
+ guint32 unk176; /* unknown - added in dbversion 0x0c, first
+ values observed in 0x0d. Observed to be
+ 0x00010000 for non-podcasts and 0x00020000
+ for a podcast. */
+ guint32 unk180, unk184;
+ guint32 samplecount;/* Number of samples in the song. First observed
+ in dbversion 0x0d, and only for AAC and WAV
+ files (not MP3?!?). */
+ guint32 unk192, unk196, unk200;
+ guint32 unk204; /* unknown - added in dbversion 0x0c, first
+ values observed in 0x0d. Observed to be 0x0
+ or 0x1. */
+ guint32 unk208, unk212, unk216, unk220, unk224;
+ guint32 unk228, unk232, unk236, unk240;
+
/* below is for use by application */
guint64 usertype;
gpointer userdata;
diff --git a/src/itdb_itunesdb.c b/src/itdb_itunesdb.c
index 8fed2c3..ad6d747 100644
--- a/src/itdb_itunesdb.c
+++ b/src/itdb_itunesdb.c
@@ -1,4 +1,4 @@
-/* Time-stamp: <2005-09-16 23:51:04 jcs>
+/* Time-stamp: <2005-09-19 17:56:57 jcs>
|
| Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
| Part of the gtkpod project.
@@ -128,16 +128,42 @@
#define ITUNESDB_COPYBLK 262144 /* blocksize for cp () */
+/* Note: some of the comments for the MHOD_IDs are copied verbatim
+ * from http://ipodlinux.org/ITunesDB */
+
enum MHOD_ID {
MHOD_ID_TITLE = 1,
- MHOD_ID_PATH = 2,
+ MHOD_ID_PATH = 2, /* file path on iPod (special format) */
MHOD_ID_ALBUM = 3,
MHOD_ID_ARTIST = 4,
MHOD_ID_GENRE = 5,
- MHOD_ID_FDESC = 6,
+ MHOD_ID_FILETYPE = 6,
+/* MHOD_ID_EQSETTING = 7, */
MHOD_ID_COMMENT = 8,
+/* Category - This is the category ("Technology", "Music", etc.) where
+ the podcast was located. Introduced in db version 0x0d. */
+ MHOD_ID_CATEGORY = 9,
MHOD_ID_COMPOSER = 12,
MHOD_ID_GROUPING = 13,
+/* Description text (such as podcast show notes). Accessible by
+ wselecting the center button on the iPod, where this string is
+ displayed along with the song title, date, and
+ timestamp. Introduced in db version 0x0d. */
+ MHOD_ID_DESCRIPTION = 14,
+/* Podcast Enclosure URL. Note: this is either a UTF-8 or ASCII
+ encoded string (NOT UTF-16). Also, there is no mhod::length value
+ for this type. Introduced in db version 0x0d. */
+ MHOD_ID_PODCASTURL = 15,
+/* Podcast RSS URL. Note: this is either a UTF-8 or ASCII encoded
+ string (NOT UTF-16). Also, there is no mhod::length value for this
+ type. Introduced in db version 0x0d. */
+ MHOD_ID_PODCASTRSS = 16,
+/* Chapter data. This is a m4a-style entry that is used to display
+ subsongs within a mhit. Introduced in db version 0x0d. */
+/* MHOD_ID_CHAPTERDATA = 17,*/
+/* Subtitle (usually the same as Description). Introduced in db
+ version 0x0d. */
+ MHOD_ID_SUBTITLE = 18,
MHOD_ID_SPLPREF = 50, /* settings for smart playlist */
MHOD_ID_SPLRULES = 51, /* rules for smart playlist */
MHOD_ID_MHYP = 52, /* unknown */
@@ -362,6 +388,22 @@ static guint8 get8int (FContents *cts, glong seek)
return n;
}
+/* Get the 2-byte-number stored at position "seek" in little endian
+ encoding. On error the GError in @cts is set. */
+static guint16 get16lint (FContents *cts, glong seek)
+{
+ guint32 n=0;
+
+ if (check_seek (cts, seek, 4))
+ {
+ g_return_val_if_fail (cts->contents, 0);
+ memcpy (&n, &cts->contents[seek], 4);
+# if (G_BYTE_ORDER == G_BIG_ENDIAN)
+ n = GUINT16_SWAP_LE_BE (n);
+# endif
+ }
+ return n;
+}
/* Get the 4-byte-number stored at position "seek" in little endian
encoding. On error the GError in @cts is set. */
@@ -380,6 +422,22 @@ static guint32 get32lint (FContents *cts, glong seek)
return n;
}
+/* Get 4 byte floating number */
+static float get32lfloat (FContents *cts, glong seek)
+{
+ union
+ {
+ guint32 i;
+ float f;
+ } flt;
+
+ g_return_val_if_fail (sizeof (float) == 4, 0);
+
+ flt.i = get32lint (cts, seek);
+
+ return flt.f;
+}
+
/* Get the 4-byte-number stored at position "seek" in big endian
encoding. On error the GError in @cts is set. */
@@ -828,21 +886,37 @@ static void *get_mhod (FContents *cts, gulong mhod_seek,
case MHOD_ID_ALBUM:
case MHOD_ID_ARTIST:
case MHOD_ID_GENRE:
- case MHOD_ID_FDESC:
+ case MHOD_ID_FILETYPE:
case MHOD_ID_COMMENT:
+ case MHOD_ID_CATEGORY:
case MHOD_ID_COMPOSER:
case MHOD_ID_GROUPING:
- xl = get32lint (cts, seek+4); /* entry length */
+ case MHOD_ID_DESCRIPTION:
+ case MHOD_ID_SUBTITLE:
+ xl = get32lint (cts, seek+4); /* length of string */
if (cts->error) return NULL;
entry_utf16 = g_new0 (gunichar2, (xl+2)/2);
if (seek_get_n_bytes (cts, (gchar *)entry_utf16, seek+16, xl))
{
- result = fixup_little_utf16 (entry_utf16);
+ fixup_little_utf16 (entry_utf16);
+ result = g_utf16_to_utf8 (entry_utf16, -1, NULL, NULL, NULL);
}
else
{ /* error */
- g_free (entry_utf16);
- return NULL;
+ result = NULL;
+ }
+ g_free (entry_utf16);
+ break;
+ case MHOD_ID_PODCASTURL:
+ case MHOD_ID_PODCASTRSS:
+ /* length of string */
+ xl = get32lint (cts, mhod_seek+8) - header_length;
+ if (cts->error) return NULL;
+ result = g_new0 (gchar, xl+1);
+ if (!seek_get_n_bytes (cts, (gchar *)result, seek, xl))
+ {
+ g_free (result);
+ result = NULL;
}
break;
case MHOD_ID_SPLPREF: /* Settings for smart playlist */
@@ -978,12 +1052,12 @@ static void *get_mhod (FContents *cts, gulong mhod_seek,
/* Returns the value of a string type mhod. return the length of the
mhod *ml, the mhod type *mty, and a string with the entry (in
- UTF16). After use you must free the string with g_free(). Returns
+ UTF8). After use you must free the string with g_free(). Returns
NULL if no string is avaible. *ml is set to -1 in case of error and
cts->error is set appropriately. */
-static gunichar2 *get_mhod_string (FContents *cts, glong seek, guint32 *ml, gint32 *mty)
+static gchar *get_mhod_string (FContents *cts, glong seek, guint32 *ml, gint32 *mty)
{
- gunichar2 *result = NULL;
+ gchar *result = NULL;
*mty = get_mhod_type (cts, seek, ml);
if (cts->error) return NULL;
@@ -995,10 +1069,15 @@ static gunichar2 *get_mhod_string (FContents *cts, glong seek, guint32 *ml, gint
case MHOD_ID_ALBUM:
case MHOD_ID_ARTIST:
case MHOD_ID_GENRE:
- case MHOD_ID_FDESC:
+ case MHOD_ID_FILETYPE:
case MHOD_ID_COMMENT:
+ case MHOD_ID_CATEGORY:
case MHOD_ID_COMPOSER:
case MHOD_ID_GROUPING:
+ case MHOD_ID_DESCRIPTION:
+ case MHOD_ID_PODCASTURL:
+ case MHOD_ID_PODCASTRSS:
+ case MHOD_ID_SUBTITLE:
result = get_mhod (cts, seek, ml, mty);
break;
case MHOD_ID_SPLPREF:
@@ -1022,7 +1101,7 @@ static glong get_playlist (FImport *fimp, glong seek)
return ((gint)a - (gint)b);
}
- gunichar2 *plname_utf16 = NULL;
+ gchar *plname_utf8 = NULL;
guint32 i, tracknum, mhod_num;
glong nextseek;
guint32 hlen;
@@ -1075,7 +1154,7 @@ static glong get_playlist (FImport *fimp, glong seek)
CHECK_ERROR (fimp, -1);
for (i=0; i < mhod_num; ++i)
{
- gunichar2 *plname_utf16_maybe;
+ gchar *plname_utf8_maybe;
SPLPref *splpref = NULL;
SPLRules *splrules = NULL;
gint32 type;
@@ -1089,13 +1168,13 @@ static glong get_playlist (FImport *fimp, glong seek)
/* here we could do something about the playlist settings */
break;
case MHOD_ID_TITLE:
- plname_utf16_maybe = get_mhod (cts, seek, &hlen, &type);
+ plname_utf8_maybe = get_mhod (cts, seek, &hlen, &type);
CHECK_ERROR (fimp, -1);
- if (plname_utf16_maybe)
+ if (plname_utf8_maybe)
{
/* sometimes there seem to be two mhod TITLE headers */
- g_free (plname_utf16);
- plname_utf16 = plname_utf16_maybe;
+ g_free (plname_utf8);
+ plname_utf8 = plname_utf8_maybe;
}
break;
case MHOD_ID_SPLPREF:
@@ -1124,10 +1203,15 @@ static glong get_playlist (FImport *fimp, glong seek)
case MHOD_ID_ALBUM:
case MHOD_ID_ARTIST:
case MHOD_ID_GENRE:
- case MHOD_ID_FDESC:
+ case MHOD_ID_FILETYPE:
case MHOD_ID_COMMENT:
+ case MHOD_ID_CATEGORY:
case MHOD_ID_COMPOSER:
case MHOD_ID_GROUPING:
+ case MHOD_ID_DESCRIPTION:
+ case MHOD_ID_PODCASTURL:
+ case MHOD_ID_PODCASTRSS:
+ case MHOD_ID_SUBTITLE:
/* these are not expected here */
break;
case MHOD_ID_MHYP:
@@ -1136,10 +1220,9 @@ static glong get_playlist (FImport *fimp, glong seek)
}
}
- if (plname_utf16)
+ if (plname_utf8)
{
- plitem->name = g_utf16_to_utf8 (plname_utf16, -1, NULL, NULL, NULL);
- g_free (plname_utf16);
+ plitem->name = plname_utf8;
}
else
{ /* we did not read a valid mhod TITLE header -> */
@@ -1239,11 +1322,11 @@ static glong get_mhit (FImport *fimp, glong seek)
{
Itdb_Track *track;
gchar *entry_utf8;
- gunichar2 *entry_utf16;
gint32 type;
+ guint32 header_len;
guint32 zip;
struct playcount *playcount;
- guint32 i, temp, mhod_nums;
+ guint32 i, mhod_nums;
FContents *cts;
#if ITUNESDB_DEBUG
@@ -1261,88 +1344,104 @@ static glong get_mhit (FImport *fimp, glong seek)
return -1;
}
+ header_len = get32lint (cts, seek+4);
+ CHECK_ERROR (fimp, -1);
+
+ /* Check if entire mhit can be read -- that way we won't have to
+ * check for read errors every time we access a single byte */
+
+ check_seek (cts, seek, header_len);
+
mhod_nums = get32lint (cts, seek+12);
CHECK_ERROR (fimp, -1);
track = itdb_track_new ();
- track->id = get32lint(cts, seek+16); /* iPod ID */
- CHECK_ERROR (fimp, -1);
- track->unk020 = get32lint (cts, seek+20);
- CHECK_ERROR (fimp, -1);
- track->unk024 = get32lint (cts, seek+24);
- CHECK_ERROR (fimp, -1);
- temp = get32lint (cts, seek+28);
- CHECK_ERROR (fimp, -1);
- track->rating = (temp & 0xff000000) >> 24; /* rating */
- track->compilation = (temp & 0x00ff0000) >> 16;
- track->type = temp & 0x0000ffff;
- track->time_added = get32lint(cts, seek+32); /* time added */
- CHECK_ERROR (fimp, -1);
- track->size = get32lint(cts, seek+36); /* file size */
- CHECK_ERROR (fimp, -1);
- track->tracklen = get32lint(cts, seek+40); /* time */
- CHECK_ERROR (fimp, -1);
- track->track_nr = get32lint(cts, seek+44); /* track number */
- CHECK_ERROR (fimp, -1);
- track->tracks = get32lint(cts, seek+48); /* nr of tracks */
- CHECK_ERROR (fimp, -1);
- track->year = get32lint(cts, seek+52); /* year */
- CHECK_ERROR (fimp, -1);
- track->bitrate = get32lint(cts, seek+56); /* bitrate */
- CHECK_ERROR (fimp, -1);
- track->samplerate = get32lint(cts,seek+60)>>16; /* sample rate */
- CHECK_ERROR (fimp, -1);
- track->volume = get32lint(cts, seek+64); /* volume adjust */
- CHECK_ERROR (fimp, -1);
- track->starttime = get32lint (cts, seek+68);
- CHECK_ERROR (fimp, -1);
- track->stoptime = get32lint (cts, seek+72);
- CHECK_ERROR (fimp, -1);
- track->soundcheck = get32lint (cts, seek+76); /* soundcheck */
- CHECK_ERROR (fimp, -1);
- track->playcount = get32lint (cts, seek+80); /* playcount */
- CHECK_ERROR (fimp, -1);
- track->unk084 = get32lint (cts, seek+84);
- CHECK_ERROR (fimp, -1);
- track->time_played = get32lint(cts, seek+88); /* last time played */
- CHECK_ERROR (fimp, -1);
- track->cd_nr = get32lint(cts, seek+92); /* CD nr */
- CHECK_ERROR (fimp, -1);
- track->cds = get32lint(cts, seek+96); /* CD nr of.. */
- CHECK_ERROR (fimp, -1);
- track->unk100 = get32lint (cts, seek+100);
- CHECK_ERROR (fimp, -1);
- track->time_modified = get32lint(cts, seek+104);/* last mod. time */
- CHECK_ERROR (fimp, -1);
- track->bookmark_time = get32lint (cts, seek+108); /* time bookmarked */
- CHECK_ERROR (fimp, -1);
- track->dbid = get64lint (cts, seek+112);
- CHECK_ERROR (fimp, -1);
- temp = get32lint (cts, seek+120);
- CHECK_ERROR (fimp, -1);
- track->BPM = temp >> 16;
- track->app_rating = (temp & 0xff00)>> 8;/* The rating set by * the
- application, as opposed to
- the rating set on the iPod
- itself */
- track->checked = temp & 0xff; /* Checked/Unchecked: 0/1 */
- track->unk124 = get32lint (cts, seek+124);
- CHECK_ERROR (fimp, -1);
- track->unk128 = get32lint (cts, seek+128);
- CHECK_ERROR (fimp, -1);
- track->unk132 = get32lint (cts, seek+132);
- CHECK_ERROR (fimp, -1);
- track->unk136 = get32lint (cts, seek+136);
- CHECK_ERROR (fimp, -1);
- track->unk140 = get32lint (cts, seek+140);
- CHECK_ERROR (fimp, -1);
- track->unk144 = get32lint (cts, seek+144);
- CHECK_ERROR (fimp, -1);
- track->unk148 = get32lint (cts, seek+148);
- CHECK_ERROR (fimp, -1);
- track->unk152 = get32lint (cts, seek+152);
- CHECK_ERROR (fimp, -1);
+ /* size of the mhit header: For dbversion <= 0x0b (iTunes 4.7 and
+ earlier), the length is 0x9c. As of dbversion 0x0c and 0x0d
+ (iTunes 4.7.1 - iTunes 4.9), the size is 0xf4. */
+ if (header_len < 0x9c)
+ {
+ g_return_val_if_fail (cts->filename, FALSE);
+ g_set_error (&fimp->error,
+ ITDB_FILE_ERROR,
+ ITDB_FILE_ERROR_CORRUPT,
+ _("mhit header length smaller than expected (%x < 0x9c) at offset %ld in file '%s'."),
+ header_len, seek, cts->filename);
+ return -1;
+ }
+
+ if (header_len >= 0x9c)
+ {
+ track->id = get32lint(cts, seek+16); /* iPod ID */
+ track->visible = get32lint (cts, seek+20);
+ seek_get_n_bytes (cts, track->filetype_marker, seek+24, 4);
+ track->type = get16lint (cts, seek+28);
+ track->compilation = get8int (cts, seek+30);
+ track->rating = get8int (cts, seek+31);
+ track->time_added = get32lint(cts, seek+32); /* time added */
+ track->size = get32lint(cts, seek+36); /* file size */
+ track->tracklen = get32lint(cts, seek+40); /* time */
+ track->track_nr = get32lint(cts, seek+44); /* track number */
+ track->tracks = get32lint(cts, seek+48); /* nr of tracks */
+ track->year = get32lint(cts, seek+52); /* year */
+ track->bitrate = get32lint(cts, seek+56); /* bitrate */
+ track->unk060 = get32lint(cts, seek+60); /* unknown */
+ track->samplerate = get16lint(cts,seek+62); /* sample rate */
+ track->volume = get32lint(cts, seek+64); /* volume adjust */
+ track->starttime = get32lint (cts, seek+68);
+ track->stoptime = get32lint (cts, seek+72);
+ track->soundcheck = get32lint (cts, seek+76);/* soundcheck */
+ track->playcount = get32lint (cts, seek+80); /* playcount */
+ track->playcount2 = get32lint (cts, seek+84);
+ track->time_played = get32lint(cts, seek+88);/* last time played */
+ track->cd_nr = get32lint(cts, seek+92); /* CD nr */
+ track->cds = get32lint(cts, seek+96); /* CD nr of.. */
+ /* Apple Store/Audible User ID (for DRM'ed files only, set to 0
+ otherwise). */
+ track->drm_userid = get32lint (cts, seek+100);
+ track->time_modified = get32lint(cts, seek+104);/* last mod. time */
+ track->bookmark_time = get32lint (cts, seek+108);/*time bookmarked*/
+ track->dbid = get64lint (cts, seek+112);
+ track->checked = get8int (cts, seek+120); /*Checked/Unchecked: 0/1*/
+ /* The rating set by the application, as opposed to the rating
+ set on the iPod itself */
+ track->app_rating = get8int (cts, seek+121);
+ track->BPM = get16lint (cts, seek+122);
+ track->artwork_count = get16lint (cts, seek+124);
+ track->unk126 = get16lint (cts, seek+126);
+ track->artwork_size = get32lint (cts, seek+128);
+ track->unk132 = get32lint (cts, seek+132);
+ track->samplerate2 = get32lfloat (cts, seek+136);
+ track->unk140 = get32lint (cts, seek+140);
+ track->unk144 = get32lint (cts, seek+144);
+ track->unk148 = get32lint (cts, seek+148);
+ track->unk152 = get32lint (cts, seek+152);
+ }
+ if (header_len >= 0xf4)
+ {
+ track->unk156 = get32lint (cts, seek+156);
+ track->unk160 = get32lint (cts, seek+160);
+ track->unk164 = get32lint (cts, seek+164);
+ track->dbid2 = get64lint (cts, seek+168);
+ track->unk176 = get32lint (cts, seek+176);
+ track->unk180 = get32lint (cts, seek+180);
+ track->unk184 = get32lint (cts, seek+184);
+ track->samplecount = get32lint (cts, seek+188);
+ track->unk192 = get32lint (cts, seek+192);
+ track->unk196 = get32lint (cts, seek+196);
+ track->unk200 = get32lint (cts, seek+200);
+ track->unk204 = get32lint (cts, seek+204);
+ track->unk208 = get32lint (cts, seek+208);
+ track->unk212 = get32lint (cts, seek+212);
+ track->unk216 = get32lint (cts, seek+216);
+ track->unk220 = get32lint (cts, seek+220);
+ track->unk224 = get32lint (cts, seek+224);
+ track->unk228 = get32lint (cts, seek+228);
+ track->unk232 = get32lint (cts, seek+232);
+ track->unk236 = get32lint (cts, seek+236);
+ track->unk240 = get32lint (cts, seek+240);
+ }
track->transferred = TRUE; /* track is on iPod! */
@@ -1407,45 +1506,58 @@ gchar *time_time_to_string (time_t time);
for (i=0; i<mhod_nums; ++i)
{
- entry_utf16 = get_mhod_string (cts, seek, &zip, &type);
+ entry_utf8 = get_mhod_string (cts, seek, &zip, &type);
CHECK_ERROR (fimp, -1);
- if (entry_utf16 != NULL)
+ if (entry_utf8 != NULL)
{
- entry_utf8 = g_utf16_to_utf8 (entry_utf16, -1, NULL, NULL, NULL);
switch ((enum MHOD_ID)type)
{
+ case MHOD_ID_TITLE:
+ track->title = entry_utf8;
+ break;
+ case MHOD_ID_PATH:
+ track->ipod_path = entry_utf8;
+ break;
case MHOD_ID_ALBUM:
track->album = entry_utf8;
break;
case MHOD_ID_ARTIST:
track->artist = entry_utf8;
break;
- case MHOD_ID_TITLE:
- track->title = entry_utf8;
- break;
case MHOD_ID_GENRE:
track->genre = entry_utf8;
break;
- case MHOD_ID_PATH:
- track->ipod_path = entry_utf8;
- break;
- case MHOD_ID_FDESC:
- track->fdesc = entry_utf8;
+ case MHOD_ID_FILETYPE:
+ track->filetype = entry_utf8;
break;
case MHOD_ID_COMMENT:
track->comment = entry_utf8;
break;
+ case MHOD_ID_CATEGORY:
+ track->category = entry_utf8;
+ break;
case MHOD_ID_COMPOSER:
track->composer = entry_utf8;
break;
case MHOD_ID_GROUPING:
track->grouping = entry_utf8;
break;
+ case MHOD_ID_DESCRIPTION:
+ track->description = entry_utf8;
+ break;
+ case MHOD_ID_PODCASTURL:
+ track->podcasturl = entry_utf8;
+ break;
+ case MHOD_ID_PODCASTRSS:
+ track->podcastrss = entry_utf8;
+ break;
+ case MHOD_ID_SUBTITLE:
+ track->subtitle = entry_utf8;
+ break;
default: /* unknown entry -- discard */
g_free (entry_utf8);
break;
}
- g_free (entry_utf16);
}
seek += zip;
}
@@ -2002,6 +2114,25 @@ static void put32lint (WContents *cts, guint32 n)
put_data (cts, (gchar *)&n, 4);
}
+/* Write 4 byte floating number */
+static void put32lfloat (WContents *cts, float f)
+{
+ union
+ {
+ guint32 i;
+ float f;
+ } flt;
+
+ if (sizeof (float) != 4)
+ {
+ put32lint (cts, 0);
+ g_return_if_reached ();
+ }
+
+ flt.f = f;
+ put32lint (cts, flt.i);
+}
+
/* Append @n times 2-byte-long zeros */
static void put16_n0 (WContents *cts, gulong n)
@@ -2121,7 +2252,6 @@ static void mk_mhbd (FExport *fexp)
put32lint (cts, 104); /* header size */
put32lint (cts, -1); /* size of whole mhdb -- fill in later */
put32lint (cts, 1); /* ? */
- if (fexp->itdb->version < 0x09) fexp->itdb->version = 0x09;
/* Version number: 0x01: iTunes 2
0x02: iTunes 3
0x09: iTunes 4.2
@@ -2188,36 +2318,36 @@ static void mk_mhit (WContents *cts, Itdb_Track *track)
g_return_if_fail (track);
put_data (cts, "mhit", 4);
- put32lint (cts, 156); /* header size */
+ put32lint (cts, 0xf4); /* header size */
put32lint (cts, -1); /* size of whole mhit -- fill in later */
put32lint (cts, -1); /* nr of mhods in this mhit -- later */
- put32lint (cts, track->id); /* track index number
- * */
- put32lint (cts, track->unk020);
- put32lint (cts, track->unk024);
- /* rating, compil., type */
- put32lint (cts, ((guint32)track->rating << 24) |
- ((guint32)track->compilation << 16) |
- ((guint32)track->type & 0x0000ffff));
-
- put32lint (cts, track->time_added); /* timestamp */
- put32lint (cts, track->size); /* filesize */
- put32lint (cts, track->tracklen); /* length of track in ms */
+ put32lint (cts, track->id); /* track index number */
+
+ put32lint (cts, track->visible);
+ put_data (cts, track->filetype_marker, 4);
+ put16lint (cts, track->type);
+ put8int (cts, track->compilation);
+ put8int (cts, track->rating);
+ put32lint (cts, track->time_added); /* timestamp */
+ put32lint (cts, track->size); /* filesize */
+ put32lint (cts, track->tracklen);/* length of track in ms */
put32lint (cts, track->track_nr);/* track number */
put32lint (cts, track->tracks); /* number of tracks */
put32lint (cts, track->year); /* the year */
put32lint (cts, track->bitrate); /* bitrate */
- put32lint (cts, track->samplerate << 16);
+ put16lint (cts, track->unk060); /* unknown */
+ put16lint (cts, track->samplerate);
put32lint (cts, track->volume); /* volume adjust */
put32lint (cts, track->starttime);
put32lint (cts, track->stoptime);
put32lint (cts, track->soundcheck);
put32lint (cts, track->playcount);/* playcount */
- put32lint (cts, track->unk084);
+ track->playcount2 = track->playcount;
+ put32lint (cts, track->playcount2);
put32lint (cts, track->time_played); /* last time played */
put32lint (cts, track->cd_nr); /* CD number */
put32lint (cts, track->cds); /* number of CDs */
- put32lint (cts, track->unk100);
+ put32lint (cts, track->drm_userid);
put32lint (cts, track->time_modified); /* timestamp */
put32lint (cts, track->bookmark_time);
put64lint (cts, track->dbid);
@@ -2225,14 +2355,37 @@ static void mk_mhit (WContents *cts, Itdb_Track *track)
else put8int (cts, 0);
put8int (cts, track->app_rating);
put16lint (cts, track->BPM);
- put32lint (cts, track->unk124);
- put32lint (cts, track->unk128);
+ put16lint (cts, track->artwork_count);
+ put16lint (cts, track->unk126);
+ put32lint (cts, track->artwork_size);
put32lint (cts, track->unk132);
- put32lint (cts, track->unk136);
+ put32lfloat (cts, track->samplerate2);
put32lint (cts, track->unk140);
put32lint (cts, track->unk144);
put32lint (cts, track->unk148);
put32lint (cts, track->unk152);
+ /* since iTunesDB version 0x0c */
+ put32lint (cts, track->unk156);
+ put32lint (cts, track->unk160);
+ put32lint (cts, track->unk164);
+ put64lint (cts, track->dbid2);
+ put32lint (cts, track->unk176);
+ put32lint (cts, track->unk180);
+ put32lint (cts, track->unk184);
+ put32lint (cts, track->samplecount);
+ put32lint (cts, track->unk192);
+ put32lint (cts, track->unk196);
+ put32lint (cts, track->unk200);
+ put32lint (cts, track->unk204);
+ put32lint (cts, track->unk208);
+ put32lint (cts, track->unk212);
+ put32lint (cts, track->unk216);
+ put32lint (cts, track->unk220);
+ put32lint (cts, track->unk224);
+ put32lint (cts, track->unk228);
+ put32lint (cts, track->unk232);
+ put32lint (cts, track->unk236);
+ put32lint (cts, track->unk240);
}
@@ -2266,10 +2419,13 @@ static void mk_mhod (WContents *cts, enum MHOD_ID type, void *data)
case MHOD_ID_ALBUM:
case MHOD_ID_ARTIST:
case MHOD_ID_GENRE:
- case MHOD_ID_FDESC:
+ case MHOD_ID_FILETYPE:
case MHOD_ID_COMMENT:
+ case MHOD_ID_CATEGORY:
case MHOD_ID_COMPOSER:
case MHOD_ID_GROUPING:
+ case MHOD_ID_DESCRIPTION:
+ case MHOD_ID_SUBTITLE:
g_return_if_fail (data);
{
/* convert to utf16 */
@@ -2280,16 +2436,29 @@ static void mk_mhod (WContents *cts, enum MHOD_ID type, void *data)
put_data (cts, "mhod", 4); /* header */
put32lint (cts, 24); /* size of header */
put32lint (cts, 2*len+40); /* size of header + body */
- put32lint (cts, type); /* type of the entry */
- put32_n0 (cts, 2); /* unknown */
+ put32lint (cts, type); /* type of the mhod */
+ put32_n0 (cts, 2); /* unknown */
/* end of header, start of data */
put32lint (cts, 1); /* always 1 for these MHOD_IDs*/
put32lint (cts, 2*len); /* size of string */
put32_n0 (cts, 2); /* unknown */
- put_data (cts, (gchar *)entry_utf16, 2*len); /* the string */
+ put_data (cts, (gchar *)entry_utf16, 2*len);/* the string */
g_free (entry_utf16);
}
break;
+ case MHOD_ID_PODCASTURL:
+ case MHOD_ID_PODCASTRSS:
+ g_return_if_fail (data);
+ {
+ guint32 len = strlen ((gchar *)data);
+ put_data (cts, "mhod", 4); /* header */
+ put32lint (cts, 24); /* size of header */
+ put32lint (cts, 24+len); /* size of header + data */
+ put32lint (cts, type); /* type of the mhod */
+ put32_n0 (cts, 2); /* unknown */
+ put_data (cts, (gchar *)data, len); /* the string */
+ }
+ break;
case MHOD_ID_PLAYLIST:
put_data (cts, "mhod", 4); /* header */
put32lint (cts, 24); /* size of header */
@@ -2548,9 +2717,9 @@ static gboolean write_mhsd_one(FExport *fexp)
mk_mhod (cts, MHOD_ID_GENRE, track->genre);
++mhod_num;
}
- if (track->fdesc && *track->fdesc)
+ if (track->filetype && *track->filetype)
{
- mk_mhod (cts, MHOD_ID_FDESC, track->fdesc);
+ mk_mhod (cts, MHOD_ID_FILETYPE, track->filetype);
++mhod_num;
}
if (track->comment && *track->comment)
@@ -2558,6 +2727,11 @@ static gboolean write_mhsd_one(FExport *fexp)
mk_mhod (cts, MHOD_ID_COMMENT, track->comment);
++mhod_num;
}
+ if (track->category && *track->category)
+ {
+ mk_mhod (cts, MHOD_ID_CATEGORY, track->category);
+ ++mhod_num;
+ }
if (track->composer && *track->composer)
{
mk_mhod (cts, MHOD_ID_COMPOSER, track->composer);
@@ -2568,6 +2742,26 @@ static gboolean write_mhsd_one(FExport *fexp)
mk_mhod (cts, MHOD_ID_GROUPING, track->grouping);
++mhod_num;
}
+ if (track->description && *track->description)
+ {
+ mk_mhod (cts, MHOD_ID_DESCRIPTION, track->description);
+ ++mhod_num;
+ }
+ if (track->subtitle && *track->subtitle)
+ {
+ mk_mhod (cts, MHOD_ID_SUBTITLE, track->subtitle);
+ ++mhod_num;
+ }
+ if (track->podcasturl && *track->podcasturl)
+ {
+ mk_mhod (cts, MHOD_ID_PODCASTURL, track->podcasturl);
+ ++mhod_num;
+ }
+ if (track->podcastrss && *track->podcastrss)
+ {
+ mk_mhod (cts, MHOD_ID_PODCASTRSS, track->podcastrss);
+ ++mhod_num;
+ }
/* Fill in the missing items of the mhit header */
fix_mhit (cts, mhit_seek, mhod_num);
}
@@ -3085,14 +3279,14 @@ gboolean itdb_shuffle_write (Itdb_iTunesDB *itdb,
gboolean itdb_shuffle_write_file (Itdb_iTunesDB *itdb,
const gchar *filename, GError **error)
{
- auto gboolean haystack (gchar *fdesc, gchar **desclist);
- gboolean haystack (gchar *fdesc, gchar **desclist)
+ auto gboolean haystack (gchar *filetype, gchar **desclist);
+ gboolean haystack (gchar *filetype, gchar **desclist)
{
gchar **dlp;
- if (!fdesc || !desclist) return FALSE;
+ if (!filetype || !desclist) return FALSE;
for (dlp=desclist; *dlp; ++dlp)
{
- if (strstr (fdesc, *dlp)) return TRUE;
+ if (strstr (filetype, *dlp)) return TRUE;
}
return FALSE;
}
@@ -3147,16 +3341,16 @@ gboolean itdb_shuffle_write_file (Itdb_iTunesDB *itdb,
/* The next one should be 0x01 for MP3,
** 0x02 for AAC, and 0x04 for WAV, but I can't find
** a suitable indicator within the track structure? */
- /* JCS: let's do heuristic on tr->fdesc which would contain
+ /* JCS: let's do heuristic on tr->filetype which would contain
"MPEG audio file", "AAC audio file", "Protected AAC audio
file", "AAC audio book file", "WAV audio file" (or similar
- if not written by gtkpod */
+ if not written by gtkpod) */
- if (haystack (tr->fdesc, mp3_desc))
+ if (haystack (tr->filetype, mp3_desc))
put24bint (cts, 0x01);
- else if (haystack (tr->fdesc, mp4_desc))
+ else if (haystack (tr->filetype, mp4_desc))
put24bint (cts, 0x02);
- else if (haystack (tr->fdesc, wav_desc))
+ else if (haystack (tr->filetype, wav_desc))
put24bint (cts, 0x04);
else
put24bint (cts, 0x01); /* default to mp3 */
diff --git a/src/itdb_playlist.c b/src/itdb_playlist.c
index 6add6f1..c228a41 100644
--- a/src/itdb_playlist.c
+++ b/src/itdb_playlist.c
@@ -1,4 +1,4 @@
-/* Time-stamp: <2005-09-11 14:58:01 jcs>
+/* Time-stamp: <2005-09-17 21:52:19 jcs>
|
| Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
| Part of the gtkpod project.
@@ -305,7 +305,7 @@ gboolean itdb_splr_eval (Itdb_iTunesDB *itdb, SPLRule *splr, Itdb_Track *track)
strcomp = track->genre;
break;
case SPLFIELD_KIND:
- strcomp = track->fdesc;
+ strcomp = track->filetype;
break;
case SPLFIELD_COMMENT:
strcomp = track->comment;
diff --git a/src/itdb_track.c b/src/itdb_track.c
index bad9f8e..41372c0 100644
--- a/src/itdb_track.c
+++ b/src/itdb_track.c
@@ -1,4 +1,4 @@
-/* Time-stamp: <2005-06-17 22:12:16 jcs>
+/* Time-stamp: <2005-09-19 17:50:14 jcs>
|
| Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
| Part of the gtkpod project.
@@ -35,10 +35,97 @@ Itdb_Track *itdb_track_new (void)
{
Itdb_Track *track = g_new0 (Itdb_Track, 1);
- track->unk020 = 1;
+ track->visible = 1;
return track;
}
+/* Attempt to set some of the unknowns to reasonable defaults */
+static void itdb_track_set_defaults (Itdb_Track *tr)
+{
+ auto gboolean haystack (gchar *filetype, gchar **desclist);
+ gboolean haystack (gchar *filetype, gchar **desclist)
+ {
+ gchar **dlp;
+ if (!filetype || !desclist) return FALSE;
+ for (dlp=desclist; *dlp; ++dlp)
+ {
+ if (strstr (filetype, *dlp)) return TRUE;
+ }
+ return FALSE;
+ }
+
+ gchar *mp3_desc[] = {"MPEG", "MP3", "mpeg", "mp3", NULL};
+ gchar *mp4_desc[] = {"AAC", "MP4", "aac", "mp4", NULL};
+ gchar *audible_subdesc[] = {"Audible", "audible", "Book", "book", NULL};
+ gchar *wav_desc[] = {"WAV", "wav", NULL};
+
+ /* The exact meaning of unk126 is unknown, but always seems to be
+ 0xffff for MP3/AAC songs, 0x0 for uncompressed songs (like WAVE
+ format), 0x1 for Audible. */
+ if (tr->unk126 == 0)
+ {
+ if (haystack (tr->filetype, mp3_desc))
+ {
+ tr->unk126 = 0xffff;
+ }
+ else if (haystack (tr->filetype, mp4_desc))
+ {
+ if (haystack (tr->filetype, audible_subdesc))
+ {
+ tr->unk126 = 0x01;
+ }
+ else
+ {
+ tr->unk126 = 0xffff;
+ }
+ }
+ else if (haystack (tr->filetype, wav_desc))
+ {
+ tr->unk126 = 0x00;
+ }
+ else
+ {
+ tr->unk126 = 0x00; /* default value */
+ }
+ }
+ /* The exact meaning of unk144 is unknown, but MP3 songs appear to
+ be always 0x0000000c or 0x0100000c (if played one or more times
+ in iTunes), AAC songs are always 0x01000033, Audible files are
+ 0x01000029, WAV files are 0x0. */
+ if (tr->unk144 == 0)
+ {
+ if (haystack (tr->filetype, mp3_desc))
+ {
+ tr->unk144 = 0x0000000c;
+ }
+ else if (haystack (tr->filetype, mp4_desc))
+ {
+ if (haystack (tr->filetype, audible_subdesc))
+ {
+ tr->unk144 = 0x01000029;
+ }
+ else
+ {
+ tr->unk144 = 0x01000033;
+ }
+ }
+ else if (haystack (tr->filetype, wav_desc))
+ {
+ tr->unk144 = 0x00;
+ }
+ else
+ {
+ tr->unk144 = 0x00; /* default value */
+ }
+ }
+ /* The sample rate of the song expressed as an IEEE 32 bit
+ floating point number. It's uncertain why this is here. itdb
+ will set this when adding a track */
+ tr->samplerate2 = tr->samplerate;
+ if (tr->dbid2 == 0) tr->dbid2 = tr->dbid;
+}
+
+
/* Add @track to @itdb->tracks at position @pos (or at the end if pos
is -1). Application is responsible to also add it to the master
@@ -51,6 +138,8 @@ void itdb_track_add (Itdb_iTunesDB *itdb, Itdb_Track *track, gint32 pos)
track->itdb = itdb;
+ itdb_track_set_defaults (track);
+
if (pos == -1) itdb->tracks = g_list_append (itdb->tracks, track);
else itdb->tracks = g_list_insert (itdb->tracks, track, pos);
}
@@ -66,7 +155,7 @@ void itdb_track_free (Itdb_Track *track)
g_free (track->genre);
g_free (track->comment);
g_free (track->composer);
- g_free (track->fdesc);
+ g_free (track->filetype);
g_free (track->grouping);
g_free (track->ipod_path);
if (track->userdata && track->userdata_destroy)
@@ -122,7 +211,7 @@ Itdb_Track *itdb_track_duplicate (Itdb_Track *tr)
tr_dup->genre = g_strdup (tr->genre);
tr_dup->comment = g_strdup (tr->comment);
tr_dup->composer = g_strdup (tr->composer);
- tr_dup->fdesc = g_strdup (tr->fdesc);
+ tr_dup->filetype = g_strdup (tr->filetype);
tr_dup->grouping = g_strdup (tr->grouping);
tr_dup->ipod_path = g_strdup (tr->ipod_path);