~snaggen/rhythmbox/bpm

« back to all changes in this revision

Viewing changes to sources/rb-ipod-source.c

  • Committer: Jonathan Matthew
  • Author(s): Jonathan Matthew
  • Date: 2007-01-07 12:16:12 UTC
  • Revision ID: git-v1:5b6958ea3cf24f2c8bcfa15b9cab1fc7798b7305
Moved lots of files around. Now everything should be in the directory it's

2007-01-07  Jonathan Matthew  <jonathan@kaolin.wh9.net>

        Moved lots of files around.  Now everything should be in the directory
        it's built in and all plugin-specific files should be installed into
        the plugin directory.

        * plugins/audioscrobbler/rb-audioscrobbler-plugin.c:
        (impl_create_configure_dialog):
        * plugins/audioscrobbler/rb-audioscrobbler.c:
        (rb_audioscrobbler_class_init), (rb_audioscrobbler_dispose),
        (rb_audioscrobbler_finalize),
        (rb_audioscrobbler_get_config_widget):
        * plugins/audioscrobbler/rb-audioscrobbler.h:
        Use rb_plugin_find_file where needed.

        * widgets/rb-uri-dialog.c: (rb_uri_dialog_class_init),
        (rb_uri_dialog_init), (rb_uri_dialog_finalize),
        (rb_uri_dialog_new), (rb_uri_dialog_response_cb),
        (rb_uri_dialog_text_changed):
        * widgets/rb-uri-dialog.h:
        New common 'enter a URI' dialog, used by iradio and podcast code.

        * plugins/iradio/rb-iradio-plugin.c: (impl_activate):
        * plugins/iradio/rb-iradio-source.c:
        (rb_iradio_source_constructor), (rb_iradio_source_new),
        (impl_song_properties), (rb_iradio_source_first_time_changed),
        (new_station_location_added), (rb_iradio_source_cmd_new_station):
        * plugins/iradio/rb-iradio-source.h:
        * plugins/iradio/rb-station-properties-dialog.c:
        (rb_station_properties_dialog_class_init),
        (rb_station_properties_dialog_init),
        (rb_station_properties_dialog_constructor),
        (rb_station_properties_dialog_set_property),
        (rb_station_properties_dialog_get_property),
        (rb_station_properties_dialog_new):
        * plugins/iradio/rb-station-properties-dialog.h:
        Use the common URI dialog, use rb_plugin_find_file where needed.

        * sources/rb-podcast-source.c:
        (rb_podcast_source_location_added_cb),
        (rb_podcast_source_cmd_new_podcast):
        Use the common URI dialog.

svn path=/trunk/; revision=4725

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 *  arch-tag: Implementation of ipod source object
3
 
 *
4
 
 *  Copyright (C) 2004 Christophe Fergeau  <teuf@gnome.org>
5
 
 *
6
 
 *  This program is free software; you can redistribute it and/or modify
7
 
 *  it under the terms of the GNU General Public License as published by
8
 
 *  the Free Software Foundation; either version 2 of the License, or
9
 
 *  (at your option) any later version.
10
 
 *
11
 
 *  This program is distributed in the hope that it will be useful,
12
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 
 *  GNU General Public License for more details.
15
 
 *
16
 
 *  You should have received a copy of the GNU General Public License
17
 
 *  along with this program; if not, write to the Free Software
18
 
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
19
 
 *
20
 
 */
21
 
 
22
 
#include "config.h"
23
 
 
24
 
#include <string.h>
25
 
 
26
 
#include <glib/gi18n.h>
27
 
#include <gtk/gtk.h>
28
 
#ifdef HAVE_HAL
29
 
#include <libhal.h>
30
 
#include <dbus/dbus.h>
31
 
#endif
32
 
#include <libgnomevfs/gnome-vfs-utils.h>
33
 
#include <libgnomevfs/gnome-vfs-volume.h>
34
 
#include <libgnomevfs/gnome-vfs-volume-monitor.h>
35
 
#include <gpod/itdb.h>
36
 
 
37
 
#include "eel-gconf-extensions.h"
38
 
#include "rb-ipod-source.h"
39
 
#include "rb-debug.h"
40
 
#include "rb-file-helpers.h"
41
 
#include "rb-plugin.h"
42
 
#include "rb-removable-media-manager.h"
43
 
#include "rb-static-playlist-source.h"
44
 
#include "rb-util.h"
45
 
#include "rhythmdb.h"
46
 
#include "rb-cut-and-paste-code.h"
47
 
 
48
 
#ifdef IPOD_SUPPORT
49
 
#define PHONE_VENDOR_ID 0x22b8
50
 
#define PHONE_PRODUCT_ID 0x4810
51
 
#endif
52
 
 
53
 
static GObject *rb_ipod_source_constructor (GType type,
54
 
                                            guint n_construct_properties,
55
 
                                            GObjectConstructParam *construct_properties);
56
 
static void rb_ipod_source_dispose (GObject *object);
57
 
 
58
 
static GObject *rb_ipod_source_constructor (GType type, guint n_construct_properties,
59
 
                               GObjectConstructParam *construct_properties);
60
 
static void rb_ipod_source_dispose (GObject *object);
61
 
 
62
 
static gboolean impl_show_popup (RBSource *source);
63
 
static void impl_move_to_trash (RBSource *asource);
64
 
static void rb_ipod_load_songs (RBiPodSource *source);
65
 
static gchar *rb_ipod_get_mount_path (GnomeVFSVolume *volume);
66
 
static void impl_delete_thyself (RBSource *source);
67
 
static GList* impl_get_ui_actions (RBSource *source);
68
 
#ifdef HAVE_HAL
69
 
static gboolean hal_udi_is_ipod (const char *udi);
70
 
#endif
71
 
 
72
 
#ifdef ENABLE_IPOD_WRITING
73
 
static void impl_paste (RBSource *source, GList *entries);
74
 
static gboolean impl_receive_drag (RBSource *asource, GtkSelectionData *data);
75
 
static gchar *
76
 
ipod_get_filename_for_uri (const gchar *mount_point, const gchar *uri_str);
77
 
static gchar *
78
 
ipod_path_from_unix_path (const gchar *mount_point, const gchar *unix_path);
79
 
#endif
80
 
static void itdb_schedule_save (Itdb_iTunesDB *db);
81
 
 
82
 
typedef struct
83
 
{
84
 
        Itdb_iTunesDB *ipod_db;
85
 
        gchar *ipod_mount_path;
86
 
        GHashTable *entry_map;
87
 
 
88
 
        GList *playlists;
89
 
 
90
 
        guint load_idle_id;
91
 
} RBiPodSourcePrivate;
92
 
 
93
 
RB_PLUGIN_DEFINE_TYPE(RBiPodSource,
94
 
                      rb_ipod_source,
95
 
                      RB_TYPE_REMOVABLE_MEDIA_SOURCE)
