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>
33 #include "rhythmdb-private.h"
34 #include "rb-file-helpers.h"
35 #include "rb-preferences.h"
36 #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
,
55 db
->priv
->changed_files
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
56 (GDestroyNotify
) g_free
,
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 g_source_remove (db
->priv
->changed_files_id
);
81 g_hash_table_destroy (db
->priv
->changed_files
);
85 rhythmdb_unmonitor_directories (char *dir
, GnomeVFSMonitorHandle
*handle
, RhythmDB
*db
)
87 gnome_vfs_monitor_cancel (handle
);
92 rhythmdb_stop_monitoring (RhythmDB
*db
)
94 g_hash_table_foreach_remove (db
->priv
->monitored_directories
,
95 (GHRFunc
) rhythmdb_unmonitor_directories
,
100 monitor_entry_file (RhythmDBEntry
*entry
, RhythmDB
*db
)
102 GError
*error
= NULL
;
104 if (entry
->type
== RHYTHMDB_ENTRY_TYPE_SONG
) {
105 rhythmdb_monitor_uri_path (db
, entry
->location
, &error
);
109 /* FIXME: should we complain to the user? */
110 rb_debug ("error while attempting to monitor library track: %s", error
->message
);
115 monitor_subdirectory (const char *uri
, RhythmDB
*db
)
117 GError
*error
= NULL
;
119 if (!rb_uri_is_directory (uri
))
122 rhythmdb_monitor_uri_path (db
, uri
, &error
);
125 /* FIXME: should we complain to the user? */
126 rb_debug ("error while attempting to monitor the library directory: %s", error
->message
);
131 monitor_library_directory (const char *uri
, RhythmDB
*db
)
133 GError
*error
= NULL
;
135 if ((strcmp (uri
, "file:///") == 0) ||
136 (strcmp (uri
, "file://") == 0)) {
137 /* display an error to the user? */
141 rb_debug ("beginning monitor of the library directory %s", uri
);
142 rhythmdb_monitor_uri_path (db
, uri
, &error
);
143 rb_uri_handle_recursively (uri
, (GFunc
) monitor_subdirectory
, NULL
, db
);
146 /* FIXME: should we complain to the user? */
147 rb_debug ("error while attempting to monitor the library directory: %s", error
->message
);
150 rb_debug ("loading new tracks from library directory %s", uri
);
151 rhythmdb_add_uri (db
, uri
);
155 rhythmdb_check_changed_file (const char *uri
, gpointer data
, RhythmDB
*db
)
158 glong time_sec
= GPOINTER_TO_INT (data
);
160 g_get_current_time (&time
);
161 if (time
.tv_sec
>= time_sec
+ RHYTHMDB_FILE_MODIFY_PROCESS_TIME
) {
162 /* process and remove from table */
163 RhythmDBEvent
*event
= g_new0 (RhythmDBEvent
, 1);
165 event
->type
= RHYTHMDB_EVENT_FILE_CREATED_OR_MODIFIED
;
166 event
->uri
= g_strdup (uri
);
168 g_async_queue_push (db
->priv
->event_queue
, event
);
169 rb_debug ("adding newly located file %s", uri
);
173 rb_debug ("waiting to add newly located file %s", uri
);
179 rhythmdb_process_changed_files (RhythmDB
*db
)
181 g_hash_table_foreach_remove (db
->priv
->changed_files
,
182 (GHRFunc
)rhythmdb_check_changed_file
, db
);
188 rhythmdb_start_monitoring (RhythmDB
*db
)
190 db
->priv
->changed_files_id
= g_timeout_add (RHYTHMDB_FILE_MODIFY_PROCESS_TIME
* 1000,
191 (GSourceFunc
) rhythmdb_process_changed_files
, db
);
193 if (!db
->priv
->library_locations
) {
194 g_slist_foreach (db
->priv
->library_locations
, (GFunc
) monitor_library_directory
, db
);
197 /* monitor every directory that contains a (TYPE_SONG) track */
198 rhythmdb_entry_foreach (db
, (GFunc
) monitor_entry_file
, db
);
202 rhythmdb_directory_change_cb (GnomeVFSMonitorHandle
*handle
,
203 const char *monitor_uri
,
204 const char *info_uri
,
205 GnomeVFSMonitorEventType vfsevent
,
208 rb_debug ("directory event %d for %s: %s", (int) vfsevent
,
209 monitor_uri
, info_uri
);
212 case GNOME_VFS_MONITOR_EVENT_CREATED
:
215 gboolean in_library
= FALSE
;
217 if (!eel_gconf_get_boolean (CONF_MONITOR_LIBRARY
))
220 /* ignore new files outside of the library locations */
221 for (cur
= db
->priv
->library_locations
; cur
!= NULL
; cur
= g_slist_next (cur
)) {
222 if (g_str_has_prefix (info_uri
, cur
->data
)) {
232 /* process directories immediately */
233 if (rb_uri_is_directory (info_uri
)) {
234 rhythmdb_monitor_uri_path (db
, info_uri
, NULL
);
235 rhythmdb_add_uri (db
, info_uri
);
239 case GNOME_VFS_MONITOR_EVENT_CHANGED
:
240 case GNOME_VFS_MONITOR_EVENT_METADATA_CHANGED
:
244 g_get_current_time (&time
);
245 g_hash_table_replace (db
->priv
->changed_files
,
247 GINT_TO_POINTER (time
.tv_sec
));
250 case GNOME_VFS_MONITOR_EVENT_DELETED
:
252 RhythmDBEvent
*event
= g_new0 (RhythmDBEvent
, 1);
254 event
->type
= RHYTHMDB_EVENT_FILE_DELETED
;
255 event
->uri
= g_strdup (info_uri
);
256 g_async_queue_push (db
->priv
->event_queue
, event
);
259 case GNOME_VFS_MONITOR_EVENT_STARTEXECUTING
:
260 case GNOME_VFS_MONITOR_EVENT_STOPEXECUTING
:
266 rhythmdb_monitor_uri_path (RhythmDB
*db
, const char *uri
, GError
**error
)
269 GnomeVFSResult vfsresult
;
270 GnomeVFSMonitorHandle
**handle
;
272 if (rb_uri_is_directory (uri
)) {
273 if (g_str_has_suffix(uri
, G_DIR_SEPARATOR_S
)) {
274 directory
= g_strdup (uri
);
276 directory
= g_strconcat (uri
, G_DIR_SEPARATOR_S
, NULL
);
279 GnomeVFSURI
*vfsuri
, *parent
;
281 vfsuri
= gnome_vfs_uri_new (uri
);
282 parent
= gnome_vfs_uri_get_parent (vfsuri
);
283 directory
= gnome_vfs_uri_to_string (parent
, GNOME_VFS_URI_HIDE_NONE
);
284 gnome_vfs_uri_unref (vfsuri
);
285 gnome_vfs_uri_unref (parent
);
288 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
);
322 entry_volume_mounted_or_unmounted (RhythmDBEntry
*entry
,
325 const char *mount_point
;
326 const char *location
;
328 if (entry
->type
!= RHYTHMDB_ENTRY_TYPE_SONG
&&
329 entry
->type
!= RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR
) {
333 mount_point
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_MOUNTPOINT
);
334 if (mount_point
== NULL
|| strcmp (mount_point
, ctxt
->mount_point
) != 0) {
337 location
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
339 if (entry
->type
== RHYTHMDB_ENTRY_TYPE_SONG
) {
341 rb_debug ("queueing stat for entry %s (mounted)", location
);
343 /* make files visible immediately,
344 * then hide any that turn out to be missing.
346 rhythmdb_entry_set_visibility (ctxt
->db
, entry
, TRUE
);
347 queue_stat_uri (location
,
349 RHYTHMDB_ENTRY_TYPE_SONG
);
354 rb_debug ("hiding entry %s (unmounted)", location
);
356 g_get_current_time (&time
);
357 g_value_init (&val
, G_TYPE_ULONG
);
358 g_value_set_ulong (&val
, time
.tv_sec
);
359 rhythmdb_entry_set_internal (ctxt
->db
, entry
, FALSE
,
360 RHYTHMDB_PROP_LAST_SEEN
, &val
);
361 g_value_unset (&val
);
363 rhythmdb_entry_set_visibility (ctxt
->db
, entry
, FALSE
);
365 } else if (entry
->type
== RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR
) {
366 /* delete import errors for files on unmounted volumes */
367 if (ctxt
->mounted
== FALSE
) {
368 rb_debug ("removing import error for %s (unmounted)", location
);
369 rhythmdb_entry_delete (ctxt
->db
, entry
);
376 rhythmdb_volume_mounted_cb (GnomeVFSVolumeMonitor
*monitor
,
377 GnomeVFSVolume
*volume
,
382 ctxt
.db
= RHYTHMDB (data
);
383 ctxt
.mount_point
= gnome_vfs_volume_get_activation_uri (volume
);
385 rhythmdb_entry_foreach (RHYTHMDB (data
),
386 (GFunc
)entry_volume_mounted_or_unmounted
,
388 rhythmdb_commit (RHYTHMDB (data
));
389 g_free (ctxt
.mount_point
);
394 rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor
*monitor
,
395 GnomeVFSVolume
*volume
,
400 ctxt
.db
= RHYTHMDB (data
);
401 ctxt
.mount_point
= gnome_vfs_volume_get_activation_uri (volume
);
402 ctxt
.mounted
= FALSE
;
403 rb_debug ("volume %s unmounted", ctxt
.mount_point
);
404 rhythmdb_entry_foreach (RHYTHMDB (data
),
405 (GFunc
)entry_volume_mounted_or_unmounted
,
407 rhythmdb_commit (RHYTHMDB (data
));
408 g_free (ctxt
.mount_point
);