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
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_unmonitor_directories (char *dir, GnomeVFSMonitorHandle *handle, RhythmDB *db)
88
gnome_vfs_monitor_cancel (handle);
93
rhythmdb_stop_monitoring (RhythmDB *db)
95
g_hash_table_foreach_remove (db->priv->monitored_directories,
96
(GHRFunc) rhythmdb_unmonitor_directories,
101
monitor_entry_file (RhythmDBEntry *entry, RhythmDB *db)
103
GError *error = NULL;
105
if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
106
rhythmdb_monitor_uri_path (db, entry->location, &error);
110
/* FIXME: should we complain to the user? */
111
rb_debug ("error while attempting to monitor library track: %s", error->message);
116
monitor_subdirectory (const char *uri, RhythmDB *db)
118
GError *error = NULL;
120
if (!rb_uri_is_directory (uri))
123
rhythmdb_monitor_uri_path (db, uri, &error);
126
/* FIXME: should we complain to the user? */
127
rb_debug ("error while attempting to monitor the library directory: %s", error->message);
132
monitor_library_directory (const char *uri, RhythmDB *db)
134
GError *error = NULL;
136
if ((strcmp (uri, "file:///") == 0) ||
137
(strcmp (uri, "file://") == 0)) {
138
/* display an error to the user? */
142
rb_debug ("beginning monitor of the library directory %s", uri);
143
rhythmdb_monitor_uri_path (db, uri, &error);
144
rb_uri_handle_recursively (uri, (GFunc) monitor_subdirectory, NULL, db);
147
/* FIXME: should we complain to the user? */
148
rb_debug ("error while attempting to monitor the library directory: %s", error->message);
151
rb_debug ("loading new tracks from library directory %s", uri);
152
rhythmdb_add_uri (db, uri);
156
rhythmdb_check_changed_file (const char *uri, gpointer data, RhythmDB *db)
159
glong time_sec = GPOINTER_TO_INT (data);
161
g_get_current_time (&time);
162
if (time.tv_sec >= time_sec + RHYTHMDB_FILE_MODIFY_PROCESS_TIME) {
163
/* process and remove from table */
164
RhythmDBEvent *event = g_new0 (RhythmDBEvent, 1);
166
event->type = RHYTHMDB_EVENT_FILE_CREATED_OR_MODIFIED;
167
event->uri = g_strdup (uri);
169
g_async_queue_push (db->priv->event_queue, event);
170
rb_debug ("adding newly located file %s", uri);
174
rb_debug ("waiting to add newly located file %s", uri);
180
rhythmdb_process_changed_files (RhythmDB *db)
182
g_hash_table_foreach_remove (db->priv->changed_files,
183
(GHRFunc)rhythmdb_check_changed_file, db);
189
rhythmdb_start_monitoring (RhythmDB *db)
191
db->priv->changed_files_id = g_timeout_add (RHYTHMDB_FILE_MODIFY_PROCESS_TIME * 1000,
192
(GSourceFunc) rhythmdb_process_changed_files, db);
194
if (!db->priv->library_locations) {
195
g_slist_foreach (db->priv->library_locations, (GFunc) monitor_library_directory, db);
198
/* monitor every directory that contains a (TYPE_SONG) track */
199
rhythmdb_entry_foreach (db, (GFunc) monitor_entry_file, db);
203
rhythmdb_directory_change_cb (GnomeVFSMonitorHandle *handle,
204
const char *monitor_uri,
205
const char *info_uri,
206
GnomeVFSMonitorEventType vfsevent,
209
rb_debug ("directory event %d for %s: %s", (int) vfsevent,
210
monitor_uri, info_uri);
213
case GNOME_VFS_MONITOR_EVENT_CREATED:
216
gboolean in_library = FALSE;
218
if (!eel_gconf_get_boolean (CONF_MONITOR_LIBRARY))
221
/* ignore new files outside of the library locations */
222
for (cur = db->priv->library_locations; cur != NULL; cur = g_slist_next (cur)) {
223
if (g_str_has_prefix (info_uri, cur->data)) {
233
/* process directories immediately */
234
if (rb_uri_is_directory (info_uri)) {
235
rhythmdb_monitor_uri_path (db, info_uri, NULL);
236
rhythmdb_add_uri (db, info_uri);
240
case GNOME_VFS_MONITOR_EVENT_CHANGED:
241
case GNOME_VFS_MONITOR_EVENT_METADATA_CHANGED:
245
g_get_current_time (&time);
246
g_hash_table_replace (db->priv->changed_files,
248
GINT_TO_POINTER (time.tv_sec));
251
case GNOME_VFS_MONITOR_EVENT_DELETED:
253
RhythmDBEvent *event = g_new0 (RhythmDBEvent, 1);
255
event->type = RHYTHMDB_EVENT_FILE_DELETED;
256
event->uri = g_strdup (info_uri);
257
g_async_queue_push (db->priv->event_queue, event);
260
case GNOME_VFS_MONITOR_EVENT_STARTEXECUTING:
261
case GNOME_VFS_MONITOR_EVENT_STOPEXECUTING:
267
rhythmdb_monitor_uri_path (RhythmDB *db, const char *uri, GError **error)
270
GnomeVFSResult vfsresult;
271
GnomeVFSMonitorHandle **handle;
273
if (rb_uri_is_directory (uri)) {
274
if (g_str_has_suffix(uri, G_DIR_SEPARATOR_S)) {
275
directory = g_strdup (uri);
277
directory = g_strconcat (uri, G_DIR_SEPARATOR_S, NULL);
280
GnomeVFSURI *vfsuri, *parent;
282
vfsuri = gnome_vfs_uri_new (uri);
283
parent = gnome_vfs_uri_get_parent (vfsuri);
284
directory = gnome_vfs_uri_to_string (parent, GNOME_VFS_URI_HIDE_NONE);
285
gnome_vfs_uri_unref (vfsuri);
286
gnome_vfs_uri_unref (parent);
289
if (directory == NULL || g_hash_table_lookup (db->priv->monitored_directories, directory))
292
handle = g_new0 (GnomeVFSMonitorHandle *, 1);
293
vfsresult = gnome_vfs_monitor_add (handle, directory,
294
GNOME_VFS_MONITOR_DIRECTORY,
295
(GnomeVFSMonitorCallback) rhythmdb_directory_change_cb,
297
if (vfsresult == GNOME_VFS_OK) {
298
rb_debug ("monitoring: %s", directory);
299
g_hash_table_insert (db->priv->monitored_directories,
304
RHYTHMDB_ERROR_ACCESS_FAILED,
305
_("Couldn't monitor %s: %s"),
307
gnome_vfs_result_to_string (vfsresult));
308
rb_debug ("failed to monitor %s", directory);
323
entry_volume_mounted_or_unmounted (RhythmDBEntry *entry,
326
const char *mount_point;
327
const char *location;
329
if (entry->type != RHYTHMDB_ENTRY_TYPE_SONG &&
330
entry->type != RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR) {
334
mount_point = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MOUNTPOINT);
335
if (mount_point == NULL || strcmp (mount_point, ctxt->mount_point) != 0) {
338
location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
340
if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
342
rb_debug ("queueing stat for entry %s (mounted)", location);
344
/* make files visible immediately,
345
* then hide any that turn out to be missing.
347
rhythmdb_entry_set_visibility (ctxt->db, entry, TRUE);
348
queue_stat_uri (location,
350
RHYTHMDB_ENTRY_TYPE_SONG);
355
rb_debug ("hiding entry %s (unmounted)", location);
357
g_get_current_time (&time);
358
g_value_init (&val, G_TYPE_ULONG);
359
g_value_set_ulong (&val, time.tv_sec);
360
rhythmdb_entry_set_internal (ctxt->db, entry, FALSE,
361
RHYTHMDB_PROP_LAST_SEEN, &val);
362
g_value_unset (&val);
364
rhythmdb_entry_set_visibility (ctxt->db, entry, FALSE);
366
} else if (entry->type == RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR) {
367
/* delete import errors for files on unmounted volumes */
368
if (ctxt->mounted == FALSE) {
369
rb_debug ("removing import error for %s (unmounted)", location);
370
rhythmdb_entry_delete (ctxt->db, entry);
377
rhythmdb_volume_mounted_cb (GnomeVFSVolumeMonitor *monitor,
378
GnomeVFSVolume *volume,
383
ctxt.db = RHYTHMDB (data);
384
ctxt.mount_point = gnome_vfs_volume_get_activation_uri (volume);
386
rhythmdb_entry_foreach (RHYTHMDB (data),
387
(GFunc)entry_volume_mounted_or_unmounted,
389
rhythmdb_commit (RHYTHMDB (data));
390
g_free (ctxt.mount_point);
395
rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor *monitor,
396
GnomeVFSVolume *volume,
401
ctxt.db = RHYTHMDB (data);
402
ctxt.mount_point = gnome_vfs_volume_get_activation_uri (volume);
403
ctxt.mounted = FALSE;
404
rb_debug ("volume %s unmounted", ctxt.mount_point);
405
rhythmdb_entry_foreach (RHYTHMDB (data),
406
(GFunc)entry_volume_mounted_or_unmounted,
408
rhythmdb_commit (RHYTHMDB (data));
409
g_free (ctxt.mount_point);