96
 
 
97
 
#define IPOD_SOURCE_GET_PRIVATE(o)   (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_IPOD_SOURCE, RBiPodSourcePrivate))
98
 
 
99
 
static void
100
 
rb_ipod_source_class_init (RBiPodSourceClass *klass)
101
 
{
102
 
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
103
 
        RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
104
 
 
105
 
        object_class->constructor = rb_ipod_source_constructor;
106
 
        object_class->dispose = rb_ipod_source_dispose;
107
 
 
108
 
        source_class->impl_show_popup = impl_show_popup;
109
 
        source_class->impl_delete_thyself = impl_delete_thyself;
110
 
        source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_true_function;
111
 
        source_class->impl_move_to_trash = impl_move_to_trash;
112
 
        source_class->impl_can_rename = (RBSourceFeatureFunc) rb_true_function;
113
 
        source_class->impl_get_ui_actions = impl_get_ui_actions;
114
 
#ifdef ENABLE_IPOD_WRITING
115
 
        source_class->impl_can_paste = (RBSourceFeatureFunc) rb_true_function;
116
 
        source_class->impl_paste = impl_paste;
117
 
        source_class->impl_receive_drag = impl_receive_drag;
118
 
#endif
119
 
 
120
 
        g_type_class_add_private (klass, sizeof (RBiPodSourcePrivate));
121
 
}
122
 
 
123
 
static void
124
 
rb_ipod_source_set_ipod_name (RBiPodSource *source, const char *name)
125
 
{
126
 
        Itdb_Playlist *mpl;
127
 
        RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
128
 
 
129
 
        mpl = itdb_playlist_mpl (priv->ipod_db);
130
 
        if (mpl->name != NULL) {
131
 
                rb_debug ("Renaming iPod from %s to %s", mpl->name, name);
132
 
                if (strcmp (mpl->name, name) == 0) {
133
 
                        rb_debug ("iPod is already named %s", name);
134
 
                        return;
135
 
                }
136
 
        }
137
 
        g_free (mpl->name);
138
 
        mpl->name = g_strdup (name);
139
 
        itdb_schedule_save (priv->ipod_db);
140
 
}
141
 
 
142
 
static void
143
 
rb_ipod_source_name_changed_cb (RBiPodSource *source, GParamSpec *spec,
144
 
                                gpointer data)
145
 
{
146
 
        char *name;
147
 
 
148
 
        g_object_get (source, "name", &name, NULL);
149
 
        rb_ipod_source_set_ipod_name (source, name);
150
 
        g_free (name);
151
 
}
152
 
 
153
 
static void
154
 
rb_ipod_source_init (RBiPodSource *source)
155
 
{
156
 
        g_signal_connect (G_OBJECT (source), "notify::name",
157
 
                          (GCallback)rb_ipod_source_name_changed_cb, NULL);
158
 
}
159
 
 
160
 
static GObject *
161
 
rb_ipod_source_constructor (GType type, guint n_construct_properties,
162
 
                            GObjectConstructParam *construct_properties)
163
 
{
164
 
        RBiPodSource *source;
165
 
        RBEntryView *songs;
166
 
        RBiPodSourcePrivate *priv;
167
 
 
168
 
        source = RB_IPOD_SOURCE (G_OBJECT_CLASS (rb_ipod_source_parent_class)->
169
 
                        constructor (type, n_construct_properties, construct_properties));
170
 
        priv = IPOD_SOURCE_GET_PRIVATE (source);
171
 
 
172
 
        songs = rb_source_get_entry_view (RB_SOURCE (source));
173
 
        rb_entry_view_append_column (songs, RB_ENTRY_VIEW_COL_RATING, FALSE);
174
 
        rb_entry_view_append_column (songs, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE);
175
 
 
176
 
        rb_ipod_load_songs (source);
177
 
 
178
 
        return G_OBJECT (source);
179
 
}
180
 
 
181
 
static void
182
 
rb_ipod_source_dispose (GObject *object)
183
 
{
184
 
        RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (object);
185
 
 
186
 
        if (priv->ipod_db != NULL) {
187
 
                itdb_free (priv->ipod_db);
188
 
                priv->ipod_db = NULL;
189
 
        }
190
 
 
191
 
        if (priv->ipod_mount_path) {
192
 
                g_free (priv->ipod_mount_path);
193
 
                priv->ipod_mount_path = NULL;
194
 
        }
195
 
 
196
 
        if (priv->entry_map) {
197
 
                g_hash_table_destroy (priv->entry_map);
198
 
                priv->entry_map = NULL;
199
 
        }
200
 
 
201
 
        if (priv->load_idle_id != 0) {
202
 
                g_source_remove (priv->load_idle_id);
203
 
                priv->load_idle_id = 0;
204
 
        }
205
 
 
206
 
        G_OBJECT_CLASS (rb_ipod_source_parent_class)->dispose (object);
207
 
}
208
 
 
209
 
RBRemovableMediaSource *
210
 
rb_ipod_source_new (RBShell *shell,
211
 
                    GnomeVFSVolume *volume)
212
 
{
213
 
        RBiPodSource *source;
214
 
        RhythmDBEntryType entry_type;
215
 
        RhythmDB *db;
216
 
 
217
 
        g_assert (rb_ipod_is_volume_ipod (volume));
218
 
 
219
 
        g_object_get (shell, "db", &db, NULL);
220
 
        entry_type =  rhythmdb_entry_register_type (db, NULL);
221
 
        entry_type->save_to_disk = FALSE;
222
 
        entry_type->category = RHYTHMDB_ENTRY_NORMAL;
223
 
        g_object_unref (db);
224
 
 
225
 
        source = RB_IPOD_SOURCE (g_object_new (RB_TYPE_IPOD_SOURCE,
226
 
                                          "entry-type", entry_type,
227
 
                                          "volume", volume,
228
 
                                          "shell", shell,
229
 
                                          "sourcelist-group", RB_SOURCELIST_GROUP_REMOVABLE,
230
 
                                          NULL));
231
 
 
232
 
        rb_shell_register_entry_type_for_source (shell, RB_SOURCE (source), entry_type);
233
 
 
234
 
        return RB_REMOVABLE_MEDIA_SOURCE (source);
235
 
}
236
 
 
237
 
static void
238
 
entry_set_string_prop (RhythmDB *db, RhythmDBEntry *entry,
239
 
                       RhythmDBPropType propid, const char *str)
240
 
{
241
 
        GValue value = {0,};
242
 
 
243
 
        if (!str)
244
 
                str = _("Unknown");
245
 
 
246
 
        g_value_init (&value, G_TYPE_STRING);
247
 
        g_value_set_static_string (&value, str);
248
 
        rhythmdb_entry_set (RHYTHMDB (db), entry, propid, &value);
249
 
        g_value_unset (&value);
250
 
}
251
 
 
252
 
