~ubuntu-branches/ubuntu/precise/rhythmbox/precise-201203091205

« back to all changes in this revision

Viewing changes to rhythmdb/rhythmdb-monitor.c

Tags: upstream-0.9.5
ImportĀ upstreamĀ versionĀ 0.9.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 
2
 *
 
3
 *  Copyright (C) 2003,2004 Colin Walters <walters@gnome.org>
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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.
 
18
 *
 
19
 */
 
20
 
 
21
#include <config.h>
 
22
 
 
23
#include <string.h>
 
24
 
 
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>
 
30
 
 
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"
 
37
 
 
38
 
 
39
#define RHYTHMDB_FILE_MODIFY_PROCESS_TIME 2
 
40
 
 
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);
 
47
 
 
48
void
 
49
rhythmdb_init_monitoring (RhythmDB *db)
 
50
{
 
51
        db->priv->monitored_directories = g_hash_table_new_full (g_str_hash, g_str_equal,
 
52
                                                                 (GDestroyNotify) g_free,
 
53
                                                                 NULL);
 
54
 
 
55
        db->priv->changed_files = g_hash_table_new_full (g_str_hash, g_str_equal,
 
56
                                                         (GDestroyNotify) g_free,
 
57
                                                         NULL);
 
58
 
 
59
        g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()), 
 
60
                          "volume-mounted", 
 
61
                          G_CALLBACK (rhythmdb_volume_mounted_cb), 
 
62
                          db);
 
63
 
 
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);
 
72
}
 
73
 
 
74
void
 
75
rhythmdb_finalize_monitoring (RhythmDB *db)
 
76
{
 
77
        rhythmdb_stop_monitoring (db);
 
78
        
 
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);
 
83
}
 
84
 
 
85
static gboolean
 
86
rhythmdb_unmonitor_directories (char *dir, GnomeVFSMonitorHandle *handle, RhythmDB *db)
 
87
{
 
88
        gnome_vfs_monitor_cancel (handle);
 
89
        return TRUE;
 
90
}
 
91
 
 
92
void
 
93
rhythmdb_stop_monitoring (RhythmDB *db)
 
94
{
 
95
        g_hash_table_foreach_remove (db->priv->monitored_directories,
 
96
                                     (GHRFunc) rhythmdb_unmonitor_directories,
 
97
                                     db);
 
98
}
 
99
 
 
100
static void
 
101
monitor_entry_file (RhythmDBEntry *entry, RhythmDB *db)
 
102
{
 
103
        GError *error = NULL;
 
104
 
 
105
        if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
 
106
                rhythmdb_monitor_uri_path (db, entry->location, &error);
 
107
        }
 
108
 
 
109
        if (error) {
 
110
                /* FIXME: should we complain to the user? */
 
111
                rb_debug ("error while attempting to monitor library track: %s", error->message);
 
112
        } 
 
113
}
 
114
 
 
115
static void
 
116
monitor_subdirectory (const char *uri, RhythmDB *db)
 
117
{
 
118
        GError *error = NULL;
 
119
 
 
120
        if (!rb_uri_is_directory (uri))
 
121
                return;
 
122
 
 
123
        rhythmdb_monitor_uri_path (db, uri, &error);
 
124
 
 
125
        if (error) {
 
126
                /* FIXME: should we complain to the user? */
 
127
                rb_debug ("error while attempting to monitor the library directory: %s", error->message);
 
128
        }
 
129
}
 
130
 
 
131
static void
 
132
monitor_library_directory (const char *uri, RhythmDB *db)
 
133
{
 
134
        GError *error = NULL;
 
135
 
 
136
        if ((strcmp (uri, "file:///") == 0) ||
 
137
            (strcmp (uri, "file://") == 0)) {
 
138
                /* display an error to the user? */
 
139
                return;
 
140
        }
 
141
        
 
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);
 
145
 
 
146
        if (error) {
 
147
                /* FIXME: should we complain to the user? */
 
148
                rb_debug ("error while attempting to monitor the library directory: %s", error->message);
 
149
        }
 
150
 
 
151
        rb_debug ("loading new tracks from library directory %s", uri);
 
152
        rhythmdb_add_uri (db, uri);
 
153
}
 
154
 
 
155
static gboolean
 
156
rhythmdb_check_changed_file (const char *uri, gpointer data, RhythmDB *db)
 
157
{
 
158
        GTimeVal time;
 
159
        glong time_sec = GPOINTER_TO_INT (data);
 
160
 
 
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);
 
