1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * Copyright (C) 2003,2004 Colin Walters <walters@gnome.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <glib-object.h>
27 #include <glib/gi18n.h>
28 #include <gconf/gconf-client.h>
29 #include <libgnomevfs/gnome-vfs-volume-monitor.h>
34 #include "rhythmdb-private.h"
35 #include "rb-file-helpers.h"
36 #include "rb-preferences.h"
37 #include "eel-gconf-extensions.h"
39 #define RHYTHMDB_FILE_MODIFY_PROCESS_TIME 2
41 static void rhythmdb_volume_mounted_cb (GnomeVFSVolumeMonitor
*monitor
,
42 GnomeVFSVolume
*volume
,
44 static void rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor
*monitor
,
45 GnomeVFSVolume
*volume
,
49 rhythmdb_init_monitoring (RhythmDB
*db
)
51 db
->priv
->monitored_directories
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
52 (GDestroyNotify
) g_free
,
53 (GDestroyNotify
)gnome_vfs_monitor_cancel
);
55 db
->priv
->changed_files
= g_hash_table_new_full (rb_refstring_hash
, rb_refstring_equal
,
56 (GDestroyNotify
) rb_refstring_unref
,
59 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
61 G_CALLBACK (rhythmdb_volume_mounted_cb
),
64 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
66 G_CALLBACK (rhythmdb_volume_unmounted_cb
),
68 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
70 G_CALLBACK (rhythmdb_volume_unmounted_cb
),
75 rhythmdb_finalize_monitoring (RhythmDB
*db
)
77 rhythmdb_stop_monitoring (db
);
79 g_hash_table_destroy (db
->priv
->monitored_directories
);
80 if (db
->priv
->changed_files_id
)
81 g_source_remove (db
->priv
->changed_files_id
);
82 g_hash_table_destroy (db
->priv
->changed_files
);
86 rhythmdb_stop_monitoring (RhythmDB
*db
)
88 g_hash_table_foreach_remove (db
->priv
->monitored_directories
,
89 (GHRFunc
) rb_true_function
,
94 monitor_entry_file (RhythmDBEntry
*entry
, RhythmDB
*db
)
96 if (entry
->type
== RHYTHMDB_ENTRY_TYPE_SONG
) {
100 loc
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
102 /* don't add add monitor if it's in the library path*/
103 for (l
= db
->priv
->library_locations
; l
!= NULL
; l
= g_slist_next (l
)) {
104 if (g_str_has_prefix (loc
, (const char*)l
->data
))
108 rhythmdb_monitor_uri_path (db
, loc
, NULL
);
113 monitor_subdirectory (const char *uri
, gboolean dir
, RhythmDB
*db
)
116 rhythmdb_monitor_uri_path (db
, uri
, NULL
);
118 rhythmdb_add_uri (db
, uri
);
122 monitor_library_directory (const char *uri
, RhythmDB
*db
)
124 if ((strcmp (uri
, "file:///") == 0) ||
125 (strcmp (uri
, "file://") == 0)) {
126 /* display an error to the user? */
130 rb_debug ("beginning monitor of the library directory %s", uri
);
131 rhythmdb_monitor_uri_path (db
, uri
, NULL
);
132 rb_uri_handle_recursively_async (uri
, (RBUriRecurseFunc
) monitor_subdirectory
, NULL
,
133 g_object_ref (db
), (GDestroyNotify
)g_object_unref
);
137 rhythmdb_check_changed_file (RBRefString
*uri
, gpointer data
, RhythmDB
*db
)
140 glong time_sec
= GPOINTER_TO_INT (data
);
142 g_get_current_time (&time
);
143 if (time
.tv_sec
>= time_sec
+ RHYTHMDB_FILE_MODIFY_PROCESS_TIME
) {
144 /* process and remove from table */
145 RhythmDBEvent
*event
= g_new0 (RhythmDBEvent
, 1);
147 event
->type
= RHYTHMDB_EVENT_FILE_CREATED_OR_MODIFIED
;
148 event
->uri
= rb_refstring_ref (uri
);
150 g_async_queue_push (db
->priv
->event_queue
, event
);
151 rb_debug ("adding newly located file %s", rb_refstring_get (uri
));
155 rb_debug ("waiting to add newly located file %s", rb_refstring_get (uri
));
161 rhythmdb_process_changed_files (RhythmDB
*db
)
163 g_hash_table_foreach_remove (db
->priv
->changed_files
,
164 (GHRFunc
)rhythmdb_check_changed_file
, db
);
169 _monitor_entry_thread (RhythmDB
*db
)
171 rhythmdb_entry_foreach (db
, (GFunc
) monitor_entry_file
, db
);
172 g_object_unref (G_OBJECT (db
));
177 rhythmdb_start_monitoring (RhythmDB
*db
)
179 db
->priv
->changed_files_id
= g_timeout_add (RHYTHMDB_FILE_MODIFY_PROCESS_TIME
* 1000,
180 (GSourceFunc
) rhythmdb_process_changed_files
, db
);
182 g_thread_create ((GThreadFunc
)_monitor_entry_thread
, g_object_ref (db
), FALSE
, NULL
);
184 /* monitor all library locations */
185 if (db
->priv
->library_locations
)
186 g_slist_foreach (db
->priv
->library_locations
, (GFunc
) monitor_library_directory
, db
);
190 rhythmdb_directory_change_cb (GnomeVFSMonitorHandle
*handle
,
191 const char *monitor_uri
,
192 const char *info_uri
,
193 GnomeVFSMonitorEventType vfsevent
,
198 canon_uri
= rb_canonicalise_uri (info_uri
);
199 rb_debug ("directory event %d for %s: %s", (int) vfsevent
,
200 monitor_uri
, canon_uri
);
203 case GNOME_VFS_MONITOR_EVENT_CREATED
:
206 gboolean in_library
= FALSE
;
208 if (!eel_gconf_get_boolean (CONF_MONITOR_LIBRARY
))
211 /* ignore new files outside of the library locations */
212 for (cur
= db
->priv
->library_locations
; cur
!= NULL
; cur
= g_slist_next (cur
)) {
213 if (g_str_has_prefix (canon_uri
, cur
->data
)) {
223 /* process directories immediately */
224 if (rb_uri_is_directory (canon_uri
)) {
225 rhythmdb_monitor_uri_path (db
, canon_uri
, NULL
);
226 rhythmdb_add_uri (db
, canon_uri
);
230 case GNOME_VFS_MONITOR_EVENT_CHANGED
:
231 case GNOME_VFS_MONITOR_EVENT_METADATA_CHANGED
:
235 g_get_current_time (&time
);
236 g_hash_table_replace (db
->priv
->changed_files
,
237 rb_refstring_new (canon_uri
),
238 GINT_TO_POINTER (time
.tv_sec
));
241 case GNOME_VFS_MONITOR_EVENT_DELETED
:
243 RhythmDBEvent
*event
= g_new0 (RhythmDBEvent
, 1);
245 event
->type
= RHYTHMDB_EVENT_FILE_DELETED
;
246 event
->uri
= rb_refstring_new (canon_uri
);
247 g_async_queue_push (db
->priv
->event_queue
, event
);
250 case GNOME_VFS_MONITOR_EVENT_STARTEXECUTING
:
251 case GNOME_VFS_MONITOR_EVENT_STOPEXECUTING
:
259 rhythmdb_monitor_uri_path (RhythmDB
*db
, const char *uri
, GError
**error
)
262 GnomeVFSResult vfsresult
;
263 GnomeVFSMonitorHandle
**handle
= NULL
;
265 if (rb_uri_is_directory (uri
)) {
266 if (g_str_has_suffix(uri
, G_DIR_SEPARATOR_S
)) {
267 directory
= g_strdup (uri
);
269 directory
= g_strconcat (uri
, G_DIR_SEPARATOR_S
, NULL
);
272 GnomeVFSURI
*vfsuri
, *parent
;
274 vfsuri
= gnome_vfs_uri_new (uri
);
275 if (vfsuri
== NULL
) {
276 rb_debug ("failed to monitor %s: couldn't create GnomeVFSURI", uri
);
280 parent
= gnome_vfs_uri_get_parent (vfsuri
);
281 directory
= gnome_vfs_uri_to_string (parent
, GNOME_VFS_URI_HIDE_NONE
);
282 gnome_vfs_uri_unref (vfsuri
);
283 gnome_vfs_uri_unref (parent
);
286 if (directory
== NULL
|| g_hash_table_lookup (db
->priv
->monitored_directories
, directory
)) {
291 handle
= g_new0 (GnomeVFSMonitorHandle
*, 1);
292 vfsresult
= gnome_vfs_monitor_add (handle
, directory
,
293 GNOME_VFS_MONITOR_DIRECTORY
,
294 (GnomeVFSMonitorCallback
) rhythmdb_directory_change_cb
,
296 if (vfsresult
== GNOME_VFS_OK
) {
297 rb_debug ("monitoring: %s", directory
);
298 g_hash_table_insert (db
->priv
->monitored_directories
,
303 RHYTHMDB_ERROR_ACCESS_FAILED
,
304 _("Couldn't monitor %s: %s"),
306 gnome_vfs_result_to_string (vfsresult
));
307 rb_debug ("failed to monitor %s", directory
);
315 RBRefString
*mount_point
;
320 entry_volume_mounted_or_unmounted (RhythmDBEntry
*entry
,
323 RBRefString
*mount_point
;
324 const char *location
;
326 if (entry
->type
!= RHYTHMDB_ENTRY_TYPE_SONG
&&
327 entry
->type
!= RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR
) {
331 mount_point
= rhythmdb_entry_get_refstring (entry
, RHYTHMDB_PROP_MOUNTPOINT
);
332 if (mount_point
== NULL
|| !rb_refstring_equal (mount_point
, ctxt
->mount_point
)) {
335 location
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
337 if (entry
->type
== RHYTHMDB_ENTRY_TYPE_SONG
) {
339 rb_debug ("queueing stat for entry %s (mounted)", location
);
341 /* make files visible immediately,
342 * then hide any that turn out to be missing.
344 rhythmdb_entry_set_visibility (ctxt
->db
, entry
, TRUE
);
345 queue_stat_uri (location
,
347 RHYTHMDB_ENTRY_TYPE_SONG
);
352 rb_debug ("hiding entry %s (unmounted)", location
);
354 g_get_current_time (&time
);
355 g_value_init (&val
, G_TYPE_ULONG
);
356 g_value_set_ulong (&val
, time
.tv_sec
);
357 rhythmdb_entry_set_internal (ctxt
->db
, entry
, FALSE
,
358 RHYTHMDB_PROP_LAST_SEEN
, &val
);
359 g_value_unset (&val
);
361 rhythmdb_entry_set_visibility (ctxt
->db
, entry
, FALSE
);
363 } else if (entry
->type
== RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR
) {
364 /* delete import errors for files on unmounted volumes */
365 if (ctxt
->mounted
== FALSE
) {
366 rb_debug ("removing import error for %s (unmounted)", location
);
367 rhythmdb_entry_delete (ctxt
->db
, entry
);
373 rhythmdb_volume_mounted_cb (GnomeVFSVolumeMonitor
*monitor
,
374 GnomeVFSVolume
*volume
,
380 mp
= gnome_vfs_volume_get_activation_uri (volume
);
381 ctxt
.mount_point
= rb_refstring_new (mp
);
384 ctxt
.db
= RHYTHMDB (data
);
386 rb_debug ("volume %s mounted", rb_refstring_get (ctxt
.mount_point
));
387 rhythmdb_entry_foreach (RHYTHMDB (data
),
388 (GFunc
)entry_volume_mounted_or_unmounted
,
390 rhythmdb_commit (RHYTHMDB (data
));
391 rb_refstring_unref (ctxt
.mount_point
);
395 rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor
*monitor
,
396 GnomeVFSVolume
*volume
,
402 mp
= gnome_vfs_volume_get_activation_uri (volume
);
403 ctxt
.mount_point
= rb_refstring_new (mp
);
406 ctxt
.db
= RHYTHMDB (data
);
407 ctxt
.mounted
= FALSE
;
408 rb_debug ("volume %s unmounted", rb_refstring_get (ctxt
.mount_point
));
409 rhythmdb_entry_foreach (RHYTHMDB (data
),
410 (GFunc
)entry_volume_mounted_or_unmounted
,
412 rhythmdb_commit (RHYTHMDB (data
));
413 rb_refstring_unref (ctxt
.mount_point
);