static char *
253
 
ipod_path_to_uri (const char *mount_point, const char *ipod_path)
254
 
{
255
 
        char *rel_pc_path;
256
 
        char *full_pc_path;
257
 
        char *uri;
258
 
 
259
 
        rel_pc_path = g_strdup (ipod_path);
260
 
        itdb_filename_ipod2fs (rel_pc_path);
261
 
        full_pc_path = g_build_filename (mount_point, rel_pc_path, NULL);
262
 
        g_free (rel_pc_path);
263
 
        uri = g_filename_to_uri (full_pc_path, NULL, NULL);
264
 
        g_free (full_pc_path);
265
 
        return uri;
266
 
}
267
 
 
268
 
static void
269
 
add_rb_playlist (RBiPodSource *source, Itdb_Playlist *playlist)
270
 
{
271
 
        RBShell *shell;
272
 
        RBSource *playlist_source;
273
 
        GList *it;
274
 
        RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
275
 
        RhythmDBEntryType entry_type;
276
 
 
277
 
        g_object_get (source,
278
 
                      "shell", &shell,
279
 
                      "entry-type", &entry_type,
280
 
                      NULL);
281
 
 
282
 
        playlist_source = rb_static_playlist_source_new (shell,
283
 
                                                         playlist->name,
284
 
                                                         FALSE,
285
 
                                                         entry_type);
286
 
        g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
287
 
 
288
 
        for (it = playlist->members; it != NULL; it = it->next) {
289
 
                Itdb_Track *song;
290
 
                char *filename;
291
 
 
292
 
                song = (Itdb_Track *)it->data;
293
 
                filename = ipod_path_to_uri (priv->ipod_mount_path,
294
 
                                            song->ipod_path);
295
 
                rb_static_playlist_source_add_location (RB_STATIC_PLAYLIST_SOURCE (playlist_source),
296
 
                                                        filename, -1);
297
 
                g_free (filename);
298
 
        }
299
 
 
300
 
        priv->playlists = g_list_prepend (priv->playlists, playlist_source);
301
 
 
302
 
        rb_shell_append_source (shell, playlist_source, RB_SOURCE (source));
303
 
        g_object_unref (shell);
304
 
}
305
 
 
306
 
static void
307
 
load_ipod_playlists (RBiPodSource *source)
308
 
{
309
 
        RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
310
 
        GList *it;
311
 
 
312
 
        for (it = priv->ipod_db->playlists; it != NULL; it = it->next) {
313
 
                Itdb_Playlist *playlist;
314
 
 
315
 
                playlist = (Itdb_Playlist *)it->data;
316
 
                if (itdb_playlist_is_mpl (playlist)) {
317
 
                        continue;
318
 
                }
319
 
                if (playlist->is_spl) {
320
 
                        continue;
321
 
                }
322
 
 
323
 
                add_rb_playlist (source, playlist);
324
 
        }
325
 
 
326
 
}
327
 
 
328
 
#ifdef ENABLE_IPOD_WRITING
329
 
static Itdb_Track *
330
 
create_ipod_song_from_entry (RhythmDBEntry *entry)
331
 
{
332
 
        Itdb_Track *track;
333
 
 
334
 
        track = itdb_track_new ();
335
 
 
336
 
        track->title = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_TITLE);
337
 
        track->album = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ALBUM);
338
 
        track->artist = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ARTIST);
339
 
        track->genre = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_GENRE);
340
 
        /*      track->filetype = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP);*/
341
 
        track->size = rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
342
 
        track->tracklen = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
343
 
        track->tracklen *= 1000;
344
 
        track->cd_nr = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER);
345
 
        track->track_nr = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER);
346
 
        track->bitrate = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_BITRATE);
347
 
        track->year = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DATE);
348
 
        track->time_added = itdb_time_get_mac_time ();
349
 
        track->time_played = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_LAST_PLAYED);
350
 
        track->time_played = itdb_time_host_to_mac (track->time_played);
351
 
        track->rating = rhythmdb_entry_get_double (entry, RHYTHMDB_PROP_RATING);
352
 
        track->app_rating = track->rating;
353
 
        track->playcount = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_PLAY_COUNT);
354
 
 
355
 
        return track;
356
 
}
357
 
#endif
358
 
 
359
 
static void
360
 
add_ipod_song_to_db (RBiPodSource *source, RhythmDB *db, Itdb_Track *song)
361
 
