[rhythmbox] ext-db: rethink how keys work



commit 0c31ced64c1e7daba3716768ee0bb349c161c1b7
Author: Jonathan Matthew <jonathan d14n org>
Date:   Mon Jan 2 22:05:12 2012 +1000

    ext-db: rethink how keys work
    
    Keys now consist of fields, only one of which can be multi-valued,
    and info.  This removes the annoying confusion between artist and
    album-artist.

 metadata/rb-ext-db-key.c                           |  438 ++++++++++++++------
 metadata/rb-ext-db-key.h                           |   29 +-
 metadata/rb-ext-db.c                               |   10 +-
 plugins/artsearch/artsearch.py                     |   26 +--
 plugins/artsearch/lastfm.py                        |   42 +-
 plugins/artsearch/local.py                         |   35 +-
 plugins/artsearch/musicbrainz.py                   |   23 +-
 plugins/artsearch/oldcache.py                      |   24 +-
 .../rb-audioscrobbler-radio-source.c               |    4 +-
 plugins/grilo/rb-grilo-plugin.c                    |    7 +-
 plugins/ipod/rb-ipod-source.c                      |    6 +-
 plugins/magnatune/MagnatuneSource.py               |    4 +-
 plugins/mtpdevice/rb-mtp-source.c                  |    7 +-
 podcast/rb-podcast-manager.c                       |    2 +-
 rhythmdb/rhythmdb.c                                |   86 ++---
 shell/rb-shell-player.c                            |    4 +-
 16 files changed, 434 insertions(+), 313 deletions(-)
---
diff --git a/metadata/rb-ext-db-key.c b/metadata/rb-ext-db-key.c
index 717599e..94555be 100644
--- a/metadata/rb-ext-db-key.c
+++ b/metadata/rb-ext-db-key.c
@@ -47,20 +47,23 @@
 typedef struct
 {
 	char *name;
-	char *value;
-	RBExtDBFieldType type;
+	GPtrArray *values;
+	gboolean match_null;
 } RBExtDBField;
 
 struct _RBExtDBKey
 {
+	gboolean lookup;
+	RBExtDBField *multi_field;
 	GList *fields;
+	GList *info;
 };
 
 static void
 rb_ext_db_field_free (RBExtDBField *field)
 {
 	g_free (field->name);
-	g_free (field->value);
+	g_ptr_array_free (field->values, TRUE);
 	g_slice_free (RBExtDBField, field);
 }
 
@@ -68,10 +71,14 @@ static RBExtDBField *
 rb_ext_db_field_copy (RBExtDBField *field)
 {
 	RBExtDBField *copy;
+	int i;
+
 	copy = g_slice_new0 (RBExtDBField);
 	copy->name = g_strdup (field->name);
-	copy->value = g_strdup (field->value);
-	copy->type = field->type;
+	copy->values = g_ptr_array_new_with_free_func (g_free);
+	for (i = 0; i < field->values->len; i++) {
+		g_ptr_array_add (copy->values, g_strdup (g_ptr_array_index (field->values, i)));
+	}
 	return copy;
 }
 
@@ -104,9 +111,14 @@ rb_ext_db_key_copy (RBExtDBKey *key)
 	GList *l;
 
 	copy = g_slice_new0 (RBExtDBKey);
+	copy->lookup = key->lookup;
+	copy->multi_field = key->multi_field;
 	for (l = key->fields; l != NULL; l = l->next) {
 		copy->fields = g_list_append (copy->fields, rb_ext_db_field_copy (l->data));
 	}
+	for (l = key->info; l != NULL; l = l->next) {
+		copy->info = g_list_append (copy->info, rb_ext_db_field_copy (l->data));
+	}
 	return copy;
 }
 
@@ -120,53 +132,152 @@ void
 rb_ext_db_key_free (RBExtDBKey *key)
 {
 	g_list_free_full (key->fields, (GDestroyNotify) rb_ext_db_field_free);
+	g_list_free_full (key->info, (GDestroyNotify) rb_ext_db_field_free);
 	g_slice_free (RBExtDBKey, key);
 }
 
+static RBExtDBKey *
+do_create (const char *field, const char *value, gboolean lookup)
+{
+	RBExtDBKey *key;
+	key = g_slice_new0 (RBExtDBKey);
+	key->lookup = lookup;
+	rb_ext_db_key_add_field (key, field, value);
+	return key;
+}
+
 /**
- * rb_ext_db_key_create:
+ * rb_ext_db_key_create_lookup:
  * @field: required field name
  * @value: value for field
  *
- * Creates a new metadata lookup key with a single required field.
+ * Creates a new metadata lookup key with a single field.
  * Use @rb_ext_db_key_add_field to add more.
  *
  * Return value: the new key
  */
 RBExtDBKey *
-rb_ext_db_key_create (const char *field, const char *value)
+rb_ext_db_key_create_lookup (const char *field, const char *value)
 {
-	RBExtDBKey *key;
+	return do_create (field, value, TRUE);
+}
 
-	key = g_slice_new0 (RBExtDBKey);
-	rb_ext_db_key_add_field (key, field, RB_EXT_DB_FIELD_REQUIRED, value);
+/**
+ * rb_ext_db_key_create_storage:
+ * @field: required field name
+ * @value: value for field
+ *
+ * Creates a new metadata storage key with a single field.
+ * Use @rb_ext_db_key_add_field to add more.
+ *
+ * Return value: the new key
+ */
+RBExtDBKey *
+rb_ext_db_key_create_storage (const char *field, const char *value)
+{
+	return do_create (field, value, FALSE);
+}
 
