diff options
author | Jorg Schuler <jcsjcs@users.sourceforge.net> | 2007-05-06 15:00:03 +0000 |
---|---|---|
committer | Jorg Schuler <jcsjcs@users.sourceforge.net> | 2007-05-06 15:00:03 +0000 |
commit | bba246f83a4e3825974b4e06c8671dc866d8a31b (patch) | |
tree | b93fee84cb706d537fe92d084ac80663d808338d | |
parent | 11007f2c0535c595001a21699c3afed8b20168aa (diff) | |
download | libgpod-bba246f83a4e3825974b4e06c8671dc866d8a31b.tar.gz libgpod-bba246f83a4e3825974b4e06c8671dc866d8a31b.tar.xz libgpod-bba246f83a4e3825974b4e06c8671dc866d8a31b.zip |
* src/itdb_itunesdb.c
src/itdb_device.c
src/itdb_private.h
src/itdb.h:
New API functions to facilitate copying to the iPod in a
background thread and implementation of own copying code.
itdb_cp_get_dest_filename(): obtain a valid filename on the iPod
to where a track can be copied. This function can be used in a
thread-safe way.
itdb_cp_finalize(): to be called after the track was
copied to the iPod to update some fields in the Itdb_Track
structure. This function can also be used in a thread-safe way.
You can use the already existing function itdb_cp() to copy a
track to the iPod and itdb_cp_track_to_ipod() remains available
unchanged in functionality.
git-svn-id: https://gtkpod.svn.sf.net/svnroot/gtkpod/libgpod/trunk@1426 f01d2545-417e-4e96-918e-98f8d0dbbcb6
-rw-r--r-- | ChangeLog | 21 | ||||
-rw-r--r-- | src/itdb.h | 22 | ||||
-rw-r--r-- | src/itdb_device.c | 51 | ||||
-rw-r--r-- | src/itdb_itunesdb.c | 471 | ||||
-rw-r--r-- | src/itdb_private.h | 1 |
5 files changed, 389 insertions, 177 deletions
@@ -1,3 +1,24 @@ +2007-05-06 Jorg Schuler <jcsjcs at users.sourceforge.net> + + * src/itdb_itunesdb.c + src/itdb_device.c + src/itdb_private.h + src/itdb.h: + New API functions to facilitate copying to the iPod in a + background thread and implementation of own copying code. + + itdb_cp_get_dest_filename(): obtain a valid filename on the iPod + to where a track can be copied. This function can be used in a + thread-safe way. + + itdb_cp_finalize(): to be called after the track was + copied to the iPod to update some fields in the Itdb_Track + structure. This function can also be used in a thread-safe way. + + You can use the already existing function itdb_cp() to copy a + track to the iPod and itdb_cp_track_to_ipod() remains available + unchanged in functionality. + 2007-05-01 Jorg Schuler <jcsjcs at users.sourceforge.net> * src/itdb_itunesdb.c (itdb_cp_track_to_ipod): removed static @@ -968,11 +968,11 @@ struct _Itdb_Track \* ------------------------------------------------------------ */ typedef enum { - ITDB_FILE_ERROR_SEEK, /* file corrupt: illegal seek occured */ - ITDB_FILE_ERROR_CORRUPT, /* file corrupt */ - ITDB_FILE_ERROR_NOTFOUND, /* file not found */ - ITDB_FILE_ERROR_RENAME, /* file could not be renamed */ - ITDB_FILE_ERROR_ITDB_CORRUPT /* iTunesDB in memory corrupt */ + ITDB_FILE_ERROR_SEEK, /* file corrupt: illegal seek occured */ + ITDB_FILE_ERROR_CORRUPT, /* file corrupt */ + ITDB_FILE_ERROR_NOTFOUND, /* file not found */ + ITDB_FILE_ERROR_RENAME, /* file could not be renamed */ + ITDB_FILE_ERROR_ITDB_CORRUPT /* iTunesDB in memory corrupt */ } ItdbFileError; @@ -1008,10 +1008,18 @@ gint itdb_musicdirs_number (Itdb_iTunesDB *itdb); gchar *itdb_resolve_path (const gchar *root, const gchar * const * components); gboolean itdb_rename_files (const gchar *mp, GError **error); -gboolean itdb_cp_track_to_ipod (Itdb_Track *track, - const gchar *filename, GError **error); +gchar *itdb_cp_get_dest_filename (Itdb_Track *track, + const gchar *mountpoint, + const gchar *filename, + GError **error); gboolean itdb_cp (const gchar *from_file, const gchar *to_file, GError **error); +Itdb_Track *itdb_cp_finalize (Itdb_Track *track, + const gchar *mountpoint, + const gchar *dest_filename, + GError **error); +gboolean itdb_cp_track_to_ipod (Itdb_Track *track, + const gchar *filename, GError **error); void itdb_filename_fs2ipod (gchar *filename); void itdb_filename_ipod2fs (gchar *ipod_file); gchar *itdb_filename_on_ipod (Itdb_Track *track); diff --git a/src/itdb_device.c b/src/itdb_device.c index 8c5b9fb..cbff36e 100644 --- a/src/itdb_device.c +++ b/src/itdb_device.c @@ -31,6 +31,7 @@ */ #include "itdb_device.h" +#include "itdb_private.h" #include <ctype.h> #include <fcntl.h> #include <stdio.h> @@ -573,6 +574,36 @@ itdb_device_get_artwork_formats (Itdb_Device *device) } + +/* Determine the number of F.. directories in iPod_Control/Music.*/ +G_GNUC_INTERNAL gint +itdb_musicdirs_number_by_mountpoint (const gchar *mountpoint) +{ + gint dir_num; + gchar *music_dir = itdb_get_music_dir (mountpoint); + + if (!music_dir) return 0; + + /* count number of dirs */ + for (dir_num=0; ;++dir_num) + { + gchar *dir_filename; + gchar dir_num_str[6]; + + g_snprintf (dir_num_str, 6, "F%02d", dir_num); + + dir_filename = itdb_get_path (music_dir, dir_num_str); + + g_free (dir_filename); + if (!dir_filename) break; + } + + g_free (music_dir); + + return dir_num; +} + + /* Determine the number of F.. directories in iPod_Control/Music. If device->musicdirs is already set, simply return the previously @@ -581,29 +612,11 @@ itdb_device_get_artwork_formats (Itdb_Device *device) G_GNUC_INTERNAL gint itdb_device_musicdirs_number (Itdb_Device *device) { - gchar *dir_filename = NULL; - gint dir_num; - g_return_val_if_fail (device, 0); if (device->musicdirs <= 0) { - gchar *music_dir = itdb_get_music_dir (device->mountpoint); - if (!music_dir) return 0; - /* count number of dirs */ - for (dir_num=0; ;++dir_num) - { - gchar dir_num_str[5]; - - g_snprintf (dir_num_str, 5, "F%02d", dir_num); - - dir_filename = itdb_get_path (music_dir, dir_num_str); - - if (!dir_filename) break; - g_free (dir_filename); - } - device->musicdirs = dir_num; - g_free (music_dir); + device->musicdirs = itdb_musicdirs_number_by_mountpoint (device->mountpoint); } return device->musicdirs; } diff --git a/src/itdb_itunesdb.c b/src/itdb_itunesdb.c index 43215d7..a7a2730 100644 --- a/src/itdb_itunesdb.c +++ b/src/itdb_itunesdb.c @@ -5482,6 +5482,287 @@ gint itdb_musicdirs_number (Itdb_iTunesDB *itdb) return itdb_device_musicdirs_number (itdb->device); } +/** + * itdb_cp_get_dest_filename: + * @track: track to transfer or NULL + * @mountpoint: mountpoint of your iPod or NULL + * @filename: the source file + * @error: return location for a #GError or NULL + * + * Creates a valid filename on the iPod where to copy @filename. + * + * You must either provide @track or @mountpoint. Providing @track is + * not thread-safe (accesses track->itdb->device and may even write to + * track->itdb->device). Providing @mountpoint is thread-safe but + * slightly slower because the number of music directories is counted + * each time the function is called. + * + * You can use #itdb_cp() to copy the track to the iPod or implement + * your own copy function. After the file was copied you have to call + * #itdb_cp_finalize() to obtain relevant update information for + * #Itdb_Track. + * + * Return value: a valid filename on the iPod to where @filename can + * be copied or NULL in case of an error. In that case @error is set + * accordingly. You must free the filename when it is no longer + * needed. + **/ +gchar *itdb_cp_get_dest_filename (Itdb_Track *track, + const gchar *mountpoint, + const gchar *filename, + GError **error) +{ + gchar *ipod_fullfile = NULL; + + /* either supply mountpoint or track */ + g_return_val_if_fail (mountpoint || track, NULL); + /* if mountpoint is not set, track->itdb is required */ + g_return_val_if_fail (mountpoint || track->itdb, NULL); + g_return_val_if_fail (filename, NULL); + + if (!mountpoint) + { + mountpoint = itdb_get_mountpoint (track->itdb); + } + + if (!mountpoint) + { + g_set_error (error, + ITDB_FILE_ERROR, + ITDB_FILE_ERROR_NOTFOUND, + _("Mountpoint not set.")); + return NULL; + } + + /* If track->ipod_path exists, we use that one instead. */ + if (track) + { + ipod_fullfile = itdb_filename_on_ipod (track); + } + + if (!ipod_fullfile) + { + gint dir_num, musicdirs_number; + gchar *dest_components[] = {NULL, NULL, NULL}; + gchar *parent_dir_filename, *music_dir; + gchar *original_suffix; + gchar dir_num_str[6]; + gint32 oops = 0; + gint32 rand = g_random_int_range (0, 899999); /* 0 to 900000 */ + + music_dir = itdb_get_music_dir (mountpoint); + if (!music_dir) + { + error_no_music_dir (mountpoint, error); + return NULL; + } + + if (track) + { + musicdirs_number = itdb_musicdirs_number (track->itdb); + } + else + { + musicdirs_number = itdb_musicdirs_number_by_mountpoint (mountpoint); + } + if (musicdirs_number <= 0) + { + g_set_error (error, + ITDB_FILE_ERROR, + ITDB_FILE_ERROR_NOTFOUND, + _("No 'F..' directories found in '%s'."), + music_dir); + g_free (music_dir); + return NULL; + } + + dir_num = g_random_int_range (0, musicdirs_number); + + g_snprintf (dir_num_str, 6, "F%02d", dir_num); + dest_components[0] = dir_num_str; + + parent_dir_filename = + itdb_resolve_path (music_dir, (const gchar **)dest_components); + if(parent_dir_filename == NULL) + { + /* Can't find the F%02d directory */ + gchar *str = g_build_filename (music_dir, + dest_components[0], NULL); + g_set_error (error, + ITDB_FILE_ERROR, + ITDB_FILE_ERROR_NOTFOUND, + _("Path not found: '%s'."), + str); + g_free (str); + g_free (music_dir); + return NULL; + } + + /* we need the original suffix of pcfile to construct a correct ipod + filename */ + original_suffix = strrchr (filename, '.'); + /* If there is no ".mp3", ".m4a" etc, set original_suffix to empty + string. Note: the iPod will most certainly ignore this file... */ + if (!original_suffix) original_suffix = ""; + + /* use lower-case version of extension as some iPods seem to + choke on upper-case extension. */ + original_suffix = g_ascii_strdown (original_suffix, -1); + + do + { /* we need to loop until we find an unused filename */ + dest_components[1] = + g_strdup_printf("gtkpod%06d%s", + rand + oops, original_suffix); + ipod_fullfile = itdb_resolve_path ( + parent_dir_filename, + (const gchar **)&dest_components[1]); + if(ipod_fullfile) + { /* already exists -- try next */ + g_free(ipod_fullfile); + ipod_fullfile = NULL; + } + else + { /* found unused file -- build filename */ + ipod_fullfile = g_build_filename (parent_dir_filename, + dest_components[1], NULL); + } + g_free (dest_components[1]); + ++oops; + } while (!ipod_fullfile); + g_free(parent_dir_filename); + g_free (music_dir); + g_free (original_suffix); + } + + return ipod_fullfile; +} + + +/** + * itdb_cp_finalize: + * @track: track to update or NULL + * @mountpoint: mountpoint of your iPod or NULL + * @dest_filename: the name of the file on the iPod copied to + * @error: return location for a #GError or NULL + * + * Updates information in @track necessary for the iPod. You must + * either supply @track or @mountpoint. If @track == NULL, a new track + * structure is created that must be freed with #itdb_track_free() + * when it is no longer needed. + * + * The following fields are updated: + * + * - ipod_path + * - filetype_marker + * - transferred + * - size + * + * Return value: on success a pointer to the #Itdb_Track item passed + * or a new #Itdb_Track item if @track was NULL. In the latter case + * you must free the memory using #itdb_track_free() when the item is + * no longer used. If an error occurs NULL is returned and @error is + * set accordingly. Errors occur when @dest_filename cannot be + * accessed or the mountpoint is not set. + **/ +Itdb_Track *itdb_cp_finalize (Itdb_Track *track, + const gchar *mountpoint, + const gchar *dest_filename, + GError **error) +{ + const gchar *suffix; + Itdb_Track *use_track; + gint i, mplen; + struct stat statbuf; + + /* either supply mountpoint or track */ + g_return_val_if_fail (mountpoint || track, NULL); + /* if mountpoint is not set, track->itdb is required */ + g_return_val_if_fail (mountpoint || track->itdb, NULL); + g_return_val_if_fail (dest_filename, NULL); + + if (!mountpoint) + { + mountpoint = itdb_get_mountpoint (track->itdb); + } + + if (!mountpoint) + { + g_set_error (error, + ITDB_FILE_ERROR, + ITDB_FILE_ERROR_NOTFOUND, + _("Mountpoint not set.")); + return NULL; + } + + if (stat (dest_filename, &statbuf) == -1) + { + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + _("'%s' could not be accessed (%s)."), + dest_filename, g_strerror (errno)); + return NULL; + } + + if (strlen (mountpoint) >= strlen (dest_filename)) + { + g_set_error (error, + ITDB_FILE_ERROR, + ITDB_FILE_ERROR_CORRUPT, + _("Destination file '%s' does not appear to be on the iPod mounted at '%s'."), + dest_filename, mountpoint); + return NULL; + } + + if (!track) + { + use_track = itdb_track_new (); + } + else + { + use_track = track; + } + + use_track->transferred = TRUE; + use_track->size = statbuf.st_size; + + /* we need the original suffix of pcfile to construct a correct ipod + filename */ + suffix = strrchr (dest_filename, '.'); + /* If there is no ".mp3", ".m4a" etc, set original_suffix to empty + string. Note: the iPod will most certainly ignore this file... */ + if (!suffix) suffix = "."; + + /* set filetype from the suffix, e.g. '.mp3' -> 'MP3 ' */ + use_track->filetype_marker = 0; + for (i=1; i<=4; ++i) /* start with i=1 to skip the '.' */ + { + use_track->filetype_marker = use_track->filetype_marker << 8; + if (strlen (suffix) > i) + use_track->filetype_marker |= g_ascii_toupper (suffix[i]); + else + use_track->filetype_marker |= ' '; + } + + /* now extract filepath for use_track->ipod_path from ipod_fullfile */ + /* ipod_path must begin with a '/' */ + g_free (use_track->ipod_path); + mplen = strlen (mountpoint); /* length of mountpoint in bytes */ + if (dest_filename[mplen] == G_DIR_SEPARATOR) + { + use_track->ipod_path = g_strdup (&dest_filename[mplen]); + } + else + { + use_track->ipod_path = g_strdup_printf ("%c%s", G_DIR_SEPARATOR, + &dest_filename[mplen]); + } + /* convert to iPod type */ + itdb_filename_fs2ipod (use_track->ipod_path); + + return use_track; +} /** @@ -5518,153 +5799,35 @@ gint itdb_musicdirs_number (Itdb_iTunesDB *itdb) gboolean itdb_cp_track_to_ipod (Itdb_Track *track, const gchar *filename, GError **error) { - gint dir_num; - gchar *track_db_path, *ipod_fullfile; - gboolean success; - gint mplen = 0; - gint i; - const gchar *mountpoint; - Itdb_iTunesDB *itdb; - - g_return_val_if_fail (track, FALSE); - g_return_val_if_fail (track->itdb, FALSE); - g_return_val_if_fail (itdb_get_mountpoint (track->itdb), FALSE); - g_return_val_if_fail (filename, FALSE); - - if(track->transferred) return TRUE; /* nothing to do */ - - mountpoint = itdb_get_mountpoint (track->itdb); - itdb = track->itdb; - - /* If track->ipod_path exists, we use that one instead. */ - ipod_fullfile = itdb_filename_on_ipod (track); - - if (!ipod_fullfile) - { - gchar *dest_components[] = {NULL, NULL, NULL}; - gchar *parent_dir_filename, *music_dir; - gchar *original_suffix; - gchar dir_num_str[5]; - gint32 oops = 0; - gint32 rand = g_random_int_range (0, 899999); /* 0 to 900000 */ - - music_dir = itdb_get_music_dir (mountpoint); - if (!music_dir) - { - error_no_music_dir (mountpoint, error); - return FALSE; - } - - if (itdb_musicdirs_number (itdb) <= 0) - { - g_set_error (error, - ITDB_FILE_ERROR, - ITDB_FILE_ERROR_NOTFOUND, - _("No 'F..' directories found in '%s'."), - music_dir); - g_free (music_dir); - return FALSE; - } - - dir_num = g_random_int_range (0, itdb_musicdirs_number (itdb)); - - g_snprintf (dir_num_str, 5, "F%02d", dir_num); - dest_components[0] = dir_num_str; - - parent_dir_filename = - itdb_resolve_path (music_dir, (const gchar **)dest_components); - if(parent_dir_filename == NULL) - { - /* Can't find the F%02d directory */ - gchar *str = g_build_filename (music_dir, - dest_components[0], NULL); - g_set_error (error, - ITDB_FILE_ERROR, - ITDB_FILE_ERROR_NOTFOUND, - _("Path not found: '%s'."), - str); - g_free (str); - g_free (music_dir); - return FALSE; - } + gchar *dest_filename; + gboolean result = FALSE; - /* we need the original suffix of pcfile to construct a correct ipod - filename */ - original_suffix = strrchr (filename, '.'); - /* If there is no ".mp3", ".m4a" etc, set original_suffix to empty - string. Note: the iPod will most certainly ignore this file... */ - if (!original_suffix) original_suffix = ""; + g_return_val_if_fail (track, FALSE); + g_return_val_if_fail (track->itdb, FALSE); + g_return_val_if_fail (itdb_get_mountpoint (track->itdb), FALSE); + g_return_val_if_fail (filename, FALSE); - /* use lower-case version of extension as some iPods seem to - choke on upper-case extension. */ - original_suffix = g_ascii_strdown (original_suffix, -1); + if(track->transferred) return TRUE; /* nothing to do */ - /* set filetype from the suffix, e.g. '.mp3' -> 'MP3 ' */ - track->filetype_marker = 0; - for (i=1; i<=4; ++i) /* start with i=1 to skip the '.' */ - { - track->filetype_marker = track->filetype_marker << 8; - if (strlen (original_suffix) > i) - track->filetype_marker |= g_ascii_toupper (original_suffix[i]); - else - track->filetype_marker |= ' '; - } - do - { /* we need to loop until we find an unused filename */ - dest_components[1] = - g_strdup_printf("gtkpod%06d%s", - rand + oops, original_suffix); - ipod_fullfile = itdb_resolve_path ( - parent_dir_filename, - (const gchar **)&dest_components[1]); - if(ipod_fullfile) - { /* already exists -- try next */ - g_free(ipod_fullfile); - ipod_fullfile = NULL; - } - else - { /* found unused file -- build filename */ - ipod_fullfile = g_build_filename (parent_dir_filename, - dest_components[1], NULL); - } - g_free (dest_components[1]); - ++oops; - } while (!ipod_fullfile); - g_free(parent_dir_filename); - g_free (music_dir); - g_free (original_suffix); - } - /* now extract filepath for track->ipod_path from ipod_fullfile */ - /* ipod_path must begin with a '/' */ - mplen = strlen (mountpoint); /* length of mountpoint in bytes */ - if (ipod_fullfile[mplen] == G_DIR_SEPARATOR) - { - track_db_path = g_strdup (&ipod_fullfile[mplen]); - } - else - { - track_db_path = g_strdup_printf ("%c%s", G_DIR_SEPARATOR, - &ipod_fullfile[mplen]); - } - /* convert to iPod type */ - itdb_filename_fs2ipod (track_db_path); - -/* printf ("ff: %s\ndb: %s\n", ipod_fullfile, track_db_path); */ + dest_filename = itdb_cp_get_dest_filename (track, NULL, filename, error); - success = itdb_cp (filename, ipod_fullfile, error); - if (success) - { - track->transferred = TRUE; - g_free (track->ipod_path); - track->ipod_path = g_strdup (track_db_path); - } + if (dest_filename) + { + if (itdb_cp (filename, dest_filename, error)) + { + if (itdb_cp_finalize (track, NULL, dest_filename, error)) + { + result = TRUE; + } + } + g_free (dest_filename); + } - g_free (track_db_path); - g_free (ipod_fullfile); - return success; + return result; } + /** * itdb_filename_on_ipod: * @track: an #Itdb_Track @@ -5684,29 +5847,35 @@ gboolean itdb_cp_track_to_ipod (Itdb_Track *track, gchar *itdb_filename_on_ipod (Itdb_Track *track) { gchar *result = NULL; + gchar *buf; const gchar *mp; g_return_val_if_fail (track, NULL); + + if (!track->ipod_path || !*track->ipod_path) + { /* No filename set */ + return NULL; + } + g_return_val_if_fail (track->itdb, NULL); if (!itdb_get_mountpoint (track->itdb)) return NULL; mp = itdb_get_mountpoint (track->itdb); - if(track->ipod_path && *track->ipod_path) + buf = g_strdup (track->ipod_path); + itdb_filename_ipod2fs (buf); + result = g_build_filename (mp, buf, NULL); + g_free (buf); + + if (!g_file_test (result, G_FILE_TEST_EXISTS)) { - gchar *buf = g_strdup (track->ipod_path); - itdb_filename_ipod2fs (buf); - result = g_build_filename (mp, buf, NULL); - g_free (buf); - if (!g_file_test (result, G_FILE_TEST_EXISTS)) - { - gchar **components = g_strsplit (track->ipod_path,":",10); - g_free (result); - result = itdb_resolve_path (mp, (const gchar **)components); - g_strfreev (components); - } + gchar **components = g_strsplit (track->ipod_path,":",10); + g_free (result); + result = itdb_resolve_path (mp, (const gchar **)components); + g_strfreev (components); } + return result; } diff --git a/src/itdb_private.h b/src/itdb_private.h index 62d3fc3..49269b2 100644 --- a/src/itdb_private.h +++ b/src/itdb_private.h @@ -155,4 +155,5 @@ G_GNUC_INTERNAL time_t itdb_time_mac_to_time_t (Itdb_iTunesDB *db, guint64 mactime); G_GNUC_INTERNAL guint64 itdb_time_time_t_to_mac (Itdb_iTunesDB *db, time_t timet); +G_GNUC_INTERNAL gint itdb_musicdirs_number_by_mountpoint (const gchar *mountpoint); #endif |