165
                event->db = db;
 
166
                event->type = RHYTHMDB_EVENT_FILE_CREATED_OR_MODIFIED;
 
167
                event->uri = g_strdup (uri);
 
168
                
 
169
                g_async_queue_push (db->priv->event_queue, event);
 
170
                rb_debug ("adding newly located file %s", uri);
 
171
                return TRUE;
 
172
        }
 
173
        
 
174
        rb_debug ("waiting to add newly located file %s", uri);
 
175
        
 
176
        return FALSE;
 
177
}
 
178
 
 
179
static gboolean
 
180
rhythmdb_process_changed_files (RhythmDB *db)
 
181
{
 
182
        g_hash_table_foreach_remove (db->priv->changed_files,
 
183
                                     (GHRFunc)rhythmdb_check_changed_file, db);
 
184
        return TRUE;
 
185
}
 
186
 
 
187
 
 
188
void
 
189
rhythmdb_start_monitoring (RhythmDB *db)
 
190
{
 
191
        db->priv->changed_files_id = g_timeout_add (RHYTHMDB_FILE_MODIFY_PROCESS_TIME * 1000,
 
192
                                                    (GSourceFunc) rhythmdb_process_changed_files, db);
 
193
 
 
194
        if (!db->priv->library_locations) {
 
195
                g_slist_foreach (db->priv->library_locations, (GFunc) monitor_library_directory, db);
 
196
        }
 
197
                
 
198
        /* monitor every directory that contains a (TYPE_SONG) track */
 
199
        rhythmdb_entry_foreach (db, (GFunc) monitor_entry_file, db);
 
200
}
 
201
 
 
202
static void
 
203
rhythmdb_directory_change_cb (GnomeVFSMonitorHandle *handle,
 
204
                              const char *monitor_uri,
 
205
                              const char *info_uri,
 
206
                              GnomeVFSMonitorEventType vfsevent,
 
207
                              RhythmDB *db)
 
208
{
 
209
        rb_debug ("directory event %d for %s: %s", (int) vfsevent,
 
210
                  monitor_uri, info_uri);
 
211
 
 
212
        switch (vfsevent) {
 
213
        case GNOME_VFS_MONITOR_EVENT_CREATED:
 
214
                {
 
215
                        GSList *cur;
 
216
                        gboolean in_library = FALSE;
 
217
                        
 
218
                        if (!eel_gconf_get_boolean (CONF_MONITOR_LIBRARY))
 
219
                                return;
 
220
 
 
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)) {
 
224
                                        in_library = TRUE;
 
225
                                        break;
 
226
                                }
 
227
                        }
 
228
                
 
229
                        if (!in_library)
 
230
                                return; 
 
231
                }
 
232
                
 
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);
 
237
                        return;
 
238
                }
 
239
                /* fall through*/
 
240
        case GNOME_VFS_MONITOR_EVENT_CHANGED:
 
241
        case GNOME_VFS_MONITOR_EVENT_METADATA_CHANGED:
 
242
                {
 
243
                        GTimeVal time;
 
244
 
 
245
                        g_get_current_time (&time);
 
246
                        g_hash_table_replace (db->priv->changed_files,
 
247
                                              g_strdup (info_uri),
 
248
                                              GINT_TO_POINTER (time.tv_sec));
 
249
                }
 
250
                break;
 
251
        case GNOME_VFS_MONITOR_EVENT_DELETED:
 
252
                {
 
253
                        RhythmDBEvent *event = g_new0 (RhythmDBEvent, 1);
 
254
                        event->db = db;
 
255
                        event->type = RHYTHMDB_EVENT_FILE_DELETED;
 
256
                        event->uri = g_strdup (info_uri);
 
257
                        g_async_queue_push (db->priv->event_queue, event);
 
258
                }
 
259
                break;
 
260
        case GNOME_VFS_MONITOR_EVENT_STARTEXECUTING:
 
261
        case GNOME_VFS_MONITOR_EVENT_STOPEXECUTING:
 
262
                break;
 
263
        }
 
264
}
 
265
 
 
266
void
 
267
rhythmdb_monitor_uri_path (RhythmDB *db, const char *uri, GError **error)
 
