2 * arch-tag: Implementation of ipod source object
4 * Copyright (C) 2004 Christophe Fergeau <teuf@gnome.org>
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.
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.
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.
26 #include <glib/gi18n.h>
30 #include <dbus/dbus.h>
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>
37 #include "eel-gconf-extensions.h"
38 #include "rb-ipod-source.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"
46 #include "rb-cut-and-paste-code.h"
49 #define PHONE_VENDOR_ID 0x22b8
50 #define PHONE_PRODUCT_ID 0x4810
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
);
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
);
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
);
69 static gboolean
hal_udi_is_ipod (const char *udi
);
72 #ifdef ENABLE_IPOD_WRITING
73 static void impl_paste (RBSource
*source
, GList
*entries
);
74 static gboolean
impl_receive_drag (RBSource
*asource
, GtkSelectionData
*data
);
76 ipod_get_filename_for_uri (const gchar
*mount_point
, const gchar
*uri_str
);
78 ipod_path_from_unix_path (const gchar
*mount_point
, const gchar
*unix_path
);
80 static void itdb_schedule_save (Itdb_iTunesDB
*db
);
84 Itdb_iTunesDB
*ipod_db
;
85 gchar
*ipod_mount_path
;
86 GHashTable
*entry_map
;
91 } RBiPodSourcePrivate
;
93 RB_PLUGIN_DEFINE_TYPE(RBiPodSource
,
95 RB_TYPE_REMOVABLE_MEDIA_SOURCE
)
97 #define IPOD_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_IPOD_SOURCE, RBiPodSourcePrivate))
100 rb_ipod_source_class_init (RBiPodSourceClass
*klass
)
102 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
103 RBSourceClass
*source_class
= RB_SOURCE_CLASS (klass
);
105 object_class
->constructor
= rb_ipod_source_constructor
;
106 object_class
->dispose
= rb_ipod_source_dispose
;
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
;
120 g_type_class_add_private (klass
, sizeof (RBiPodSourcePrivate
));
124 rb_ipod_source_set_ipod_name (RBiPodSource
*source
, const char *name
)
127 RBiPodSourcePrivate
*priv
= IPOD_SOURCE_GET_PRIVATE (source
);
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
);
138 mpl
->name
= g_strdup (name
);
139 itdb_schedule_save (priv
->ipod_db
);
143 rb_ipod_source_name_changed_cb (RBiPodSource
*source
, GParamSpec
*spec
,
148 g_object_get (source
, "name", &name
, NULL
);
149 rb_ipod_source_set_ipod_name (source
, name
);
154 rb_ipod_source_init (RBiPodSource
*source
)
156 g_signal_connect (G_OBJECT (source
), "notify::name",
157 (GCallback
)rb_ipod_source_name_changed_cb
, NULL
);
161 rb_ipod_source_constructor (GType type
, guint n_construct_properties
,
162 GObjectConstructParam
*construct_properties
)
164 RBiPodSource
*source
;
166 RBiPodSourcePrivate
*priv
;
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
);
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
);
176 rb_ipod_load_songs (source
);
178 return G_OBJECT (source
);
182 rb_ipod_source_dispose (GObject
*object
)
184 RBiPodSourcePrivate
*priv
= IPOD_SOURCE_GET_PRIVATE (object
);
186 if (priv
->ipod_db
!= NULL
) {
187 itdb_free (priv
->ipod_db
);
188 priv
->ipod_db
= NULL
;
191 if (priv
->ipod_mount_path
) {
192 g_free (priv
->ipod_mount_path
);
193 priv
->ipod_mount_path
= NULL
;
196 if (priv
->entry_map
) {
197 g_hash_table_destroy (priv
->entry_map
);
198 priv
->entry_map
= NULL
;
201 if (priv
->load_idle_id
!= 0) {
202 g_source_remove (priv
->load_idle_id
);
203 priv
->load_idle_id
= 0;
206 G_OBJECT_CLASS (rb_ipod_source_parent_class
)->dispose (object
);
209 RBRemovableMediaSource
*
210 rb_ipod_source_new (RBShell
*shell
,
211 GnomeVFSVolume
*volume
)
213 RBiPodSource
*source
;
214 RhythmDBEntryType entry_type
;
217 g_assert (rb_ipod_is_volume_ipod (volume
));
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
;
225 source
= RB_IPOD_SOURCE (g_object_new (RB_TYPE_IPOD_SOURCE
,
226 "entry-type", entry_type
,
229 "sourcelist-group", RB_SOURCELIST_GROUP_REMOVABLE
,
232 rb_shell_register_entry_type_for_source (shell
, RB_SOURCE (source
), entry_type
);
234 return RB_REMOVABLE_MEDIA_SOURCE (source
);
238 entry_set_string_prop (RhythmDB
*db
, RhythmDBEntry
*entry
,
239 RhythmDBPropType propid
, const char *str
)
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
);
253 ipod_path_to_uri (const char *mount_point
, const char *ipod_path
)
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
);
269 add_rb_playlist (RBiPodSource
*source
, Itdb_Playlist
*playlist
)
272 RBSource
*playlist_source
;
274 RBiPodSourcePrivate
*priv
= IPOD_SOURCE_GET_PRIVATE (source
);
275 RhythmDBEntryType entry_type
;
277 g_object_get (source
,
279 "entry-type", &entry_type
,
282 playlist_source
= rb_static_playlist_source_new (shell
,
286 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, entry_type
);
288 for (it
= playlist
->members
; it
!= NULL
; it
= it
->next
) {
292 song
= (Itdb_Track
*)it
->data
;
293 filename
= ipod_path_to_uri (priv
->ipod_mount_path
,
295 rb_static_playlist_source_add_location (RB_STATIC_PLAYLIST_SOURCE (playlist_source
),
300 priv
->playlists
= g_list_prepend (priv
->playlists
, playlist_source
);
302 rb_shell_append_source (shell
, playlist_source
, RB_SOURCE (source
));
303 g_object_unref (shell
);
307 load_ipod_playlists (RBiPodSource
*source
)
309 RBiPodSourcePrivate
*priv
= IPOD_SOURCE_GET_PRIVATE (source
);
312 for (it
= priv
->ipod_db
->playlists
; it
!= NULL
; it
= it
->next
) {
313 Itdb_Playlist
*playlist
;
315 playlist
= (Itdb_Playlist
*)it
->data
;
316 if (itdb_playlist_is_mpl (playlist
)) {
319 if (playlist
->is_spl
) {
323 add_rb_playlist (source
, playlist
);
328 #ifdef ENABLE_IPOD_WRITING
330 create_ipod_song_from_entry (RhythmDBEntry
*entry
)
334 track
= itdb_track_new ();
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
);
360 add_ipod_song_to_db (RBiPodSource
*source
, RhythmDB
*db
, Itdb_Track
*song
)
362 RhythmDBEntry
*entry
;
363 RhythmDBEntryType entry_type
;
364 RBiPodSourcePrivate
*priv
= IPOD_SOURCE_GET_PRIVATE (source
);
368 g_object_get (source
, "entry-type", &entry_type
,
371 pc_path
= ipod_path_to_uri (priv
->ipod_mount_path
,
373 entry
= rhythmdb_entry_new (RHYTHMDB (db
), entry_type
,
375 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, entry_type
);
378 rb_debug ("cannot create entry %s", pc_path
);
383 rb_debug ("Adding %s from iPod", pc_path
);
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
,
394 g_value_unset (&value
);
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
,
405 g_value_unset (&value
);
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
,
416 g_value_unset (&value
);
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
,
427 g_value_unset (&value
);
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
,
438 g_value_unset (&value
);
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
,
449 g_value_unset (&value
);
453 if (song
->year
!= 0) {
456 GValue value
= {0, };
458 date
= g_date_new_dmy (1, G_DATE_JANUARY
, song
->year
);
460 type
= rhythmdb_get_property_type (RHYTHMDB(db
),
463 g_value_init (&value
, type
);
464 g_value_set_ulong (&value
, (date
? g_date_get_julian (date
) : 0));
466 rhythmdb_entry_set (RHYTHMDB (db
), entry
,
469 g_value_unset (&value
);
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
,
482 g_value_unset (&value
);
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
,
493 g_value_unset (&value
);
497 entry_set_string_prop (RHYTHMDB (db
), entry
,
498 RHYTHMDB_PROP_TITLE
, song
->title
);
500 /* Set album, artist and genre from iTunesDB */
501 entry_set_string_prop (RHYTHMDB (db
), entry
,
502 RHYTHMDB_PROP_ARTIST
, song
->artist
);
504 entry_set_string_prop (RHYTHMDB (db
), entry
,
505 RHYTHMDB_PROP_ALBUM
, song
->album
);
507 entry_set_string_prop (RHYTHMDB (db
), entry
,
508 RHYTHMDB_PROP_GENRE
, song
->genre
);
510 g_hash_table_insert (priv
->entry_map
, entry
, song
);
512 rhythmdb_commit (RHYTHMDB (db
));
516 get_db_for_source (RBiPodSource
*source
)
521 g_object_get (source
, "shell", &shell
, NULL
);
522 g_object_get (shell
, "db", &db
, NULL
);
523 g_object_unref (shell
);
529 load_ipod_db_idle_cb (RBiPodSource
*source
)
533 RBiPodSourcePrivate
*priv
= IPOD_SOURCE_GET_PRIVATE (source
);
535 GDK_THREADS_ENTER ();
537 db
= get_db_for_source (source
);
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
);
544 load_ipod_playlists (source
);
548 GDK_THREADS_LEAVE ();
549 priv
->load_idle_id
= 0;
554 rb_ipod_load_songs (RBiPodSource
*source
)
556 RBiPodSourcePrivate
*priv
= IPOD_SOURCE_GET_PRIVATE (source
);
557 GnomeVFSVolume
*volume
;
559 g_object_get (source
, "volume", &volume
, NULL
);
560 priv
->ipod_mount_path
= rb_ipod_get_mount_path (volume
);
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
)) {
567 /* FIXME: we could set a different icon depending on the iPod
570 mpl
= itdb_playlist_mpl (priv
->ipod_db
);
571 if (mpl
&& mpl
->name
) {
572 g_object_set (RB_SOURCE (source
),
576 priv
->load_idle_id
= g_idle_add ((GSourceFunc
)load_ipod_db_idle_cb
, source
);
579 g_object_unref (volume
);
583 rb_ipod_get_mount_path (GnomeVFSVolume
*volume
)
588 uri
= gnome_vfs_volume_get_activation_uri (volume
);
589 path
= g_filename_from_uri (uri
, NULL
, NULL
);
590 g_assert (path
!= NULL
);
597 rb_ipod_get_itunesdb_path (GnomeVFSVolume
*volume
)
599 gchar
*mount_point_uri
;
603 mount_point_uri
= gnome_vfs_volume_get_activation_uri (volume
);
604 if (mount_point_uri
== NULL
) {
607 mount_point
= g_filename_from_uri (mount_point_uri
, NULL
, NULL
);
608 g_free (mount_point_uri
);
609 if (mount_point
== NULL
) {
614 result
= itdb_get_itunesdb_path (mount_point
);
616 result
= g_build_filename (mount_point
,
617 "iPod_Control/iTunes/iTunesDB",
621 g_free (mount_point
);
626 rb_ipod_volume_has_ipod_db (GnomeVFSVolume
*volume
)
631 itunesdb_path
= rb_ipod_get_itunesdb_path (volume
);
633 if (itunesdb_path
!= NULL
) {
634 result
= g_file_test (itunesdb_path
, G_FILE_TEST_EXISTS
);
638 g_free (itunesdb_path
);
644 rb_ipod_is_volume_ipod (GnomeVFSVolume
*volume
)
649 if (gnome_vfs_volume_get_volume_type (volume
) != GNOME_VFS_VOLUME_TYPE_MOUNTPOINT
) {
654 udi
= gnome_vfs_volume_get_hal_udi (volume
);
658 result
= hal_udi_is_ipod (udi
);
660 if (result
== FALSE
) {
666 return rb_ipod_volume_has_ipod_db (volume
);
672 hal_udi_is_ipod (const char *udi
)
675 DBusConnection
*conn
;
680 gboolean inited
= FALSE
;
683 dbus_error_init (&error
);
689 ctx
= libhal_ctx_new ();
691 /* FIXME: should we return an error somehow so that we can
692 * fall back to a check for iTunesDB presence instead ?
694 rb_debug ("cannot connect to HAL");
697 conn
= dbus_bus_get (DBUS_BUS_SYSTEM
, &error
);
698 if (conn
== NULL
|| dbus_error_is_set (&error
))
701 libhal_ctx_set_dbus_connection (ctx
, conn
);
702 if (!libhal_ctx_init (ctx
, &error
) || dbus_error_is_set (&error
))
706 parent_udi
= libhal_device_get_property_string (ctx
, udi
,
707 "info.parent", &error
);
708 if (parent_udi
== NULL
|| dbus_error_is_set (&error
))
711 parent_name
= libhal_device_get_property_string (ctx
, parent_udi
,
712 "storage.model", &error
);
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
);
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
);
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
);
750 if (vnd_id
== PHONE_VENDOR_ID
&& product_id
== PHONE_PRODUCT_ID
) {
755 if (parent_name
== NULL
|| dbus_error_is_set (&error
))
758 if (strcmp (parent_name
, "iPod") == 0)
763 g_free (parent_name
);
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
);
773 libhal_ctx_shutdown (ctx
, &error
);
774 libhal_ctx_free(ctx
);
777 dbus_error_free (&error
);
785 hal_udi_is_ipod (const char *udi
)
793 ctx
= hal_initialize (NULL
, FALSE
);
795 /* FIXME: should we return an error somehow so that we can
796 * fall back to a check for iTunesDB presence instead ?
800 parent_udi
= hal_device_get_property_string (ctx
, udi
,
802 parent_name
= hal_device_get_property_string (ctx
, parent_udi
,
806 if (parent_name
!= NULL
&& strcmp (parent_name
, "iPod") == 0) {
810 g_free (parent_name
);
819 impl_get_ui_actions (RBSource
*source
)
821 GList
*actions
= NULL
;
823 actions
= g_list_prepend (actions
, g_strdup ("RemovableSourceEject"));
829 impl_show_popup (RBSource
*source
)
831 _rb_source_show_popup (RB_SOURCE (source
), "/iPodSourcePopup");
836 remove_track_from_db (Itdb_Track
*track
)
840 for (it
= track
->itdb
->playlists
; it
!= NULL
; it
= it
->next
) {
841 itdb_playlist_remove_track ((Itdb_Playlist
*)it
->data
, track
);
843 itdb_track_remove (track
);
847 impl_move_to_trash (RBSource
*asource
)
852 RBiPodSourcePrivate
*priv
= IPOD_SOURCE_GET_PRIVATE (asource
);
853 RBiPodSource
*source
= RB_IPOD_SOURCE (asource
);
855 db
= get_db_for_source (source
);
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
;
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
);
869 g_warning ("Couldn't find track on ipod! (%s)", uri
);
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
);
880 itdb_write (priv
->ipod_db
, NULL
);
889 itdb_schedule_save (Itdb_iTunesDB
*db
)
891 /* FIXME: should probably be delayed a bit to avoid doing
892 * it after each file when we are copying several files
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
898 itdb_write (db
, NULL
);
901 #ifdef ENABLE_IPOD_WRITING
903 build_filename (RBSource
*asource
, RhythmDBEntry
*entry
)
909 RBiPodSourcePrivate
*priv
= IPOD_SOURCE_GET_PRIVATE (asource
);
911 uri
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
912 dest
= ipod_get_filename_for_uri (priv
->ipod_mount_path
, uri
);
914 dest_uri
= g_filename_to_uri (dest
, NULL
, NULL
);
923 completed_cb (RhythmDBEntry
*entry
, const char *dest
, RBiPodSource
*source
)
928 db
= get_db_for_source (source
);
930 song
= create_ipod_song_from_entry (entry
);
932 RBiPodSourcePrivate
*priv
= IPOD_SOURCE_GET_PRIVATE (source
);
935 filename
= g_filename_from_uri (dest
, NULL
, NULL
);
936 song
->ipod_path
= ipod_path_from_unix_path (priv
->ipod_mount_path
, filename
);
938 itdb_track_add (priv
->ipod_db
, song
, -1);
939 itdb_playlist_add_track (itdb_playlist_mpl (priv
->ipod_db
),
942 add_ipod_song_to_db (source
, db
, song
);
943 itdb_schedule_save (priv
->ipod_db
);
950 impl_paste (RBSource
*asource
, GList
*entries
)
952 RBRemovableMediaManager
*rm_mgr
;
956 g_object_get (asource
, "shell", &shell
, NULL
);
958 "removable-media-manager", &rm_mgr
,
960 g_object_unref (shell
);
962 for (l
= entries
; l
!= NULL
; l
= l
->next
) {
963 RhythmDBEntry
*entry
;
964 RhythmDBEntryType entry_type
;
965 RhythmDBEntryType ipod_entry_type
;
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
,
973 if (entry_type
== ipod_entry_type
||
974 entry_type
->category
!= RHYTHMDB_ENTRY_NORMAL
) {
975 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, entry_type
);
979 dest
= build_filename (asource
, entry
);
981 rb_debug ("could not create destination path for entry");
982 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, entry_type
);
985 rb_removable_media_manager_queue_transfer (rm_mgr
, entry
,
987 (RBTranferCompleteCallback
)completed_cb
, asource
);
989 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, entry_type
);
992 g_object_unref (rm_mgr
);
996 impl_receive_drag (RBSource
*asource
, GtkSelectionData
*data
)
998 RBBrowserSource
*source
= RB_BROWSER_SOURCE (asource
);
1000 GList
*entries
= NULL
;
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
));
1008 db
= get_db_for_source (RB_IPOD_SOURCE (source
));
1010 for (i
= list
; i
!= NULL
; i
= g_list_next (i
)) {
1011 if (i
->data
!= NULL
) {
1012 char *uri
= i
->data
;
1013 RhythmDBEntry
*entry
;
1015 entry
= rhythmdb_entry_lookup_from_string (db
, uri
, is_id
);
1017 if (entry
== NULL
) {
1018 /* add to the library */
1019 g_print ("Where does that come from?\n");
1021 /* add to list of entries to copy */
1022 entries
= g_list_prepend (entries
, entry
);
1028 g_object_unref (db
);
1032 entries
= g_list_reverse (entries
);
1033 if (rb_source_can_paste (asource
))
1034 rb_source_paste (asource
, entries
);
1035 g_list_free (entries
);
1041 /* Generation of the filename for the ipod */
1043 #define IPOD_MAX_PATH_LEN 56
1046 test_dir_on_ipod (const char *mountpoint
, const char *dirname
)
1051 fullpath
= g_build_filename (mountpoint
, dirname
, NULL
);
1052 result
= g_file_test (fullpath
, G_FILE_TEST_IS_DIR
);
1059 ipod_mkdir_with_parents (const char *mountpoint
, const char *dirname
)
1064 fullpath
= g_build_filename (mountpoint
, dirname
, NULL
);
1065 result
= g_mkdir_with_parents (fullpath
, 0770);
1072 build_ipod_dir_name (const char *mountpoint
)
1074 /* FAT sucks, filename can be lowercase or uppercase, and if we try to
1075 * open the wrong one, we lose :-/
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
);
1087 if (test_dir_on_ipod (mountpoint
, relpath
) != FALSE
) {
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
);
1097 if (test_dir_on_ipod (mountpoint
, relpath
) != FALSE
) {
1101 if (ipod_mkdir_with_parents (mountpoint
, relpath
) == 0) {
1110 get_ipod_filename (const char *mount_point
, const char *filename
)
1116 dirname
= build_ipod_dir_name (mount_point
);
1117 if (dirname
== NULL
) {
1120 result
= g_build_filename (dirname
, filename
, NULL
);
1123 if (strlen (result
) >= IPOD_MAX_PATH_LEN
) {
1126 ext
= strrchr (result
, '.');
1128 result
[IPOD_MAX_PATH_LEN
- 1] = '\0';
1130 memmove (&result
[IPOD_MAX_PATH_LEN
- strlen (ext
) - 1] ,
1131 ext
, strlen (ext
) + 1);
1135 tmp
= g_build_filename (mount_point
, result
, NULL
);
1142 /* Strips non UTF8 characters from a string replacing them with _ */
1144 utf8_to_ascii (const gchar
*utf8
)
1147 const guchar
*it
= (const guchar
*)utf8
;
1149 string
= g_string_new ("");
1150 while ((it
!= NULL
) && (*it
!= '\0')) {
1151 /* Do we have a 7 bit char ? */
1153 g_string_append_c (string
, *it
);
1155 g_string_append_c (string
, '_');
1157 it
= (const guchar
*)g_utf8_next_char (it
);
1160 return g_string_free (string
, FALSE
);
1164 generate_ipod_filename (const gchar
*mount_point
, const gchar
*filename
)
1166 gchar
*ipod_filename
= NULL
;
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,
1176 pc_filename
= utf8_to_ascii (tmp
);
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
1184 g_free (ipod_filename
);
1185 ipod_filename
= get_ipod_filename (mount_point
, pc_filename
);
1187 if (tries
> MAX_TRIES
) {
1190 } while ((ipod_filename
== NULL
)
1191 || (g_file_test (ipod_filename
, G_FILE_TEST_EXISTS
)));
1193 g_free (pc_filename
);
1195 if (tries
> MAX_TRIES
) {
1196 /* FIXME: should create a unique filename */
1199 return ipod_filename
;
1204 ipod_get_filename_for_uri (const gchar
*mount_point
, const gchar
*uri_str
)
1210 escaped
= rb_uri_get_short_path_name (uri_str
);
1211 if (escaped
== NULL
) {
1214 filename
= gnome_vfs_unescape_string (escaped
, G_DIR_SEPARATOR_S
);
1216 if (filename
== NULL
) {
1220 result
= generate_ipod_filename (mount_point
, filename
);
1226 /* End of generation of the filename on the iPod */
1229 ipod_path_from_unix_path (const gchar
*mount_point
, const gchar
*unix_path
)
1233 g_assert (g_utf8_validate (unix_path
, -1, NULL
));
1235 if (!g_str_has_prefix (unix_path
, mount_point
)) {
1239 ipod_path
= g_strdup (unix_path
+ strlen (mount_point
));
1240 if (*ipod_path
!= G_DIR_SEPARATOR
) {
1242 tmp
= g_strdup_printf ("/%s", ipod_path
);
1247 /* Make sure the filename doesn't contain any ':' */
1248 g_strdelimit (ipod_path
, ":", ';');
1250 /* Convert path to a Mac path where the dir separator is ':' */
1251 itdb_filename_fs2ipod (ipod_path
);
1258 impl_delete_thyself (RBSource
*source
)
1260 RBiPodSourcePrivate
*priv
= IPOD_SOURCE_GET_PRIVATE (source
);
1263 for (p
= priv
->playlists
; p
!= NULL
; p
= p
->next
) {
1264 RBSource
*playlist
= RB_SOURCE (p
->data
);
1265 rb_source_delete_thyself (playlist
);
1267 g_list_free (priv
->playlists
);
1268 priv
->playlists
= NULL
;
1270 itdb_free (priv
->ipod_db
);
1271 priv
->ipod_db
= NULL
;
1273 RB_SOURCE_CLASS (rb_ipod_source_parent_class
)->impl_delete_thyself (source
);