18
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
#if defined(HAVE_HAL_0_5) || defined(HAVE_HAL_0_2)
21
28
#include <gtk/gtktreeview.h>
22
29
#include <string.h>
24
30
#include "rhythmdb.h"
25
31
#include <libgnome/gnome-i18n.h>
34
#include <dbus/dbus.h>
36
#include <libgnomevfs/gnome-vfs-volume.h>
37
#include <libgnomevfs/gnome-vfs-volume-monitor.h>
38
#include <gpod/itdb.h>
26
39
#include "eel-gconf-extensions.h"
27
40
#include "rb-ipod-source.h"
28
#include "rb-stock-icons.h"
29
#define DEFAULT_MOUNT_PATH "/mnt/ipod"
30
#define GCONF_MOUNT_PATH "/apps/qahog/mount_path"
33
static void rb_ipod_source_init (RBiPodSource *source);
34
static void rb_ipod_source_finalize (GObject *object);
35
static void rb_ipod_source_class_init (RBiPodSourceClass *klass);
37
static gboolean ipod_itunesdb_monitor_cb (RBiPodSource *source);
41
struct RBiPodSourcePrivate
43
guint ipod_polling_id;
48
rb_ipod_source_get_type (void)
50
static GType rb_ipod_source_type = 0;
52
if (rb_ipod_source_type == 0)
54
static const GTypeInfo our_info =
56
sizeof (RBiPodSourceClass),
59
(GClassInitFunc) rb_ipod_source_class_init,
62
sizeof (RBiPodSource),
64
(GInstanceInitFunc) rb_ipod_source_init
67
rb_ipod_source_type = g_type_register_static (RB_TYPE_LIBRARY_SOURCE,
73
return rb_ipod_source_type;
42
#include "rb-playlist-source.h"
46
static GObject *rb_ipod_source_constructor (GType type, guint n_construct_properties,
47
GObjectConstructParam *construct_properties);
48
static void rb_ipod_source_dispose (GObject *object);
50
static GObject *rb_ipod_source_constructor (GType type, guint n_construct_properties,
51
GObjectConstructParam *construct_properties);
52
static void rb_ipod_source_dispose (GObject *object);
54
static gboolean impl_show_popup (RBSource *source);
55
static void rb_ipod_load_songs (RBiPodSource *source);
56
static gchar *rb_ipod_get_mount_path (GnomeVFSVolume *volume);
57
static void impl_delete_thyself (RBSource *source);
60
static gboolean hal_udi_is_ipod (const char *udi);
65
Itdb_iTunesDB *ipod_db;
66
gchar *ipod_mount_path;
67
} RBiPodSourcePrivate;
70
G_DEFINE_TYPE (RBiPodSource, rb_ipod_source, RB_TYPE_REMOVABLE_MEDIA_SOURCE)
71
#define IPOD_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_IPOD_SOURCE, RBiPodSourcePrivate))
77
75
rb_ipod_source_class_init (RBiPodSourceClass *klass)
79
77
GObjectClass *object_class = G_OBJECT_CLASS (klass);
81
object_class->finalize = rb_ipod_source_finalize;
78
RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
80
object_class->constructor = rb_ipod_source_constructor;
81
object_class->dispose = rb_ipod_source_dispose;
83
source_class->impl_show_popup = impl_show_popup;
84
source_class->impl_delete_thyself = impl_delete_thyself;
86
g_type_class_add_private (klass, sizeof (RBiPodSourcePrivate));
85
90
rb_ipod_source_init (RBiPodSource *source)
87
source->priv = g_new0 (RBiPodSourcePrivate, 1);
88
source->priv->ipod_polling_id = g_timeout_add (1000, (GSourceFunc)ipod_itunesdb_monitor_cb, source);
96
rb_ipod_source_constructor (GType type, guint n_construct_properties,
97
GObjectConstructParam *construct_properties)
99
GObjectClass *klass, *parent_class;
100
RBiPodSource *source;
102
klass = G_OBJECT_CLASS (g_type_class_peek (type));
103
parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
104
source = RB_IPOD_SOURCE (parent_class->constructor (type, n_construct_properties, construct_properties));
106
rb_ipod_load_songs (source);
108
return G_OBJECT (source);
93
rb_ipod_source_finalize (GObject *object)
112
rb_ipod_source_dispose (GObject *object)
95
RBiPodSource *source = RB_IPOD_SOURCE (object);
97
if (source->priv->ipod_polling_id) {
98
g_source_remove (source->priv->ipod_polling_id);
114
RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (object);
116
if (priv->ipod_db != NULL) {
117
itdb_free (priv->ipod_db);
118
priv->ipod_db = NULL;
121
if (priv->ipod_mount_path) {
122
g_free (priv->ipod_mount_path);
123
priv->ipod_mount_path = NULL;
126
G_OBJECT_CLASS (rb_ipod_source_parent_class)->dispose (object);
104
rb_ipod_source_new (RBShell *shell, RhythmDB *db, BonoboUIComponent *component)
129
RBRemovableMediaSource *
130
rb_ipod_source_new (RBShell *shell, GnomeVFSVolume *volume)
107
GtkWidget *dummy = gtk_tree_view_new ();
110
icon = gtk_widget_render_icon (dummy, RB_STOCK_IPOD,
111
GTK_ICON_SIZE_LARGE_TOOLBAR,
113
gtk_widget_destroy (dummy);
115
/* FIXME: need to set icon */
116
source = RB_SOURCE (g_object_new (RB_TYPE_IPOD_SOURCE,
132
RBiPodSource *source;
133
RhythmDBEntryType entry_type;
135
g_assert (rb_ipod_is_volume_ipod (volume));
137
entry_type = rhythmdb_entry_register_type ();
139
source = RB_IPOD_SOURCE (g_object_new (RB_TYPE_IPOD_SOURCE,
117
140
"name", _("iPod"),
118
"entry-type", RHYTHMDB_ENTRY_TYPE_IPOD,
119
"internal-name", "<ipod>",
122
"component", component,
141
"entry-type", entry_type,
125
rb_shell_register_entry_type_for_source (shell, source,
126
RHYTHMDB_ENTRY_TYPE_IPOD);
134
ipod_get_mount_path (void)
138
path = eel_gconf_get_string (GCONF_MOUNT_PATH);
139
if (path == NULL || strcmp (path, "") == 0)
140
return g_strdup (DEFAULT_MOUNT_PATH);
146
ipod_get_itunesdb_path (void)
149
gchar *mount_path = ipod_get_mount_path ();
151
result = g_build_filename (mount_path,
152
"iPod_Control/iTunes/iTunesDB",
159
entry_set_locked (RhythmDB *db, RhythmDBEntry *entry,
160
RhythmDBPropType propid, GValue *value)
162
rhythmdb_write_lock (RHYTHMDB (db));
163
rhythmdb_entry_set (RHYTHMDB (db), entry, propid, value);
164
rhythmdb_write_unlock (RHYTHMDB (db));
146
rb_shell_register_entry_type_for_source (shell, RB_SOURCE (source), entry_type);
148
return RB_REMOVABLE_MEDIA_SOURCE (source);
169
153
RhythmDBPropType propid, const char *str)
171
155
GValue value = {0,};
175
tmp = g_strdup (_("Unknown"));
177
tmp = g_strdup (str);
180
160
g_value_init (&value, G_TYPE_STRING);
181
g_value_set_string_take_ownership (&value, tmp);
182
entry_set_locked (RHYTHMDB (db), entry, propid, &value);
161
g_value_set_static_string (&value, str);
162
rhythmdb_entry_set_uninserted (RHYTHMDB (db), entry, propid, &value);
183
163
g_value_unset (&value);
187
#define MAX_SONGS_LOADED_AT_ONCE 250
168
ipod_path_to_uri (const char *mount_point, const char *ipod_path)
174
rel_pc_path = g_strdup (ipod_path);
175
itdb_filename_ipod2fs (rel_pc_path);
176
full_pc_path = g_build_filename (mount_point, rel_pc_path, NULL);
177
g_free (rel_pc_path);
178
uri = g_filename_to_uri (full_pc_path, NULL, NULL);
179
g_free (full_pc_path);
184
add_rb_playlist (RBiPodSource *source, Itdb_Playlist *playlist)
187
RBSource *playlist_source;
189
RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
190
RhythmDBEntryType entry_type;
192
g_object_get (G_OBJECT (source),
194
"entry-type", &entry_type,
197
playlist_source = RB_SOURCE (g_object_new (RB_TYPE_PLAYLIST_SOURCE,
198
"name", playlist->name,
202
"entry-type", entry_type,
205
for (it = playlist->members; it != NULL; it = it->next) {
209
song = (Itdb_Track *)it->data;
210
filename = ipod_path_to_uri (priv->ipod_mount_path,
212
rb_playlist_source_add_location (RB_PLAYLIST_SOURCE (playlist_source),
217
rb_shell_append_source (shell, playlist_source, RB_SOURCE (source));
218
g_object_unref (G_OBJECT (shell));
223
load_ipod_playlists (RBiPodSource *source)
225
RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
228
for (it = priv->ipod_db->playlists; it != NULL; it = it->next) {
229
Itdb_Playlist *playlist;
231
playlist = (Itdb_Playlist *)it->data;
232
if (itdb_playlist_is_mpl (playlist)) {
235
if (playlist->is_spl) {
239
add_rb_playlist (source, playlist);
245
load_ipod_db_idle_cb (RBiPodSource *source)
192
} RBiPodSongAdderCtxt;
196
load_ipod_db_idle_cb (RBiPodSongAdderCtxt *ctxt)
198
RhythmDBTreeEntry *entry;
201
for (i = 0; i < MAX_SONGS_LOADED_AT_ONCE; i++) {
207
item = ipod_get_next_item (ctxt->parser);
208
if ((item == NULL) || (item->type != IPOD_ITEM_SONG)) {
209
ipod_item_destroy (item);
212
song = (iPodSong *)item->data;
215
mount_path = ipod_get_mount_path ();
216
pc_path = itunesdb_get_track_name_on_ipod (mount_path, song);
218
rhythmdb_write_lock (RHYTHMDB (ctxt->db));
219
entry = rhythmdb_entry_new (RHYTHMDB (ctxt->db),
220
RHYTHMDB_ENTRY_TYPE_IPOD,
222
rhythmdb_write_unlock (RHYTHMDB (ctxt->db));
250
RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
252
g_object_get (G_OBJECT (source), "shell", &shell, NULL);
253
g_object_get (G_OBJECT (shell), "db", &db, NULL);
254
g_object_unref (G_OBJECT (shell));
256
g_assert (db != NULL);
257
for (it = priv->ipod_db->tracks; it != NULL; it = it->next) {
259
RhythmDBEntry *entry;
260
RhythmDBEntryType entry_type;
263
song = (Itdb_Track *)it->data;
266
g_object_get (G_OBJECT (source), "entry-type", &entry_type,
269
pc_path = ipod_path_to_uri (priv->ipod_mount_path,
271
entry = rhythmdb_entry_new (RHYTHMDB (db), entry_type,
275
rb_debug ("Adding %s from iPod", pc_path);
225
277
/* Set track number */
226
278
if (song->track_nr != 0) {
227
279
GValue value = {0, };
228
g_value_init (&value, G_TYPE_INT);
229
g_value_set_int (&value, song->track_nr);
230
entry_set_locked (RHYTHMDB (ctxt->db), entry,
231
RHYTHMDB_PROP_TRACK_NUMBER,
280
g_value_init (&value, G_TYPE_ULONG);
281
g_value_set_ulong (&value, song->track_nr);
282
rhythmdb_entry_set_uninserted (RHYTHMDB (db), entry,
283
RHYTHMDB_PROP_TRACK_NUMBER,
233
285
g_value_unset (&value);
238
290
GValue value = {0, };
239
291
g_value_init (&value, G_TYPE_ULONG);
240
292
g_value_set_ulong (&value, song->cd_nr);
241
rhythmdb_entry_set (RHYTHMDB (ctxt->db), entry,
242
RHYTHMDB_PROP_DISC_NUMBER,
293
rhythmdb_entry_set_uninserted (RHYTHMDB (db), entry,
294
RHYTHMDB_PROP_DISC_NUMBER,
244
296
g_value_unset (&value);
247
299
/* Set bitrate */
248
300
if (song->bitrate != 0) {
249
301
GValue value = {0, };
250
g_value_init (&value, G_TYPE_INT);
251
g_value_set_int (&value, song->bitrate);
252
entry_set_locked (RHYTHMDB (ctxt->db), entry,
253
RHYTHMDB_PROP_BITRATE,
302
g_value_init (&value, G_TYPE_ULONG);
303
g_value_set_ulong (&value, song->bitrate);
304
rhythmdb_entry_set_uninserted (RHYTHMDB (db), entry,
305
RHYTHMDB_PROP_BITRATE,
255
307
g_value_unset (&value);
259
311
if (song->tracklen != 0) {
260
312
GValue value = {0, };
261
g_value_init (&value, G_TYPE_LONG);
262
g_value_set_long (&value, song->tracklen/1000);
263
entry_set_locked (RHYTHMDB (ctxt->db), entry,
264
RHYTHMDB_PROP_DURATION,
313
g_value_init (&value, G_TYPE_ULONG);
314
g_value_set_ulong (&value, song->tracklen/1000);
315
rhythmdb_entry_set_uninserted (RHYTHMDB (db), entry,
316
RHYTHMDB_PROP_DURATION,
266
318
g_value_unset (&value);
271
323
GValue value = {0, };
272
324
g_value_init (&value, G_TYPE_UINT64);
273
325
g_value_set_uint64 (&value, song->size);
274
entry_set_locked (RHYTHMDB (ctxt->db), entry,
275
RHYTHMDB_PROP_FILE_SIZE,
326
rhythmdb_entry_set_uninserted (RHYTHMDB (db), entry,
327
RHYTHMDB_PROP_FILE_SIZE,
277
329
g_value_unset (&value);
282
entry_set_string_prop (RHYTHMDB (ctxt->db), entry,
333
entry_set_string_prop (RHYTHMDB (db), entry,
283
334
RHYTHMDB_PROP_TITLE, song->title);
285
336
/* Set album, artist and genre from iTunesDB */
286
entry_set_string_prop (RHYTHMDB (ctxt->db), entry,
337
entry_set_string_prop (RHYTHMDB (db), entry,
287
338
RHYTHMDB_PROP_ARTIST, song->artist);
289
entry_set_string_prop (RHYTHMDB (ctxt->db), entry,
340
entry_set_string_prop (RHYTHMDB (db), entry,
290
341
RHYTHMDB_PROP_ALBUM, song->album);
292
entry_set_string_prop (RHYTHMDB (ctxt->db), entry,
343
entry_set_string_prop (RHYTHMDB (db), entry,
293
344
RHYTHMDB_PROP_GENRE, song->genre);
295
ipod_item_destroy (item);
297
/* FIXME: item is leaked */
302
context_free (gpointer data)
304
RBiPodSongAdderCtxt *ctxt = (RBiPodSongAdderCtxt *)data;
308
if (ctxt->parser != NULL) {
309
ipod_parser_destroy (ctxt->parser);
314
/* We need to be locked to use this function */
315
static GnomeVFSResult
316
add_ipod_songs_to_db (RhythmDB *db)
320
RBiPodSongAdderCtxt *ctxt;
322
ctxt = g_new0 (RBiPodSongAdderCtxt, 1);
324
return GNOME_VFS_ERROR_NO_MEMORY;
327
path = ipod_get_mount_path ();
328
parser = ipod_parser_new (path);
332
ctxt->parser = parser;
334
g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
335
(GSourceFunc)load_ipod_db_idle_cb,
343
ipod_load_songs (RhythmDB *rdb)
347
res = add_ipod_songs_to_db (rdb);
349
if (res != GNOME_VFS_OK) {
350
g_warning ("Error loading iPod database");
355
ipod_unload_songs (RhythmDB *db)
357
rhythmdb_write_lock (db);
358
rhythmdb_entry_delete_by_type (db, RHYTHMDB_ENTRY_TYPE_IPOD);
359
rhythmdb_write_unlock (db);
363
ipod_itunesdb_monitor_cb (RBiPodSource *source)
346
rhythmdb_commit (RHYTHMDB (db));
350
load_ipod_playlists (source);
352
g_object_unref (G_OBJECT (db));
357
rb_ipod_load_songs (RBiPodSource *source)
359
RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
360
GnomeVFSVolume *volume;
362
g_object_get (G_OBJECT (source), "volume", &volume, NULL);
363
priv->ipod_mount_path = rb_ipod_get_mount_path (volume);
365
priv->ipod_db = itdb_parse (priv->ipod_mount_path, NULL);
366
if (priv->ipod_db != NULL) {
367
g_idle_add ((GSourceFunc)load_ipod_db_idle_cb, source);
372
rb_ipod_get_mount_path (GnomeVFSVolume *volume)
377
uri = gnome_vfs_volume_get_activation_uri (volume);
378
path = g_filename_from_uri (uri, NULL, NULL);
379
g_assert (path != NULL);
386
rb_ipod_get_itunesdb_path (GnomeVFSVolume *volume)
388
gchar *mount_point_uri;
392
mount_point_uri = gnome_vfs_volume_get_activation_uri (volume);
393
if (mount_point_uri == NULL) {
396
mount_point = g_filename_from_uri (mount_point_uri, NULL, NULL);
397
g_free (mount_point_uri);
398
if (mount_point == NULL) {
401
result = g_build_filename (mount_point,
402
"iPod_Control/iTunes/iTunesDB",
404
g_free (mount_point);
409
rb_ipod_is_volume_ipod (GnomeVFSVolume *volume)
366
411
gchar *itunesdb_path;
367
static gboolean was_present = FALSE;
369
itunesdb_path = ipod_get_itunesdb_path ();
370
g_assert (itunesdb_path != NULL);
371
is_present = g_file_test (itunesdb_path, G_FILE_TEST_EXISTS);
373
if (is_present && !was_present) {
376
g_print ("iPod plugged\n");
378
g_object_get (G_OBJECT (source), "db", &db, NULL);
379
ipod_load_songs (db);
380
/* FIXME: should we suspend this monitor until the iPod
381
* database has been read and fed to rhythmbox?
383
} else if (!is_present && was_present) {
386
g_print ("iPod unplugged\n");
388
g_object_get (G_OBJECT (source), "db", &db, NULL);
389
ipod_unload_songs (db);
391
g_free (itunesdb_path);
412
gboolean result = FALSE;
416
if (gnome_vfs_volume_get_volume_type (volume) != GNOME_VFS_VOLUME_TYPE_MOUNTPOINT) {
421
udi = gnome_vfs_volume_get_hal_udi (volume);
425
result = hal_udi_is_ipod (udi);
431
itunesdb_path = rb_ipod_get_itunesdb_path (volume);
432
if (itunesdb_path != NULL) {
433
result = g_file_test (itunesdb_path, G_FILE_TEST_EXISTS);
434
g_free (itunesdb_path);
443
hal_udi_is_ipod (const char *udi)
446
DBusConnection *conn;
447
char *parent_udi, *parent_name;
452
dbus_error_init (&error);
454
ctx = libhal_ctx_new ();
456
/* FIXME: should we return an error somehow so that we can
457
* fall back to a check for iTunesDB presence instead ?
459
g_print ("Error: %s\n", error.message);
462
conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
464
g_print ("Error: %s\n", error.message);
467
libhal_ctx_set_dbus_connection (ctx, conn);
468
if (!libhal_ctx_init (ctx, &error)) {
469
g_print ("Error: %s\n", error.message);
473
parent_udi = libhal_device_get_property_string (ctx, udi,
474
"info.parent", &error);
475
parent_name = libhal_device_get_property_string (ctx, parent_udi,
476
"storage.model", &error);
478
if (parent_name != NULL && strcmp (parent_name, "iPod") == 0) {
482
g_free (parent_name);
485
libhal_ctx_shutdown (ctx, &error);
486
libhal_ctx_free(ctx);
488
dbus_error_free (&error);
496
hal_udi_is_ipod (const char *udi)
499
char *parent_udi, *parent_name;
503
ctx = hal_initialize (NULL, FALSE);
505
/* FIXME: should we return an error somehow so that we can
506
* fall back to a check for iTunesDB presence instead ?
510
parent_udi = hal_device_get_property_string (ctx, udi,
512
parent_name = hal_device_get_property_string (ctx, parent_udi,
516
if (parent_name != NULL && strcmp (parent_name, "iPod") == 0) {
520
g_free (parent_name);
529
impl_show_popup (RBSource *source)
531
_rb_source_show_popup (RB_SOURCE (source), "/iPodSourcePopup");
395
RhythmDBEntryType rhythmdb_entry_ipod_get_type (void)
536
impl_delete_thyself (RBSource *source)
397
static RhythmDBEntryType ipod_type = -1;
399
if (ipod_type == -1) {
400
ipod_type = rhythmdb_entry_register_type ();
538
RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
540
itdb_free (priv->ipod_db);
541
priv->ipod_db = NULL;
543
RB_SOURCE_CLASS (rb_ipod_source_parent_class)->impl_delete_thyself (source);