[rhythmbox] rhythmdb: use metadata caches (via the entry type) during import
- From: Jonathan Matthew <jmatthew src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rhythmbox] rhythmdb: use metadata caches (via the entry type) during import
- Date: Tue, 18 Aug 2015 13:03:59 +0000 (UTC)
commit 3d34053382f539a69e0f73fa4b0d63807a16e4e2
Author: Jonathan Matthew <jonathan d14n org>
Date: Tue Aug 18 22:36:37 2015 +1000
rhythmdb: use metadata caches (via the entry type) during import
After reading metadata from a file, store it in the cache. Ignore entries
may be cached too, since rereading them can take a significant amount of time.
After statting a file, attempt to load its metadata from the cache (if the
entry type has one); if metadata is found, compare the file size and
modification time with the stat results and discard on mismatches.
Ignore entries read from the cache are identified by their media type.
application/octet-stream will never be a valid audio media type, so it must
indicate an ignore entry, as errors are not cached.
rhythmdb/rhythmdb-private.h | 2 +
rhythmdb/rhythmdb.c | 254 +++++++++++++++++++++++++++++++++----------
2 files changed, 197 insertions(+), 59 deletions(-)
---
diff --git a/rhythmdb/rhythmdb-private.h b/rhythmdb/rhythmdb-private.h
index a8abc58..579f785 100644
--- a/rhythmdb/rhythmdb-private.h
+++ b/rhythmdb/rhythmdb-private.h
@@ -201,6 +201,7 @@ typedef struct
enum {
RHYTHMDB_EVENT_STAT,
RHYTHMDB_EVENT_METADATA_LOAD,
+ RHYTHMDB_EVENT_METADATA_CACHE,
RHYTHMDB_EVENT_DB_LOAD,
RHYTHMDB_EVENT_THREAD_EXITED,
RHYTHMDB_EVENT_DB_SAVED,
@@ -219,6 +220,7 @@ typedef struct
/* STAT */
GFileInfo *file_info;
/* LOAD */
+ GArray cached_metadata;
RBMetaData *metadata;
/* QUERY_COMPLETE */
RhythmDBQueryResults *results;
diff --git a/rhythmdb/rhythmdb.c b/rhythmdb/rhythmdb.c
index 98c8da1..9537c55 100644
--- a/rhythmdb/rhythmdb.c
+++ b/rhythmdb/rhythmdb.c
@@ -965,6 +965,20 @@ rhythmdb_action_free (RhythmDB *db,
}
static void
+free_cached_metadata (GArray *metadata)
+{
+ RhythmDBEntryChange *fields = (RhythmDBEntryChange *)metadata->data;
+ int i;
+
+ for (i = 0; i < metadata->len; i++) {
+ g_value_unset (&fields[i].new);
+ }
+ g_free (fields);
+ metadata->data = NULL;
+ metadata->len = 0;
+}
+
+static void
rhythmdb_event_free (RhythmDB *db,
RhythmDBEvent *result)
{
@@ -984,6 +998,9 @@ rhythmdb_event_free (RhythmDB *db,
case RHYTHMDB_EVENT_ENTRY_SET:
g_value_unset (&result->change.new);
break;
+ case RHYTHMDB_EVENT_METADATA_CACHE:
+ free_cached_metadata(&result->cached_metadata);
+ break;
}
if (result->error)
g_error_free (result->error);
@@ -2231,14 +2248,61 @@ rhythmdb_process_stat_event (RhythmDB *db,
rhythmdb_commit (db);
}
-typedef struct
+static RhythmDBEntry *
+create_blank_entry (RhythmDB *db, RhythmDBEvent *event)
{
- RhythmDB *db;
- char *uri;
- char *msg;
-} RhythmDBLoadErrorData;
+ RhythmDBEntry *entry;
+ GTimeVal time;
+ GValue value = {0,};
+
+ entry = rhythmdb_entry_new (db, event->entry_type, rb_refstring_get (event->real_uri));
+ if (entry == NULL) {
+ rb_debug ("entry already exists");
+ return NULL;
+ }
+
+ /* initialize the last played date to 0=never */
+ g_value_init (&value, G_TYPE_ULONG);
+ g_value_set_ulong (&value, 0);
+ rhythmdb_entry_set (db, entry,
+ RHYTHMDB_PROP_LAST_PLAYED, &value);
+ g_value_unset (&value);
+
+ /* initialize the rating */
+ g_value_init (&value, G_TYPE_DOUBLE);
+ g_value_set_double (&value, 0);
+ rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_RATING, &value);
+ g_value_unset (&value);
+
+ /* first seen */
+ g_get_current_time (&time);
+ g_value_init (&value, G_TYPE_ULONG);
+ g_value_set_ulong (&value, time.tv_sec);
+ rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_FIRST_SEEN, &value);
+ g_value_unset (&value);
+
+ return entry;
+}
static void
+apply_mtime (RhythmDB *db, RhythmDBEntry *entry, GFileInfo *file_info)
+{
+ guint64 mtime;
+ GValue value = {0,};
+
+ if (file_info == NULL) {
+ return;
+ }
+
+ mtime = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+ g_value_init (&value, G_TYPE_ULONG);
+ g_value_set_ulong (&value, (gulong)mtime);
+ rhythmdb_entry_set_internal (db, entry, TRUE, RHYTHMDB_PROP_MTIME, &value);
+ g_value_unset (&value);
+}
+
+
+static RhythmDBEntry *
rhythmdb_add_import_error_entry (RhythmDB *db,
RhythmDBEvent *event,
RhythmDBEntryType *error_entry_type)
@@ -2248,7 +2312,7 @@ rhythmdb_add_import_error_entry (RhythmDB *db,
if (error_entry_type == NULL) {
/* we don't have an error entry type, so we can't add an import error */
- return;
+ return NULL;
}
rb_debug ("adding import error type %s for %s: %s",
rhythmdb_entry_type_get_name (error_entry_type),
@@ -2262,7 +2326,7 @@ rhythmdb_add_import_error_entry (RhythmDB *db,
entry_type != event->ignore_type) {
/* FIXME we've successfully read this file before.. so what should we do? */
rb_debug ("%s already exists in the library.. ignoring import error?",
rb_refstring_get (event->real_uri));
- return;
+ return NULL;
}
if (entry_type != error_entry_type) {
@@ -2279,13 +2343,8 @@ rhythmdb_add_import_error_entry (RhythmDB *db,
/* no need to update the ignored file entry */
}
- if (entry && event->file_info) {
- /* mtime */
- guint64 new_mtime = g_file_info_get_attribute_uint64 (event->file_info,
G_FILE_ATTRIBUTE_TIME_MODIFIED);
- g_value_init (&value, G_TYPE_ULONG);
- g_value_set_ulong (&value, new_mtime); /* hmm, cast */
- rhythmdb_entry_set(db, entry, RHYTHMDB_PROP_MTIME, &value);
- g_value_unset (&value);
+ if (entry) {
+ apply_mtime (db, entry, event->file_info);
}
rhythmdb_add_timeout_commit (db, FALSE);
@@ -2295,7 +2354,7 @@ rhythmdb_add_import_error_entry (RhythmDB *db,
/* create a new import error or ignore entry */
entry = rhythmdb_entry_new (db, error_entry_type, rb_refstring_get (event->real_uri));
if (entry == NULL)
- return;
+ return NULL;
/* if we have missing plugin details, store them in the
* comment field so we can collect them later, and set a
@@ -2364,24 +2423,76 @@ rhythmdb_add_import_error_entry (RhythmDB *db,
rhythmdb_add_timeout_commit (db, FALSE);
}
+
+ return entry;
+}
+
+static gboolean
+rhythmdb_process_metadata_cache (RhythmDB *db, RhythmDBEvent *event)
+{
+ RhythmDBEntry *entry;
+ RhythmDBEntryChange *fields;
+ gboolean monitor;
+ int i;
+
+ fields = (RhythmDBEntryChange *)event->cached_metadata.data;
+ for (i = 0; i < event->cached_metadata.len; i++) {
+ if (fields[i].prop == RHYTHMDB_PROP_MEDIA_TYPE) {
+ const char *media_type;
+ media_type = g_value_get_string (&fields[i].new);
+ /* if no media type is set, it's an ignore entry */
+ if (g_strcmp0 (media_type, "application/octet-stream") == 0) {
+ rhythmdb_add_import_error_entry (db, event, event->ignore_type);
+ return TRUE;
+ }
+ break;
+ }
+ }
+
+ entry = rhythmdb_entry_lookup_by_location_refstring (db, event->real_uri);
+ if (entry == NULL) {
+ entry = create_blank_entry (db, event);
+ if (entry == NULL) {
+ return TRUE;
+ }
+ }
+
+ apply_mtime (db, entry, event->file_info);
+
+ rhythmdb_entry_apply_cached_metadata (entry, &event->cached_metadata);
+
+ rhythmdb_entry_update_availability (entry, RHYTHMDB_ENTRY_AVAIL_CHECKED);
+
+ /* Remember the mount point of the volume the song is on */
+ rhythmdb_entry_set_mount_point (db, entry, rb_refstring_get (event->real_uri));
+
+ /* monitor the file for changes */
+ /* FIXME: watch for errors */
+ monitor = g_settings_get_boolean (db->priv->settings, "monitor-library");
+ if (monitor && event->entry_type == RHYTHMDB_ENTRY_TYPE_SONG)
+ rhythmdb_monitor_uri_path (db, rb_refstring_get (entry->location), NULL);
+
+ rhythmdb_commit_internal (db, FALSE, g_thread_self ());
+
+ return TRUE;
}
static gboolean
rhythmdb_process_metadata_load (RhythmDB *db, RhythmDBEvent *event)
{
RhythmDBEntry *entry;
- GValue value = {0,};
GTimeVal time;
gboolean monitor;
+ entry = NULL;
+
if (event->entry_type == NULL)
event->entry_type = RHYTHMDB_ENTRY_TYPE_SONG;
if (event->metadata != NULL) {
/* always ignore anything with video in it */
if (rb_metadata_has_video (event->metadata)) {
- rhythmdb_add_import_error_entry (db, event, event->ignore_type);
- return TRUE;
+ entry = rhythmdb_add_import_error_entry (db, event, event->ignore_type);
}
/* if we identified the media type, we can ignore anything
@@ -2389,13 +2500,17 @@ rhythmdb_process_metadata_load (RhythmDB *db, RhythmDBEvent *event)
* as well as anything that doesn't contain audio.
*/
const char *media_type = rb_metadata_get_media_type (event->metadata);
- if (media_type != NULL && media_type[0] != '\0') {
+ if (entry == NULL && media_type != NULL && media_type[0] != '\0') {
if (rhythmdb_ignore_media_type (media_type) ||
rb_metadata_has_audio (event->metadata) == FALSE) {
- rhythmdb_add_import_error_entry (db, event, event->ignore_type);
- return TRUE;
+ entry = rhythmdb_add_import_error_entry (db, event, event->ignore_type);
}
}
+
+ if (entry != NULL) {
+ rhythmdb_entry_cache_metadata (entry);
+ return TRUE;
+ }
}
/* also ignore really small files we can't identify */
@@ -2412,7 +2527,8 @@ rhythmdb_process_metadata_load (RhythmDB *db, RhythmDBEvent *event)
RB_METADATA_ERROR_EMPTY_FILE,
_("Empty file"));
} else if (file_size < REALLY_SMALL_FILE_SIZE) {
- rhythmdb_add_import_error_entry (db, event, event->ignore_type);
+ entry = rhythmdb_add_import_error_entry (db, event, event->ignore_type);
+ rhythmdb_entry_cache_metadata (entry);
return TRUE;
}
}
@@ -2438,31 +2554,11 @@ rhythmdb_process_metadata_load (RhythmDB *db, RhythmDBEvent *event)
}
if (entry == NULL) {
-
- entry = rhythmdb_entry_new (db, event->entry_type, rb_refstring_get (event->real_uri));
+ entry = create_blank_entry (db, event);
if (entry == NULL) {
rb_debug ("entry already exists");
return TRUE;
}
-
- /* initialize the last played date to 0=never */
- g_value_init (&value, G_TYPE_ULONG);
- g_value_set_ulong (&value, 0);
- rhythmdb_entry_set (db, entry,
- RHYTHMDB_PROP_LAST_PLAYED, &value);
- g_value_unset (&value);
-
- /* initialize the rating */
- g_value_init (&value, G_TYPE_DOUBLE);
- g_value_set_double (&value, 0);
- rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_RATING, &value);
- g_value_unset (&value);
-
- /* first seen */
- g_value_init (&value, G_TYPE_ULONG);
- g_value_set_ulong (&value, time.tv_sec);
- rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_FIRST_SEEN, &value);
- g_value_unset (&value);
}
if ((event->entry_type != NULL) && (entry->type != event->entry_type)) {
@@ -2470,23 +2566,15 @@ rhythmdb_process_metadata_load (RhythmDB *db, RhythmDBEvent *event)
return TRUE;
}
- /* mtime */
- if (event->file_info) {
- guint64 mtime;
-
- mtime = g_file_info_get_attribute_uint64 (event->file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
-
- g_value_init (&value, G_TYPE_ULONG);
- g_value_set_ulong (&value, (gulong)mtime);
- rhythmdb_entry_set_internal (db, entry, TRUE, RHYTHMDB_PROP_MTIME, &value);
- g_value_unset (&value);
- }
+ apply_mtime (db, entry, event->file_info);
if (event->entry_type != event->ignore_type &&
event->entry_type != event->error_type) {
set_props_from_metadata (db, entry, event->file_info, event->metadata);
}
+ rhythmdb_entry_cache_metadata (entry);
+
rhythmdb_entry_update_availability (entry, RHYTHMDB_ENTRY_AVAIL_CHECKED);
/* Remember the mount point of the volume the song is on */
@@ -2532,6 +2620,7 @@ rhythmdb_process_one_event (RhythmDBEvent *event, RhythmDB *db)
if (rhythmdb_get_readonly (db) &&
((event->type == RHYTHMDB_EVENT_STAT)
|| (event->type == RHYTHMDB_EVENT_METADATA_LOAD)
+ || (event->type == RHYTHMDB_EVENT_METADATA_CACHE)
|| (event->type == RHYTHMDB_EVENT_ENTRY_SET))) {
rb_debug ("Database is read-only, delaying event processing");
g_async_queue_push (db->priv->delayed_write_queue, event);
@@ -2547,6 +2636,10 @@ rhythmdb_process_one_event (RhythmDBEvent *event, RhythmDB *db)
rb_debug ("processing RHYTHMDB_EVENT_METADATA_LOAD");
free = rhythmdb_process_metadata_load (db, event);
break;
+ case RHYTHMDB_EVENT_METADATA_CACHE:
+ rb_debug ("processing RHTHMDB_EVENT_METADATA_CACHE");
+ free = rhythmdb_process_metadata_cache (db, event);
+ break;
case RHYTHMDB_EVENT_ENTRY_SET:
rb_debug ("processing RHYTHMDB_EVENT_ENTRY_SET");
rhythmdb_process_queued_entry_set_event (db, event);
@@ -2714,11 +2807,54 @@ rhythmdb_execute_load (RhythmDB *db,
g_object_unref (event->file_info);
event->file_info = NULL;
}
- } else if (event->type == RHYTHMDB_EVENT_METADATA_LOAD) {
- event->metadata = rb_metadata_new ();
- rb_metadata_load (event->metadata,
- rb_refstring_get (event->real_uri),
- &event->error);
+ } else {
+ gboolean valid;
+
+ valid = FALSE;
+ if (rhythmdb_entry_type_fetch_metadata (event->entry_type, uri, &event->cached_metadata)) {
+ RhythmDBEntryChange *fields = (RhythmDBEntryChange *)event->cached_metadata.data;
+ guint64 new_filesize;
+ guint64 new_mtime;
+ int i;
+
+ valid = TRUE;
+ new_filesize = g_file_info_get_attribute_uint64 (event->file_info,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE);
+ new_mtime = g_file_info_get_attribute_uint64 (event->file_info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED);
+ for (i = 0; i < event->cached_metadata.len; i++) {
+ switch (fields[i].prop) {
+ case RHYTHMDB_PROP_MTIME:
+ if (new_mtime != g_value_get_ulong (&fields[i].new)) {
+ rb_debug ("mtime mismatch, ignoring cached metadata");
+ valid = FALSE;
+ }
+ break;
+ case RHYTHMDB_PROP_FILE_SIZE:
+ if (new_filesize != g_value_get_uint64 (&fields[i].new)) {
+ rb_debug ("size mismatch, ignoring cached metadata");
+ valid = FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (valid) {
+ event->type = RHYTHMDB_EVENT_METADATA_CACHE;
+ rb_debug ("got valid cached metadata");
+ } else {
+ free_cached_metadata(&event->cached_metadata);
+ }
+ }
+
+ if (valid == FALSE) {
+ event->metadata = rb_metadata_new ();
+ rb_metadata_load (event->metadata,
+ rb_refstring_get (event->real_uri),
+ &event->error);
+ }
}
rhythmdb_push_event (db, event);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]