-	return key;
+/**
+ * rb_ext_db_key_is_lookup:
+ * @key: a #RBExtDBKey
+ *
+ * Returns %TRUE if the key is a lookup key
+ *
+ * Return value: whether the key is a lookup key
+ */
+gboolean
+rb_ext_db_key_is_lookup (RBExtDBKey *key)
+{
+	return key->lookup;
+}
+
+static void
+add_to_list (GList **list, RBExtDBField **multi, const char *name, const char *value)
+{
+	RBExtDBField *f;
+	GList *l;
+	int i;
+
+	for (l = *list; l != NULL; l = l->next) {
+		f = l->data;
+		if (strcmp (f->name, name) == 0) {
+			g_assert (multi != NULL);
+
+			if (value != NULL) {
+				for (i = 0; i < f->values->len; i++) {
+					if (strcmp (g_ptr_array_index (f->values, i), value) == 0) {
+						/* duplicate value */
+						return;
+					}
+				}
+				g_assert (*multi == NULL || *multi == f);
+				g_ptr_array_add (f->values, g_strdup (value));
+				*multi = f;
+			} else {
+				g_assert (*multi == NULL || *multi == f);
+				f->match_null = TRUE;
+				*multi = f;
+			}
+			return;
+		}
+	}
+
+	f = g_slice_new0 (RBExtDBField);
+	f->name = g_strdup (name);
+	f->values = g_ptr_array_new_with_free_func (g_free);
+	g_ptr_array_add (f->values, g_strdup (value));
+	*list = g_list_append (*list, f);
+}
+
+static char **
+get_list_names (GList *list)
+{
+	char **names;
+	GList *l;
+	int i;
+
+	names = g_new0 (char *, g_list_length (list) + 1);
+	i = 0;
+	for (l = list; l != NULL; l = l->next) {
+		RBExtDBField *f = l->data;
+		names[i++] = g_strdup (f->name);
+	}
+
+	return names;
 }
 