{
362
 
        RhythmDBEntry *entry;
363
 
        RhythmDBEntryType entry_type;
364
 
        RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
365
 
        char *pc_path;
366
 
 
367
 
        /* Set URI */
368
 
        g_object_get (source, "entry-type", &entry_type,
369
 
                      NULL);
370
 
 
371
 
        pc_path = ipod_path_to_uri (priv->ipod_mount_path,
372
 
                                    song->ipod_path);
373
 
        entry = rhythmdb_entry_new (RHYTHMDB (db), entry_type,
374
 
                                    pc_path);
375
 
        g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
376
 
 
377
 
        if (entry == NULL) {
378
 
                rb_debug ("cannot create entry %s", pc_path);
379
 
                g_free (pc_path);
380
 
                return;
381
 
        }
382
 
 
383
 
        rb_debug ("Adding %s from iPod", pc_path);
384
 
        g_free (pc_path);
385
 
 
386
 
        /* Set track number */
387
 
        if (song->track_nr != 0) {
388
 
                GValue value = {0, };
389
 
                g_value_init (&value, G_TYPE_ULONG);
390
 
                g_value_set_ulong (&value, song->track_nr);
391
 
                rhythmdb_entry_set (RHYTHMDB (db), entry,
392
 
                                               RHYTHMDB_PROP_TRACK_NUMBER,
393
 
                                               &value);
394
 
                g_value_unset (&value);
395
 
        }
396
 
 
397
 
        /* Set disc number */
398
 
        if (song->cd_nr != 0) {
399
 
                GValue value = {0, };
400
 
                g_value_init (&value, G_TYPE_ULONG);
401
 
                g_value_set_ulong (&value, song->cd_nr);
402
 
                rhythmdb_entry_set (RHYTHMDB (db), entry,
403
 
                                               RHYTHMDB_PROP_DISC_NUMBER,
404
 
                                               &value);
405
 
                g_value_unset (&value);
406
 
        }
407
 
 
408
 
        /* Set bitrate */
409
 
        if (song->bitrate != 0) {
410
 
                GValue value = {0, };
411
 
                g_value_init (&value, G_TYPE_ULONG);
412
 
                g_value_set_ulong (&value, song->bitrate);
413
 
                rhythmdb_entry_set (RHYTHMDB (db), entry,
414
 
                                               RHYTHMDB_PROP_BITRATE,
415
 
                                               &value);
416
 
                g_value_unset (&value);
417
 
        }
418
 
 
419
 
        /* Set length */
420
 
        if (song->tracklen != 0) {
421
 
                GValue value = {0, };
422
 
                g_value_init (&value, G_TYPE_ULONG);
423
 
                g_value_set_ulong (&value, song->tracklen/1000);
424
 
                rhythmdb_entry_set (RHYTHMDB (db), entry,
425
 
                                               RHYTHMDB_PROP_DURATION,
426
 
                                               &value);
427
 
                g_value_unset (&value);
428
 
        }
429
 
 
430
 
        /* Set file size */
431
 
        if (song->size != 0) {
432
 
                GValue value = {0, };
433
 
                g_value_init (&value, G_TYPE_UINT64);
434
 
                g_value_set_uint64 (&value, song->size);
435
 
                rhythmdb_entry_set (RHYTHMDB (db), entry,
436
 
                                               RHYTHMDB_PROP_FILE_SIZE,
437
 
                                               &value);
438
 
                g_value_unset (&value);
439
 
        }
440
 
 
441
 
        /* Set playcount */
442
 
        if (song->playcount != 0) {
443
 
                GValue value = {0, };
444
 
                g_value_init (&value, G_TYPE_ULONG);
445
 
                g_value_set_ulong (&value, song->playcount);
446
 
                rhythmdb_entry_set (RHYTHMDB (db), entry,
447
 
                                               RHYTHMDB_PROP_PLAY_COUNT,
448
 
                                               &value);
449
 
                g_value_unset (&value);
450
 
        }
451
 
 
452
 
        /* Set year */
453
 
        if (song->year != 0) {
454
 
                GDate *date = NULL;
455
 
                GType type;
456
 
                GValue value = {0, };
457
 
 
458
 
                date = g_date_new_dmy (1, G_DATE_JANUARY, song->year);
459
 
 
460
 
                type = rhythmdb_get_property_type (RHYTHMDB(db),
461
 
                                                   RHYTHMDB_PROP_DATE);
462
 
 
463
 
                g_value_init (&value, type);
464
 
                g_value_set_ulong (&value, (date ? g_date_get_julian (date) : 0));
465
 
 
466
 
                rhythmdb_entry_set (RHYTHMDB (db), entry,
467
 
                                               RHYTHMDB_PROP_DATE,
468
 
                                               &value);
469
 
                g_value_unset (&value);
470
 
                if (date)
471
 
                        g_date_free (date);
472
 
        }
473
 
 
474
 
        /* Set rating */
475
 
        if (song->rating != 0) {
476
 
                GValue value = {0, };
477
 
                g_value_init (&value, G_TYPE_DOUBLE);
478
 
                g_value_set_double (&value, song->rating/20.0);
479
 
                rhythmdb_entry_set (RHYTHMDB (db), entry,
480
 
                                               RHYTHMDB_PROP_RATING,
481
 
                                               &value);
482
 
                g_value_unset (&value);
483
 
        }
484
 
 
485
 
        /* Set last played */
486
 
        if (song->time_played != 0) {
487
 
                GValue value = {0, };
488
 
                g_value_init (&value, G_TYPE_ULONG);
489
 
                g_value_set_ulong (&value, itdb_time_mac_to_host (song->time_played));
490
 
                rhythmdb_entry_set (RHYTHMDB (db), entry,
491
 
                                               RHYTHMDB_PROP_LAST_PLAYED,
492
 
                                               &value);
493
 
                g_value_unset (&value);
494
 
        }
495
 
 
496
 
        /* Set title */
497
 
        entry_set_string_prop (RHYTHMDB (db), entry,
498
 
                               RHYTHMDB_PROP_TITLE, song->title);
499
 
 
500
 
        /* Set album, artist and genre from iTunesDB */
501
 
        entry_set_string_prop (RHYTHMDB (db), entry,
502
 
                               RHYTHMDB_PROP_ARTIST, song->artist);
503
 
 
504
 
        entry_set_string_prop (RHYTHMDB (db), entry,
505
 
                               RHYTHMDB_PROP_ALBUM, song->album);
506
 
 
507
 
        entry_set_string_prop (RHYTHMDB (db), entry,
508
 
                               RHYTHMDB_PROP_GENRE, song->genre);
509
 
 
510
 
        g_hash_table_insert (priv->entry_map, entry, song);
511
 
 
512
 
        rhythmdb_commit (RHYTHMDB (db));
513
 
}
514
 
 
515
 
static RhythmDB *
516
 
get_db_for_source (RBiPodSource *source)
517
 
{
518
 
        RBShell *shell;
519
 
        RhythmDB *db;
520
 
 
521
 
        g_object_get (source, "shell", &shell, NULL);
522
 
        g_object_get (shell, "db", &db, NULL);
523
 
        g_object_unref (shell);
524
 
 
525
 
        return db;
526
 
}
527
 
 
528
 
static gboolean
529
 
load_ipod_db_idle_cb (RBiPodSource *source)
530
 
{
531
 
        RhythmDB *db;
532
 
        GList *it;
533
 
        RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
534
 
 
535
 
        GDK_THREADS_ENTER ();
536
 
 
537
 
        db = get_db_for_source (source);
538
 
 
539
 
        g_assert (db != NULL);
540
 
        for (it = priv->ipod_db->tracks; it != NULL; it = it->next) {
541
 
                add_ipod_song_to_db (source, db, (Itdb_Track *)it->data);
542
 
        }
543
 
 
544
 
        load_ipod_playlists (source);
545
 
 
546
 
        g_object_unref (db);
547
 
 
548
 
        GDK_THREADS_LEAVE ();
549
 
        priv->load_idle_id = 0;
550
 
        return FALSE;
551
 
}
552
 
 
553
 
static void
554
 
rb_ipod_load_songs (RBiPodSource *source)
555
 
{
556
 
        RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
557
 
        GnomeVFSVolume *volume;
558
 
 
559
 
        g_object_get (source, "volume", &volume, NULL);
560
 
        priv->ipod_mount_path = rb_ipod_get_mount_path (volume);
561
 
 
562
 
        priv->ipod_db = itdb_parse (priv->ipod_mount_path, NULL);
563
 
        priv->entry_map = g_hash_table_new (g_direct_hash, g_direct_equal);
564
 
        if ((priv->ipod_db != NULL) && (priv->entry_map != NULL)) {
565
 
                Itdb_Playlist *mpl;
566
 
 
567
 
                /* FIXME: we could set a different icon depending on the iPod
568
 
                 * model
569
 
                 */
570
 
                mpl = itdb_playlist_mpl (priv->ipod_db);
571
 
                if (mpl && mpl->name) {
572
 
                        g_object_set (RB_SOURCE (source),
573
 
                                      "name", mpl->name,
574
 
                                      NULL);
575
 
                }
576
 
                priv->load_idle_id = g_idle_add ((GSourceFunc)load_ipod_db_idle_cb, source);
577
 
        }
578
 
 
579
 
        g_object_unref (volume);
580
 
}
581
 
 
582
 
