>> Now that I am writing my own application, it seems like it might be >> more convenient/consistent if the DMAP plugin imposed some >> (artificial, >> with respect to the DMAP protocol itself) structure on the media it >> finds. That is, I like the way the Jamendo plugin presents Artists, >> Albums, Feeds; then a list of each; and then the songs themselves. >> >> Would it make sense if I modified the DMAP plugin to behave more like >> Jamendo? > For me, it would make sense. > > Also, you can still allow the flat list through the search: doing a > search of a NULL text means "search all the content". If a source > implements it, it just return the list of all media.
Please see below for an initial patch. With this patch, the DMAP plugin will present: (Albums, Artists) -> (Album_1, Album_2, ..., Album_n) or (Artist_1, ...) -> (Track_1, Track_2, ..., Track_n) I have found this much more convenient than a flat listing from the point of view of the application programmer. As I mentioned earlier, I am working on an application, so you can expect continued bugfixes for this plugin. >From c2b1db5c76ce860a3802a56c763ad13da9eb29ff Mon Sep 17 00:00:00 2001 From: "W. Michael Petullo" <m...@flyn.org> Date: Sun, 2 Feb 2014 22:00:02 -0500 Subject: [PATCH] dmap: provide a hierarchical view of media database Signed-off-by: W. Michael Petullo <m...@flyn.org> --- src/dmap/grl-dmap.c | 191 ++++++++------------------ src/dmap/simple-dmap-db.c | 333 ++++++++++++++++++++++++++++++++++++++++------ src/dmap/simple-dmap-db.h | 24 +++- 3 files changed, 368 insertions(+), 180 deletions(-) diff --git a/src/dmap/grl-dmap.c b/src/dmap/grl-dmap.c index b31c235..bcc8b50 100644 --- a/src/dmap/grl-dmap.c +++ b/src/dmap/grl-dmap.c @@ -66,13 +66,12 @@ struct _GrlDmapSourcePrivate { typedef struct _ResultCbAndArgs { GrlSourceResultCb callback; GrlSource *source; + GrlMedia *container; guint op_id; - gint code_error; GHRFunc predicate; gchar *predicate_data; guint skip; guint count; - guint remaining; gpointer user_data; } ResultCbAndArgs; @@ -129,7 +128,7 @@ grl_dmap_plugin_init (GrlRegistry *registry, bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); browser = dmap_mdns_browser_new (DMAP_MDNS_BROWSER_SERVICE_TYPE_DAAP); - connections = g_hash_table_new (g_str_hash, g_str_equal); + connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); sources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); g_signal_connect (G_OBJECT (browser), @@ -232,140 +231,71 @@ build_url (DMAPMdnsBrowserService *service) } static void -add_media_from_service (gpointer id, - DAAPRecord *record, - ResultCbAndArgs *cb) +do_browse (ResultCbAndArgsAndDb *cb_and_db) { - gint duration = 0; - gint32 bitrate = 0, - track = 0; - gchar *id_s = NULL, - *title = NULL, - *album = NULL, - *artist = NULL, - *genre = NULL, - *url = NULL; - gboolean has_video; - GrlMedia *media; - - g_object_get (record, - "songalbum", - &album, - "songartist", - &artist, - "bitrate", - &bitrate, - "duration", - &duration, - "songgenre", - &genre, - "title", - &title, - "track", - &track, - "location", - &url, - "has-video", - &has_video, - NULL); - - id_s = g_strdup_printf ("%u", GPOINTER_TO_UINT (id)); - - if (has_video == TRUE) { - media = grl_media_video_new (); - } else { - media = grl_media_audio_new (); - } - - grl_media_set_id (media, id_s); - grl_media_set_duration (media, duration); - - if (title) { - grl_media_set_title (media, title); - } - - if (url) { - // Replace URL's daap:// with http://. - url[0] = 'h'; url[1] = 't'; url[2] = 't'; url[3] = 'p'; - grl_media_set_url (media, url); - } - - if (has_video == FALSE) { - GrlMediaAudio *media_audio = GRL_MEDIA_AUDIO (media); - - grl_media_audio_set_bitrate (media_audio, bitrate); - grl_media_audio_set_track_number (media_audio, track); + simple_dmap_db_browse (cb_and_db->db, + cb_and_db->cb.container, + cb_and_db->cb.source, + cb_and_db->cb.op_id, + cb_and_db->cb.skip, + cb_and_db->cb.count, + cb_and_db->cb.callback, + cb_and_db->cb.user_data); - if (album) { - grl_media_audio_set_album (media_audio, album); - } - - if (artist) { - grl_media_audio_set_artist (media_audio, artist); - } - - if (genre) { - grl_media_audio_set_genre (media_audio, genre); - } - } - - g_free (id_s); - - cb->callback (cb->source, - cb->op_id, - media, - --cb->remaining, - cb->user_data, - NULL); + g_free (cb_and_db); } static void -add_to_hash_table (gpointer key, gpointer value, GHashTable *hash_table) +do_search (ResultCbAndArgsAndDb *cb_and_db) { - g_hash_table_insert (hash_table, key, value); + simple_dmap_db_search (cb_and_db->db, + cb_and_db->cb.source, + cb_and_db->cb.op_id, + (GHRFunc) cb_and_db->cb.predicate, + cb_and_db->cb.predicate_data, + cb_and_db->cb.callback, + cb_and_db->cb.user_data); + + g_free (cb_and_db); } static void -add_filtered_media_from_service (ResultCbAndArgsAndDb *cb_and_db) +browse_connected_cb (DMAPConnection *connection, + gboolean result, + const char *reason, + ResultCbAndArgsAndDb *cb_and_db) { - GHashTable *hash_table; - hash_table = g_hash_table_new (g_direct_hash, g_direct_equal); - - simple_dmap_db_filtered_foreach (cb_and_db->db, - cb_and_db->cb.skip, - cb_and_db->cb.count, - (GHRFunc) cb_and_db->cb.predicate, - cb_and_db->cb.predicate_data, - (GHFunc) add_to_hash_table, - hash_table); - - cb_and_db->cb.remaining = g_hash_table_size (hash_table); - if (cb_and_db->cb.remaining > 0) { - g_hash_table_foreach (hash_table, (GHFunc) add_media_from_service, &cb_and_db->cb); - } else { + GError *error; + + // NOTE: connection argument is required by API but ignored in this case. + if (!result) { + error = g_error_new_literal (GRL_CORE_ERROR, + GRL_CORE_ERROR_BROWSE_FAILED, + reason); cb_and_db->cb.callback (cb_and_db->cb.source, cb_and_db->cb.op_id, NULL, 0, cb_and_db->cb.user_data, - NULL); + error); + g_error_free (error); + } else { + do_browse (cb_and_db); } - g_hash_table_destroy (hash_table); - g_free (cb_and_db); } static void -connected_cb (DMAPConnection *connection, - gboolean result, - const char *reason, - ResultCbAndArgsAndDb *cb_and_db) +search_connected_cb (DMAPConnection *connection, + gboolean result, + const char *reason, + ResultCbAndArgsAndDb *cb_and_db) { GError *error; // NOTE: connection argument is required by API but ignored in this case. if (!result) { error = g_error_new_literal (GRL_CORE_ERROR, - cb_and_db->cb.code_error, + GRL_CORE_ERROR_BROWSE_FAILED, reason); cb_and_db->cb.callback (cb_and_db->cb.source, cb_and_db->cb.op_id, @@ -375,7 +305,7 @@ connected_cb (DMAPConnection *connection, error); g_error_free (error); } else { - add_filtered_media_from_service (cb_and_db); + do_search (cb_and_db); } } @@ -428,16 +358,15 @@ grl_dmap_connect (gchar *name, gchar *host, guint port, ResultCbAndArgsAndDb *cb } static gboolean -always_true (gpointer key, gpointer value, gpointer user_data) +match (GrlMedia *media, gpointer val, gpointer user_data) { - return TRUE; -} + g_assert (GRL_IS_MEDIA_AUDIO (media) || GRL_IS_MEDIA_VIDEO (media)); -static gboolean -match (gpointer key, DAAPRecord *record, gpointer user_data) -{ - char *title; - g_object_get (record, "title", &title, NULL); + if (NULL == user_data) { + return TRUE; + } + + const char *title = grl_media_get_title (media); return strstr (title, user_data) != NULL; } @@ -480,17 +409,15 @@ grl_dmap_source_browse (GrlSource *source, cb_and_db->cb.callback = bs->callback; cb_and_db->cb.source = bs->source; + cb_and_db->cb.container = bs->container; cb_and_db->cb.op_id = bs->operation_id; - cb_and_db->cb.code_error = GRL_CORE_ERROR_BROWSE_FAILED; - cb_and_db->cb.predicate = always_true; - cb_and_db->cb.predicate_data = NULL; cb_and_db->cb.skip = grl_operation_options_get_skip (bs->options); cb_and_db->cb.count = grl_operation_options_get_count (bs->options); cb_and_db->cb.user_data = bs->user_data; if ((cb_and_db->db = g_hash_table_lookup (connections, url))) { // Just call directly; already connected, already populated database. - connected_cb (NULL, TRUE, NULL, cb_and_db); + browse_connected_cb (NULL, TRUE, NULL, cb_and_db); } else { // Connect. cb_and_db->db = simple_dmap_db_new (); @@ -499,9 +426,9 @@ grl_dmap_source_browse (GrlSource *source, dmap_source->priv->service->host, dmap_source->priv->service->port, cb_and_db, - (DMAPConnectionCallback) connected_cb); + (DMAPConnectionCallback) browse_connected_cb); - g_hash_table_insert (connections, (gpointer) url, cb_and_db->db); + g_hash_table_insert (connections, g_strdup (url), cb_and_db->db); } g_free (url); @@ -520,22 +447,20 @@ static void grl_dmap_source_search (GrlSource *source, cb_and_db->cb.callback = ss->callback; cb_and_db->cb.source = ss->source; + cb_and_db->cb.container = NULL; cb_and_db->cb.op_id = ss->operation_id; - cb_and_db->cb.code_error = GRL_CORE_ERROR_SEARCH_FAILED; cb_and_db->cb.predicate = (GHRFunc) match; cb_and_db->cb.predicate_data = ss->text; - cb_and_db->cb.skip = grl_operation_options_get_skip (ss->options); - cb_and_db->cb.count = grl_operation_options_get_count (ss->options); cb_and_db->cb.user_data = ss->user_data; if ((cb_and_db->db = g_hash_table_lookup (connections, url))) { // Just call directly; already connected, already populated database. - connected_cb (NULL, TRUE, NULL, cb_and_db); + search_connected_cb (NULL, TRUE, NULL, cb_and_db); } else { // Connect. cb_and_db->db = simple_dmap_db_new (); - grl_dmap_connect (service->name, service->host, service->port, cb_and_db, (DMAPConnectionCallback) connected_cb); - g_hash_table_insert (connections, url, cb_and_db->db); + grl_dmap_connect (service->name, service->host, service->port, cb_and_db, (DMAPConnectionCallback) search_connected_cb); + g_hash_table_insert (connections, g_strdup (url), cb_and_db->db); } g_free (url); diff --git a/src/dmap/simple-dmap-db.c b/src/dmap/simple-dmap-db.c index f49ee6a..63b5f4a 100644 --- a/src/dmap/simple-dmap-db.c +++ b/src/dmap/simple-dmap-db.c @@ -15,23 +15,64 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* This DMAP database implementation maintains a series of hash tables that + * represent sets of media. The tables include: root, albums, and artists. + * Root contains albums and artists, and albums and artists each contain + * the set of albums and artists in the database, respectively. Thus this + * database implementation imposes a hierarchical structure, whereas DMAP + * normally provides a flat structure. + * + * Each hash table/set is a mapping between a GrlMediaBox and a series of + * GrlMedia objects (either more GrlMediaBox objects, or, in the case of a + * leaf, GrlMediaAudio objects). The constant GrlMediaBox objects (e.g., + * albums_box and artists_box) facilitate this, along with additional + * GrlMediaAudio objects that the simple_dmap_db_add function creates. + * + * An application will normally first browse using the NULL container, + * and thus will first receive albums_box and artists_box. Browsing + * albums_box will provide the application the GrlMediaBox objects in + * albums. Further browsing one of these objects will provide the + * application with the songs contained therein. + * + * Grilo IDs must be unique. Here the convention is: * + * 1. Top-level IDs resemble their name (e.g., Album's ID is "albums"). + * 2. The ID for albums, artists, etc. is the item name prefixed by + * the category name (e.g., albums-NAME-OF-ALBUM). + * 3. The ID for songs is the string form of the integer identifier used + * to identify the song to libdmapsharing. */ -#include <string.h> +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <glib/gi18n-lib.h> #include <sys/stat.h> #include <sys/types.h> #include <glib.h> +#include <string.h> #include "simple-dmap-db.h" /* Media ID's start at max and go down. Container ID's start at 1 and go up. */ static guint nextid = G_MAXINT; /* NOTE: this should be G_MAXUINT, but iPhoto can't handle it. */ +static const gchar *ALBUMS_ID = "albums"; +static const gchar *ALBUMS_NAME = "Albums"; +static const gchar *ARTISTS_ID = "artists"; +static const gchar *ARTISTS_NAME = "Artists"; + struct SimpleDMAPDbPrivate { - GHashTable *db; + GrlMediaBox *albums_box; // Contains each album box (tracked with albums hash table). + GrlMediaBox *artists_box; // Contains each artist box (tracked with artist hash table). + + GHashTable *root; + GHashTable *albums; + GHashTable *artists; }; enum { @@ -39,21 +80,31 @@ enum { PROP_RECORD_FACTORY, }; +static guint +box_hash (gconstpointer a) +{ + return g_str_hash (grl_media_get_id (GRL_MEDIA (a))); +} + +static gboolean +box_equal (gconstpointer a, gconstpointer b) +{ + return g_str_equal (grl_media_get_id (GRL_MEDIA (a)), grl_media_get_id (GRL_MEDIA (b))); +} + SimpleDMAPDb * simple_dmap_db_new (void) { - return SIMPLE_DMAP_DB (g_object_new (TYPE_SIMPLE_DMAP_DB, NULL)); + SimpleDMAPDb *db = g_object_new (TYPE_SIMPLE_DMAP_DB, NULL); + + return db; } static DMAPRecord * simple_dmap_db_lookup_by_id (const DMAPDb *db, guint id) { - DMAPRecord *record; - - record = g_hash_table_lookup (SIMPLE_DMAP_DB (db)->priv->db, GUINT_TO_POINTER (id)); - g_object_ref (record); - - return record; + g_error ("Not implemented"); + return NULL; } static void @@ -61,22 +112,125 @@ simple_dmap_db_foreach (const DMAPDb *db, GHFunc func, gpointer data) { - g_hash_table_foreach (SIMPLE_DMAP_DB (db)->priv->db, func, data); + g_error ("Not implemented"); } static gint64 simple_dmap_db_count (const DMAPDb *db) { - return g_hash_table_size (SIMPLE_DMAP_DB (db)->priv->db); + g_error ("Not implemented"); + return 0; +} + +static void +set_insert (GHashTable *category, const char *category_name, char *set_name, GrlMedia *media) +{ + gchar *id = NULL; + GrlMedia *box; + GHashTable *set; + + id = g_strdup_printf ("%s-%s", category_name, set_name); + + box = g_object_new (GRL_TYPE_MEDIA_BOX, NULL); + grl_media_set_id (box, id); + grl_media_set_title (box, set_name); + + set = g_hash_table_lookup (category, box); + if (NULL == set) { + set = g_hash_table_new_full (box_hash, box_equal, g_object_unref, NULL); + g_hash_table_insert (category, g_object_ref (box), set); + } + + g_hash_table_insert (set, g_object_ref (media), NULL); + + g_free (id); + g_object_unref (box); } static guint -simple_dmap_db_add (DMAPDb *db, DMAPRecord *record) +simple_dmap_db_add (DMAPDb *_db, DMAPRecord *record) { - g_object_ref (record); - g_hash_table_insert (SIMPLE_DMAP_DB (db)->priv->db, GUINT_TO_POINTER (nextid--), record); + SimpleDMAPDb *db = SIMPLE_DMAP_DB (_db); + gint duration = 0; + gint32 bitrate = 0, + track = 0; + gchar *id_s = NULL, + *title = NULL, + *album = NULL, + *artist = NULL, + *genre = NULL, + *url = NULL; + gboolean has_video; + GrlMedia *media; + + g_object_get (record, + "songalbum", + &album, + "songartist", + &artist, + "bitrate", + &bitrate, + "duration", + &duration, + "songgenre", + &genre, + "title", + &title, + "track", + &track, + "location", + &url, + "has-video", + &has_video, + NULL); + + id_s = g_strdup_printf ("%u", nextid); + + if (has_video == TRUE) { + media = grl_media_video_new (); + } else { + media = grl_media_audio_new (); + } + + grl_media_set_id (media, id_s); + grl_media_set_duration (media, duration); + + if (title) { + grl_media_set_title (media, title); + } + + if (url) { + // Replace URL's daap:// with http://. + url[0] = 'h'; url[1] = 't'; url[2] = 't'; url[3] = 'p'; + grl_media_set_url (media, url); + } + + if (has_video == FALSE) { + GrlMediaAudio *media_audio = GRL_MEDIA_AUDIO (media); - return nextid; + grl_media_audio_set_bitrate (media_audio, bitrate); + grl_media_audio_set_track_number (media_audio, track); + + if (album) { + grl_media_audio_set_album (media_audio, album); + } + + if (artist) { + grl_media_audio_set_artist (media_audio, artist); + } + + if (genre) { + grl_media_audio_set_genre (media_audio, genre); + } + } + + set_insert (db->priv->artists, ARTISTS_ID, artist, media); + set_insert (db->priv->albums, ALBUMS_ID, album, media); + + g_free (id_s); + g_object_unref (media); + + return --nextid; } static void @@ -92,31 +246,113 @@ simple_dmap_db_interface_init (gpointer iface, gpointer data) dmap_db->count = simple_dmap_db_count; } +static gboolean +same_media (GrlMedia *a, GrlMedia *b) +{ + return ! strcmp (grl_media_get_id (a), grl_media_get_id (b)); +} + void -simple_dmap_db_filtered_foreach (SimpleDMAPDb *db, - guint skip, - guint count, - GHRFunc predicate, - gpointer pred_user_data, - GHFunc func, - gpointer user_data) +simple_dmap_db_browse (SimpleDMAPDb *db, + GrlMedia *container, + GrlSource *source, + guint op_id, + guint skip, + guint count, + GrlSourceResultCb func, + gpointer user_data) { + int i; + guint remaining; + GHashTable *hash_table; GHashTableIter iter; gpointer key, val; - guint i; - g_hash_table_iter_init (&iter, db->priv->db); - for (i = 0; g_hash_table_iter_next (&iter, &key, &val); i++) { + const gchar *box_id = grl_media_get_id (container); + if (NULL == box_id) { + hash_table = db->priv->root; + } else if (same_media (container, GRL_MEDIA (db->priv->albums_box))) { + hash_table = db->priv->albums; + } else if (same_media (container, GRL_MEDIA (db->priv->artists_box))) { + hash_table = db->priv->artists; + } else { + hash_table = g_hash_table_lookup (db->priv->artists, container); + if (NULL == hash_table) { + hash_table = g_hash_table_lookup (db->priv->albums, container); + } + } + + // Should not be NULL; this means the container requested + // does not exist in the database. + if (NULL == hash_table) { + GError *error = g_error_new (GRL_CORE_ERROR, + GRL_CORE_ERROR_BROWSE_FAILED, + _("Invalid container identifier %s"), + box_id); + func (source, op_id, NULL, 0, user_data, error); + goto done; + } + + remaining = g_hash_table_size (hash_table) - skip; + remaining = remaining < count ? remaining : count; + g_hash_table_iter_init (&iter, hash_table); + for (i = 0; g_hash_table_iter_next (&iter, &key, &val) && i < skip + count; i++) { if (i < skip) { continue; } - if (i == skip + count) { - break; + if (GRL_IS_MEDIA_BOX (key)) { + grl_media_box_set_childcount (GRL_MEDIA_BOX (key), g_hash_table_size (val)); } - if (predicate (key, val, pred_user_data)) { - func (key, val, user_data); + func (source, op_id, GRL_MEDIA (g_object_ref (key)), --remaining, user_data, NULL); + } +done: + return; +} + +void +simple_dmap_db_search (SimpleDMAPDb *db, + GrlSource *source, + guint op_id, + GHRFunc predicate, + gpointer pred_user_data, + GrlSourceResultCb func, + gpointer user_data) +{ + gint i, j, k; + guint remaining = 0; + gpointer key1, val1, key2, val2; + GHashTable *hash_table[] = { db->priv->albums, db->priv->artists }; + GHashTable *results = NULL; // Use hash table to avoid duplicates. + GHashTableIter iter1, iter2; + + results = g_hash_table_new (g_str_hash, g_str_equal); + + // For albums and artists... + for (i = 0; i < 2; i++) { + g_hash_table_iter_init (&iter1, hash_table[i]); + // For each album or artist in above... + for (j = 0; g_hash_table_iter_next (&iter1, &key1, &val1); j++) { + if (GRL_IS_MEDIA_BOX (key1)) { + g_hash_table_iter_init (&iter2, val1); + // For each media item in above... + for (k = 0; g_hash_table_iter_next (&iter2, &key2, &val2); k++) { + const char *id = grl_media_get_id (GRL_MEDIA (key2)); + // If the predicate returns true, add to results set. + if (predicate (key2, val2, pred_user_data) + && ! g_hash_table_contains (results, id)) { + remaining++; + g_hash_table_insert (results, (gpointer) id, key2); + } + } + } } } + + // Process results set. + g_hash_table_iter_init (&iter1, results); + for (i = 0; g_hash_table_iter_next (&iter1, &key1, &val1); i++) { + func (source, op_id, GRL_MEDIA (g_object_ref (val1)), --remaining, user_data, NULL); + } } G_DEFINE_TYPE_WITH_CODE (SimpleDMAPDb, simple_dmap_db, G_TYPE_OBJECT, @@ -132,13 +368,26 @@ simple_dmap_db_constructor (GType type, guint n_construct_params, GObjectConstru return object; } -static void simple_dmap_db_init (SimpleDMAPDb *db) +static void +simple_dmap_db_init (SimpleDMAPDb *db) { db->priv = SIMPLE_DMAP_DB_GET_PRIVATE (db); - db->priv->db = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - g_object_unref); + + db->priv->albums_box = g_object_new (GRL_TYPE_MEDIA_BOX, NULL); + db->priv->artists_box = g_object_new (GRL_TYPE_MEDIA_BOX, NULL); + + grl_media_set_id (GRL_MEDIA (db->priv->albums_box), ALBUMS_ID); + grl_media_set_title (GRL_MEDIA (db->priv->albums_box), _(ALBUMS_NAME)); + + grl_media_set_id (GRL_MEDIA (db->priv->artists_box), ARTISTS_ID); + grl_media_set_title (GRL_MEDIA (db->priv->artists_box), _(ARTISTS_NAME)); + + db->priv->root = g_hash_table_new_full (box_hash, box_equal, g_object_unref, (GDestroyNotify) g_hash_table_destroy); + db->priv->albums = g_hash_table_new_full (box_hash, box_equal, g_object_unref, (GDestroyNotify) g_hash_table_destroy); + db->priv->artists = g_hash_table_new_full (box_hash, box_equal, g_object_unref, (GDestroyNotify) g_hash_table_destroy); + + g_hash_table_insert (db->priv->root, g_object_ref (db->priv->albums_box), db->priv->albums); + g_hash_table_insert (db->priv->root, g_object_ref (db->priv->artists_box), db->priv->artists); } static void @@ -146,10 +395,13 @@ simple_dmap_db_finalize (GObject *object) { SimpleDMAPDb *db = SIMPLE_DMAP_DB (object); - g_debug ("Finalizing SimpleDMAPDb (%d records)", - g_hash_table_size (db->priv->db)); + g_debug ("Finalizing SimpleDMAPDb"); - g_hash_table_destroy (db->priv->db); + g_object_unref (db->priv->albums_box); + g_object_unref (db->priv->artists_box); + + g_hash_table_destroy (db->priv->albums); + g_hash_table_destroy (db->priv->artists); } static void @@ -179,7 +431,8 @@ simple_dmap_db_get_property (GObject *object, } -static void simple_dmap_db_class_init (SimpleDMAPDbClass *klass) +static void +simple_dmap_db_class_init (SimpleDMAPDbClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); diff --git a/src/dmap/simple-dmap-db.h b/src/dmap/simple-dmap-db.h index f0e5ae5..fd890ae 100644 --- a/src/dmap/simple-dmap-db.h +++ b/src/dmap/simple-dmap-db.h @@ -22,6 +22,7 @@ #define __SIMPLE_DMAP_DB #include <libdmapsharing/dmap.h> +#include <grilo.h> G_BEGIN_DECLS @@ -65,13 +66,22 @@ typedef struct { GObjectClass parent; } SimpleDMAPDbClass; -void simple_dmap_db_filtered_foreach (SimpleDMAPDb *db, - guint skip, - guint count, - GHRFunc predicate, - gpointer pred_user_data, - GHFunc func, - gpointer user_data); +void simple_dmap_db_browse (SimpleDMAPDb *db, + GrlMedia *container, + GrlSource *source, + guint op_id, + guint skip, + guint count, + GrlSourceResultCb func, + gpointer user_data); + +void simple_dmap_db_search (SimpleDMAPDb *db, + GrlSource *source, + guint op_id, + GHRFunc predicate, + gpointer pred_user_data, + GrlSourceResultCb func, + gpointer user_data); SimpleDMAPDb *simple_dmap_db_new (void); -- 1.8.5.3 _______________________________________________ grilo-list mailing list grilo-list@gnome.org https://mail.gnome.org/mailman/listinfo/grilo-list