Updated Finnish translation
[rhythmbox.git] / rhythmdb / rhythmdb-monitor.c
blob5c220819208d91685a2a25ad93a3756cc11d7b84
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 "rhythmdb.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,
42 gpointer data);
43 static void rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor *monitor,
44 GnomeVFSVolume *volume,
45 gpointer data);
47 void
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,
52 NULL);
54 db->priv->changed_files = g_hash_table_new_full (rb_refstring_hash, rb_refstring_equal,
55 (GDestroyNotify) rb_refstring_unref,
56 NULL);
58 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
59 "volume-mounted",
60 G_CALLBACK (rhythmdb_volume_mounted_cb),
61 db);
63 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
64 "volume-pre-unmount",
65 G_CALLBACK (rhythmdb_volume_unmounted_cb),
66 db);
67 g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
68 "volume-unmounted",
69 G_CALLBACK (rhythmdb_volume_unmounted_cb),
70 db);
73 void
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);
84 static gboolean
85 rhythmdb_unmonitor_directories (char *dir, GnomeVFSMonitorHandle *handle, RhythmDB *db)
87 gnome_vfs_monitor_cancel (handle);
88 return TRUE;
91 void
92 rhythmdb_stop_monitoring (RhythmDB *db)
94 g_hash_table_foreach_remove (db->priv->monitored_directories,
95 (GHRFunc) rhythmdb_unmonitor_directories,
96 db);
99 static void
100 monitor_entry_file (RhythmDBEntry *entry, RhythmDB *db)
102 GError *error = NULL;
104 if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
105 const char *loc;
107 loc = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
108 rhythmdb_monitor_uri_path (db, loc, &error);
111 if (error) {
112 /* FIXME: should we complain to the user? */
113 rb_debug ("error while attempting to monitor library track: %s", error->message);
117 static void
118 monitor_subdirectory (const char *uri, RhythmDB *db)
120 GError *error = NULL;
122 if (!rb_uri_is_directory (uri))
123 return;
125 rhythmdb_monitor_uri_path (db, uri, &error);
127 if (error) {
128 /* FIXME: should we complain to the user? */
129 rb_debug ("error while attempting to monitor the library directory: %s", error->message);
133 static void
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? */
141 return;
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);
148 if (error) {
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);
157 static gboolean
158 rhythmdb_check_changed_file (RBRefString *uri, gpointer data, RhythmDB *db)
160 GTimeVal time;
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);
167 event->db = db;
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));
173 return TRUE;
176 rb_debug ("waiting to add newly located file %s", rb_refstring_get (uri));
178 return FALSE;
181 static gboolean
182 rhythmdb_process_changed_files (RhythmDB *db)
184 g_hash_table_foreach_remove (db->priv->changed_files,
185 (GHRFunc)rhythmdb_check_changed_file, db);
186 return TRUE;
189 void
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);
203 static void
204 rhythmdb_directory_change_cb (GnomeVFSMonitorHandle *handle,
205 const char *monitor_uri,
206 const char *info_uri,
207 GnomeVFSMonitorEventType vfsevent,
208 RhythmDB *db)
210 char *canon_uri;
212 canon_uri = rb_canonicalise_uri (info_uri);
213 rb_debug ("directory event %d for %s: %s", (int) vfsevent,
214 monitor_uri, canon_uri);
216 switch (vfsevent) {
217 case GNOME_VFS_MONITOR_EVENT_CREATED:
219 GSList *cur;
220 gboolean in_library = FALSE;
222 if (!eel_gconf_get_boolean (CONF_MONITOR_LIBRARY))
223 break;
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)) {
228 in_library = TRUE;
229 break;
233 if (!in_library)
234 break;
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);
241 break;
243 /* fall through*/
244 case GNOME_VFS_MONITOR_EVENT_CHANGED:
245 case GNOME_VFS_MONITOR_EVENT_METADATA_CHANGED:
247 GTimeVal time;
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));
254 break;
255 case GNOME_VFS_MONITOR_EVENT_DELETED:
257 RhythmDBEvent *event = g_new0 (RhythmDBEvent, 1);
258 event->db = db;
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);
263 break;
264 case GNOME_VFS_MONITOR_EVENT_STARTEXECUTING:
265 case GNOME_VFS_MONITOR_EVENT_STOPEXECUTING:
266 break;
269 g_free (canon_uri);
272 void
273 rhythmdb_monitor_uri_path (RhythmDB *db, const char *uri, GError **error)
275 char *directory;
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);
282 } else {
283 directory = g_strconcat (uri, G_DIR_SEPARATOR_S, NULL);
285 } else {
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))
296 return;
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,
302 db);
303 if (vfsresult == GNOME_VFS_OK) {
304 rb_debug ("monitoring: %s", directory);
305 g_hash_table_insert (db->priv->monitored_directories,
306 directory, *handle);
307 } else {
308 g_set_error (error,
309 RHYTHMDB_ERROR,
310 RHYTHMDB_ERROR_ACCESS_FAILED,
311 _("Couldn't monitor %s: %s"),
312 directory,
313 gnome_vfs_result_to_string (vfsresult));
314 rb_debug ("failed to monitor %s", directory);
315 g_free (directory);
316 g_free (handle);
320 typedef struct
322 RhythmDB *db;
323 RBRefString *mount_point;
324 gboolean mounted;
325 } MountCtxt;
327 static void
328 entry_volume_mounted_or_unmounted (RhythmDBEntry *entry,
329 MountCtxt *ctxt)
331 RBRefString *mount_point;
332 const char *location;
334 if (entry->type != RHYTHMDB_ENTRY_TYPE_SONG &&
335 entry->type != RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR) {
336 return;
339 mount_point = rhythmdb_entry_get_refstring (entry, RHYTHMDB_PROP_MOUNTPOINT);
340 if (mount_point == NULL || !rb_refstring_equal (mount_point, ctxt->mount_point)) {
341 return;
343 location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
345 if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
346 if (ctxt->mounted) {
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,
354 ctxt->db,
355 RHYTHMDB_ENTRY_TYPE_SONG);
356 } else {
357 GTimeVal time;
358 GValue val = {0, };
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);
380 static void
381 rhythmdb_volume_mounted_cb (GnomeVFSVolumeMonitor *monitor,
382 GnomeVFSVolume *volume,
383 gpointer data)
385 MountCtxt ctxt;
386 char *mp;
388 mp = gnome_vfs_volume_get_activation_uri (volume);
389 ctxt.mount_point = rb_refstring_new (mp);
390 g_free (mp);
392 ctxt.db = RHYTHMDB (data);
393 ctxt.mounted = TRUE;
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,
397 &ctxt);
398 rhythmdb_commit (RHYTHMDB (data));
399 rb_refstring_unref (ctxt.mount_point);
402 static void
403 rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor *monitor,
404 GnomeVFSVolume *volume,
405 gpointer data)
407 MountCtxt ctxt;
408 char *mp;
410 mp = gnome_vfs_volume_get_activation_uri (volume);
411 ctxt.mount_point = rb_refstring_new (mp);
412 g_free (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,
419 &ctxt);
420 rhythmdb_commit (RHYTHMDB (data));
421 rb_refstring_unref (ctxt.mount_point);