static gchar *
583
 
rb_ipod_get_mount_path (GnomeVFSVolume *volume)
584
 
{
585
 
        gchar *path;
586
 
        gchar *uri;
587
 
 
588
 
        uri = gnome_vfs_volume_get_activation_uri (volume);
589
 
        path = g_filename_from_uri (uri, NULL, NULL);
590
 
        g_assert (path != NULL);
591
 
        g_free (uri);
592
 
 
593
 
        return path;
594
 
}
595
 
 
596
 
static gchar *
597
 
rb_ipod_get_itunesdb_path (GnomeVFSVolume *volume)
598
 
{
599
 
        gchar *mount_point_uri;
600
 
        gchar *mount_point;
601
 
        gchar *result;
602
 
 
603
 
        mount_point_uri = gnome_vfs_volume_get_activation_uri (volume);
604
 
        if (mount_point_uri == NULL) {
605
 
                return NULL;
606
 
        }
607
 
        mount_point = g_filename_from_uri (mount_point_uri, NULL, NULL);
608
 
        g_free (mount_point_uri);
609
 
        if (mount_point == NULL) {
610
 
                return NULL;
611
 
        }
612
 
 
613
 
#ifdef IPOD_SUPPORT
614
 
        result = itdb_get_itunesdb_path (mount_point);
615
 
#else
616
 
        result = g_build_filename (mount_point,
617
 
                                   "iPod_Control/iTunes/iTunesDB",
618
 
                                   NULL);
619
 
#endif
620
 
 
621
 
        g_free (mount_point);
622
 
        return result;
623
 
}
624
 
 
625
 
static gboolean
626
 
rb_ipod_volume_has_ipod_db (GnomeVFSVolume *volume)
627
 
{
628
 
        char *itunesdb_path;
629
 
        gboolean result;
630
 
 
631
 
        itunesdb_path = rb_ipod_get_itunesdb_path (volume);
632
 
 
633
 
        if (itunesdb_path != NULL) {
634
 
                result = g_file_test (itunesdb_path, G_FILE_TEST_EXISTS);
635
 
        } else {
636
 
                result = FALSE;
637
 
        }
638
 
        g_free (itunesdb_path);
639
 
 
640
 
        return result;
641
 
}
642
 
 
643
 
gboolean
644
 
rb_ipod_is_volume_ipod (GnomeVFSVolume *volume)
645
 
{
646
 
#ifdef HAVE_HAL
647
 
        gchar *udi;
648
 
#endif
649
 
        if (gnome_vfs_volume_get_volume_type (volume) != GNOME_VFS_VOLUME_TYPE_MOUNTPOINT) {
650
 
                return FALSE;
651
 
        }
652
 
 
653
 
#ifdef HAVE_HAL
654
 
        udi = gnome_vfs_volume_get_hal_udi (volume);
655
 
        if (udi != NULL) {
656
 
                gboolean result;
657
 
 
658
 
                result = hal_udi_is_ipod (udi);
659
 
                g_free (udi);
660
 
                if (result == FALSE) {
661
 
                        return FALSE;
662
 
                }
663
 
        }
664
 
#endif
665
 
 
666
 
        return rb_ipod_volume_has_ipod_db (volume);
667
 
}
668
 
 
669
 
#ifdef HAVE_HAL_0_5
670
 
 
671
 
static gboolean
672
 
hal_udi_is_ipod (const char *udi)
673
 
{
674
 
        LibHalContext *ctx;
675
 
        DBusConnection *conn;
676
 
        char *parent_udi;
677
 
        char *parent_name;
678
 
        gboolean result;
679
 
        DBusError error;
680
 
        gboolean inited = FALSE;
681
 
 
682
 
        result = FALSE;
683
 
        dbus_error_init (&error);
684
 
 
685
 
        conn = NULL;
686
 
        parent_udi = NULL;
687
 
        parent_name = NULL;
688
 
 
689
 
        ctx = libhal_ctx_new ();
690
 
        if (ctx == NULL) {
691
 
                /* FIXME: should we return an error somehow so that we can
692
 
                 * fall back to a check for iTunesDB presence instead ?
693
 
                 */
694
 
                rb_debug ("cannot connect to HAL");
695
 
                goto end;
696
 
        }
697
 
        conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
698
 
        if (conn == NULL || dbus_error_is_set (&error))
699
 
                goto end;
700
 
 
701
 
        libhal_ctx_set_dbus_connection (ctx, conn);
702
 
        if (!libhal_ctx_init (ctx, &error) || dbus_error_is_set (&error))
703
 
                goto end;
704
 
 
705
 
        inited = TRUE;
706
 
        parent_udi = libhal_device_get_property_string (ctx, udi,
707
 
                        "info.parent", &error);
708
 
        if (parent_udi == NULL || dbus_error_is_set (&error))
709
 
                goto end;
710
 
 
711
 
        parent_name = libhal_device_get_property_string (ctx, parent_udi,
712
 
                        "storage.model", &error);
713
 
#ifdef IPOD_SUPPORT
714
 
        {
715
 
                char *spider_udi;
716
 
                int vnd_id = 0;
717
 
                int product_id = 0;
718
 
 
719
 
                spider_udi = g_strdup(parent_udi);
720
 
                while (vnd_id == 0 && product_id == 0 && spider_udi != NULL) {
721
 
                        char *old_udi = spider_udi;
722
 
                        spider_udi =  libhal_device_get_property_string (ctx, spider_udi,
723
 
                                        "info.parent", &error);
724
 
                        if (dbus_error_is_set (&error)) {
725
 
                                dbus_error_free (&error);
726
 
                                dbus_error_init (&error);
727
 
                                spider_udi = NULL;
728
 
                                break;
729
 
                        }
730
 
                        g_free(old_udi);
731
 
 
732
 
                        vnd_id = libhal_device_get_property_int (ctx, spider_udi,
733
 
                                "usb.vendor_id", &error);
734
 
                        if (dbus_error_is_set(&error)) {
735
 
                                dbus_error_free (&error);
736
 
                                dbus_error_init (&error);
737
 
                                vnd_id = 0;
738
 
                        }
739
 
 
740
 
                        product_id = libhal_device_get_property_int (ctx, spider_udi,
741
 
                                "usb.product_id", &error);
742
 
                        if (dbus_error_is_set(&error)) {
743
 
                                dbus_error_free (&error);
744
 
                                dbus_error_init (&error);
745
 
                                product_id = 0;
746
 
                        }
747
 
                }
748
 
                g_free (spider_udi);
749
 
 
750
 
                if (vnd_id == PHONE_VENDOR_ID && product_id == PHONE_PRODUCT_ID) {
751
 
                        result = TRUE;
752
 
                }
753
 
        }
754
 
#endif
755
 
        if (parent_name == NULL || dbus_error_is_set (&error))
756
 
                goto end;
757
 
 
758
 
        if (strcmp (parent_name, "iPod") == 0)
759
 
                result = TRUE;
760
 
 
761
 
end:
762
 
        g_free (parent_udi);
763
 
        g_free (parent_name);
764
 
 
765
 
        if (dbus_error_is_set (&error)) {
766
 
                rb_debug ("Error: %s\n", error.message);
767
 
                dbus_error_free (&error);
768
 
                dbus_error_init (&error);
769
 
        }
770
 
 
771
 
        if (ctx) {
772
 
                if (inited)
773
 
                        libhal_ctx_shutdown (ctx, &error);
774
 
                libhal_ctx_free(ctx);
775
 
        }
776
 
 
777
 
        dbus_error_free (&error);
778
 
 
779
 
        return result;
780
 
}
781
 
 
782
 