268
{
 
269
        char *directory;
 
270
        GnomeVFSResult vfsresult;
 
271
        GnomeVFSMonitorHandle **handle;
 
272
 
 
273
        if (rb_uri_is_directory (uri)) {
 
274
                if (g_str_has_suffix(uri, G_DIR_SEPARATOR_S)) {
 
275
                        directory = g_strdup (uri);
 
276
                } else {
 
277
                        directory = g_strconcat (uri, G_DIR_SEPARATOR_S, NULL);
 
278
                }
 
279
        } else {
 
280
                GnomeVFSURI *vfsuri, *parent;
 
281
                
 
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);
 
287
        }
 
288
 
 
289
        if (directory == NULL || g_hash_table_lookup (db->priv->monitored_directories, directory))
 
290
                return;
 
291
 
 
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,
 
296
                                           db);
 
297
        if (vfsresult == GNOME_VFS_OK) {
 
298
                rb_debug ("monitoring: %s", directory);
 
299
                g_hash_table_insert (db->priv->monitored_directories,
 
300
                                     directory, *handle);
 
301
        } else {
 
302
                g_set_error (error,
 
303
                             RHYTHMDB_ERROR,
 
304
                             RHYTHMDB_ERROR_ACCESS_FAILED,
 
305
                             _("Couldn't monitor %s: %s"),
 
306
                             directory,
 
307
                             gnome_vfs_result_to_string (vfsresult));
 
308
                rb_debug ("failed to monitor %s", directory);
 
309
                g_free (directory);
 
310
                g_free (handle);
 
311
        }
 
312
}
 
313
 
 
314
 
 
315
typedef struct
 
316
{
 
317
        RhythmDB *db;
 
318
        char *mount_point;
 
319
        gboolean mounted;
 
320
} MountCtxt;
 
321
 
 
322
static void 
 
323
entry_volume_mounted_or_unmounted (RhythmDBEntry *entry, 
 
324
                                   MountCtxt *ctxt)
 
325
{
 
326
        const char *mount_point;
 
327
        const char *location;
 
328
        
 
329
        if (entry->type != RHYTHMDB_ENTRY_TYPE_SONG &&
 
330
            entry->type != RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR) {
 
331
                return;
 
332
        }
 
333
        
 
334
        mount_point = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MOUNTPOINT);
 
335
        if (mount_point == NULL || strcmp (mount_point, ctxt->mount_point) != 0) {
 
336
                return;
 
337
        }
 
338
        location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
 
339
 
 
340
        if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
 
341
                if (ctxt->mounted) {
 
342
                        rb_debug ("queueing stat for entry %s (mounted)", location);
 
343
 
 
344
                        /* make files visible immediately, 
 
345
                         * then hide any that turn out to be missing.
 
346
                         */
 
347
                        rhythmdb_entry_set_visibility (ctxt->db, entry, TRUE);
 
348
                        queue_stat_uri (location, 
 
349
                                        ctxt->db,
 
350
                                        RHYTHMDB_ENTRY_TYPE_SONG);
 
351
                } else {
 
352
                        GTimeVal time;
 
353
                        GValue val = {0, };
 
354
 
 
355
                        rb_debug ("hiding entry %s (unmounted)", location);
 
356
                        
 
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);
 
363
 
 
364
                        rhythmdb_entry_set_visibility (ctxt->db, entry, FALSE);
 
365
                }
 
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);
 
371
                }
 
372
        }
 
373
}
 
374
 
 
375
 
 
376
static void 
 
377
rhythmdb_volume_mounted_cb (GnomeVFSVolumeMonitor *monitor,
 
378
                            GnomeVFSVolume *volume, 
 
379
                            gpointer data)
 
380
{
 
381
        MountCtxt ctxt;
 
382
 
 
383
        ctxt.db = RHYTHMDB (data);
 
384
        ctxt.mount_point = gnome_vfs_volume_get_activation_uri (volume);
 
385
        ctxt.mounted = TRUE;
 
386
        rhythmdb_entry_foreach (RHYTHMDB (data), 
 
387
                                (GFunc)entry_volume_mounted_or_unmounted, 
 
388
                                &ctxt);
 
389
        rhythmdb_commit (RHYTHMDB (data));
 
390
        g_free (ctxt.mount_point);
 
391
}
 
392
 
 
393
 
 
394
static void 
 
395
rhythmdb_volume_unmounted_cb (GnomeVFSVolumeMonitor *monitor,
 
396
                              GnomeVFSVolume *volume, 
 
397
                              gpointer data)
 
398
{
 
399
        MountCtxt ctxt;
 
400
 
 
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, 
 
407
                                &ctxt);
 
408
        rhythmdb_commit (RHYTHMDB (data));
 
409
        g_free (ctxt.mount_point);
 
410
}
 
411