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);