#elif HAVE_HAL_0_2
783
 
 
784
 
static gboolean
785
 
hal_udi_is_ipod (const char *udi)
786
 
{
787
 
        LibHalContext *ctx;
788
 
        char *parent_udi;
789
 
        char *parent_name;
790
 
        gboolean result;
791
 
 
792
 
        result = FALSE;
793
 
        ctx = hal_initialize (NULL, FALSE);
794
 
        if (ctx == NULL) {
795
 
                /* FIXME: should we return an error somehow so that we can
796
 
                 * fall back to a check for iTunesDB presence instead ?
797
 
                 */
798
 
                return FALSE;
799
 
        }
800
 
        parent_udi = hal_device_get_property_string (ctx, udi,
801
 
                        "info.parent");
802
 
        parent_name = hal_device_get_property_string (ctx, parent_udi,
803
 
                        "storage.model");
804
 
        g_free (parent_udi);
805
 
 
806
 
        if (parent_name != NULL && strcmp (parent_name, "iPod") == 0) {
807
 
                result = TRUE;
808
 
        }
809
 
 
810
 
        g_free (parent_name);
811
 
        hal_shutdown (ctx);
812
 
 
813
 
        return result;
814
 
}
815
 
 
816
 
#endif
817
 
 
818
 
static GList*
819
 
impl_get_ui_actions (RBSource *source)
820
 
{
821
 
        GList *actions = NULL;
822
 
 
823
 
        actions = g_list_prepend (actions, g_strdup ("RemovableSourceEject"));
824
 
 
825
 
        return actions;
826
 
}
827
 
 
828
 
static gboolean
829
 
impl_show_popup (RBSource *source)
830
 
{
831
 
        _rb_source_show_popup (RB_SOURCE (source), "/iPodSourcePopup");
832
 
        return TRUE;
833
 
}
834
 
 
835
 
static void
836
 
remove_track_from_db (Itdb_Track *track)
837
 
{
838
 
        GList *it;
839
 
 
840
 
        for (it = track->itdb->playlists; it != NULL; it = it->next) {
841
 
                itdb_playlist_remove_track ((Itdb_Playlist *)it->data, track);
842
 
        }
843
 
        itdb_track_remove (track);
844
 
}
845
 
 
846
 
static void
847
 
impl_move_to_trash (RBSource *asource)
848
 
{
849
 
        GList *sel, *tem;
850
 
        RBEntryView *songs;
851
 
        RhythmDB *db;
852
 
        RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (asource);
853
 
        RBiPodSource *source = RB_IPOD_SOURCE (asource);
854
 
 
855
 
        db = get_db_for_source (source);
856
 
 
857
 
        songs = rb_source_get_entry_view (RB_SOURCE (asource));
858
 
        sel = rb_entry_view_get_selected_entries (songs);
859
 
        for (tem = sel; tem != NULL; tem = tem->next) {
860
 
                RhythmDBEntry *entry;
861
 
                const gchar *uri;
862
 
                Itdb_Track *track;
863
 
 
864
 
                entry = (RhythmDBEntry *)tem->data;
865
 
                uri = rhythmdb_entry_get_string (entry,
866
 
                                                 RHYTHMDB_PROP_LOCATION);
867
 
                track = g_hash_table_lookup (priv->entry_map, entry);
868
 
                if (track == NULL) {
869
 
                        g_warning ("Couldn't find track on ipod! (%s)", uri);
870
 
                        continue;
871
 
                }
872
 
 
873
 
                remove_track_from_db (track);
874
 
                g_hash_table_remove (priv->entry_map, entry);
875
 
                rhythmdb_entry_move_to_trash (db, entry);
876
 
                rhythmdb_commit (db);
877
 
        }
878
 
 
879
 
        if (sel != NULL) {
880
 
                itdb_write (priv->ipod_db, NULL);
881
 
        }
882
 
 
883
 
        g_object_unref (db);
884
 
 
885
 
        g_list_free (sel);
886
 
}
887
 
 
888
 
static void
889
 
itdb_schedule_save (Itdb_iTunesDB *db)
890
 
{
891
 
       /* FIXME: should probably be delayed a bit to avoid doing
892
 
        * it after each file when we are copying several files
893
 
        * consecutively
894
 
        * FIXME: or this function could be called itdb_set_dirty, and we'd
895
 
        * have a timeout firing every 5 seconds and saving the db if it's
896
 
        * dirty
897
 
        */
898
 
       itdb_write (db, NULL);
899
 
}
900
 
 
901
 
#ifdef ENABLE_IPOD_WRITING
902
 
static char *
903
 
build_filename (RBSource *asource, RhythmDBEntry *entry)
904
 
{
905
 
        const char *uri;
906
 
        char *dest;
907
 
        char *dest_uri;
908
 
 
909
 
        RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (asource);
910
 
 
911
 
        uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
912
 
        dest = ipod_get_filename_for_uri (priv->ipod_mount_path,  uri);
913
 
        if (dest != NULL) {
914
 
                dest_uri = g_filename_to_uri (dest, NULL, NULL);
915
 
                g_free (dest);
916
 
                return dest_uri;
917
 
        }
918
 
 
919
 
        return NULL;
920
 
}
921
 
 
922
 
static void
923
 
completed_cb (RhythmDBEntry *entry, const char *dest, RBiPodSource *source)
924
 
{
925
 
        RhythmDB *db;
926
 
        Itdb_Track *song;
927
 
 
928
 
        db = get_db_for_source (source);
929
 
 
930
 
        song = create_ipod_song_from_entry (entry);
931
 
        if (song != NULL) {
932
 
                RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
933
 
                char *filename;
934
 
 
935
 
                filename = g_filename_from_uri (dest, NULL, NULL);
936
 
                song->ipod_path = ipod_path_from_unix_path (priv->ipod_mount_path, filename);
937
 
                g_free (filename);
938
 
                itdb_track_add (priv->ipod_db, song, -1);
939
 
                itdb_playlist_add_track (itdb_playlist_mpl (priv->ipod_db),
940
 
                                         song, -1);
941
 
 
942
 
                add_ipod_song_to_db (source, db, song);
943
 
                itdb_schedule_save (priv->ipod_db);
944
 
        }
945
 
 
946
 
        g_object_unref (db);
947
 
}
948
 
 
949
 