+static GPtrArray *
+get_list_values (GList *list, const char *field)
+{
+	RBExtDBField *f;
+	GList *l;
+
+	for (l = list; l != NULL; l = l->next) {
+		f = l->data;
+		if (strcmp (f->name, field) == 0) {
+			return f->values;
+		}
+	}
+
+	return NULL;
+}
+
+
 /**
  * rb_ext_db_key_add_field:
  * @key: a #RBExtDBKey
  * @field: name of the field to add
- * @field_type: field type (required, optional, or informational)
  * @value: field value
  *
- * Adds a field to the key.  Does not check that the field does not
- * already exist.
+ * Adds a field to the key, or an additional value to an existing field.
  */
 void
 rb_ext_db_key_add_field (RBExtDBKey *key,
 			 const char *field,
-			 RBExtDBFieldType field_type,
 			 const char *value)
 {
-	RBExtDBField *f;
-
-	f = g_slice_new0 (RBExtDBField);
-	f->name = g_strdup (field);
-	f->value = g_strdup (value);
-	f->type = field_type;
-	key->fields = g_list_append (key->fields, f);
+	add_to_list (&key->fields, &key->multi_field, field, value);
 }
 
 /**
@@ -181,18 +292,7 @@ rb_ext_db_key_add_field (RBExtDBKey *key,
 char **
 rb_ext_db_key_get_field_names (RBExtDBKey *key)
 {
-	char **names;
-	GList *l;
-	int i;
-
-	names = g_new0 (char *, g_list_length (key->fields) + 1);
-	i = 0;
-	for (l = key->fields; l != NULL; l = l->next) {
-		RBExtDBField *f = l->data;
-		names[i++] = g_strdup (f->name);
-	}
-
-	return names;
+	return get_list_names (key->fields);
 }
 
 /**
@@ -200,68 +300,124 @@ rb_ext_db_key_get_field_names (RBExtDBKey *key)
  * @key: a #RBExtDBKey
  * @field: field to retrieve
  *
- * Extracts the value for the specified field.
+ * Extracts the value for a single-valued field.
  *
  * Return value: field value, or NULL
  */
 const char *
 rb_ext_db_key_get_field (RBExtDBKey *key, const char *field)
 {
-	GList *l;
-
-	for (l = key->fields; l != NULL; l = l->next) {
-		RBExtDBField *f = l->data;
-		if (g_strcmp0 (field, f->name) == 0) {
-			return f->value;
-		}
+	GPtrArray *v = get_list_values (key->fields, field);
+	if (v != NULL && v->len > 0) {
+		return g_ptr_array_index (v, 0);
+	} else {
+		return NULL;
 	}
-
-	return NULL;
 }
 
+
 /**
- * rb_ext_db_key_get_field_type:
+ * rb_ext_db_key_get_field_values:
  * @key: a #RBExtDBKey
  * @field: field to retrieve
  *
- * Extracts the field type for the specified field.
+ * Extracts the values for the specified field.
  *
- * Return value: field type value
+ * Return value: (transfer full): field values, or NULL
  */
-RBExtDBFieldType
-rb_ext_db_key_get_field_type (RBExtDBKey *key, const char *field)
+char **
+rb_ext_db_key_get_field_values (RBExtDBKey *key, const char *field)
 {
-	GList *l;
+	GPtrArray *v = get_list_values (key->fields, field);
+	char **strv;
+	int i;
 
-	for (l = key->fields; l != NULL; l = l->next) {
-		RBExtDBField *f = l->data;
-		if (g_strcmp0 (field, f->name) == 0) {
-			return f->type;
-		}
+	if (v == NULL) {
+		return NULL;
 	}
 
-	/* check that the field exists before calling this */
-	return RB_EXT_DB_FIELD_INFORMATIONAL;
+	strv = g_new0 (char *, v->len + 1);
+	for (i = 0; i < v->len; i++) {
+		strv[i] = g_strdup (g_ptr_array_index (v, i));
+	}
+	return strv;
 }
 
-static gboolean
-match_field (RBExtDBKey *key, RBExtDBField *field)
+/**
+ * rb_ext_db_key_add_info:
+ * @key: a #RBExtDBKey
+ * @field: name of the field to add
+ * @value: field value
+ *
+ * Adds an information field to the key.
+ */
+void
+rb_ext_db_key_add_info (RBExtDBKey *key,
+			const char *field,
+			const char *value)
 {
-	const char *value;
+	add_to_list (&key->info, NULL, field, value);
+}
 
-	if (field->type == RB_EXT_DB_FIELD_INFORMATIONAL)
-		return TRUE;
+/**
+ * rb_ext_db_key_get_info_names:
+ * @key: a #RBExtDBKey
+ *
+ * Returns a NULL-terminated array containing the names of the info
+ * fields * present in the key.
+ *
+ * Return value: (transfer full): array of info field names
+ */
+char **
+rb_ext_db_key_get_info_names (RBExtDBKey *key)
+{
+	return get_list_names (key->info);
+}
 
-	value = rb_ext_db_key_get_field (key, field->name);
-	if (value == NULL) {
-		if (field->type == RB_EXT_DB_FIELD_REQUIRED)
-			return FALSE;
+/**
+ * rb_ext_db_key_get_info:
+ * @key: a #RBExtDBKey
+ * @name: info field to retrieve
+ *
+ * Extracts the value for the specified info field.
+ *
+ * Return value: field value, or NULL
+ */
+const char *
+rb_ext_db_key_get_info (RBExtDBKey *key, const char *name)
+{
+	GPtrArray *v = get_list_values (key->fields, name);
+	if (v != NULL && v->len > 0) {
+		return g_ptr_array_index (v, 0);
 	} else {
-		if (g_strcmp0 (value, field->value) != 0)
-			return FALSE;
+		return NULL;
 	}
+}
 
-	return TRUE;
+
+
+
+static gboolean
+match_field (RBExtDBKey *key, RBExtDBField *field)
+{
+	GPtrArray *values;
+	int i;
+	int j;
+
+	values = get_list_values (key->fields, field->name);
+	if (values == NULL) {
+		return field->match_null;
+	}
+
+	for (i = 0; i < field->values->len; i++) {
+		const char *a = g_ptr_array_index (field->values, i);
+		for (j = 0; j < values->len; j++) {
+			const char *b = g_ptr_array_index (values, j);
+			if (strcmp (a, b) == 0)
+				return TRUE;
+		}
+	}
+	return FALSE;
 }
 
 /**
@@ -298,46 +454,90 @@ rb_ext_db_key_matches (RBExtDBKey *a, RBExtDBKey *b)
 	return TRUE;
 }
 
-static void
-create_store_key (RBExtDBKey *key, guint optional_count, guint optional_fields, TDB_DATA *data)
+/**
+ * rb_ext_db_key_field_matches:
+ * @key: an #RBExtDBKey
+ * @field: a field to check
+ * @value: a value to match against
+ *
+ * Checks whether a specified field in @key matches a value.
+ * This can be used to match keys against other types of data.
+ * To match keys against each other, use @rb_ext_db_key_matches.
+ *
+ * Return value: %TRUE if the field matches the value
+ */
+gboolean
+rb_ext_db_key_field_matches (RBExtDBKey *key, const char *field, const char *value)
+{
+	GPtrArray *v;
+	int i;
+
+	v = get_list_values (key->fields, field);
+	if (v == NULL) {
+		/* if the key doesn't have this field, anything matches */
+		return TRUE;
+	}
+
+	if (value == NULL) {
+		if (key->multi_field == NULL) {
+			/* no multi field, so null can't match */
+			return FALSE;
+		}
+
+		if (g_strcmp0 (field, key->multi_field->name) == 0) {
+			/* this is the multi field, so null might match */
+			return key->multi_field->match_null;
+		} else {
+			/* this isn't the multi field, null can't match */
+			return FALSE;
+		}
+	}
+
+	for (i = 0; i < v->len; i++) {
+		if (strcmp (g_ptr_array_index (v, i), value) == 0) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static gboolean
+create_store_key (RBExtDBKey *key, int option, TDB_DATA *data)
 {
 	GByteArray *k;
 	GList *l;
-	int opt = 0;
 	guint8 nul = '\0';
 
+	if (key->multi_field != NULL &&
+	    option > key->multi_field->values->len &&
+	    key->multi_field->match_null == FALSE) {
+		return FALSE;
+	} else if (key->multi_field == NULL && option != 0) {
+		return FALSE;
+	}
+
 	k = g_byte_array_sized_new (512);
 	for (l = key->fields; l != NULL; l = l->next) {
 		RBExtDBField *f = l->data;
-		switch (f->type) {
-		case RB_EXT_DB_FIELD_OPTIONAL:
-			/* decide if we want to include this one */
-			if (optional_fields != G_MAXUINT) {
-				int bit = 1 << ((optional_count-1) - opt);
-				opt++;
-				if ((optional_fields & bit) == 0)
-					break;
-			}
-			/* fall through */
-		case RB_EXT_DB_FIELD_REQUIRED:
-			g_byte_array_append (k, (guint8 *)f->name, strlen (f->name));
-			g_byte_array_append (k, &nul, 1);
-			g_byte_array_append (k, (guint8 *)f->value, strlen (f->value));
-			g_byte_array_append (k, &nul, 1);
-			break;
-
-		case RB_EXT_DB_FIELD_INFORMATIONAL:
-			break;
-
-		default:
-			g_assert_not_reached ();
-			break;
+		const char *value;
+
+		if (f != key->multi_field) {
+			value = g_ptr_array_index (f->values, 0);
+		} else if (option < f->values->len) {
+			value = g_ptr_array_index (f->values, option);
+		} else {
+			continue;
 		}
-
+		g_byte_array_append (k, (guint8 *)f->name, strlen (f->name));
+		g_byte_array_append (k, &nul, 1);
+		g_byte_array_append (k, (guint8 *)value, strlen (value));
+		g_byte_array_append (k, &nul, 1);
 	}
 
 	data->dsize = k->len;
 	data->dptr = g_byte_array_free (k, FALSE);
+	return TRUE;
 }
 
 /**
@@ -358,28 +558,21 @@ rb_ext_db_key_lookups (RBExtDBKey *key,
 		       RBExtDBKeyLookupCallback callback,
 		       gpointer user_data)
 {
-	int optional_count = 0;
-	int optional_keys;
-	GList *l;
-
-	for (l = key->fields; l != NULL; l = l->next) {
-		RBExtDBField *field = l->data;
-		if (field->type == RB_EXT_DB_FIELD_OPTIONAL) {
-			optional_count++;
-		}
-	}
-
-	for (optional_keys = (1<<optional_count)-1; optional_keys >= 0; optional_keys--) {
+	int i = 0;
+	while (TRUE) {
 		TDB_DATA sk;
 		gboolean result;
 
-		create_store_key (key, optional_count, optional_keys, &sk);
+		if (create_store_key (key, i, &sk) == FALSE)
+			break;
 
 		result = callback (sk, user_data);
 		g_free (sk.dptr);
 
 		if (result == FALSE)
 			break;
+
+		i++;
 	}
 }
 
@@ -403,29 +596,6 @@ TDB_DATA
 rb_ext_db_key_to_store_key (RBExtDBKey *key)
 {
 	TDB_DATA k = {0,};
-	/* include all optional keys */
-	create_store_key (key, G_MAXUINT, G_MAXUINT, &k);
+	create_store_key (key, 0, &k);
 	return k;
 }
-
-
-
-#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
-
-GType
-rb_ext_db_field_type_get_type (void)
-{
-	static GType etype = 0;
-
-	if (etype == 0) {
-		static const GEnumValue values[] = {
-			ENUM_ENTRY(RB_EXT_DB_FIELD_REQUIRED, "required"),
-			ENUM_ENTRY(RB_EXT_DB_FIELD_OPTIONAL, "optional"),
-			ENUM_ENTRY(RB_EXT_DB_FIELD_INFORMATIONAL, "informational"),
-			{ 0, 0, 0 }
-		};
-		etype = g_enum_register_static ("RBExtDBFieldType", values);
-	}
-
-	return etype;
-}
diff --git a/metadata/rb-ext-db-key.h b/metadata/rb-ext-db-key.h
index 959007f..06b8545 100644
--- a/metadata/rb-ext-db-key.h
+++ b/metadata/rb-ext-db-key.h
@@ -37,16 +37,6 @@
 
 G_BEGIN_DECLS
 
-
-typedef enum {
-	RB_EXT_DB_FIELD_REQUIRED,		/* results must match this; stored */
-	RB_EXT_DB_FIELD_OPTIONAL,		/* results should match this; stored */
-	RB_EXT_DB_FIELD_INFORMATIONAL		/* may be used to find results, not stored */
-} RBExtDBFieldType;
-
-GType				   rb_ext_db_field_type_get_type (void);
-#define RB_TYPE_EXT_DB_FIELD_TYPE (rb_ext_db_field_type_get_type ())
-
 typedef struct _RBExtDBKey RBExtDBKey;
 struct _RBExtDBKey;
 
@@ -58,19 +48,30 @@ GType			rb_ext_db_key_get_type		(void);
 RBExtDBKey *		rb_ext_db_key_copy		(RBExtDBKey *key);
 void			rb_ext_db_key_free		(RBExtDBKey *key);
 
-RBExtDBKey *		rb_ext_db_key_create		(const char *field,
+RBExtDBKey *		rb_ext_db_key_create_lookup	(const char *field,
 							 const char *value);
+RBExtDBKey *		rb_ext_db_key_create_storage	(const char *field,
+							 const char *value);
+gboolean		rb_ext_db_key_is_lookup		(RBExtDBKey *key);
 
 void			rb_ext_db_key_add_field		(RBExtDBKey *key,
 							 const char *field,
-							 RBExtDBFieldType field_type,
 							 const char *value);
-
 char **			rb_ext_db_key_get_field_names	(RBExtDBKey *key);
 const char *		rb_ext_db_key_get_field		(RBExtDBKey *key,
 							 const char *field);
-RBExtDBFieldType	rb_ext_db_key_get_field_type	(RBExtDBKey *key,
+char **			rb_ext_db_key_get_field_values	(RBExtDBKey *key,
 							 const char *field);
+gboolean		rb_ext_db_key_field_matches	(RBExtDBKey *key,
+							 const char *field,
+							 const char *value);
+
+void			rb_ext_db_key_add_info		(RBExtDBKey *key,
+							 const char *name,
+							 const char *value);
+char **			rb_ext_db_key_get_info_names	(RBExtDBKey *key);
+const char *		rb_ext_db_key_get_info		(RBExtDBKey *key,
+							 const char *name);
 
 gboolean		rb_ext_db_key_matches		(RBExtDBKey *a,
 							 RBExtDBKey *b);
diff --git a/metadata/rb-ext-db.c b/metadata/rb-ext-db.c
index ab90387..194edb0 100644
--- a/metadata/rb-ext-db.c
+++ b/metadata/rb-ext-db.c
@@ -676,18 +676,20 @@ rb_ext_db_request (RBExtDB *store,
 		return FALSE;
 	}
 
-	/* discard duplicate requests */
+	/* discard duplicate requests, combine equivalent requests */
 	for (l = store->priv->requests; l != NULL; l = l->next) {
 		req = l->data;
+		if (rb_ext_db_key_matches (key, req->key) == FALSE)
+			continue;
+
 		if (req->callback == callback &&
 		    req->user_data == user_data &&
-		    req->destroy_notify == destroy &&
-		    rb_ext_db_key_matches (key, req->key)) {
+		    req->destroy_notify == destroy) {
 			rb_debug ("found matching existing request");
 			if (destroy)
 				destroy (user_data);
 			return TRUE;
-		} else if (rb_ext_db_key_matches (key, req->key)) {
+		} else {
 			rb_debug ("found existing equivalent request");
 			emit_request = FALSE;
 		}
diff --git a/plugins/artsearch/artsearch.py b/plugins/artsearch/artsearch.py
index bb14573..d8d5304 100644
--- a/plugins/artsearch/artsearch.py
+++ b/plugins/artsearch/artsearch.py
@@ -41,40 +41,18 @@ class Search(object):
 		self.last_time = last_time
 		self.searches = searches
 
-
 	def next_search(self):
 		if len(self.searches) == 0:
 			self.store.store(self.key, RB.ExtDBSourceType.NONE, None)
 			return False
 
 		search = self.searches.pop(0)
-		search.search(self.key, self.last_time, self.search_results, None)
+		search.search(self.key, self.last_time, self.store, self.search_done, None)
 		return True
 
-
-	def search_results(self, storekey, data, source_type, args):
-
-		if data is None or storekey is None:
-			pass
-		elif isinstance(data, str) or isinstance(data, unicode):
-			print "got a uri: %s" % str(data)
-			self.store.store_uri(storekey, source_type, data)
-		elif isinstance(data, list) or isinstance(data, tuple):
-			print "got a uri list: %s" % str(data)
-			for u in data:
-				self.store.store_uri(storekey, source_type, u)
-		elif isinstance(data, GdkPixbuf.Pixbuf):
-			print "got a pixbuf"
-			self.store.store(storekey, source_type, data)
-		else:
-			# complain quietly
-			print "got mystery meat: %s" % data
-			pass
-
-		# keep going anyway
+	def search_done(self, args):
 		self.next_search()
 
-
 class ArtSearchPlugin (GObject.GObject, Peas.Activatable):
 	__gtype_name__ = 'ArtSearchPlugin'
 	object = GObject.property(type=GObject.GObject)
diff --git a/plugins/artsearch/lastfm.py b/plugins/artsearch/lastfm.py
index 33db7bb..8ad267a 100644
--- a/plugins/artsearch/lastfm.py
+++ b/plugins/artsearch/lastfm.py
@@ -123,20 +123,21 @@ class LastFMSearch (object):
 		if len(image_urls) > 0:
 			# images tags appear in order of increasing size, and we want the largest.  probably.
 			url = image_urls.pop()
-			self.callback(self.current_key, url, RB.ExtDBSourceType.SEARCH, self.callback_args)
+			self.store.store_uri(self.current_key, RB.ExtDBSourceType.SEARCH, url)
+			self.callback(self.callback_args)
 		else:
 			self.search_next()
 
 
 	def search_next (self):
 		if len(self.searches) == 0:
-			self.callback(None, None, RB.ExtDBSourceType.NONE, self.callback_args)
+			self.callback(self.callback_args)
 			return
 
-		(artist, album, album_mbid, artist_field) = self.searches.pop(0)
-		self.current_key = RB.ExtDBKey.create("album", album)
+		(artist, album, album_mbid) = self.searches.pop(0)
+		self.current_key = RB.ExtDBKey.create_storage("album", album)
 		if artist is not None:
-			self.current_key.add_field(artist_field, RB.ExtDBFieldType.OPTIONAL, artist)
+			self.current_key.add_field("artist", artist)
 
 		url = self.search_url(artist, album, album_mbid)
 
@@ -144,41 +145,36 @@ class LastFMSearch (object):
 		l.get_url(url, self.album_info_cb)
 
 
-	def search(self, key, last_time, callback, args):
+	def search(self, key, last_time, store, callback, args):
 		if last_time > (time.time() - REPEAT_SEARCH_PERIOD):
 			print "we already tried this one"
-			callback (None, None, RB.ExtDBSourceType.NONE, args)
+			callback (args)
 			return
 
 		if user_has_account() == False:
 			print "can't search: no last.fm account details"
-			callback (None, None, RB.ExtDBSourceType.NONE, args)
+			callback (args)
 			return
 
 		album = key.get_field("album")
-		albumartist = key.get_field("album-artist")
-		album_mbid = key.get_field("musicbrainz-albumid")
-		artist = key.get_field("artist")
-
-		if albumartist in ("", _("Unknown")):
-			albumartist = None
-		if artist in ("", _("Unknown")):
-			artist = None
+		artists = key.get_field_values("artist")
+		album_mbid = key.get_info("musicbrainz-albumid")
+
+		artists = filter(lambda x: x not in (None, "", _("Unknown")), artists)
 		if album in ("", _("Unknown")):
 			album = None
 
-		if album == None or (albumartist == None and artist == None):
+		if album == None or len(artists) == 0:
 			print "can't search: no useful details"
-			callback (None, None, RB.ExtDBSourceType.NONE, args)
+			callback (args)
 			return
 
 		self.searches = []
-		if artist != albumartist and artist != None:
-			self.searches.append([artist, album, album_mbid, "artist"])
-		if albumartist != None:
-			self.searches.append([albumartist, album, album_mbid, "album-artist"])
-		self.searches.append(["Various Artists", album, album_mbid, "album-artist"])
+		for a in artists:
+			self.searches.append([a, album, album_mbid])
+		self.searches.append(["Various Artists", album, album_mbid])
 
+		self.store = store
 		self.callback = callback
 		self.callback_args = args
 		self.search_next()
diff --git a/plugins/artsearch/local.py b/plugins/artsearch/local.py
index 79be909..b14eb91 100644
--- a/plugins/artsearch/local.py
+++ b/plugins/artsearch/local.py
@@ -51,21 +51,28 @@ class LocalSearch:
 	def finished(self, results):
 		parent = self.file.get_parent()
 		ordered = []
+		key = RB.ExtDBKey.create_storage("album", self.album)
+		key.add_field("artist", self.artist)
 
 		# Compare lower case, without file extension
 		for name in [file_root (self.file.get_basename())] + IMAGE_NAMES:
 			for f_name in results:
 				if file_root (f_name) == name:
-					ordered.append(parent.resolve_relative_path(f_name).get_uri())
+					uri = parent.resolve_relative_path(f_name).get_uri()
+					self.store.store_uri(key, RB.ExtDBSourceType.USER, uri)
 
 		# look for file names containing the artist and album (case-insensitive)
 		# (mostly for jamendo downloads)
-		artist = self.artist.lower()
 		album = self.album.lower()
 		for f_name in results:
 			f_root = file_root (f_name).lower()
-			if f_root.find (artist) != -1 and f_root.find (album) != -1:
-				ordered.append(parent.resolve_relative_path(f_name).get_uri())
+			for artist in self.artists:
+				artist = artist.lower()
+				if f_root.find (artist) != -1 and f_root.find (album) != -1:
+					nkey = RB.ExtDBKey.create_storage("album", album)
+					nkey.add_field("artist", artist)
+					uri = parent.resolve_relative_path(f_name).get_uri()
+					self.store.store_uri(nkey. RB.ExtDBSourceType.USER, uri)
 
 		# if that didn't work, look for the longest shared prefix
 		# only accept matches longer than 2 to avoid weird false positives
@@ -78,11 +85,10 @@ class LocalSearch:
 				match = f_name
 
 		if match is not None:
-			ordered.append(parent.resolve_relative_path(match).get_uri())
+			uri = parent.resolve_relative_path(match).get_uri()
+			self.store.store_uri(nkey. RB.ExtDBSourceType.USER, uri)
 
-		key = RB.ExtDBKey.create("album", self.album)
-		key.add_field("album-artist", RB.ExtDBFieldType.OPTIONAL, self.artist)
-		self.callback(key, ordered, RB.ExtDBSourceType.USER, self.callback_args)
+		self.callback(self.callback_args)
 
 	def _enum_dir_cb(self, fileenum, result, results):
 		try:
@@ -117,28 +123,27 @@ class LocalSearch:
 			print "okay, probably done: %s" % e
 			import sys
 			sys.excepthook(*sys.exc_info())
-			self.callback(None, None, RB.ExtDBSourceType.NONE, self.callback_args)
+			self.callback(self.callback_args)
 
 
-	def search (self, key, last_time, callback, args):
+	def search (self, key, last_time, store, callback, args):
 		# ignore last_time
 
 		location = key.get_field("location")
 		if location is None:
 			print "not searching, we don't have a location"
-			callback(None, None, RB.ExtDBSourceType.NONE, args)
+			callback(args)
 			return
 
 		self.file = Gio.file_new_for_uri(location)
 		if self.file.get_uri_scheme() in IGNORED_SCHEMES:
 			print 'not searching for local art for %s' % (self.file.get_uri())
-			callback(None, None, RB.ExtDBSourceType.NONE, args)
+			callback(args)
 			return
 
 		self.album = key.get_field("album")
-		self.artist = key.get_field("album-artist")
-		if self.artist in (None, ""):
-			self.artist = key.get_field("artist")
+		self.artists = key.get_field_values("artist")
+		self.store = store
 		self.callback = callback
 		self.callback_args = args
 
diff --git a/plugins/artsearch/musicbrainz.py b/plugins/artsearch/musicbrainz.py
index fdf90b7..6b38d2d 100644
--- a/plugins/artsearch/musicbrainz.py
+++ b/plugins/artsearch/musicbrainz.py
@@ -43,16 +43,16 @@ AMAZON_IMAGE_URL = "http://images.amazon.com/images/P/%s.01.LZZZZZZZ.jpg";
 class MusicBrainzSearch(object):
 
 	def get_release_cb (self, data, args):
-		(key, callback, cbargs) = args
+		(key, store, callback, cbargs) = args
 		if data is None:
 			print "musicbrainz release request returned nothing"
-			callback(None, None, RB.ExtDBSourceType.NONE, *cbargs)
+			callback(*cbargs)
 			return
 
 		try:
 			parsed = dom.parseString(data)
 
-			storekey = RB.ExtDBKey.create('album', key.get_field('album'))
+			storekey = RB.ExtDBKey.create_storage('album', key.get_field('album'))
 
 			# check that there's an artist that isn't 'various artists'
 			artist_tags = parsed.getElementsByTagName('artist')
@@ -64,7 +64,7 @@ class MusicBrainzSearch(object):
 					if len(nametags) > 0:
 						artistname = nametags[0].firstChild.data
 						print "got musicbrainz artist name %s" % artistname
-						storekey.add_field('album-artist', RB.ExtDBFieldType.OPTIONAL, artistname)
+						storekey.add_field('artist', artistname)
 
 
 			# look for an ASIN tag
@@ -75,20 +75,21 @@ class MusicBrainzSearch(object):
 				print "got ASIN %s" % asin
 				image_url = AMAZON_IMAGE_URL % asin
 
-				callback(storekey, image_url, RB.ExtDBSourceType.SEARCH, *cbargs)
+				store.store_uri(storekey, RB.ExtDBSourceType.SEARCH, image_url)
 			else:
 				print "no ASIN for this release"
-				callback(None, None, RB.ExtDBSourceType.NONE, *cbargs)
+
+			callback(*cbargs)
 		except Exception, e:
 			print "exception parsing musicbrainz response: %s" % e
-			callback(None, None, RB.ExtDBSourceType.NONE, *cbargs)
+			callback(*cbargs)
 
-	def search(self, key, last_time, callback, *args):
+	def search(self, key, last_time, store, callback, *args):
 		key = key.copy()	# ugh
-		album_id = key.get_field("musicbrainz-albumid")
+		album_id = key.get_info("musicbrainz-albumid")
 		if album_id is None:
 			print "no musicbrainz release ID for this track"
-			callback(None, None, RB.ExtDBSourceType.NONE, *args)
+			callback(*args)
 			return
 
 		if album_id.startswith(MUSICBRAINZ_RELEASE_PREFIX):
@@ -101,4 +102,4 @@ class MusicBrainzSearch(object):
 
 		url = MUSICBRAINZ_RELEASE_URL % (album_id)
 		loader = rb.Loader()
-		loader.get_url(url, self.get_release_cb, (key, callback, args))
+		loader.get_url(url, self.get_release_cb, (key, store, callback, args))
diff --git a/plugins/artsearch/oldcache.py b/plugins/artsearch/oldcache.py
index eecf125..f689e07 100644
--- a/plugins/artsearch/oldcache.py
+++ b/plugins/artsearch/oldcache.py
@@ -45,25 +45,21 @@ class OldCacheSearch(object):
 		album = album.replace('/', '-')
 		return os.path.join(ART_FOLDER, '%s - %s.%s' % (artist, album, extension))
 
-	def search(self, key, last_time, callback, *args):
+	def search(self, key, last_time, store, callback, *args):
 		album = key.get_field("album")
-		artist = key.get_field("artist")
-		albumartist = key.get_field("album-artist")
-
-		print "looking for %s by (%s, %s)" % (album, artist, albumartist)
-		for field in ('album-artist', 'artist'):
-			artist = key.get_field(field)
-			if artist is None:
-				continue
+		artists = key.get_field_values("artist") or []
 
+		print "looking for %s by %s" % (album, str(artists))
+		for artist in artists:
 			for ext in ('jpg', 'png'):
-				path = self.filename(album, field, ext)
+				path = self.filename(album, artist, ext)
 				if os.path.exists(path):
 					print "found %s" % path
 					uri = "file://" + urllib.pathname2url(path)
-					storekey = RB.ExtDBKey.create('album', album)
-					storekey.add_field(field, RB.ExtDBFieldType.OPTIONAL, artist)
-					callback(storekey, uri, RB.ExtDBSourceType.SEARCH, *args)
+					storekey = RB.ExtDBKey.create_storage('album', album)
+					storekey.add_field("artist", artist)
+					store.store_uri(storekey, RB.ExtDBSourceType.SEARCH, uri)
+					callback(*args)
 					return
 
-		callback(None, None, RB.ExtDBSourceType.NONE, *args)
+		callback(*args)
diff --git a/plugins/audioscrobbler/rb-audioscrobbler-radio-source.c b/plugins/audioscrobbler/rb-audioscrobbler-radio-source.c
index 6efd109..8f5831b 100644
--- a/plugins/audioscrobbler/rb-audioscrobbler-radio-source.c
+++ b/plugins/audioscrobbler/rb-audioscrobbler-radio-source.c
@@ -698,8 +698,8 @@ playing_song_changed_cb (RBShellPlayer *player,
 		}
 
 		/* provide cover art */
-		key = rb_ext_db_key_create ("album", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
-		rb_ext_db_key_add_field (key, "artist", RB_EXT_DB_FIELD_OPTIONAL, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
+		key = rb_ext_db_key_create_storage ("album", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
+		rb_ext_db_key_add_field (key, "artist", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
 		track_data = RHYTHMDB_ENTRY_GET_TYPE_DATA(entry, RBAudioscrobblerRadioTrackData);
 		rb_ext_db_store_uri (source->priv->art_store,
 				     key,
diff --git a/plugins/grilo/rb-grilo-plugin.c b/plugins/grilo/rb-grilo-plugin.c
index 75c990a..e9032a0 100644
--- a/plugins/grilo/rb-grilo-plugin.c
+++ b/plugins/grilo/rb-grilo-plugin.c
@@ -153,11 +153,8 @@ playing_song_changed_cb (RBShellPlayer *player, RhythmDBEntry *entry, RBGriloPlu
 	if (uri != NULL) {
 		RBExtDBKey *key;
 
-		key = rb_ext_db_key_create ("album", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
-		rb_ext_db_key_add_field (key,
-					 "artist",
-					 RB_EXT_DB_FIELD_OPTIONAL,
-					 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
+		key = rb_ext_db_key_create_storage ("album", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
+		rb_ext_db_key_add_field (key, "artist", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
 
 		rb_ext_db_store_uri (plugin->art_store,
 				     key,
diff --git a/plugins/ipod/rb-ipod-source.c b/plugins/ipod/rb-ipod-source.c
index 021f399..39717ce 100644
--- a/plugins/ipod/rb-ipod-source.c
+++ b/plugins/ipod/rb-ipod-source.c
@@ -1572,10 +1572,10 @@ impl_track_added (RBTransferTarget *target,
 		device = rb_ipod_db_get_device (priv->ipod_db);
 		if (device && itdb_device_supports_artwork (device)) {
 			RBExtDBKey *key;
-			key = rb_ext_db_key_create ("album", song->album);
-			rb_ext_db_key_add_field (key, "artist", RB_EXT_DB_FIELD_OPTIONAL, song->artist);
+			key = rb_ext_db_key_create_lookup ("album", song->album);
+			rb_ext_db_key_add_field (key, "artist", song->artist);
 			if (song->albumartist) {
-				rb_ext_db_key_add_field (key, "album-artist", RB_EXT_DB_FIELD_OPTIONAL, song->albumartist);
+				rb_ext_db_key_add_field (key, "artist", song->albumartist);
 			}
 
 			rb_ext_db_request (priv->art_store,
diff --git a/plugins/magnatune/MagnatuneSource.py b/plugins/magnatune/MagnatuneSource.py
index 3f5f561..d269bbe 100644
--- a/plugins/magnatune/MagnatuneSource.py
+++ b/plugins/magnatune/MagnatuneSource.py
@@ -553,8 +553,8 @@ class MagnatuneSource(RB.BrowserSource):
 			return
 
 		sku = self.__sku_dict[entry.get_string(RB.RhythmDBPropType.LOCATION)]
-		key = RB.ExtDBKey.create("album", entry.get_string(RB.RhythmDBPropType.ALBUM))
-		key.add_field("artist", RB.ExtDBFieldType.OPTIONAL, entry.get_string(RB.RhythmDBPropType.ARTIST))
+		key = RB.ExtDBKey.create_storage("album", entry.get_string(RB.RhythmDBPropType.ALBUM))
+		key.add_field("artist", entry.get_string(RB.RhythmDBPropType.ARTIST))
 		self.__art_store.store_uri(key, self.__art_dict[sku])
 
 GObject.type_register(MagnatuneSource)
diff --git a/plugins/mtpdevice/rb-mtp-source.c b/plugins/mtpdevice/rb-mtp-source.c
index d559633..f2af5a7 100644
--- a/plugins/mtpdevice/rb-mtp-source.c
+++ b/plugins/mtpdevice/rb-mtp-source.c
@@ -1093,11 +1093,8 @@ impl_track_added (RBTransferTarget *target,
 			RBExtDBKey *key;
 
 			/* need to do this in an idle handler? */
-			key = rb_ext_db_key_create ("album", track->album);
-			rb_ext_db_key_add_field (key,
-						 "artist",
-						 RB_EXT_DB_FIELD_OPTIONAL,
-						 track->artist);
+			key = rb_ext_db_key_create_lookup ("album", track->album);
+			rb_ext_db_key_add_field (key, "artist", track->artist);
 			rb_ext_db_request (priv->art_store,
 					   key,
 					   (RBExtDBRequestCallback) art_request_cb,
diff --git a/podcast/rb-podcast-manager.c b/podcast/rb-podcast-manager.c
index b4f02e7..b04e35f 100644
--- a/podcast/rb-podcast-manager.c
+++ b/podcast/rb-podcast-manager.c
@@ -1993,7 +1993,7 @@ rb_podcast_manager_insert_feed (RBPodcastManager *pd, RBPodcastChannel *data)
 		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_IMAGE, &image_val);
 		g_value_unset (&image_val);
 
-		key = rb_ext_db_key_create ("album", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
+		key = rb_ext_db_key_create_storage ("album", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
 		rb_ext_db_store_uri (pd->priv->art_store,
 				     key,
 				     RB_EXT_DB_SOURCE_SEARCH,	/* sort of */
diff --git a/rhythmdb/rhythmdb.c b/rhythmdb/rhythmdb.c
index df2617d..86e6c64 100644
--- a/rhythmdb/rhythmdb.c
+++ b/rhythmdb/rhythmdb.c
@@ -5386,60 +5386,39 @@ rhythmdb_entry_create_ext_db_key (RhythmDBEntry *entry, RhythmDBPropType prop)
 
 	switch (prop) {
 	case RHYTHMDB_PROP_ALBUM:
-		key = rb_ext_db_key_create ("album", rhythmdb_entry_get_string (entry, prop));
+		key = rb_ext_db_key_create_lookup ("album", rhythmdb_entry_get_string (entry, prop));
 		rb_ext_db_key_add_field (key,
 					 "artist",
-					 RB_EXT_DB_FIELD_OPTIONAL,
 					 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
 		str = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM_ARTIST);
 		if (g_strcmp0 (str, "") != 0 && g_strcmp0 (str, _("Unknown")) != 0) {
-			rb_ext_db_key_add_field (key,
-						 "album-artist",
-						 RB_EXT_DB_FIELD_OPTIONAL,
-						 str);
-		} else {
-			/* try artist name as album-artist too */
-			rb_ext_db_key_add_field (key,
-						 "album-artist",
-						 RB_EXT_DB_FIELD_OPTIONAL,
-						 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
+			rb_ext_db_key_add_field (key, "artist", str);
 		}
 
 		str = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MUSICBRAINZ_ALBUMID);
 		if (g_strcmp0 (str, "") != 0 && g_strcmp0 (str, _("Unknown")) != 0) {
-			rb_ext_db_key_add_field (key,
-						 "musicbrainz-albumid",
-						 RB_EXT_DB_FIELD_INFORMATIONAL,
-						 str);
+			rb_ext_db_key_add_info (key, "musicbrainz-albumid", str);
 		}
 
 		break;
 
 	case RHYTHMDB_PROP_TITLE:
-		key = rb_ext_db_key_create ("title", rhythmdb_entry_get_string (entry, prop));
-		rb_ext_db_key_add_field (key,
-					 "artist",
-					 RB_EXT_DB_FIELD_OPTIONAL,
-					 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
-		rb_ext_db_key_add_field (key,
-					 "album",
-					 RB_EXT_DB_FIELD_OPTIONAL,
-					 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
+		key = rb_ext_db_key_create_lookup ("title", rhythmdb_entry_get_string (entry, prop));
+		/* maybe these should be info? */
+		rb_ext_db_key_add_field (key, "artist", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
+		rb_ext_db_key_add_field (key, "album", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
 		break;
 
 	case RHYTHMDB_PROP_ARTIST:
 		/* not really sure what this might be useful for */
-		key = rb_ext_db_key_create ("artist", rhythmdb_entry_get_string (entry, prop));
+		key = rb_ext_db_key_create_lookup ("artist", rhythmdb_entry_get_string (entry, prop));
 		break;
 
 	default:
 		g_assert_not_reached ();
 	}
 
-	rb_ext_db_key_add_field (key,
-				 "location",
-				 RB_EXT_DB_FIELD_INFORMATIONAL,
-				 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
+	rb_ext_db_key_add_info (key, "location", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
 	return key;
 }
 
@@ -5461,38 +5440,37 @@ rhythmdb_entry_matches_ext_db_key (RhythmDB *db, RhythmDBEntry *entry, RBExtDBKe
 
 	fields = rb_ext_db_key_get_field_names (key);
 	for (i = 0; fields[i] != NULL; i++) {
-		const char *value;
-		const char *entry_value;
 		RhythmDBPropType prop;
+		RhythmDBPropType extra_prop;
+		const char *v;
 
 		prop = rhythmdb_propid_from_nice_elt_name (db, (const xmlChar *)fields[i]);
-		value = rb_ext_db_key_get_field (key, fields[i]);
-
-		switch (rb_ext_db_key_get_field_type (key, fields[i])) {
-		case RB_EXT_DB_FIELD_INFORMATIONAL:
-			/* ignore */
-			break;
-
-		case RB_EXT_DB_FIELD_OPTIONAL:
-			if (prop == -1)
-				break;
-
-			entry_value = rhythmdb_entry_get_string (entry, prop);
-			if (entry_value != NULL && strlen (entry_value) > 0 && strcmp (entry_value, value) != 0) {
+		if (prop == -1) {
+			if (rb_ext_db_key_field_matches (key, fields[i], NULL) == FALSE)
 				return FALSE;
-			}
-			break;
 
-		case RB_EXT_DB_FIELD_REQUIRED:
-			if (prop == -1)
-				return FALSE;
+			continue;
+		}
 
-			entry_value = rhythmdb_entry_get_string (entry, prop);
-			if (entry_value == NULL || strcmp (entry_value, value) != 0) {
-				return FALSE;
-			}
+		/* check additional values for some fields */
+		switch (prop) {
+		case RHYTHMDB_PROP_ARTIST:
+			extra_prop = RHYTHMDB_PROP_ALBUM_ARTIST;
+			break;
+		default:
+			extra_prop = -1;
 			break;
 		}
+
+		if (extra_prop != -1) {
+			v = rhythmdb_entry_get_string (entry, extra_prop);
+			if (rb_ext_db_key_field_matches (key, fields[i], v))
+				continue;
+		}
+
+		v = rhythmdb_entry_get_string (entry, prop);
+		if (rb_ext_db_key_field_matches (key, fields[i], v) == FALSE)
+			return FALSE;
 	}
 
 	return TRUE;
diff --git a/shell/rb-shell-player.c b/shell/rb-shell-player.c
index 01854fe..2eac737 100644
--- a/shell/rb-shell-player.c
+++ b/shell/rb-shell-player.c
@@ -3727,12 +3727,12 @@ player_image_cb (RBPlayer *player,
 
 	store = rb_ext_db_new ("album-art");
 
-	key = rb_ext_db_key_create ("album", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
+	key = rb_ext_db_key_create_storage ("album", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
 	artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM_ARTIST);
 	if (artist == NULL || artist[0] == '\0' || strcmp (artist, _("Unknown")) == 0) {
 		artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
 	}
-	rb_ext_db_key_add_field (key, "album-artist", RB_EXT_DB_FIELD_OPTIONAL, artist);
+	rb_ext_db_key_add_field (key, "artist", artist);
 
 	g_value_init (&v, GDK_TYPE_PIXBUF);
 	g_value_set_object (&v, image);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]