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"
38 #define RHYTHMDB_FILE_MODIFY_PROCESS_TIME 2
40 static void rhythmdb_volume_mounted_cb (GnomeVFSVolumeMonitor
*monitor
,
41 GnomeVFSVolume
*volume
,
43 static void rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor
*monitor
,
44 GnomeVFSVolume
*volume
,
48 rhythmdb_init_monitoring (RhythmDB
*db
)
50 db
->priv
->monitored_directories
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
51 (GDestroyNotify
) g_free
,
54 db
->priv
->changed_files
= g_hash_table_new_full (rb_refstring_hash
, rb_refstring_equal
,
55 (GDestroyNotify
) rb_refstring_unref
,
58 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
60 G_CALLBACK (rhythmdb_volume_mounted_cb
),
63 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
65 G_CALLBACK (rhythmdb_volume_unmounted_cb
),
67 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
69 G_CALLBACK (rhythmdb_volume_unmounted_cb
),
74 rhythmdb_finalize_monitoring (RhythmDB
*db
)
76 rhythmdb_stop_monitoring (db
);
78 g_hash_table_destroy (db
->priv
->monitored_directories
);
79 if (db
->priv
->changed_files_id
)
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
) {
107 loc
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
108 rhythmdb_monitor_uri_path (db
, loc
, &error
);
112 /* FIXME: should we complain to the user? */
113 rb_debug ("error while attempting to monitor library track: %s", error
->message
);
118 monitor_subdirectory (const char *uri
, RhythmDB
*db
)
120 GError
*error
= NULL
;
122 if (!rb_uri_is_directory (uri
))
125 rhythmdb_monitor_uri_path (db
, uri
, &error
);
128 /* FIXME: should we complain to the user? */
129 rb_debug ("error while attempting to monitor the library directory: %s", error
->message
);
134 monitor_library_directory (const char *uri
, RhythmDB
*db
)
136 GError
*error
= NULL
;
138 if ((strcmp (uri
, "file:///") == 0) ||
139 (strcmp (uri
, "file://") == 0)) {
140 /* display an error to the user? */
144 rb_debug ("beginning monitor of the library directory %s", uri
);
145 rhythmdb_monitor_uri_path (db
, uri
, &error
);
146 rb_uri_handle_recursively (uri
, (GFunc
) monitor_subdirectory
, NULL
, db
);
149 /* FIXME: should we complain to the user? */
150 rb_debug ("error while attempting to monitor the library directory: %s", error
->message
);
153 rb_debug ("loading new tracks from library directory %s", uri
);
154 rhythmdb_add_uri (db
, uri
);
158 rhythmdb_check_changed_file (RBRefString
*uri
, gpointer data
, RhythmDB
*db
)
161 glong time_sec
= GPOINTER_TO_INT (data
);
163 g_get_current_time (&time
);
164 if (time
.tv_sec
>= time_sec
+ RHYTHMDB_FILE_MODIFY_PROCESS_TIME
) {
165 /* process and remove from table */
166 RhythmDBEvent
*event
= g_new0 (RhythmDBEvent
, 1);
168 event
->type
= RHYTHMDB_EVENT_FILE_CREATED_OR_MODIFIED
;
169 event
->uri
= rb_refstring_ref (uri
);
171 g_async_queue_push (db
->priv
->event_queue
, event
);
172 rb_debug ("adding newly located file %s", rb_refstring_get (uri
));
176 rb_debug ("waiting to add newly located file %s", rb_refstring_get (uri
));
182 rhythmdb_process_changed_files (RhythmDB
*db
)
184 g_hash_table_foreach_remove (db
->priv
->changed_files
,
185 (GHRFunc
)rhythmdb_check_changed_file
, db
);
190 rhythmdb_start_monitoring (RhythmDB
*db
)
192 db
->priv
->changed_files_id
= g_timeout_add (RHYTHMDB_FILE_MODIFY_PROCESS_TIME
* 1000,
193 (GSourceFunc
) rhythmdb_process_changed_files
, db
);
195 if (db
->priv
->library_locations
) {
196 g_slist_foreach (db
->priv
->library_locations
, (GFunc
) monitor_library_directory
, db
);
199 /* monitor every directory that contains a (TYPE_SONG) track */
200 rhythmdb_entry_foreach (db
, (GFunc
) monitor_entry_file
, db
);
204 rhythmdb_directory_change_cb (GnomeVFSMonitorHandle
*handle
,
205 const char *monitor_uri
,
206 const char *info_uri
,
207 GnomeVFSMonitorEventType vfsevent
,
212 canon_uri
= rb_canonicalise_uri (info_uri
);
213 rb_debug ("directory event %d for %s: %s", (int) vfsevent
,
214 monitor_uri
, canon_uri
);
217 case GNOME_VFS_MONITOR_EVENT_CREATED
:
220 gboolean in_library
= FALSE
;
222 if (!eel_gconf_get_boolean (CONF_MONITOR_LIBRARY
))
225 /* ignore new files outside of the library locations */
226 for (cur
= db
->priv
->library_locations
; cur
!= NULL
; cur
= g_slist_next (cur
)) {
227 if (g_str_has_prefix (canon_uri
, cur
->data
)) {
237 /* process directories immediately */
238 if (rb_uri_is_directory (canon_uri
)) {
239 rhythmdb_monitor_uri_path (db
, canon_uri
, NULL
);
240 rhythmdb_add_uri (db
, canon_uri
);
244 case GNOME_VFS_MONITOR_EVENT_CHANGED
:
245 case GNOME_VFS_MONITOR_EVENT_METADATA_CHANGED
:
249 g_get_current_time (&time
);
250 g_hash_table_replace (db
->priv
->changed_files
,
251 rb_refstring_new (canon_uri
),
252 GINT_TO_POINTER (time
.tv_sec
));
255 case GNOME_VFS_MONITOR_EVENT_DELETED
:
257 RhythmDBEvent
*event
= g_new0 (RhythmDBEvent
, 1);
259 event
->type
= RHYTHMDB_EVENT_FILE_DELETED
;
260 event
->uri
= rb_refstring_new (canon_uri
);
261 g_async_queue_push (db
->priv
->event_queue
, event
);
264 case GNOME_VFS_MONITOR_EVENT_STARTEXECUTING
:
265 case GNOME_VFS_MONITOR_EVENT_STOPEXECUTING
:
273 rhythmdb_monitor_uri_path (RhythmDB
*db
, const char *uri
, GError
**error
)
276 GnomeVFSResult vfsresult
;
277 GnomeVFSMonitorHandle
**handle
;
279 if (rb_uri_is_directory (uri
)) {
280 if (g_str_has_suffix(uri
, G_DIR_SEPARATOR_S
)) {
281 directory
= g_strdup (uri
);
283 directory
= g_strconcat (uri
, G_DIR_SEPARATOR_S
, NULL
);
286 GnomeVFSURI
*vfsuri
, *parent
;
288 vfsuri
= gnome_vfs_uri_new (uri
);
289 parent
= gnome_vfs_uri_get_parent (vfsuri
);
290 directory
= gnome_vfs_uri_to_string (parent
, GNOME_VFS_URI_HIDE_NONE
);
291 gnome_vfs_uri_unref (vfsuri
);
292 gnome_vfs_uri_unref (parent
);
295 if (directory
== NULL
|| g_hash_table_lookup (db
->priv
->monitored_directories
, directory
))
298 handle
= g_new0 (GnomeVFSMonitorHandle
*, 1);
299 vfsresult
= gnome_vfs_monitor_add (handle
, directory
,
300 GNOME_VFS_MONITOR_DIRECTORY
,
301 (GnomeVFSMonitorCallback
) rhythmdb_directory_change_cb
,
303 if (vfsresult
== GNOME_VFS_OK
) {
304 rb_debug ("monitoring: %s", directory
);
305 g_hash_table_insert (db
->priv
->monitored_directories
,
310 RHYTHMDB_ERROR_ACCESS_FAILED
,
311 _("Couldn't monitor %s: %s"),
313 gnome_vfs_result_to_string (vfsresult
));
314 rb_debug ("failed to monitor %s", directory
);
323 RBRefString
*mount_point
;
328 entry_volume_mounted_or_unmounted (RhythmDBEntry
*entry
,
331 RBRefString
*mount_point
;
332 const char *location
;
334 if (entry
->type
!= RHYTHMDB_ENTRY_TYPE_SONG
&&
335 entry
->type
!= RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR
) {
339 mount_point
= rhythmdb_entry_get_refstring (entry
, RHYTHMDB_PROP_MOUNTPOINT
);
340 if (mount_point
== NULL
|| !rb_refstring_equal (mount_point
, ctxt
->mount_point
)) {
343 location
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
345 if (entry
->type
== RHYTHMDB_ENTRY_TYPE_SONG
) {
347 rb_debug ("queueing stat for entry %s (mounted)", location
);
349 /* make files visible immediately,
350 * then hide any that turn out to be missing.
352 rhythmdb_entry_set_visibility (ctxt
->db
, entry
, TRUE
);
353 queue_stat_uri (location
,
355 RHYTHMDB_ENTRY_TYPE_SONG
);
360 rb_debug ("hiding entry %s (unmounted)", location
);
362 g_get_current_time (&time
);
363 g_value_init (&val
, G_TYPE_ULONG
);
364 g_value_set_ulong (&val
, time
.tv_sec
);
365 rhythmdb_entry_set_internal (ctxt
->db
, entry
, FALSE
,
366 RHYTHMDB_PROP_LAST_SEEN
, &val
);
367 g_value_unset (&val
);
369 rhythmdb_entry_set_visibility (ctxt
->db
, entry
, FALSE
);
371 } else if (entry
->type
== RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR
) {
372 /* delete import errors for files on unmounted volumes */
373 if (ctxt
->mounted
== FALSE
) {
374 rb_debug ("removing import error for %s (unmounted)", location
);
375 rhythmdb_entry_delete (ctxt
->db
, entry
);
381 rhythmdb_volume_mounted_cb (GnomeVFSVolumeMonitor
*monitor
,
382 GnomeVFSVolume
*volume
,
388 mp
= gnome_vfs_volume_get_activation_uri (volume
);
389 ctxt
.mount_point
= rb_refstring_new (mp
);
392 ctxt
.db
= RHYTHMDB (data
);
394 rb_debug ("volume %s mounted", rb_refstring_get (ctxt
.mount_point
));
395 rhythmdb_entry_foreach (RHYTHMDB (data
),
396 (GFunc
)entry_volume_mounted_or_unmounted
,
398 rhythmdb_commit (RHYTHMDB (data
));
399 rb_refstring_unref (ctxt
.mount_point
);
403 rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor
*monitor
,
404 GnomeVFSVolume
*volume
,
410 mp
= gnome_vfs_volume_get_activation_uri (volume
);
411 ctxt
.mount_point
= rb_refstring_new (mp
);
414 ctxt
.db
= RHYTHMDB (data
);
415 ctxt
.mounted
= FALSE
;
416 rb_debug ("volume %s unmounted", rb_refstring_get (ctxt
.mount_point
));
417 rhythmdb_entry_foreach (RHYTHMDB (data
),
418 (GFunc
)entry_volume_mounted_or_unmounted
,
420 rhythmdb_commit (RHYTHMDB (data
));
421 rb_refstring_unref (ctxt
.mount_point
);