static void
950
 
impl_paste (RBSource *asource, GList *entries)
951
 
{
952
 
        RBRemovableMediaManager *rm_mgr;
953
 
        GList *l;
954
 
        RBShell *shell;
955
 
 
956
 
        g_object_get (asource, "shell", &shell, NULL);
957
 
        g_object_get (shell,
958
 
                      "removable-media-manager", &rm_mgr,
959
 
                      NULL);
960
 
        g_object_unref (shell);
961
 
 
962
 
        for (l = entries; l != NULL; l = l->next) {
963
 
                RhythmDBEntry *entry;
964
 
                RhythmDBEntryType entry_type;
965
 
                RhythmDBEntryType ipod_entry_type;
966
 
                char *dest;
967
 
 
968
 
                entry = (RhythmDBEntry *)l->data;
969
 
                entry_type = rhythmdb_entry_get_entry_type (entry);
970
 
                g_object_get (asource,
971
 
                              "entry-type", &ipod_entry_type,
972
 
                              NULL);
973
 
                if (entry_type == ipod_entry_type ||
974
 
                    entry_type->category != RHYTHMDB_ENTRY_NORMAL) {
975
 
                        g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
976
 
                        continue;
977
 
                }
978
 
 
979
 
                dest = build_filename (asource, entry);
980
 
                if (dest == NULL) {
981
 
                        rb_debug ("could not create destination path for entry");
982
 
                        g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
983
 
                        continue;
984
 
                }
985
 
                rb_removable_media_manager_queue_transfer (rm_mgr, entry,
986
 
                                                           dest, NULL,
987
 
                                                           (RBTranferCompleteCallback)completed_cb, asource);
988
 
                g_free (dest);
989
 
                g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
990
 
        }
991
 
 
992
 
        g_object_unref (rm_mgr);
993
 
}
994
 
 
995
 
static gboolean
996
 
impl_receive_drag (RBSource *asource, GtkSelectionData *data)
997
 
{
998
 
        RBBrowserSource *source = RB_BROWSER_SOURCE (asource);
999
 
        GList *list, *i;
1000
 
        GList *entries = NULL;
1001
 
        RhythmDB *db;
1002
 
        gboolean is_id;
1003
 
 
1004
 
        rb_debug ("parsing uri list");
1005
 
        list = rb_uri_list_parse ((const char *) data->data);
1006
 
        is_id = (data->type == gdk_atom_intern ("application/x-rhythmbox-entry", TRUE));
1007
 
 
1008
 
        db = get_db_for_source (RB_IPOD_SOURCE (source));
1009
 
 
1010
 
        for (i = list; i != NULL; i = g_list_next (i)) {
1011
 
                if (i->data != NULL) {
1012
 
                        char *uri = i->data;
1013
 
                        RhythmDBEntry *entry;
1014
 
 
1015
 
                        entry = rhythmdb_entry_lookup_from_string (db, uri, is_id);
1016
 
 
1017
 
                        if (entry == NULL) {
1018
 
                                /* add to the library */
1019
 
                                g_print ("Where does that come from?\n");
1020
 
                        } else {
1021
 
                                /* add to list of entries to copy */
1022
 
                                entries = g_list_prepend (entries, entry);
1023
 
                        }
1024
 
 
1025
 
                        g_free (uri);
1026
 
                }
1027
 
        }
1028
 
        g_object_unref (db);
1029
 
        g_list_free (list);
1030
 
 
1031
 
        if (entries) {
1032
 
                entries = g_list_reverse (entries);
1033
 
                if (rb_source_can_paste (asource))
1034
 
                        rb_source_paste (asource, entries);
1035
 
                g_list_free (entries);
1036
 
        }
1037
 
 
1038
 
        return TRUE;
1039
 
}
1040
 
 
1041
 
/* Generation of the filename for the ipod */
1042
 
 
1043
 
#define IPOD_MAX_PATH_LEN 56
1044
 
 
1045
 
static gboolean
1046
 
test_dir_on_ipod (const char *mountpoint, const char *dirname)
1047
 
{
1048
 
        char *fullpath;
1049
 
        gboolean result;
1050
 
 
1051
 
        fullpath  = g_build_filename (mountpoint, dirname, NULL);
1052
 
        result = g_file_test (fullpath, G_FILE_TEST_IS_DIR);
1053
 
        g_free (fullpath);
1054
 
 
1055
 
        return result;
1056
 
}
1057
 
 
1058
 
static int
1059
 
ipod_mkdir_with_parents (const char *mountpoint, const char *dirname)
1060
 
{
1061
 
        char *fullpath;
1062
 
        int result;
1063
 
 
1064
 
        fullpath  = g_build_filename (mountpoint, dirname, NULL);
1065
 
        result = g_mkdir_with_parents (fullpath, 0770);
1066
 
        g_free (fullpath);
1067
 
 
1068
 
        return result;
1069
 
}
1070
 
 
1071
 
static gchar *
1072
 
build_ipod_dir_name (const char *mountpoint)
1073
 
{
1074
 
        /* FAT sucks, filename can be lowercase or uppercase, and if we try to
1075
 
         * open the wrong one, we lose :-/
1076
 
         */
1077
 
        char *dirname;
1078
 
        char *relpath;
1079
 
        gint32 suffix;
1080
 
 
1081
 
        suffix = g_random_int_range (0, 100);
1082
 
        dirname = g_strdup_printf ("F%02d", suffix);
1083
 
        relpath = g_build_filename (G_DIR_SEPARATOR_S, "iPod_Control",
1084
 
                                    "Music", dirname, NULL);
1085
 
        g_free (dirname);
1086
 
 
1087
 
        if (test_dir_on_ipod (mountpoint, relpath) != FALSE) {
1088
 
                return relpath;
1089
 
        }
1090
 
 
1091
 
        g_free (relpath);
1092
 
        dirname = g_strdup_printf ("f%02d", g_random_int_range (0, 100));
1093
 
        relpath = g_build_filename (G_DIR_SEPARATOR_S, "iPod_Control",
1094
 
                                    "Music", dirname, NULL);
1095
 
        g_free (dirname);
1096
 
 
1097
 
        if (test_dir_on_ipod (mountpoint, relpath) != FALSE) {
1098
 
                return relpath;
1099
 
        }
1100
 
 
1101
 
        if (ipod_mkdir_with_parents (mountpoint, relpath) == 0) {
1102
 
                return relpath;
1103
 
        }
1104
 
 
1105
 
        g_free (relpath);
1106
 
        return NULL;
1107
 
}
1108
 
 
1109
 
