udapted vi.po
[rhythmbox.git] / rhythmdb / rhythmdb-monitor.c
blob1220d7a2a45eff1236c6db0567980147ee7d4477
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.
21 #include <config.h>
23 #include <string.h>
25 #include <glib.h>
26 #include <glib-object.h>
27 #include <glib/gi18n.h>
28 #include <gconf/gconf-client.h>
29 #include <libgnomevfs/gnome-vfs-volume-monitor.h>
31 #include "rb-debug.h"
32 #include "rb-util.h"
33 #include "rhythmdb.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,
43 gpointer data);
44 static void rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor *monitor,
45 GnomeVFSVolume *volume,
46 gpointer data);
48 void
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,
57 NULL);
59 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
60 "volume-mounted",
61 G_CALLBACK (rhythmdb_volume_mounted_cb),
62 db);
64 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
65 "volume-pre-unmount",
66 G_CALLBACK (rhythmdb_volume_unmounted_cb),
67 db);
68 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
69 "volume-unmounted",
70 G_CALLBACK (rhythmdb_volume_unmounted_cb),
71 db);
74 void
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);
85 void
86 rhythmdb_stop_monitoring (RhythmDB *db)
88 g_hash_table_foreach_remove (db->priv->monitored_directories,
89 (GHRFunc) rb_true_function,
90 db);
93 static void
94 monitor_entry_file (RhythmDBEntry *entry, RhythmDB *db)
96 if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
97 const char *loc;
98 GSList *l;
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))
105 return;
108 rhythmdb_monitor_uri_path (db, loc, NULL);
112 static void
113 monitor_subdirectory (const char *uri, gboolean dir, RhythmDB *db)
115 if (dir)
116 rhythmdb_monitor_uri_path (db, uri, NULL);
117 else
118 rhythmdb_add_uri (db, uri);
121 static void
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? */
127 return;
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);
136 static gboolean
137 rhythmdb_check_changed_file (RBRefString *uri, gpointer data, RhythmDB *db)
139 GTimeVal time;
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);
146 event->db = db;
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));
152 return TRUE;
155 rb_debug ("waiting to add newly located file %s", rb_refstring_get (uri));
157 return FALSE;
160 static gboolean
161 rhythmdb_process_changed_files (RhythmDB *db)
163 g_hash_table_foreach_remove (db->priv->changed_files,
164 (GHRFunc)rhythmdb_check_changed_file, db);
165 return TRUE;
168 static gpointer
169 _monitor_entry_thread (RhythmDB *db)
171 rhythmdb_entry_foreach (db, (GFunc) monitor_entry_file, db);
172 g_object_unref (G_OBJECT (db));
173 return NULL;
176 void
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);
189 static void
190 rhythmdb_directory_change_cb (GnomeVFSMonitorHandle *handle,
191 const char *monitor_uri,
192 const char *info_uri,
193 GnomeVFSMonitorEventType vfsevent,
194 RhythmDB *db)
196 char *canon_uri;
198 canon_uri = rb_canonicalise_uri (info_uri);
199 rb_debug ("directory event %d for %s: %s", (int) vfsevent,
200 monitor_uri, canon_uri);
202 switch (vfsevent) {
203 case GNOME_VFS_MONITOR_EVENT_CREATED:
205 GSList *cur;
206 gboolean in_library = FALSE;
208 if (!eel_gconf_get_boolean (CONF_MONITOR_LIBRARY))
209 break;
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)) {
214 in_library = TRUE;
215 break;
219 if (!in_library)
220 break;
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);
227 break;
229 /* fall through*/
230 case GNOME_VFS_MONITOR_EVENT_CHANGED:
231 case GNOME_VFS_MONITOR_EVENT_METADATA_CHANGED:
233 GTimeVal time;
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));
240 break;
241 case GNOME_VFS_MONITOR_EVENT_DELETED:
243 RhythmDBEvent *event = g_new0 (RhythmDBEvent, 1);
244 event->db = db;
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);
249 break;
250 case GNOME_VFS_MONITOR_EVENT_STARTEXECUTING:
251 case GNOME_VFS_MONITOR_EVENT_STOPEXECUTING:
252 break;
255 g_free (canon_uri);
258 void
259 rhythmdb_monitor_uri_path (RhythmDB *db, const char *uri, GError **error)
261 char *directory;
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);
268 } else {
269 directory = g_strconcat (uri, G_DIR_SEPARATOR_S, NULL);
271 } else {
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);
277 return;
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)) {
287 g_free (directory);
288 return;
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,
295 db);
296 if (vfsresult == GNOME_VFS_OK) {
297 rb_debug ("monitoring: %s", directory);
298 g_hash_table_insert (db->priv->monitored_directories,
299 directory, *handle);
300 } else {
301 g_set_error (error,
302 RHYTHMDB_ERROR,
303 RHYTHMDB_ERROR_ACCESS_FAILED,
304 _("Couldn't monitor %s: %s"),
305 directory,
306 gnome_vfs_result_to_string (vfsresult));
307 rb_debug ("failed to monitor %s", directory);
308 g_free (directory);
312 typedef struct
314 RhythmDB *db;
315 RBRefString *mount_point;
316 gboolean mounted;
317 } MountCtxt;
319 static void
320 entry_volume_mounted_or_unmounted (RhythmDBEntry *entry,
321 MountCtxt *ctxt)
323 RBRefString *mount_point;
324 const char *location;
326 if (entry->type != RHYTHMDB_ENTRY_TYPE_SONG &&
327 entry->type != RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR) {
328 return;
331 mount_point = rhythmdb_entry_get_refstring (entry, RHYTHMDB_PROP_MOUNTPOINT);
332 if (mount_point == NULL || !rb_refstring_equal (mount_point, ctxt->mount_point)) {
333 return;
335 location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
337 if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
338 if (ctxt->mounted) {
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,
346 ctxt->db,
347 RHYTHMDB_ENTRY_TYPE_SONG);
348 } else {
349 GTimeVal time;
350 GValue val = {0, };
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);
372 static void
373 rhythmdb_volume_mounted_cb (GnomeVFSVolumeMonitor *monitor,
374 GnomeVFSVolume *volume,
375 gpointer data)
377 MountCtxt ctxt;
378 char *mp;
380 mp = gnome_vfs_volume_get_activation_uri (volume);
381 ctxt.mount_point = rb_refstring_new (mp);
382 g_free (mp);
384 ctxt.db = RHYTHMDB (data);
385 ctxt.mounted = TRUE;
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,
389 &ctxt);
390 rhythmdb_commit (RHYTHMDB (data));
391 rb_refstring_unref (ctxt.mount_point);
394 static void
395 rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor *monitor,
396 GnomeVFSVolume *volume,
397 gpointer data)
399 MountCtxt ctxt;
400 char *mp;
402 mp = gnome_vfs_volume_get_activation_uri (volume);
403 ctxt.mount_point = rb_refstring_new (mp);
404 g_free (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,
411 &ctxt);
412 rhythmdb_commit (RHYTHMDB (data));
413 rb_refstring_unref (ctxt.mount_point);