diff options
-rw-r--r-- | ChangeLog | 59 | ||||
-rw-r--r-- | src/db-artwork-parser.c | 38 | ||||
-rw-r--r-- | src/db-artwork-writer.c | 7 | ||||
-rw-r--r-- | src/db-itunes-parser.h | 38 | ||||
-rw-r--r-- | src/itdb.h | 215 | ||||
-rw-r--r-- | src/itdb_photoalbum.c | 559 | ||||
-rw-r--r-- | tests/test-photos.c | 351 |
7 files changed, 947 insertions, 320 deletions
@@ -1,9 +1,66 @@ -2006-10-02 Jorg Schuler <jcsjcs at users.sourceforge.net> +2006-10-29 Jorg Schuler <jcsjcs at users.sourceforge.net> + + Major rework of picture support. + + * src/db-artwork-parser.c, src/db-artwork-writer.c, src/itdb.h, + src/db-itunes-parser.h: renamed 'master' to 'album_type' in + MhbaHeader and Itdb_PhotoAlbum. + + * src/db-artwork-parser.c, src/db-artwork-writer.c, + src/itdb_photoalbum.c: Itdb_Photoalbum->members are now pointers + to the corresponding Itdb_Artwork instead of image_ids. + + * src/itdb_photoalbum.c: album_ids and image_ids are set just + before writing the PhotoDB in itdb_photodb_write(). + + * src/itdb_photoalbum.c: new interface, basically use as follows: + + itdb_photodb_parse(): + Read an existing PhotoDB. + + itdb_photodb_create(): + Create a new Itdb_PhotoDB structure. The Photo Library Album is + (first album) is created automatically. + + itdb_photodb_add_photo(), itdb_photodb_add_photo_from_data(): + Add a photo to the PhotoDB (from file or from a chunk of + memory). It is automatically added to the Photo Library Album + (first album), which is created if it does not exist already. + + itdb_photodb_photoalbum_craete(): + Create and add a new photoalbum. + + itdb_photodb_photoalbum_add_photo(): + Add a photo (Itdb_Artwork) to an existing photoalbum. + + itdb_photodb_photoalbum_remove(): + Remove an existing photoalbum. Pictures can be kept in the + Photo Library or automatically removed as well. + + itdb_photodb_remove_photo(): + Remove a photo either from a photoalbum or completely from the + database. + + itdb_photodb_write(): + Write out your PhotoDB. + + itdb_photodb_free(): + Free all memory taken by the PhotoDB. + + itdb_photodb_photoalbum_by_name(): + Find the first photoalbum with a given name. * src/itdb_playlist.c (itdb_playlist_add, itdb_playlist_add_track): src/itdb_track.c (itdb_track_add): simplify code by using g_list_insert(). + * tests/test-photos.c: change to new interface, add new commands + 'list' to list photo IDs in the database, 'remove' to remove IDs + from an album or the iPod, or remove entire photoalbums from the + iPod. + + * src/db-itunes-parser.h: added comments to _MhbaHeader definition. + 2006-10-02 Jorg Schuler <jcsjcs at users.sourceforge.net> * tests/Makefile.am diff --git a/src/db-artwork-parser.c b/src/db-artwork-parser.c index 4a848ce..51d81fa 100644 --- a/src/db-artwork-parser.c +++ b/src/db-artwork-parser.c @@ -79,7 +79,7 @@ static int parse_mhia (DBParseContext *ctx, Itdb_PhotoAlbum *photo_album, GError *error) { MhiaHeader *mhia; - gint image_id; + guint32 image_id; mhia = db_parse_context_get_m_header (ctx, MhiaHeader, "mhia"); if (mhia == NULL) { @@ -88,7 +88,7 @@ parse_mhia (DBParseContext *ctx, Itdb_PhotoAlbum *photo_album, GError *error) dump_mhia (mhia); image_id = get_gint32 (mhia->image_id, ctx->byte_order); photo_album->members = g_list_append (photo_album->members, - GINT_TO_POINTER(image_id)); + GUINT_TO_POINTER(image_id)); db_parse_context_set_total_len (ctx, get_gint32_db (ctx->db, mhia->total_len)); return 0; @@ -402,7 +402,7 @@ parse_mhba (DBParseContext *ctx, GError *error) photo_album = g_new0 (Itdb_PhotoAlbum, 1); photo_album->num_images = get_gint32( mhba->num_mhias, ctx->byte_order); photo_album->album_id = get_gint32( mhba->playlist_id, ctx->byte_order); - photo_album->master = get_gint32( mhba->master, ctx->byte_order); + photo_album->album_type = get_gint32( mhba->album_type, ctx->byte_order); photo_album->prev_album_id = get_gint32( mhba->prev_playlist_id, ctx->byte_order); cur_offset = ctx->header_len; @@ -766,6 +766,9 @@ ipod_parse_photo_db (Itdb_PhotoDB *photodb) char *filename; Itdb_DB db; + GList *gl; + GHashTable *hash; + db.db.photodb = photodb; db.db_type = DB_TYPE_PHOTO; @@ -784,6 +787,35 @@ ipod_parse_photo_db (Itdb_PhotoDB *photodb) parse_mhfd (ctx, NULL); db_parse_context_destroy (ctx, TRUE); + /* Now we need to replace references to artwork_ids in the + * photo albums with references to the actual artwork + * structure. Since we cannot guarantee that the list with the + * photos is read before the album list, we cannot safely do + * this at the time of reading the ids. */ + + /* Create a hash for faster lookup */ + hash = g_hash_table_new (g_int_hash, g_int_equal); + for (gl=photodb->photos; gl; gl=gl->next) + { + Itdb_Artwork *photo = gl->data; + g_return_val_if_fail (photo, -1); + g_hash_table_insert (hash, &photo->id, photo); +/* printf ("id: %d, photo: %p\n", photo->id, photo);*/ + } + for (gl=photodb->photoalbums; gl; gl=gl->next) + { + GList *glp; + Itdb_PhotoAlbum *album = gl->data; + g_return_val_if_fail (album, -1); + for (glp=album->members; glp; glp=glp->next) + { + guint image_id = GPOINTER_TO_UINT (glp->data); + Itdb_Artwork *photo = g_hash_table_lookup (hash, &image_id); +/* printf ("id: %d, photo: %p\n", image_id, photo);*/ + glp->data = photo; + } + } + g_hash_table_destroy (hash); return 0; } diff --git a/src/db-artwork-writer.c b/src/db-artwork-writer.c index f072059..80ab33f 100644 --- a/src/db-artwork-writer.c +++ b/src/db-artwork-writer.c @@ -685,7 +685,7 @@ write_mhba (Itdb_PhotoAlbum *photo_album, iPodBuffer *buffer) } mhba->num_mhods = get_gint32(1, buffer->byte_order); mhba->playlist_id = get_gint32(photo_album->album_id, buffer->byte_order); - mhba->master = get_gint32(photo_album->master, buffer->byte_order); + mhba->album_type = get_gint32(photo_album->album_type, buffer->byte_order); mhba->prev_playlist_id = get_gint32(photo_album->prev_album_id, buffer->byte_order); mhba->num_mhias = get_gint32(photo_album->num_images, buffer->byte_order); total_bytes = get_gint32 (mhba->header_len, buffer->byte_order); @@ -704,13 +704,14 @@ write_mhba (Itdb_PhotoAlbum *photo_album, iPodBuffer *buffer) total_bytes += bytes_written; for (it = photo_album->members; it != NULL; it = it->next) { - gint image_id = GPOINTER_TO_INT(it->data); + Itdb_Artwork *photo = it->data; + g_return_val_if_fail (photo, -1); sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes); if (sub_buffer == NULL) { return -1; } - bytes_written = write_mhia (image_id, sub_buffer); + bytes_written = write_mhia (photo->id, sub_buffer); ipod_buffer_destroy (sub_buffer); if (bytes_written == -1) { return -1; diff --git a/src/db-itunes-parser.h b/src/db-itunes-parser.h index 344efa4..1b2cfff 100644 --- a/src/db-itunes-parser.h +++ b/src/db-itunes-parser.h @@ -612,22 +612,26 @@ struct _MhbaHeader { unsigned char header_id[4]; gint32 header_len; gint32 total_len; - gint32 num_mhods; /* number of Data Objects in the List, probably always 1 */ - gint32 num_mhias; /* number of pictures in the album */ - gint32 playlist_id; /* starts out at $64 and increments by 1 */ - gint32 unknown2; /* unknown, seems to be always 0 */ - gint16 unknown3; /* unknown, seems to be always 0 */ - gchar master; - /* FIXME: not sure if I have these right; iPod doesn't seem to listen to them */ - guint8 playmusic; - guint8 repeat; - guint8 random; - guint8 show_titles; - guint8 transition_direction; - gint32 slide_duration; - gint32 transition_duration; - gint32 unknown4; - gint32 unknown5; + gint32 num_mhods; /* number of Data Objects in the List, + probably always 1 */ + gint32 num_mhias; /* number of pictures in the album */ + gint32 playlist_id;/* starts out at $64 and increments by 1 */ + gint32 unknown2; /* unknown, seems to be always 0 */ + gint16 unknown3; /* unknown, seems to be always 0 */ + gchar album_type; /* 1 = master photo list ("Photo Library"), + 2 = normal album, sometimes 4 and 5 */ + guint8 playmusic; /* play music during slideshow (from iPhoto setting) */ + guint8 repeat; /* repeat the slideshow (from iPhoto setting) */ + guint8 random; /* show the slides in random order (from iPhoto + setting) */ + guint8 show_titles;/* show slide captions (from iPhoto setting) */ + guint8 transition_direction; /* 0=none, 1=left-to-right, + 2=right-to-left, 3=top-to-bottom, + 4=bottom-to-top (from iPhoto setting) */ + gint32 slide_duration; /* in seconds (from iPhoto setting) */ + gint32 transition_duration; /* in milliseconds (from iPhoto setting) */ + gint32 unk044; + gint32 unk048; gint64 song_id; gint32 prev_playlist_id; /* the id of the previous playlist */ unsigned char padding[]; @@ -656,7 +660,7 @@ struct _MhiaHeader { gint32 total_len; /* probably the size of the header and all child records; * as there aren't any child records this is equal to header length */ gint32 unknown1; /* seems to be zero */ - gint32 image_id; /* the id of the mhii record this mhia refers to */ + guint32 image_id; /* the id of the mhii record this mhia refers to */ unsigned char padding[]; }; @@ -1,4 +1,4 @@ -/* Time-stamp: <2006-09-23 21:25:40 jcs> +/* Time-stamp: <2006-10-29 15:03:21 jcs> | | Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net> | Part of the gtkpod project. @@ -70,74 +70,6 @@ typedef struct _Itdb_IpodInfo Itdb_IpodInfo; /* ------------------------------------------------------------ *\ * - * Thumbnail-relevant definitions - * -\* ------------------------------------------------------------ */ - -/* Types of thumbnails in Itdb_Image */ -typedef enum { - ITDB_THUMB_COVER_SMALL, - ITDB_THUMB_COVER_LARGE, - ITDB_THUMB_PHOTO_SMALL, - ITDB_THUMB_PHOTO_LARGE, - ITDB_THUMB_PHOTO_FULL_SCREEN, - ITDB_THUMB_PHOTO_TV_SCREEN -} ItdbThumbType; - - -/* The Itdb_Thumb structure can represent two slightly different - thumbnails: - - a) a thumbnail before it's transferred to the iPod. - - offset and size are 0 - - width and height, if unequal 0, will indicate the size on the - iPod. width and height are set the first time a pixbuf is - requested for this thumbnail. - - type is set according to the type this thumbnail represents - - filename point to a 'real' image file OR image_data and - image_data_len are set. - - b) a thumbnail (big or small) stored on a database in the iPod. In - these cases, id corresponds to the ID originally used in the - database, filename points to a .ithmb file on the iPod - */ -struct _Itdb_Thumb { - ItdbThumbType type; - gchar *filename; - guchar *image_data; /* holds the thumbnail data of - non-transfered thumbnails when - filename == NULL */ - gsize image_data_len; /* length of data */ - guint32 offset; - guint32 size; - gint16 width; - gint16 height; - gint16 horizontal_padding; - gint16 vertical_padding; -}; - -struct _Itdb_Artwork { - GList *thumbnails; /* list of Itdb_Thumbs */ - guint32 artwork_size; /* Size in bytes of the original source image */ - guint32 id; /* Artwork id used by photoalbums, starts at - * 0x40... libgpod will set this on sync. */ - gint32 creation_date; /* Date the image was created */ - /* below is for use by application */ - guint64 usertype; - gpointer userdata; - /* function called to duplicate userdata */ - ItdbUserDataDuplicateFunc userdata_duplicate; - /* function called to free userdata */ - ItdbUserDataDestroyFunc userdata_destroy; -}; - - -/* ------------------------------------------------------------ *\ - * * iPod model-relevant definitions * \* ------------------------------------------------------------ */ @@ -459,24 +391,84 @@ struct _SPLRules /* ------------------------------------------------------------ *\ * - * iTunesDB, Playlists, Tracks + * iTunesDB, Playlists, Tracks, PhotoDB, Artwork, Thumbnails * \* ------------------------------------------------------------ */ /* one star is how much (track->rating) */ #define ITDB_RATING_STEP 20 +/* Types of thumbnails in Itdb_Image */ +typedef enum { + ITDB_THUMB_COVER_SMALL, + ITDB_THUMB_COVER_LARGE, + ITDB_THUMB_PHOTO_SMALL, + ITDB_THUMB_PHOTO_LARGE, + ITDB_THUMB_PHOTO_FULL_SCREEN, + ITDB_THUMB_PHOTO_TV_SCREEN +} ItdbThumbType; + + +/* The Itdb_Thumb structure can represent two slightly different + thumbnails: + + a) a thumbnail before it's transferred to the iPod. + + offset and size are 0 + + width and height, if unequal 0, will indicate the size on the + iPod. width and height are set the first time a pixbuf is + requested for this thumbnail. + + type is set according to the type this thumbnail represents + + filename point to a 'real' image file OR image_data and + image_data_len are set. + + b) a thumbnail (big or small) stored on a database in the iPod. In + these cases, id corresponds to the ID originally used in the + database, filename points to a .ithmb file on the iPod + */ +struct _Itdb_Thumb { + ItdbThumbType type; + gchar *filename; + guchar *image_data; /* holds the thumbnail data of + non-transfered thumbnails when + filename == NULL */ + gsize image_data_len; /* length of data */ + guint32 offset; + guint32 size; + gint16 width; + gint16 height; + gint16 horizontal_padding; + gint16 vertical_padding; +}; + +struct _Itdb_Artwork { + GList *thumbnails; /* list of Itdb_Thumbs */ + guint32 artwork_size; /* Size in bytes of the original source image */ + guint32 id; /* Artwork id used by photoalbums, starts at + * 0x40... libgpod will set this on sync. */ + gint32 creation_date; /* Date the image was created */ + /* below is for use by application */ + guint64 usertype; + gpointer userdata; + /* functions called to duplicate/free userdata */ + ItdbUserDataDuplicateFunc userdata_duplicate; + ItdbUserDataDestroyFunc userdata_destroy; +}; + + struct _Itdb_PhotoDB { - GList *photos; - GList *photoalbums; + GList *photos; /* (Itdb_Artwork *) */ + GList *photoalbums; /* (Itdb_PhotoAlbum *) */ Itdb_Device *device;/* iPod device info */ /* below is for use by application */ guint64 usertype; gpointer userdata; - /* function called to duplicate userdata */ + /* functions called to duplicate/free userdata */ ItdbUserDataDuplicateFunc userdata_duplicate; - /* function called to free userdata */ ItdbUserDataDestroyFunc userdata_destroy; }; @@ -491,26 +483,26 @@ struct _Itdb_iTunesDB /* below is for use by application */ guint64 usertype; gpointer userdata; - /* function called to duplicate userdata */ + /* functions called to duplicate/free userdata */ ItdbUserDataDuplicateFunc userdata_duplicate; - /* function called to free userdata */ ItdbUserDataDestroyFunc userdata_destroy; }; struct _Itdb_PhotoAlbum { gchar *name; /* name of photoalbum in UTF8 */ - GList *members; /* photos in album (Track *) */ + GList *members; /* photos in album (Itdb_Artwork *) */ gint num_images; /* number of photos in album */ - gint master; /* 0x01 for master, 0x00 otherwise */ - gint album_id; - gint prev_album_id; + gint album_type; /* 0x01 for master (Photo Library), + 0x02 otherwise */ + /* set automatically at time of writing the PhotoDB */ + gint32 album_id; + gint32 prev_album_id; /* below is for use by application */ guint64 usertype; gpointer userdata; - /* function called to duplicate userdata */ + /* functions called to duplicate/free userdata */ ItdbUserDataDuplicateFunc userdata_duplicate; - /* function called to free userdata */ ItdbUserDataDestroyFunc userdata_destroy; }; @@ -550,9 +542,8 @@ struct _Itdb_Playlist /* below is for use by application */ guint64 usertype; gpointer userdata; - /* function called to duplicate userdata */ + /* functions called to duplicate/free userdata */ ItdbUserDataDuplicateFunc userdata_duplicate; - /* function called to free userdata */ ItdbUserDataDestroyFunc userdata_destroy; }; @@ -732,7 +723,7 @@ struct _Itdb_Track 0x000c, AAC songs are always 0x0033, Audible files are 0x0029, WAV files are 0x0. itdb will attempt to set this value when adding a - track. */ + track. */ guint16 unk146; /* unknown, but appears to be 1 if played at least once in iTunes and 0 otherwise. */ guint32 unk148; /* unknown - used for Apple Store DRM songs @@ -791,9 +782,8 @@ struct _Itdb_Track /* below is for use by application */ guint64 usertype; gpointer userdata; - /* function called to duplicate userdata */ + /* functions called to duplicate/free userdata */ ItdbUserDataDuplicateFunc userdata_duplicate; - /* function called to free userdata */ ItdbUserDataDestroyFunc userdata_destroy; }; /* (gtkpod note: don't forget to add fields read from the file to @@ -939,7 +929,7 @@ void itdb_spl_update (Itdb_Playlist *spl); void itdb_spl_update_all (Itdb_iTunesDB *itdb); void itdb_spl_update_live (Itdb_iTunesDB *itdb); -/* thumbnails functions */ +/* thumbnails functions for coverart */ /* itdb_track_... */ gboolean itdb_track_set_thumbnails (Itdb_Track *track, const gchar *filename); @@ -948,25 +938,38 @@ gboolean itdb_track_set_thumbnails_from_data (Itdb_Track *track, gsize image_data_len); void itdb_track_remove_thumbnails (Itdb_Track *track); -/* photoalbum functions */ +/* photoalbum functions -- see itdb_photoalbum.c for instructions on + * how to use. */ Itdb_PhotoDB *itdb_photodb_parse (const gchar *mp, GError **error); -gboolean itdb_photodb_add_photo (Itdb_PhotoDB *db, - const gchar *albumname, - const gchar *filename); -gboolean itdb_photodb_add_photo_from_data (Itdb_PhotoDB *db, - const gchar *albumname, - const guchar *image_data, - gsize image_data_len); -Itdb_PhotoAlbum *itdb_photodb_photoalbum_new (Itdb_PhotoDB *db, - const gchar *album_name); -Itdb_PhotoDB *itdb_photodb_new (void); +Itdb_Artwork *itdb_photodb_add_photo (Itdb_PhotoDB *db, const gchar *filename, + GError **error); +Itdb_Artwork *itdb_photodb_add_photo_from_data (Itdb_PhotoDB *db, + const guchar *image_data, + gsize image_data_len, + GError **error); +void itdb_photodb_photoalbum_add_photo (Itdb_PhotoDB *db, + Itdb_PhotoAlbum *album, + Itdb_Artwork *photo); +Itdb_PhotoAlbum *itdb_photodb_photoalbum_create (Itdb_PhotoDB *db, + const gchar *albumname, + gint pos); +Itdb_PhotoDB *itdb_photodb_create (const gchar *mountpoint); void itdb_photodb_free (Itdb_PhotoDB *photodb); gboolean itdb_photodb_write (Itdb_PhotoDB *db, GError **error); -void itdb_photodb_photoalbum_free (Itdb_PhotoAlbum *pa); -gboolean itdb_photodb_remove_photo (Itdb_PhotoDB *db, - const gint photo_id ); - -/* itdb_artwork_... */ +void itdb_photodb_remove_photo (Itdb_PhotoDB *db, + Itdb_PhotoAlbum *album, + Itdb_Artwork *photo); +void itdb_photodb_photoalbum_remove (Itdb_PhotoDB *db, + Itdb_PhotoAlbum *album, + gboolean remove_pics); +Itdb_PhotoAlbum *itdb_photodb_photoalbum_by_name(Itdb_PhotoDB *db, + const gchar *albumname ); + +/* itdb_artwork_... -- you probably won't need many of these (probably + * with the exception of itdb_artwork_get_thumb_by_type() and + * itdb_thumb_get_gdk_pixbuf() probably). Use the itdb_photodb_...() + * functions when adding photos, and the itdb_track_...() functions + * when adding coverart to audio. */ Itdb_Artwork *itdb_artwork_new (void); Itdb_Artwork *itdb_artwork_duplicate (Itdb_Artwork *artwork); void itdb_artwork_free (Itdb_Artwork *artwork); @@ -983,7 +986,7 @@ void itdb_artwork_remove_thumbnail (Itdb_Artwork *artwork, Itdb_Thumb *thumb); void itdb_artwork_remove_thumbnails (Itdb_Artwork *artwork); /* itdb_thumb_... */ -/* the following funciton returns a pointer to a GdkPixbuf if +/* the following function returns a pointer to a GdkPixbuf if gdk-pixbuf is installed -- a NULL pointer otherwise. */ gpointer itdb_thumb_get_gdk_pixbuf (Itdb_Device *device, Itdb_Thumb *thumb); @@ -997,7 +1000,7 @@ guint64 itdb_time_get_mac_time (void); time_t itdb_time_mac_to_host (guint64 mactime); guint64 itdb_time_host_to_mac (time_t time); -/* Initialise a blank ipod */ +/* Initialize a blank ipod */ gboolean itdb_init_ipod (const gchar *mountpoint, const gchar *model_number, const gchar *ipod_name, diff --git a/src/itdb_photoalbum.c b/src/itdb_photoalbum.c index 003222d..4a06a28 100644 --- a/src/itdb_photoalbum.c +++ b/src/itdb_photoalbum.c @@ -1,3 +1,31 @@ +/* Time-stamp: <2006-10-22 20:19:45 jcs> +| +| Copyright (C) 2002-2006 Jorg Schuler <jcsjcs at users sourceforge net> +| Part of the gtkpod project. +| +| URL: http://www.gtkpod.org/ +| URL: http://gtkpod.sourceforge.net/ +| +| The code contained in this file is free software; you can redistribute +| it and/or modify it under the terms of the GNU Lesser General Public +| License as published by the Free Software Foundation; either version +| 2.1 of the License, or (at your option) any later version. +| +| This file is distributed in the hope that it will be useful, +| but WITHOUT ANY WARRANTY; without even the implied warranty of +| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +| Lesser General Public License for more details. +| +| You should have received a copy of the GNU Lesser General Public +| License along with this code; if not, write to the Free Software +| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +| +| iTunes and iPod are trademarks of Apple +| +| This product is not supported/written/published by Apple! +| +| $Id$ +*/ #include <config.h> #include "itdb_private.h" @@ -9,6 +37,59 @@ #include <glib/gi18n-lib.h> +/* Short summary: + + itdb_photodb_parse(): + Read an existing PhotoDB. + + itdb_photodb_create(): + Create a new Itdb_PhotoDB structure. The Photo Library Album is + (first album) is created automatically. + + itdb_photodb_add_photo(), itdb_photodb_add_photo_from_data(): + Add a photo to the PhotoDB (from file or from a chunk of + memory). It is automatically added to the Photo Library Album + (first album), which is created if it does not exist already. + + itdb_photodb_photoalbum_craete(): + Create and add a new photoalbum. + + itdb_photodb_photoalbum_add_photo(): + Add a photo (Itdb_Artwork) to an existing photoalbum. + + itdb_photodb_photoalbum_remove(): + Remove an existing photoalbum. Pictures can be kept in the + Photo Library or automatically removed as well. + + itdb_photodb_remove_photo(): + Remove a photo either from a photoalbum or completely from the database. + + itdb_photodb_write(): + Write out your PhotoDB. + + itdb_photodb_free(): + Free all memory taken by the PhotoDB. + + itdb_photodb_photoalbum_by_name(): + Find the first photoalbum with a given name. + + + If you cannot add photos because your iPod is not recognized, you + may have to set the iPod model by calling + + itdb_device_set_sysinfo (db->device, "ModelNumStr", model); + + For example, "MA450" would stand for an 80 GB 6th generation iPod + Video. See itdb_device.c for a list of supported models. + + This information will be written to the iPod when the PhotoDB is + saved (itdb_device_write_sysinfo() is called). +*/ + + +static Itdb_PhotoDB *itdb_photodb_new (void); +static void itdb_photodb_photoalbum_free (Itdb_PhotoAlbum *pa); + /* Set @error with standard error message */ static void error_no_photos_dir (const gchar *mp, GError **error) { @@ -146,14 +227,36 @@ Itdb_PhotoDB *itdb_photodb_parse (const gchar *mp, GError **error) /** - * itdb_photodb_new: + * itdb_photodb_create: * - * Creates a new Itdb_PhotoDB + * @mountpoint: mountpoint or NULL. + * + * Creates a new Itdb_PhotoDB. If mountpoint is NULL, you will have to + * set it manually later by calling itdb_device_set_mountpoint(). * * Return value: a newly created Itdb_PhotoDB to be freed with - * itdb_photodb_free() when it's no longer needed + * itdb_photodb_free() when it's no longer needed. The Photo Library + * Album is created automatically. **/ -Itdb_PhotoDB *itdb_photodb_new (void) +Itdb_PhotoDB *itdb_photodb_create (const gchar *mountpoint) +{ + Itdb_PhotoDB *photodb = itdb_photodb_new (); + Itdb_PhotoAlbum *album; + + album = itdb_photodb_photoalbum_create (photodb, _("Photo Libarary"), -1); + album->album_type = 1; /* Photo Library */ + + if (mountpoint) + { + itdb_device_set_mountpoint (photodb->device, mountpoint); + } + + return photodb; +} + + + +static Itdb_PhotoDB *itdb_photodb_new (void) { Itdb_PhotoDB *photodb; @@ -164,6 +267,7 @@ Itdb_PhotoDB *itdb_photodb_new (void) } + /** * itdb_photodb_free: * @photodb: an #Itdb_PhotoDB @@ -207,181 +311,353 @@ G_GNUC_INTERNAL gint itdb_get_free_photo_id ( Itdb_PhotoDB *db ) return photo_id + 1; } -static gint itdb_get_free_photoalbum_id ( Itdb_PhotoDB *db ) +static void itdb_photodb_photoalbum_free (Itdb_PhotoAlbum *album) { - gint album_id = 0; - GList *it; + if (album) + { + g_free (album->name); + g_list_free (album->members); - for (it = db->photoalbums; it != NULL; it = it->next) { - Itdb_PhotoAlbum *album; + if (album->userdata && album->userdata_destroy) + (*album->userdata_destroy) (album->userdata); - album = (Itdb_PhotoAlbum *)it->data; - if( album->album_id > album_id ) - album_id = album->album_id; - } - return album_id + 1; + g_free (album); + } } -static Itdb_PhotoAlbum *itdb_get_photoalbum ( Itdb_PhotoDB *db, const gchar *albumname ) + +/* called by itdb_photodb_add_photo() and + itdb_photodb_add_photo_from_data() */ +static Itdb_Artwork *itdb_photodb_add_photo_internal (Itdb_PhotoDB *db, + const gchar *filename, + const guchar *image_data, + gsize image_data_len, + GError **error) { - GList *it; +#ifdef HAVE_GDKPIXBUF + gboolean result; + Itdb_Artwork *artwork; + Itdb_PhotoAlbum *album; + const Itdb_ArtworkFormat *format; - if( strcmp( albumname, "master" ) == 0 ) - return g_list_nth_data (db->photoalbums, 0); + g_return_val_if_fail (db, NULL); + g_return_val_if_fail (db->device, NULL); + g_return_val_if_fail (filename || image_data, NULL); + g_return_val_if_fail (!(image_data && (image_data_len == 0)), NULL); - for (it = db->photoalbums; it != NULL; it = it->next) { - Itdb_PhotoAlbum *album; + if (!ipod_supports_photos (db->device)) + { + const Itdb_IpodInfo *ipodinfo = itdb_device_get_ipod_info (db->device); + const gchar *model, *generation; - album = (Itdb_PhotoAlbum *)it->data; - if( strcmp(album->name, albumname) == 0 ) - return album; + if (!ipodinfo) + { + g_set_error (error, 0, -1, + _("You need to specify the iPod model used before photos can be added.")); + return NULL; + /* For information: The model is set by calling the rather + unintuitive function itdb_device_set_sysinfo as + follows: + + itdb_device_set_sysinfo (db->device, "ModelNumStr", model); + + For example, "MA450" would stand for an 80 GB 6th + generation iPod Video. See itdb_device.c for a list of + supported models. + + This information will be written to the iPod when the + PhotoDB is saved (itdb_device_write_sysinfo() is called). + */ } - return (Itdb_PhotoAlbum *)NULL; -} + model = itdb_info_get_ipod_model_name_string (ipodinfo->ipod_model); + generation = itdb_info_get_ipod_generation_string (ipodinfo->ipod_generation); + g_return_val_if_fail (model && generation, NULL); + g_set_error (error, 0, -1, + _("Your iPod does not seem to support photos. Maybe you need to specify the correct iPod model number? It is currently set to 'x%s' (%s/%s)."), + ipodinfo->model_number, generation, model); + return NULL; + } -void itdb_photodb_photoalbum_free (Itdb_PhotoAlbum *pa) -{ - if (pa) + /* check if filename is valid */ + if (filename) { - g_free (pa->name); - g_list_free (pa->members); - - if (pa->userdata && pa->userdata_destroy) - (*pa->userdata_destroy) (pa->userdata); - - g_free (pa); + struct stat statbuf; + if (g_stat (filename, &statbuf) != 0) + { + g_set_error (error, 0, -1, + _("Could not access file '%s'. Photo not added.")); + return NULL; + } } -} - -static gboolean itdb_photodb_add_photo_internal (Itdb_PhotoDB *db, - const gchar *albumname, - const gchar *filename, - const guchar *image_data, - gsize image_data_len) -{ - gboolean result; - Itdb_Artwork *artwork; - Itdb_PhotoAlbum *photoalbum; - const Itdb_ArtworkFormat *format; - gint photo_id; - g_return_val_if_fail (db, FALSE); + artwork = itdb_artwork_new (); - artwork = itdb_artwork_new (); + /* Add a thumbnail for every supported format */ + format = itdb_device_get_artwork_formats (db->device); + g_return_val_if_fail (format, NULL); - photo_id = itdb_get_free_photo_id( db ); - artwork->id = photo_id; - - /* Add a thumbnail for every supported format */ - format = itdb_device_get_artwork_formats(db->device); - for( result = TRUE; format->type != -1 && result == TRUE; format++) + for(result = TRUE; format->type != -1 && result == TRUE; format++) + { + if((format->type == ITDB_THUMB_COVER_SMALL) || + (format->type == ITDB_THUMB_COVER_LARGE)) + continue; + if (filename) { - if((format->type == ITDB_THUMB_COVER_SMALL) || - (format->type == ITDB_THUMB_COVER_LARGE)) - continue; - if (filename) - { - result = itdb_artwork_add_thumbnail (artwork, - format->type, - filename); - } - if (image_data) - { - result = itdb_artwork_add_thumbnail_from_data (artwork, - format->type, - image_data, - image_data_len); - } + result = itdb_artwork_add_thumbnail (artwork, + format->type, + filename); } - - if (result != TRUE) + if (image_data) { - itdb_artwork_remove_thumbnails (artwork); - return result; + result = itdb_artwork_add_thumbnail_from_data (artwork, + format->type, + image_data, + image_data_len); } - db->photos = g_list_append (db->photos, artwork); + } - photoalbum = itdb_get_photoalbum( db, albumname ); - if( photoalbum == NULL ) - photoalbum = itdb_photodb_photoalbum_new( db, albumname ); - photoalbum->num_images++; - photoalbum->members = g_list_append (photoalbum->members, GINT_TO_POINTER(photo_id)); + if (result != TRUE) + { + itdb_artwork_free (artwork); + g_set_error (error, 0, -1, + _("Unexpected error in itdb_photodb_add_photo_internal() while adding photo, please report.")); + return NULL; + } - return result; + /* Add artwork to the list of photos */ + db->photos = g_list_append (db->photos, artwork); + + /* Add artwork to the first playlist */ + album = itdb_photodb_photoalbum_by_name (db, NULL); + if (!album) + { + album = itdb_photodb_photoalbum_create (db, _("Photo Libarary"), -1); + album->album_type = 1; /* Photo Library */ + } + itdb_photodb_photoalbum_add_photo (db, album, artwork); + + return artwork; +#else + g_set_error (error, 0, -1, + _("Library compiled without gkdpixbuf support. Picture support is disabled.")); + return NULL; +#endif } -gboolean itdb_photodb_add_photo (Itdb_PhotoDB *db, - const gchar *albumname, - const gchar *filename) + +/** + * itdb_photodb_add_photo: + * @db: the #Itdb_PhotoDB to add the photo to. + * @filename: file with the photo to add. + * @error: return location for a #GError or NULL + * + * Add a photo to the PhotoDB. The photo is automatically added to the + * first Photoalbum, which by default contains a list of all photos in + * the database. If no Photoalbums exist one is created automatically. + * + * Return value: a pointer to the added photo. + **/ +Itdb_Artwork *itdb_photodb_add_photo (Itdb_PhotoDB *db, + const gchar *filename, + GError **error) { g_return_val_if_fail (db, FALSE); g_return_val_if_fail (filename, FALSE); - return itdb_photodb_add_photo_internal (db, albumname, filename, - NULL, 0); + return itdb_photodb_add_photo_internal (db, filename, NULL, 0, error); } -gboolean itdb_photodb_add_photo_from_data (Itdb_PhotoDB *db, - const gchar *albumname, - const guchar *image_data, - gsize image_data_len) +/** + * itdb_photodb_add_photo_from_data: + * @db: the #Itdb_PhotoDB to add the photo to. + * @filename: file with the photo to add. + * @error: return location for a #GError or NULL + * + * Add a photo to the PhotoDB. The photo is automatically added to the + * first Photoalbum, which by default contains a list of all photos in + * the database. If no Photoalbums exist one is created automatically. + **/ +Itdb_Artwork *itdb_photodb_add_photo_from_data (Itdb_PhotoDB *db, + const guchar *image_data, + gsize image_data_len, + GError **error) { g_return_val_if_fail (db, FALSE); g_return_val_if_fail (image_data, FALSE); - return itdb_photodb_add_photo_internal (db, albumname, NULL, - image_data, image_data_len); + return itdb_photodb_add_photo_internal (db, NULL, image_data, image_data_len, + error); } -gboolean itdb_photodb_remove_photo (Itdb_PhotoDB *db, - const gint photo_id ) +/** + * itdb_photodb_remove_photo: + * @db: the #Itdb_PhotoDB to remove the photo from + * @album: the album to remove the photo from. If album is NULL, then + * it will first be removed from all photoalbums and then from the + * photo database as well. + * @photo: #Itdb_Artwork (photo) to remove. + * + * Remove photo. If @album is not the first photoalbum, the photo will + * be removed from that album only. If @album is NULL or the first + * photoalbum (Photo Library), the photo will be removed from all + * albums and the #Itdb_PhotoDB. + * + * @photo will be freed and can no longer be used if removed from the + * first photoalbum. + */ +void itdb_photodb_remove_photo (Itdb_PhotoDB *db, + Itdb_PhotoAlbum *album, + Itdb_Artwork *photo) { - gboolean result = TRUE; - GList *it; + GList *it; - g_return_val_if_fail (db, FALSE); + g_return_if_fail (db); - /* Remove the photo from the image list */ - for (it = db->photos; it != NULL; it = it->next) { - Itdb_Artwork *artwork; + /* If album==NULL, or album is the master album, remove from all + * albums */ + if ((album == NULL) || (album == g_list_nth_data (db->photoalbums, 0))) + { + /* Remove the photo from any albums containing it */ + for (it = db->photoalbums; it != NULL; it = it->next) + { + Itdb_PhotoAlbum *_album = it->data; + while (g_list_find (_album->members, photo)) + { + _album->members = g_list_remove (_album->members, photo); + --_album->num_images; + } + } + /* Remove the photo from the image list */ + db->photos = g_list_remove (db->photos, photo); + /* Free the photo */ + itdb_artwork_free (photo); + } + /* If album is specified, only remove it from that album */ + else + { + album->members = g_list_remove (album->members, photo); + --album->num_images; + } +} - artwork = (Itdb_Artwork *)it->data; - if( artwork->id == photo_id ) { - db->photos = g_list_remove (db->photos, artwork); - break; - } - } +/** + * itdb_photodb_photoalbum_by_name: + * Return a pointer to the first photoalbum named @albumname + * + * @db: the #Itdb_PhotoDB to retrieve the album from + * @albumname: the name of the photoalbum to get or NULL for the + * master photoalbum. + * + * Returns the photoalbum if found, else NULL + */ +Itdb_PhotoAlbum *itdb_photodb_photoalbum_by_name (Itdb_PhotoDB *db, const gchar *albumname) +{ + GList *it; + + if( albumname == NULL ) + return g_list_nth_data (db->photoalbums, 0); - /* Remove the photo from any albums containing it */ for (it = db->photoalbums; it != NULL; it = it->next) { Itdb_PhotoAlbum *album; album = (Itdb_PhotoAlbum *)it->data; - album->members = g_list_remove (album->members, GINT_TO_POINTER(photo_id)); - album->num_images = g_list_length( album->members ); + if( strcmp(album->name, albumname) == 0 ) + return album; } - return result; + return NULL; +} + +/** + * itdb_photodb_remove_photoalbum: + * @db: the #Itdb_PhotoDB to apply changes to + * @album: the album to be removed from the database + * @remove_pics: TRUE to remove pics in that album permanently from + * the database. + * + * Remove @album from the Photo Database. If remove_pics is TRUE, + * remove all photos contained in @album from the Photo Database. + * + * Memory used by the removed album will be freed and the album cannot + * be accessed any more. + **/ +void itdb_photodb_photoalbum_remove (Itdb_PhotoDB *db, + Itdb_PhotoAlbum *album, + gboolean remove_pics) +{ + GList *it; + + g_return_if_fail (db); + g_return_if_fail (album); + + /* if remove_pics, iterate over the photos within that album + * and remove them from the database */ + if (remove_pics) + { + for (it = album->members; it != NULL; it = it->next ) + { + Itdb_Artwork *photo = it->data; + itdb_photodb_remove_photo (db, NULL, photo); + } + } + db->photoalbums = g_list_remove (db->photoalbums, album); + itdb_photodb_photoalbum_free (album); +} + +/** + * itdb_photodb_photoalbum_add_photo: + * @db: the #Itdb_PhotoDB to act on + * @album: the #Itdb_PhotoAlbum to add the photo to + * @photo: a pointer to the photo (#Itdb_Artwork) to add to the album + * + * Adds a photo already in the library to the specified album + * @album. Photos are automatically added to the first album (Photo + * Library) when calling itdb_photodb_add_photo() or + * itdb_photodb_add_photo_from_data(), so you don't have to use this + * function to add them there. + * + */ + +void itdb_photodb_photoalbum_add_photo (Itdb_PhotoDB *db, + Itdb_PhotoAlbum *album, + Itdb_Artwork *photo) +{ + g_return_if_fail (db); + g_return_if_fail (album); + g_return_if_fail (photo); + + album->members = g_list_append (album->members, photo); + ++album->num_images; } -Itdb_PhotoAlbum *itdb_photodb_photoalbum_new (Itdb_PhotoDB *db, - const gchar *album_name) + +/** + * itdb_photodb_photoalbum_create: + * @db: The database to create a new album in + * @album_name: the name of the new album + * @pos: position where to insert the newly created album (-1 for + * append to end). + * + * Return value: the album which was created and added. + */ +Itdb_PhotoAlbum *itdb_photodb_photoalbum_create (Itdb_PhotoDB *db, + const gchar *albumname, + gint pos) { - Itdb_PhotoAlbum *photoalbum; + Itdb_PhotoAlbum *album; - g_return_val_if_fail (db, FALSE); - g_return_val_if_fail (album_name, FALSE); + g_return_val_if_fail (db, NULL); + g_return_val_if_fail (albumname, NULL); - photoalbum = g_new0 (Itdb_PhotoAlbum, 1); - photoalbum->album_id = itdb_get_free_photoalbum_id( db ); - photoalbum->prev_album_id = photoalbum->album_id - 1; - photoalbum->name = g_strdup( album_name ); - db->photoalbums = g_list_append (db->photoalbums, photoalbum); + album = g_new0 (Itdb_PhotoAlbum, 1); + album->album_type = 2; /* normal album, set to 1 for Photo Library */ + album->name = g_strdup(albumname); + db->photoalbums = g_list_insert (db->photoalbums, album, pos); - return photoalbum; + return album; } /** @@ -398,7 +674,9 @@ Itdb_PhotoAlbum *itdb_photodb_photoalbum_new (Itdb_PhotoDB *db, **/ gboolean itdb_photodb_write (Itdb_PhotoDB *photodb, GError **error) { - gboolean result; + gint result; + GList *gl; + gint32 id, prev_id; g_return_val_if_fail (photodb, FALSE); g_return_val_if_fail (photodb->device, FALSE); @@ -406,12 +684,41 @@ gboolean itdb_photodb_write (Itdb_PhotoDB *photodb, GError **error) if (photodb->device->byte_order == 0) itdb_device_autodetect_endianess (photodb->device); + /* set up photo_ids */ + id = 0x40; + for (gl=photodb->photos; gl; gl=gl->next) + { + Itdb_Artwork *photo = gl->data; + g_return_val_if_fail (photo, FALSE); + photo->id = id; + ++id; + } + /* set up album_ids -- this is how my iPod Nano does it... */ + prev_id = 0x64; + id = prev_id + g_list_length (photodb->photos); + for (gl=photodb->photoalbums; gl; gl=gl->next) + { + Itdb_PhotoAlbum *album = gl->data; + g_return_val_if_fail (album, FALSE); + album->album_id = id; + album->prev_album_id = prev_id; + ++id; + ++prev_id; + if (gl != photodb->photoalbums) + { /* except for the first album */ + prev_id += g_list_length (album->members); + } + } + result = ipod_write_photo_db (photodb); /* Write SysInfo file if it has changed */ - if (!(*error) && photodb->device->sysinfo_changed) + if (!error || !(*error)) { - itdb_device_write_sysinfo (photodb->device, error); + if (photodb->device->sysinfo_changed) + { + itdb_device_write_sysinfo (photodb->device, error); + } } if (result == -1) diff --git a/tests/test-photos.c b/tests/test-photos.c index a63d51b..36c3bf5 100644 --- a/tests/test-photos.c +++ b/tests/test-photos.c @@ -31,6 +31,34 @@ #include <gdk-pixbuf/gdk-pixbuf.h> #include <glib/gi18n-lib.h> +static void usage (int argc, char **argv) +{ +/* gchar *name = argv[0];*/ + gchar *name = "test-photos"; + + g_print (_("Usage to add photos:\n %s add <mountpoint> <albumname> [<filename(s)>]\n <albumname> should be set to 'NULL' to add photos to the master photo album\n (Photo Library) only. If you don't specify any filenames an empty album will\n be created.\n"), name); + g_print (_("Usage to dump all photos to <output_dir>:\n %s dump <mountpoint> <output_dir>\n"), name); + g_print (_("Usage to list all photos IDs to stdout:\n %s list <mountpoint>\n"), name); + g_print (_("Usage to remove photo IDs from Photo Library:\n %s remove <mountpoint> <albumname> [<ID(s)>]\n <albumname> should be set to 'NULL' to to remove photos from the iPod\n altogether. If you don't specify any IDs, the photoalbum will be removed\n instead.\n WARNING: IDs may change when writing the PhotoDB file.\n"), name); +} + +/* Retrieve the photo whose ID is @id */ +static Itdb_Artwork *get_photo_by_id (Itdb_PhotoDB *db, guint32 id) +{ + GList *gl; + + g_return_val_if_fail (db, NULL); + + for (gl=db->photos; gl; gl=gl->next) + { + Itdb_Artwork *photo = gl->data; + g_return_val_if_fail (photo, NULL); + + if (photo->id == id) return photo; + } + return NULL; +} + static void save_itdb_thumb (Itdb_PhotoDB *itdb, Itdb_Thumb *thumb, const gchar *filename) @@ -69,24 +97,6 @@ dump_thumbs (Itdb_PhotoDB *db, Itdb_Artwork *artwork, } static void -dump_artwork (Itdb_PhotoDB *db, gint photo_id, - const gchar *album_name, const gchar *dir) -{ - GList *it; - - for (it = db->photos; it != NULL; it = it->next) { - Itdb_Artwork *artwork; - - artwork = (Itdb_Artwork *)it->data; - g_return_if_fail (artwork); - if( artwork->id == photo_id ) { - dump_thumbs (db, artwork, album_name, dir); - break; - } - } -} - -static void dump_albums (Itdb_PhotoDB *db, const gchar *dir) { GList *it; @@ -99,91 +109,304 @@ dump_albums (Itdb_PhotoDB *db, const gchar *dir) g_return_if_fail (album); for (it2 = album->members; it2 != NULL; it2 = it2->next) { - gint photo_id = GPOINTER_TO_INT(it2->data); - dump_artwork (db, photo_id, album->name, dir); + Itdb_Artwork *photo = it2->data; + dump_thumbs (db, photo, album->name, dir); } } } -int -main (int argc, char **argv) + +static int do_dump (int argc, char **argv) +{ + GError *error = NULL; + Itdb_PhotoDB *db; + + if (argc != 4) + { + g_print (_("Wrong number of command line arguments.\n")); + usage (argc, argv); + return 1; + } + + if (!g_file_test (argv[3], G_FILE_TEST_EXISTS)) + { + if (mkdir (argv[3], 0777) == -1) + { + g_print (_("Error creating '%s' (mkdir)\n"), argv[3]); + return 1; + } + } + if (!g_file_test (argv[3], G_FILE_TEST_IS_DIR)) + { + g_print (_("Error: '%s' is not a directory\n"), argv[3]); + return 1; + } + + db = itdb_photodb_parse (argv[2], &error); + if (db == NULL) + { + if (error) + { + g_print (_("Error reading iPod photo database (%s).\n"), error->message); + g_error_free (error); + error = NULL; + } + else + { + g_print (_("Error reading iPod photo database.\n")); + } + return 1; + } + dump_albums (db, argv[3]); + itdb_photodb_free (db); + return 0; +} + +static int do_list (int argc, char **argv) +{ + GError *error = NULL; + Itdb_PhotoDB *db; + GList *gl_album; + + + if (argc != 3) + { + g_print (_("Insufficient number of command line arguments.\n")); + usage (argc, argv); + return 1; + } + + db = itdb_photodb_parse (argv[2], &error); + if (db == NULL) + { + if (error) + { + g_print (_("Error reading iPod photo database (%s).\n"), error->message); + g_error_free (error); + error = NULL; + } + else + { + g_print (_("Error reading iPod photo database.\n")); + } + return 1; + } + + for (gl_album=db->photoalbums; gl_album; gl_album=gl_album->next) + { + GList *gl_photo; + Itdb_PhotoAlbum *album = gl_album->data; + g_return_val_if_fail (album, 1); + + g_print ("%s: ", album->name?album->name:_("<Unnamed>")); + + for (gl_photo=album->members; gl_photo; gl_photo=gl_photo->next) + { + Itdb_Artwork *photo = gl_photo->data; + g_return_val_if_fail (photo, 1); + + g_print ("%d ", photo->id); + } + if (g_list_length (album->members) > 0) + { + g_print ("\n"); + } + else + { + g_print (_("<No members>\n")); + } + } + itdb_photodb_free (db); + return 0; +} + + +static int do_add (int argc, char **argv) { GError *error = NULL; + Itdb_PhotoAlbum *album = NULL; Itdb_PhotoDB *db; gint i; - if (argc < 4) { - g_print (_("Usage to add photos:\n")); - g_print (_("%s <mountpoint> <albumname> <filename(s)>\n"), argv[0]); - g_print (_("albumname should be set to 'master' to add photos to the master photo album\n")); - g_print (_("\n")); - g_print (_("Usage to dump all photos to <output_dir>:\n")); - g_print (_("%s dump <mountpoint> <output_dir>\n"), argv[0]); - return 1; + if (argc < 4) + { + g_print (_("Insufficient number of command line arguments.\n")); + usage (argc, argv); + return 1; } - setlocale (LC_ALL, ""); - g_type_init (); - if (strcmp (argv[1], "dump") == 0) + db = itdb_photodb_parse (argv[2], &error); + if (db == NULL) { - if (!g_file_test (argv[3], G_FILE_TEST_EXISTS)) + if (error) { - if (mkdir (argv[3], 0777) == -1) - { - g_print (_("Error creating '%s' (mkdir)\n"), argv[3]); - return 1; - } + g_print (_("Error reading iPod photo database (%s).\nWill attempt to create a new database.\n"), error->message); + g_error_free (error); + error = NULL; } - if (!g_file_test (argv[3], G_FILE_TEST_IS_DIR)) + else { - g_print (_("Error: '%s' is not a directory\n"), argv[3]); - return 1; + g_print (_("Error reading iPod photo database, will attempt to create a new database\n")); + } + db = itdb_photodb_create (argv[2]); + } + + /* Find or create specified photoalbum */ + if (strcmp (argv[3], "NULL") != 0) + { + album = itdb_photodb_photoalbum_by_name (db, argv[3]); + if (!album) + { + album = itdb_photodb_photoalbum_create (db, argv[3], -1); } + } - db = itdb_photodb_parse (argv[2], &error); - if (db == NULL) + for (i=4; i<argc; ++i) + { + Itdb_Artwork *photo; + + photo = itdb_photodb_add_photo (db, argv[i], &error); + if (photo == NULL) { if (error) { - g_print (_("Error reading iPod photo database (%s).\n"), error->message); + g_print (_("Error adding photo (%s) to photo database: %s\n"), + argv[i], error->message); g_error_free (error); error = NULL; } - else + } + else + { + if (album) { - g_print (_("Error reading iPod photo database.\n")); + itdb_photodb_photoalbum_add_photo (db, album, photo); } + } + } + + itdb_photodb_write (db, NULL); + itdb_photodb_free (db); + return 0; +} + + +static int do_remove (int argc, char **argv) +{ + GError *error = NULL; + Itdb_PhotoDB *db; + Itdb_PhotoAlbum *album = NULL; + + if (argc < 4) + { + g_print (_("Insufficient number of command line arguments.\n")); + usage (argc, argv); + return 1; + } + + db = itdb_photodb_parse (argv[2], &error); + if (db == NULL) + { + if (error) + { + g_print (_("Error reading iPod photo database (%s).\n"), + error->message); + g_error_free (error); + error = NULL; + } + else + { + g_print (_("Error reading iPod photo database")); + } + return 1; + } + + /* Find specified photoalbum */ + if (strcmp (argv[3], "NULL") != 0) + { + album = itdb_photodb_photoalbum_by_name (db, argv[3]); + if (!album) + { + g_print (_("Specified album '%s' not found. Aborting.\n"), + argv[3]); + itdb_photodb_free (db); + return 1; + } + } + + if (argc == 4) + { + /* Remove photoalbum altogether, but preserve pics */ + if (album == NULL) + { + g_print (_("Cannot remove Photo Libarary playlist. Aborting.\n")); + itdb_photodb_free (db); return 1; } - dump_albums (db, argv[3]); - itdb_photodb_free (db); + itdb_photodb_photoalbum_remove (db, album, FALSE); } else { - db = itdb_photodb_parse (argv[1], &error); - if (db == NULL) + /* Remove specified pictures */ + int i; + for (i=4; i<argc; ++i) { - if (error) + Itdb_Artwork *photo; + guint32 id; + + id = g_strtod (argv[i], NULL); + + photo = get_photo_by_id (db, id); + + if (photo == NULL) { - g_print (_("Error reading iPod photo database (%s).\nWill attempt to create a new database.\n"), error->message); - g_error_free (error); - error = NULL; + g_print (_("Error: could not find photo with ID <%d>. Skipping...\n"), + id); } else { - g_print (_("Error reading iPod photo database, will attempt to create a new database\n")); + itdb_photodb_remove_photo (db, album, photo); } - db = itdb_photodb_new (); - itdb_device_set_mountpoint (db->device, argv[1]); - } - for (i=3; i<argc; ++i) - { - itdb_photodb_add_photo (db, argv[2], argv[i]); } + } + + itdb_photodb_write (db, NULL); + itdb_photodb_free (db); + return 0; +} + + - itdb_photodb_write (db, NULL); - itdb_photodb_free (db); +int +main (int argc, char **argv) +{ + if (argc < 2) + { + g_print (_("Insufficient number of command line arguments.\n")); + usage (argc, argv); + return 1; + } + setlocale (LC_ALL, ""); + g_type_init (); + + if (strcmp (argv[1], "dump") == 0) + { + return do_dump (argc, argv); + } + if (strcmp (argv[1], "add") == 0) + { + return do_add (argc, argv); + } + if (strcmp (argv[1], "list") == 0) + { + return do_list (argc, argv); + } + if (strcmp (argv[1], "remove") == 0) + { + return do_remove (argc, argv); } + g_print (_("Unknown command '%s'\n"), argv[1]); + usage (argc, argv); return 0; } |