static gchar *
1110
 
get_ipod_filename (const char *mount_point, const char *filename)
1111
 
{
1112
 
        char *dirname;
1113
 
        char *result;
1114
 
        char *tmp;
1115
 
 
1116
 
        dirname = build_ipod_dir_name (mount_point);
1117
 
        if (dirname == NULL) {
1118
 
                return NULL;
1119
 
        }
1120
 
        result = g_build_filename (dirname, filename, NULL);
1121
 
        g_free (dirname);
1122
 
 
1123
 
        if (strlen (result) >= IPOD_MAX_PATH_LEN) {
1124
 
                char *ext;
1125
 
 
1126
 
                ext = strrchr (result, '.');
1127
 
                if (ext == NULL) {
1128
 
                        result [IPOD_MAX_PATH_LEN - 1] = '\0';
1129
 
                } else {
1130
 
                        memmove (&result[IPOD_MAX_PATH_LEN - strlen (ext) - 1] ,
1131
 
                                 ext, strlen (ext) + 1);
1132
 
                }
1133
 
        }
1134
 
 
1135
 
        tmp = g_build_filename (mount_point, result, NULL);
1136
 
        g_free (result);
1137
 
        return tmp;
1138
 
}
1139
 
 
1140
 
#define MAX_TRIES 5
1141
 
 
1142
 
/* Strips non UTF8 characters from a string replacing them with _ */
1143
 
static gchar *
1144
 
utf8_to_ascii (const gchar *utf8)
1145
 
{
1146
 
        GString *string;
1147
 
        const guchar *it = (const guchar *)utf8;
1148
 
 
1149
 
        string = g_string_new ("");
1150
 
        while ((it != NULL) && (*it != '\0')) {
1151
 
                /* Do we have a 7 bit char ? */
1152
 
                if (*it < 0x80) {
1153
 
                        g_string_append_c (string, *it);
1154
 
                } else {
1155
 
                        g_string_append_c (string, '_');
1156
 
                }
1157
 
                it = (const guchar *)g_utf8_next_char (it);
1158
 
        }
1159
 
 
1160
 
        return g_string_free (string, FALSE);
1161
 
}
1162
 
 
1163
 
static gchar *
1164
 
generate_ipod_filename (const gchar *mount_point, const gchar *filename)
1165
 
{
1166
 
        gchar *ipod_filename = NULL;
1167
 
        gchar *pc_filename;
1168
 
        gchar *tmp;
1169
 
        gint tries = 0;
1170
 
 
1171
 
        /* First, we need a valid UTF-8 filename, strip all non-UTF-8 chars */
1172
 
        tmp = rb_make_valid_utf8 (filename, '_');
1173
 
        /* The iPod doesn't seem to recognize non-ascii chars in filenames,
1174
 
         * so we strip them
1175
 
         */
1176
 
        pc_filename = utf8_to_ascii (tmp);
1177
 
        g_free (tmp);
1178
 
 
1179
 
        g_assert (g_utf8_validate (pc_filename, -1, NULL));
1180
 
        /* Now we have a valid UTF-8 filename, try to find out where to put
1181
 
         * it on the iPod
1182
 
         */
1183
 
        do {
1184
 
                g_free (ipod_filename);
1185
 
                ipod_filename = get_ipod_filename (mount_point, pc_filename);
1186
 
                tries++;
1187
 
                if (tries > MAX_TRIES) {
1188
 
                        break;
1189
 
                }
1190
 
        } while ((ipod_filename == NULL)
1191
 
                 || (g_file_test (ipod_filename, G_FILE_TEST_EXISTS)));
1192
 
 
1193
 
        g_free (pc_filename);
1194
 
 
1195
 
        if (tries > MAX_TRIES) {
1196
 
                /* FIXME: should create a unique filename */
1197
 
                return NULL;
1198
 
        } else {
1199
 
                return ipod_filename;
1200
 
        }
1201
 
}
1202
 
 
1203
 
static gchar *
1204
 
ipod_get_filename_for_uri (const gchar *mount_point, const gchar *uri_str)
1205
 
{
1206
 
        gchar *escaped;
1207
 
        gchar *filename;
1208
 
        gchar *result;
1209
 
 
1210
 
        escaped = rb_uri_get_short_path_name (uri_str);
1211
 
        if (escaped == NULL) {
1212
 
                return NULL;
1213
 
        }
1214
 
        filename = gnome_vfs_unescape_string (escaped, G_DIR_SEPARATOR_S);
1215
 
        g_free (escaped);
1216
 
        if (filename == NULL) {
1217
 
                return NULL;
1218
 
        }
1219
 
 
1220
 
        result = generate_ipod_filename (mount_point, filename);
1221
 
        g_free (filename);
1222
 
 
1223
 
        return result;
1224
 
}
1225
 
 
1226
 
/* End of generation of the filename on the iPod */
1227
 
 
1228
 
static gchar *
1229
 
ipod_path_from_unix_path (const gchar *mount_point, const gchar *unix_path)
1230
 
{
1231
 
        gchar *ipod_path;
1232
 
 
1233
 
        g_assert (g_utf8_validate (unix_path, -1, NULL));
1234
 
 
1235
 
        if (!g_str_has_prefix (unix_path, mount_point)) {
1236
 
                return NULL;
1237
 
        }
1238
 
 
1239
 
        ipod_path = g_strdup (unix_path + strlen (mount_point));
1240
 
        if (*ipod_path != G_DIR_SEPARATOR) {
1241
 
                gchar *tmp;
1242
 
                tmp = g_strdup_printf ("/%s", ipod_path);
1243
 
                g_free (ipod_path);
1244
 
                ipod_path = tmp;
1245
 
        }
1246
 
 
1247
 
        /* Make sure the filename doesn't contain any ':' */
1248
 
        g_strdelimit (ipod_path, ":", ';');
1249
 
 
1250
 
        /* Convert path to a Mac path where the dir separator is ':' */
1251
 
        itdb_filename_fs2ipod (ipod_path);
1252
 
 
1253
 
        return ipod_path;
1254
 
}
1255
 
#endif
1256
 
 
1257
 
static void
1258
 
impl_delete_thyself (RBSource *source)
1259
 
{
1260
 
        RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
1261
 
        GList *p;
1262
 
 
1263
 
        for (p = priv->playlists; p != NULL; p = p->next) {
1264
 
                RBSource *playlist = RB_SOURCE (p->data);
1265
 
                rb_source_delete_thyself (playlist);
1266
 
        }
1267
 
        g_list_free (priv->playlists);
1268
 
        priv->playlists = NULL;
1269
 
 
1270
 
        itdb_free (priv->ipod_db);
1271
 
        priv->ipod_db = NULL;
1272
 
 
1273
 
        RB_SOURCE_CLASS (rb_ipod_source_parent_class)->impl_delete_thyself (source);
1274
 
}