[grilo] core: Merge GrlMediaSource and GrlMetadataSource into GrlSource
- From: Juan A. Suarez Romero <jasuarez src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [grilo] core: Merge GrlMediaSource and GrlMetadataSource into GrlSource
- Date: Mon, 6 Aug 2012 16:50:47 +0000 (UTC)
commit b9b02d0cff289c8e88f3555cceda8154fd295bf3
Author: Juan A. Suarez Romero <jasuarez igalia com>
Date: Fri Jun 3 08:32:35 2011 +0200
core: Merge GrlMediaSource and GrlMetadataSource into GrlSource
It merges both types of sources in just one, improving also the full
resolution algorithm.
bindings/vala/grilo-0.2-custom.vala | 64 +-
bindings/vala/grilo-0.2.metadata | 50 +-
bindings/vala/grilo-uninstalled.files.in | 3 -
doc/grilo/Makefile.am | 1 -
examples/browsing.c | 14 +-
examples/efficient-metadata-resolution.c | 22 +-
examples/multivalues.c | 4 +-
examples/searching.c | 4 +-
src/Makefile.am | 15 +-
src/grilo.h | 3 +-
src/grl-error.h | 6 +-
src/grl-log-priv.h | 2 -
src/grl-log.c | 4 -
src/grl-media-source.c | 2931 ----------------------
src/grl-media-source.h | 521 ----
src/grl-metadata-source.c | 694 ------
src/grl-metadata-source.h | 252 --
src/grl-multiple.c | 82 +-
src/grl-multiple.h | 8 +-
src/grl-operation-options.c | 4 +-
src/grl-operation-options.h | 22 +-
src/grl-plugin-registry.h | 4 +-
src/grl-plugin.c | 2 +-
src/grl-source-priv.h | 75 -
src/grl-source.c | 3964 ++++++++++++++++++++++++++++--
src/grl-source.h | 480 ++++-
tools/grilo-inspect/grl-inspect.c | 13 +-
tools/grilo-test-ui/main.c | 200 +-
tools/vala/grilo-test.vala | 14 +-
29 files changed, 4458 insertions(+), 5000 deletions(-)
---
diff --git a/bindings/vala/grilo-0.2-custom.vala b/bindings/vala/grilo-0.2-custom.vala
index d3e998a..fad2a0f 100644
--- a/bindings/vala/grilo-0.2-custom.vala
+++ b/bindings/vala/grilo-0.2-custom.vala
@@ -1,77 +1,77 @@
namespace Grl {
[CCode (instance_pos = 3.1)]
- public delegate void MediaSourceMetadataCb (Grl.MediaSource source, uint operation_id, Grl.Media? media, GLib.Error error);
+ public delegate void SourceResolveCb (Grl.Source source, uint operation_id, Grl.Media? media, GLib.Error error);
[CCode (instance_pos = 2.1)]
- public delegate void MediaSourceRemoveCb (Grl.MediaSource source, Grl.Media? media, GLib.Error error);
+ public delegate void SourceRemoveCb (Grl.Source source, Grl.Media? media, GLib.Error error);
[CCode (instance_pos = 4.1)]
- public delegate void MediaSourceResultCb (Grl.MediaSource source, uint operation_id, Grl.Media? media, uint remaining, GLib.Error? error);
+ public delegate void SourceResultCb (Grl.Source source, uint operation_id, Grl.Media? media, uint remaining, GLib.Error? error);
[CCode (instance_pos = 3.1)]
- public delegate void MediaSourceStoreCb (Grl.MediaSource source, Grl.MediaBox? parent, Grl.Media? media, GLib.Error? error);
+ public delegate void SourceStoreCb (Grl.Source source, Grl.MediaBox? parent, Grl.Media? media, GLib.Error? error);
[CCode (instance_pos = 3.1)]
- public delegate void MetadataSourceResolveCb (Grl.MetadataSource source, uint operation_id, Grl.Media? media, GLib.Error? error);
+ public delegate void SourceResolveCb (Grl.Source source, uint operation_id, Grl.Media? media, GLib.Error? error);
[CCode (instance_pos = 3.1)]
- public delegate void MetadataSourceSetMetadataCb (Grl.MetadataSource source, Grl.Media? media, GLib.List failed_keys, GLib.Error? error);
+ public delegate void SourceStoreMetadataCb (Grl.Source source, Grl.Media? media, GLib.List failed_keys, GLib.Error? error);
[Compact]
public class MetadataKey {
[CCode (cname ="GRL_METADATA_KEY_ALBUM")]
- public static GLib.ParamSpec ALBUM;
+ public GLib.ParamSpec ALBUM;
[CCode (cname ="GRL_METADATA_KEY_ARTIST")]
- public static GLib.ParamSpec ARTIST;
+ public GLib.ParamSpec ARTIST;
[CCode (cname ="GRL_METADATA_KEY_AUTHOR")]
- public static GLib.ParamSpec AUTHOR;
+ public GLib.ParamSpec AUTHOR;
[CCode (cname ="GRL_METADATA_KEY_BITRATE")]
- public static GLib.ParamSpec BITRATE;
+ public GLib.ParamSpec BITRATE;
[CCode (cname ="GRL_METADATA_KEY_CERTIFICATE")]
- public static GLib.ParamSpec CERTIFICATE;
+ public GLib.ParamSpec CERTIFICATE;
[CCode (cname ="GRL_METADATA_KEY_CHILDCOUNT")]
- public static GLib.ParamSpec CHILDCOUNT;
+ public GLib.ParamSpec CHILDCOUNT;
[CCode (cname ="GRL_METADATA_KEY_DATE")]
- public static GLib.ParamSpec DATE;
+ public GLib.ParamSpec DATE;
[CCode (cname ="GRL_METADATA_KEY_DESCRIPTION")]
- public static GLib.ParamSpec DESCRIPTION;
+ public GLib.ParamSpec DESCRIPTION;
[CCode (cname ="GRL_METADATA_KEY_DURATION")]
- public static GLib.ParamSpec DURATION;
+ public GLib.ParamSpec DURATION;
[CCode (cname ="GRL_METADATA_KEY_EXTERNAL_PLAYER")]
- public static GLib.ParamSpec EXTERNAL_PLAYER;
+ public GLib.ParamSpec EXTERNAL_PLAYER;
[CCode (cname ="GRL_METADATA_KEY_EXTERNAL_URL")]
- public static GLib.ParamSpec EXTERNAL_URL;
+ public GLib.ParamSpec EXTERNAL_URL;
[CCode (cname ="GRL_METADATA_KEY_FRAMERATE")]
- public static GLib.ParamSpec FRAMERATE;
+ public GLib.ParamSpec FRAMERATE;
[CCode (cname ="GRL_METADATA_KEY_GENRE")]
- public static GLib.ParamSpec GENRE;
+ public GLib.ParamSpec GENRE;
[CCode (cname ="GRL_METADATA_KEY_HEIGHT")]
- public static GLib.ParamSpec HEIGHT;
+ public GLib.ParamSpec HEIGHT;
[CCode (cname ="GRL_METADATA_KEY_ID")]
public static GLib.ParamSpec ID;
[CCode (cname ="GRL_METADATA_KEY_LAST_PLAYED")]
- public static GLib.ParamSpec LAST_PLAYED;
+ public GLib.ParamSpec LAST_PLAYED;
[CCode (cname ="GRL_METADATA_KEY_LAST_POSITION")]
- public static GLib.ParamSpec LAST_POSITION;
+ public GLib.ParamSpec LAST_POSITION;
[CCode (cname ="GRL_METADATA_KEY_LICENSE")]
- public static GLib.ParamSpec LICENSE;
+ public GLib.ParamSpec LICENSE;
[CCode (cname ="GRL_METADATA_KEY_LYRICS")]
- public static GLib.ParamSpec LYRICS;
+ public GLib.ParamSpec LYRICS;
[CCode (cname ="GRL_METADATA_KEY_MIME")]
- public static GLib.ParamSpec MIME;
+ public GLib.ParamSpec MIME;
[CCode (cname ="GRL_METADATA_KEY_PLAY_COUNT")]
- public static GLib.ParamSpec PLAY_COUNT;
+ public GLib.ParamSpec PLAY_COUNT;
[CCode (cname ="GRL_METADATA_KEY_RATING")]
- public static GLib.ParamSpec RATING;
+ public GLib.ParamSpec RATING;
[CCode (cname ="GRL_METADATA_KEY_SITE")]
- public static GLib.ParamSpec SITE;
+ public GLib.ParamSpec SITE;
[CCode (cname ="GRL_METADATA_KEY_SOURCE")]
- public static GLib.ParamSpec SOURCE;
+ public GLib.ParamSpec SOURCE;
[CCode (cname ="GRL_METADATA_KEY_STUDIO")]
- public static GLib.ParamSpec STUDIO;
+ public GLib.ParamSpec STUDIO;
[CCode (cname ="GRL_METADATA_KEY_THUMBNAIL")]
- public static GLib.ParamSpec THUMBNAIL;
+ public GLib.ParamSpec THUMBNAIL;
[CCode (cname ="GRL_METADATA_KEY_TITLE")]
public static GLib.ParamSpec TITLE;
[CCode (cname ="GRL_METADATA_KEY_URL")]
public static GLib.ParamSpec URL;
[CCode (cname ="GRL_METADATA_KEY_WIDTH")]
- public static GLib.ParamSpec WIDTH;
+ public GLib.ParamSpec WIDTH;
public static unowned GLib.List list_new (GLib.ParamSpec p, ...);
}
diff --git a/bindings/vala/grilo-0.2.metadata b/bindings/vala/grilo-0.2.metadata
index 2c7de4f..4c59024 100644
--- a/bindings/vala/grilo-0.2.metadata
+++ b/bindings/vala/grilo-0.2.metadata
@@ -13,32 +13,25 @@ grl_plugin_registry_get_sources_by_operations type_arguments="unowned Plugin" tr
# GrlPlugin
grl_plugin_get_info_keys type_arguments="unowned string" transfer_ownership="1"
-#GrlSource
+# GrlSource
grl_source_slow_keys type_arguments="unowned KeyID" transfer_ownership="0"
grl_source_supported_keys type_arguments="unowned KeyID" transfer_ownership="0"
grl_source_writable_keys type_arguments="unowned KeyID" transfer_ownership="0"
-
-# GrlMediaSource
-grl_media_source_browse.keys type_arguments="KeyID"
-grl_media_source_browse_sync type_arguments="Media" transfer_ownership="1"
-grl_media_source_browse_sync.keys type_arguments="KeyID"
-grl_media_source_metadata.keys type_arguments="KeyID"
-grl_media_source_metadata_sync transfer_ownership="1"
-grl_media_source_metadata_sync.keys type_arguments="KeyID"
-grl_media_source_query.keys type_arguments="KeyID"
-grl_media_source_query_sync type_arguments="Media" transfer_ownership="1"
-grl_media_source_query_sync.keys type_arguments="KeyID"
-grl_media_source_search.keys type_arguments="KeyID"
-grl_media_source_search_sync type_arguments="Media" transfer_ownership="1"
-grl_media_source_search_sync.keys type_arguments="KeyID"
-
-# GrlMetadataSource
-grl_metadata_source_may_resolve.missing_keys type_arguments="unowned KeyID" is_out="1" transfer_ownership="1"
-grl_metadata_source_resolve.keys type_arguments="KeyID"
-grl_metadata_source_resolve_sync.keys type_arguments="KeyID"
-grl_metadata_source_set_metadata.keys type_arguments="KeyID"
-grl_metadata_source_set_metadata_sync.keys type_arguments="KeyID"
-grl_metadata_source_key_depends type_arguments="unowned KeyID" transfer_ownership="0"
+grl_source_browse.keys type_arguments="KeyID"
+grl_source_browse_sync type_arguments="Media" transfer_ownership="1"
+grl_source_browse_sync.keys type_arguments="KeyID"
+grl_source_query.keys type_arguments="KeyID"
+grl_source_query_sync type_arguments="Media" transfer_ownership="1"
+grl_source_query_sync.keys type_arguments="KeyID"
+grl_source_search.keys type_arguments="KeyID"
+grl_source_search_sync type_arguments="Media" transfer_ownership="1"
+grl_source_search_sync.keys type_arguments="KeyID"
+grl_source_may_resolve.missing_keys type_arguments="unowned KeyID" is_out="1" transfer_ownership="1"
+grl_source_resolve.keys type_arguments="KeyID"
+grl_source_resolve_sync.keys type_arguments="KeyID"
+grl_source_store_metadata.keys type_arguments="KeyID"
+grl_source_set_metadata_sync.keys type_arguments="KeyID"
+grl_source_key_depends type_arguments="unowned KeyID" transfer_ownership="0"
# GrlData
grl_data_get_keys type_arguments="unowned KeyID" transfer_ownership="1"
@@ -47,12 +40,11 @@ grl_data_get_keys type_arguments="unowned KeyID" transfer_ownership="1"
GrlCoreError errordomain="1"
# Callbacks
-GrlMediaSourceMetadataCb hidden="1"
-GrlMediaSourceRemoveCb hidden="1"
-GrlMediaSourceResultCb hidden="1"
-GrlMediaSourceStoreCb hidden="1"
-GrlMetadataSourceResolveCb hidden="1"
-GrlMetadataSourceSetMetadataCb hidden="1"
+GrlSourceRemoveCb hidden="1"
+GrlSourceResultCb hidden="1"
+GrlSourceStoreCb hidden="1"
+GrlSourceStoreMetadataCb hidden="1"
+GrlSourceResolveCb hidden="1"
# Util
grl_list_from_va ellipsis="1"
diff --git a/bindings/vala/grilo-uninstalled.files.in b/bindings/vala/grilo-uninstalled.files.in
index e1e99f2..5ea9b65 100644
--- a/bindings/vala/grilo-uninstalled.files.in
+++ b/bindings/vala/grilo-uninstalled.files.in
@@ -3,14 +3,11 @@
@top_builddir@/src/grl-log.h
@top_builddir@/src/grl-plugin.h
@top_builddir@/src/grl-source.h
- top_builddir@/src/grl-media-source.h
@top_builddir@/src/grl-metadata-key.h
@top_builddir@/src/grl-caps.h
@top_builddir@/src/grl-operation-options.h
- top_builddir@/src/grl-metadata-source.h
@top_builddir@/src/grl-multiple.h
@top_builddir@/src/grl-plugin-registry.h
@top_builddir@/src/grl-util.h
@top_builddir@/src/data/
@top_builddir@/src/.libs/libgrilo-0.2.so
-
diff --git a/doc/grilo/Makefile.am b/doc/grilo/Makefile.am
index 1e47c42..7157aa6 100644
--- a/doc/grilo/Makefile.am
+++ b/doc/grilo/Makefile.am
@@ -60,7 +60,6 @@ EXTRA_HFILES=
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
IGNORE_HFILES=config.h \
grl-media-plugin-priv.h \
- grl-metadata-source-priv.h \
grl-operations-priv.h \
grl-sync-priv.h \
grl-metadata-key-priv.h \
diff --git a/examples/browsing.c b/examples/browsing.c
index d9459da..78967e0 100644
--- a/examples/browsing.c
+++ b/examples/browsing.c
@@ -20,7 +20,7 @@ GRL_LOG_DOMAIN_STATIC(example_log_domain);
5) User data passed to the grl_media_source_browse method.
6) A GError if an error happened, NULL otherwise */
static void
-browse_cb (GrlMediaSource *source,
+browse_cb (GrlSource *source,
guint browse_id,
GrlMedia *media,
guint remaining,
@@ -96,12 +96,12 @@ source_added_cb (GrlPluginRegistry *registry, GrlSource *source, gpointer user_d
grl_operation_options_set_count (options, 5);
grl_operation_options_set_flags (options, GRL_RESOLVE_IDLE_RELAY);
- grl_media_source_browse (GRL_MEDIA_SOURCE (source),
- NULL,
- keys,
- options,
- browse_cb,
- NULL);
+ grl_source_browse (source,
+ NULL,
+ keys,
+ options,
+ browse_cb,
+ NULL);
g_object_unref (caps);
g_object_unref (options);
}
diff --git a/examples/efficient-metadata-resolution.c b/examples/efficient-metadata-resolution.c
index 79a6b06..a5fdc9d 100644
--- a/examples/efficient-metadata-resolution.c
+++ b/examples/efficient-metadata-resolution.c
@@ -14,14 +14,14 @@ GRL_LOG_DOMAIN_STATIC(example_log_domain);
const gchar *target_source_id = NULL;
static void
-metadata_cb (GrlMediaSource *source,
- guint metadata_id,
- GrlMedia *media,
- gpointer user_data,
- const GError *error)
+resolve_cb (GrlSource *source,
+ guint operation_id,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
{
if (error)
- g_error ("Metadata operation failed. Reason: %s", error->message);
+ g_error ("Resolve operation failed. Reason: %s", error->message);
const gchar *url = grl_media_get_url (media);
g_debug ("\tURL: %s", url);
@@ -30,7 +30,7 @@ metadata_cb (GrlMediaSource *source,
}
static void
-search_cb (GrlMediaSource *source,
+search_cb (GrlSource *source,
guint browse_id,
GrlMedia *media,
guint remaining,
@@ -59,14 +59,14 @@ search_cb (GrlMediaSource *source,
GrlCaps *caps;
GList *keys = grl_metadata_key_list_new (GRL_METADATA_KEY_URL, NULL);
- caps = grl_source_get_caps (GRL_SOURCE (source), GRL_OP_METADATA);
+ caps = grl_source_get_caps (source, GRL_OP_RESOLVE);
options = grl_operation_options_new (caps);
grl_operation_options_set_flags (options, GRL_RESOLVE_IDLE_RELAY);
- grl_media_source_metadata (source,
+ grl_source_resolve (source,
media,
keys,
options,
- metadata_cb,
+ resolve_cb,
NULL);
g_object_unref (caps);
g_object_unref (options);
@@ -102,7 +102,7 @@ source_added_cb (GrlPluginRegistry *registry, GrlSource *source, gpointer user_d
/* Retrieve the first media from the source matching the text "rock" */
g_debug ("Searching \"rock\" in \"%s\"", source_id);
- grl_media_source_search (GRL_MEDIA_SOURCE (source),
+ grl_source_search (source,
"rock",
keys,
options,
diff --git a/examples/multivalues.c b/examples/multivalues.c
index 0043afb..429a846 100644
--- a/examples/multivalues.c
+++ b/examples/multivalues.c
@@ -12,7 +12,7 @@
GRL_LOG_DOMAIN_STATIC(example_log_domain);
static void
-search_cb (GrlMediaSource *source,
+search_cb (GrlSource *source,
guint browse_id,
GrlMedia *media,
guint remaining,
@@ -77,7 +77,7 @@ source_added_cb (GrlPluginRegistry *registry, GrlSource *source, gpointer user_d
grl_operation_options_set_flags (options, GRL_RESOLVE_IDLE_RELAY);
g_debug ("Searching \"rock\" in Youtube");
- grl_media_source_search (GRL_MEDIA_SOURCE (source),
+ grl_source_search (source,
"rock",
keys,
options,
diff --git a/examples/searching.c b/examples/searching.c
index 45b4ce7..540f03e 100644
--- a/examples/searching.c
+++ b/examples/searching.c
@@ -11,7 +11,7 @@
GRL_LOG_DOMAIN_STATIC(example_log_domain);
static void
-search_cb (GrlMediaSource *source,
+search_cb (GrlSource *source,
guint browse_id,
GrlMedia *media,
guint remaining,
@@ -73,7 +73,7 @@ source_added_cb (GrlPluginRegistry *registry, GrlSource *source, gpointer user_d
grl_operation_options_set_flags (options, GRL_RESOLVE_IDLE_RELAY);
g_debug ("Searching \"rock\" in Jamendo");
- grl_media_source_search (GRL_MEDIA_SOURCE (source),
+ grl_source_search (source,
"rock",
keys,
options,
diff --git a/src/Makefile.am b/src/Makefile.am
index 4a6bc7e..0b3b636 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,7 +15,7 @@ grl-marshal.c: grl-marshal.h grl-marshal.list
$(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix grl_marshal \
--body grl-marshal.list >> $@
-enum_headers = grl-media-source.h grl-caps.h
+enum_headers = grl-source.h grl-caps.h grl-operation-options.h
grl-type-builtins.h: $(enum_headers) grl-type-builtins.h.template
$(AM_V_GEN) $(GLIB_MKENUMS) --template grl-type-builtins.h.template \
@@ -43,16 +43,14 @@ lib GRL_NAME@_la_LDFLAGS = \
lib GRL_NAME@_la_SOURCES = \
grl-plugin.c grl-plugin-priv.h \
grl-plugin-registry.c grl-plugin-registry-priv.h \
- grl-source.c grl-source-priv.h \
grl-metadata-key.c grl-metadata-key-priv.h \
- grl-metadata-source.c \
- grl-operation.c grl-operation.h \
grl-type-builtins.c grl-type-builtins.h \
grl-marshal.c grl-marshal.h \
- grl-media-source.c grl-util.c \
- grl-multiple.c \
+ grl-operation.c grl-operation.h \
+ grl-operation-priv.h grl-sync.c \
+ grl-source.c \
+ grl-util.c grl-multiple.c \
grl-log.c grl-log-priv.h \
- grl-sync.c \
grl-value-helper.c \
grl-caps.c \
grl-operation-options.c grl-operation-options-priv.h \
@@ -81,8 +79,6 @@ lib GRL_NAME@inc_HEADERS = \
grl-plugin-registry.h \
grl-metadata-key.h \
grl-source.h \
- grl-metadata-source.h \
- grl-media-source.h \
grl-log.h \
grl-multiple.h \
grl-util.h \
@@ -108,7 +104,6 @@ lib GRL_NAME@inc_HEADERS += $(data_h_headers)
noinst_HEADERS = \
grl-plugin-registry-priv.h \
grl-plugin-priv.h \
- grl-source-priv.h \
grl-metadata-key-priv.h \
grl-operation-priv.h \
grl-sync-priv.h \
diff --git a/src/grilo.h b/src/grilo.h
index b424571..dce9115 100644
--- a/src/grilo.h
+++ b/src/grilo.h
@@ -31,8 +31,6 @@
#include <grl-log.h>
#include <grl-plugin-registry.h>
#include <grl-plugin.h>
-#include <grl-media-source.h>
-#include <grl-metadata-source.h>
#include <grl-metadata-key.h>
#include <grl-data.h>
#include <grl-media.h>
@@ -42,6 +40,7 @@
#include <grl-media-box.h>
#include <grl-config.h>
#include <grl-related-keys.h>
+#include <grl-source.h>
#include <grl-multiple.h>
#include <grl-util.h>
#include <grl-definitions.h>
diff --git a/src/grl-error.h b/src/grl-error.h
index 256aeed..cddf0af 100644
--- a/src/grl-error.h
+++ b/src/grl-error.h
@@ -42,12 +42,11 @@
* @GRL_CORE_ERROR_SEARCH_FAILED: The search operation failed
* @GRL_CORE_ERROR_SEARCH_NULL_UNSUPPORTED: Searching NULL-text is not supported
* @GRL_CORE_ERROR_QUERY_FAILED: The query operation failed
- * @GRL_CORE_ERROR_METADATA_FAILED: The metadata search failed
* @GRL_CORE_ERROR_RESOLVE_FAILED: The resolution operation failed
* @GRL_CORE_ERROR_MEDIA_NOT_FOUND: The media was not found
* @GRL_CORE_ERROR_STORE_FAILED: The store operation failed
+ * @GRL_CORE_ERROR_STORE_METADATA_FAILED: The store metadata operation failed
* @GRL_CORE_ERROR_REMOVE_FAILED: The removal operation failed
- * @GRL_CORE_ERROR_SET_METADATA_FAILED: The set metadata operation failed
* @GRL_CORE_ERROR_MEDIA_FROM_URI_FAILED: The media from_uri operation failed
* @GRL_CORE_ERROR_CONFIG_LOAD_FAILED: Failed to load plugin configuration from a file
* @GRL_CORE_ERROR_CONFIG_FAILED: Failed to set configuration for plugin
@@ -65,12 +64,11 @@ typedef enum {
GRL_CORE_ERROR_SEARCH_FAILED,
GRL_CORE_ERROR_SEARCH_NULL_UNSUPPORTED,
GRL_CORE_ERROR_QUERY_FAILED,
- GRL_CORE_ERROR_METADATA_FAILED,
GRL_CORE_ERROR_RESOLVE_FAILED,
GRL_CORE_ERROR_MEDIA_NOT_FOUND,
GRL_CORE_ERROR_STORE_FAILED,
+ GRL_CORE_ERROR_STORE_METADATA_FAILED,
GRL_CORE_ERROR_REMOVE_FAILED,
- GRL_CORE_ERROR_SET_METADATA_FAILED,
GRL_CORE_ERROR_MEDIA_FROM_URI_FAILED,
GRL_CORE_ERROR_CONFIG_LOAD_FAILED,
GRL_CORE_ERROR_CONFIG_FAILED,
diff --git a/src/grl-log-priv.h b/src/grl-log-priv.h
index d6049a2..d0e6c6d 100644
--- a/src/grl-log-priv.h
+++ b/src/grl-log-priv.h
@@ -35,8 +35,6 @@ GRL_LOG_DOMAIN_EXTERN(data_log_domain);
GRL_LOG_DOMAIN_EXTERN(media_log_domain);
GRL_LOG_DOMAIN_EXTERN(plugin_log_domain);
GRL_LOG_DOMAIN_EXTERN(source_log_domain);
-GRL_LOG_DOMAIN_EXTERN(media_source_log_domain);
-GRL_LOG_DOMAIN_EXTERN(metadata_source_log_domain);
GRL_LOG_DOMAIN_EXTERN(multiple_log_domain);
GRL_LOG_DOMAIN_EXTERN(plugin_registry_log_domain);
diff --git a/src/grl-log.c b/src/grl-log.c
index 45127af..0cbc7ca 100644
--- a/src/grl-log.c
+++ b/src/grl-log.c
@@ -328,8 +328,6 @@ _grl_log_init_core_domains (void)
DOMAIN_INIT (media_log_domain, "media");
DOMAIN_INIT (plugin_log_domain, "plugin");
DOMAIN_INIT (source_log_domain, "source");
- DOMAIN_INIT (media_source_log_domain, "media-source");
- DOMAIN_INIT (metadata_source_log_domain, "metadata-source");
DOMAIN_INIT (multiple_log_domain, "multiple");
DOMAIN_INIT (plugin_registry_log_domain, "plugin-registry");
@@ -373,9 +371,7 @@ _grl_log_free_core_domains (void)
DOMAIN_FREE (config_log_domain);
DOMAIN_FREE (media_log_domain);
DOMAIN_FREE (plugin_log_domain);
- DOMAIN_FREE (media_source_log_domain);
DOMAIN_FREE (source_log_domain);
- DOMAIN_FREE (metadata_source_log_domain);
DOMAIN_FREE (multiple_log_domain);
DOMAIN_FREE (plugin_registry_log_domain);
diff --git a/src/grl-multiple.c b/src/grl-multiple.c
index 32ca126..e7cf706 100644
--- a/src/grl-multiple.c
+++ b/src/grl-multiple.c
@@ -58,7 +58,7 @@ struct MultipleSearchData {
GList *sources_more;
gchar *text;
GrlOperationOptions *options;
- GrlMediaSourceResultCb user_callback;
+ GrlSourceResultCb user_callback;
gpointer user_data;
};
@@ -70,26 +70,24 @@ struct ResultCount {
};
struct CallbackData {
- GrlMediaSourceResultCb user_callback;
+ GrlSourceResultCb user_callback;
gpointer user_data;
};
struct MediaFromUriCallbackData {
gchar *uri;
- GrlMediaSourceMetadataCb user_callback;
+ GrlSourceResolveCb user_callback;
gpointer user_data;
};
-static void multiple_search_cb (GrlMediaSource *source,
- guint search_id,
- GrlMedia *media,
- guint remaining,
- gpointer user_data,
- const GError *error);
-static void multiple_search_cancel_cb (struct MultipleSearchData *msd);
-
+static void multiple_search_cb (GrlSource *source,
+ guint search_id,
+ GrlMedia *media,
+ guint remaining,
+ gpointer user_data,
+ const GError *error);
-/* ================= Globals ================= */
+static void multiple_search_cancel_cb (struct MultipleSearchData *msd);
/* ================ Utitilies ================ */
@@ -132,7 +130,7 @@ handle_no_searchable_sources_idle (gpointer user_data)
}
static void
-handle_no_searchable_sources (GrlMediaSourceResultCb callback, gpointer user_data)
+handle_no_searchable_sources (GrlSourceResultCb callback, gpointer user_data)
{
struct CallbackData *callback_data = g_new0 (struct CallbackData, 1);
callback_data->user_callback = callback;
@@ -148,7 +146,7 @@ start_multiple_search_operation (guint search_id,
const GList *skip_counts,
gint count,
GrlOperationOptions *options,
- GrlMediaSourceResultCb user_callback,
+ GrlSourceResultCb user_callback,
gpointer user_data)
{
GRL_DEBUG ("start_multiple_search_operation");
@@ -186,12 +184,12 @@ start_multiple_search_operation (guint search_id,
iter_skips = (GList *) skip_counts;
n = 0;
while (iter_sources) {
- GrlMediaSource *source;
+ GrlSource *source;
guint c, id;
struct ResultCount *rc;
guint skip;
- source = GRL_MEDIA_SOURCE (iter_sources->data);
+ source = GRL_SOURCE (iter_sources->data);
/* c is the count to use for this source */
c = (n == 0) ? first_count : individual_count;
@@ -215,13 +213,13 @@ start_multiple_search_operation (guint search_id,
skip = 0;
}
- source_caps = grl_source_get_caps (GRL_SOURCE (source), GRL_OP_SEARCH);
+ source_caps = grl_source_get_caps (source, GRL_OP_SEARCH);
grl_operation_options_obey_caps (options, source_caps, &source_options, NULL);
grl_operation_options_set_skip (source_options, skip);
grl_operation_options_set_count (source_options, rc->count);
/* Execute the search on this source */
- id = grl_media_source_search (source,
+ id = grl_source_search (source,
msd->text,
msd->keys,
source_options,
@@ -260,14 +258,14 @@ chain_multiple_search_operation (struct MultipleSearchData *old_msd)
GList *skip_list = NULL;
GList *source_iter;
struct ResultCount *rc;
- GrlMediaSource *source;
+ GrlSource *source;
struct MultipleSearchData *msd;
/* Compute skip parameter for each of the sources that can still
provide more results */
source_iter = old_msd->sources_more;
while (source_iter) {
- source = GRL_MEDIA_SOURCE (source_iter->data);
+ source = GRL_SOURCE (source_iter->data);
rc = (struct ResultCount *)
g_hash_table_lookup (old_msd->table, (gpointer) source);
skip_list = g_list_prepend (skip_list,
@@ -294,7 +292,7 @@ chain_multiple_search_operation (struct MultipleSearchData *old_msd)
}
static void
-multiple_result_async_cb (GrlMediaSource *source,
+multiple_result_async_cb (GrlSource *source,
guint op_id,
GrlMedia *media,
guint remaining,
@@ -327,14 +325,14 @@ multiple_result_async_cb (GrlMediaSource *source,
}
static void
-multiple_search_cb (GrlMediaSource *source,
+multiple_search_cb (GrlSource *source,
guint search_id,
GrlMedia *media,
guint remaining,
gpointer user_data,
const GError *error)
{
- GRL_DEBUG ("multiple_search_cb");
+ GRL_DEBUG (__FUNCTION__);
struct MultipleSearchData *msd;
gboolean emit;
@@ -452,7 +450,6 @@ multiple_search_cb (GrlMediaSource *source,
operation_done:
GRL_DEBUG ("Multiple operation finished (%u)", msd->search_id);
-
grl_operation_remove (msd->search_id);
}
@@ -465,11 +462,11 @@ free_media_from_uri_data (struct MediaFromUriCallbackData *mfucd)
}
static void
-media_from_uri_cb (GrlMediaSource *source,
+media_from_uri_cb (GrlSource *source,
guint operation_id,
- GrlMedia *media,
- gpointer user_data,
- const GError *error)
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
{
struct MediaFromUriCallbackData *mfucd =
(struct MediaFromUriCallbackData *) user_data;
@@ -495,8 +492,8 @@ media_from_uri_cb (GrlMediaSource *source,
/**
* grl_multiple_search:
- * @sources: (element-type Grl.MediaSource) (allow-none):
- * a #GList of #GrlMediaSource<!-- -->s to search from (%NULL for all
+ * @sources: (element-type Grl.Source) (allow-none):
+ * a #GList of #GrlSource<!-- -->s to search from (%NULL for all
* searchable sources)
* @text: the text to search for
* @keys: (element-type GrlKeyID): the #GList of
@@ -508,7 +505,7 @@ media_from_uri_cb (GrlMediaSource *source,
* Search for @text in all the sources specified in @sources.
*
* If @text is @NULL then NULL-text searchs will be used for each searchable
- * plugin (see #grl_media_source_search for more details).
+ * plugin (see #grl_source_search for more details).
*
* This method is asynchronous.
*
@@ -521,7 +518,7 @@ grl_multiple_search (const GList *sources,
const gchar *text,
const GList *keys,
GrlOperationOptions *options,
- GrlMediaSourceResultCb callback,
+ GrlSourceResultCb callback,
gpointer user_data)
{
GrlPluginRegistry *registry;
@@ -533,7 +530,7 @@ grl_multiple_search (const GList *sources,
GRL_DEBUG ("grl_multiple_search");
g_return_val_if_fail (callback != NULL, 0);
- g_return_val_if_fail (grl_operation_options_get_count (options) != 0, 0);
+ g_return_val_if_fail (GRL_IS_OPERATION_OPTIONS (options), 0);
/* If no sources have been provided then get the list of all
searchable sources from the registry */
@@ -598,8 +595,8 @@ multiple_search_cancel_cb (struct MultipleSearchData *msd)
/**
* grl_multiple_search_sync:
- * @sources: (element-type Grl.MediaSource) (allow-none):
- * a #GList of #GrlMediaSource<!-- -->s where to search from (%NULL for all
+ * @sources: (element-type Grl.Source) (allow-none):
+ * a #GList of #GrlSource<!-- -->s where to search from (%NULL for all
* available sources with search capability)
* @text: the text to search for
* @keys: (element-type GrlKeyID): the #GList of
@@ -668,10 +665,10 @@ grl_multiple_search_sync (const GList *sources,
*/
void
grl_multiple_get_media_from_uri (const gchar *uri,
- const GList *keys,
- GrlOperationOptions *options,
- GrlMediaSourceMetadataCb callback,
- gpointer user_data)
+ const GList *keys,
+ GrlOperationOptions *options,
+ GrlSourceResolveCb callback,
+ gpointer user_data)
{
GrlPluginRegistry *registry;
GList *sources, *iter;
@@ -680,6 +677,7 @@ grl_multiple_get_media_from_uri (const gchar *uri,
g_return_if_fail (uri != NULL);
g_return_if_fail (keys != NULL);
g_return_if_fail (callback != NULL);
+ g_return_if_fail (GRL_IS_OPERATION_OPTIONS (options));
registry = grl_plugin_registry_get_default ();
sources =
@@ -690,8 +688,8 @@ grl_multiple_get_media_from_uri (const gchar *uri,
/* Look for the first source that knows how to deal with 'uri' */
iter = sources;
while (iter && !found) {
- GrlMediaSource *source = GRL_MEDIA_SOURCE (iter->data);
- if (grl_media_source_test_media_from_uri (source, uri)) {
+ GrlSource *source = GRL_SOURCE (iter->data);
+ if (grl_source_test_media_from_uri (source, uri)) {
struct MediaFromUriCallbackData *mfucd =
g_new0 (struct MediaFromUriCallbackData, 1);
@@ -699,7 +697,7 @@ grl_multiple_get_media_from_uri (const gchar *uri,
mfucd->user_data = user_data;
mfucd->uri = g_strdup (uri);
- grl_media_source_get_media_from_uri (source,
+ grl_source_get_media_from_uri (source,
uri,
keys,
options,
diff --git a/src/grl-multiple.h b/src/grl-multiple.h
index cb7bd44..dc29bb8 100644
--- a/src/grl-multiple.h
+++ b/src/grl-multiple.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, 2011 Igalia S.L.
+ * Copyright (C) 2010 Igalia S.L.
*
* Contact: Iago Toral Quiroga <itoral igalia com>
*
@@ -29,14 +29,14 @@
#include <glib.h>
-#include "grl-media-source.h"
+#include "grl-source.h"
#include "grl-operation-options.h"
guint grl_multiple_search (const GList *sources,
const gchar *text,
const GList *keys,
GrlOperationOptions *options,
- GrlMediaSourceResultCb callback,
+ GrlSourceResultCb callback,
gpointer user_data);
GList *grl_multiple_search_sync (const GList *sources,
@@ -48,7 +48,7 @@ GList *grl_multiple_search_sync (const GList *sources,
void grl_multiple_get_media_from_uri (const gchar *uri,
const GList *keys,
GrlOperationOptions *options,
- GrlMediaSourceMetadataCb callback,
+ GrlSourceResolveCb callback,
gpointer user_data);
#endif
diff --git a/src/grl-operation-options.c b/src/grl-operation-options.c
index 9f681d8..b440f82 100644
--- a/src/grl-operation-options.c
+++ b/src/grl-operation-options.c
@@ -419,7 +419,7 @@ grl_operation_options_get_count (GrlOperationOptions *options)
*/
gboolean
grl_operation_options_set_flags (GrlOperationOptions *options,
- GrlMetadataResolutionFlags flags)
+ GrlResolutionFlags flags)
{
GValue value = { 0, };
@@ -440,7 +440,7 @@ grl_operation_options_set_flags (GrlOperationOptions *options,
* Returns: resolution flags of @options.
*
*/
-GrlMetadataResolutionFlags
+GrlResolutionFlags
grl_operation_options_get_flags (GrlOperationOptions *options)
{
const GValue *value = g_hash_table_lookup (options->priv->data,
diff --git a/src/grl-operation-options.h b/src/grl-operation-options.h
index fdf6f57..f06ecab 100644
--- a/src/grl-operation-options.h
+++ b/src/grl-operation-options.h
@@ -58,20 +58,32 @@ typedef struct {
#define GRL_OPERATION_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GRL_OPERATION_OPTIONS_TYPE, GrlOperationOptionsClass))
/**
- * GrlMetadataResolutionFlags:
+ * GrlResolutionFlags:
* @GRL_RESOLVE_NORMAL: Normal mode.
* @GRL_RESOLVE_FULL: Try other plugins if necessary.
* @GRL_RESOLVE_IDLE_RELAY: Use idle loop to relay results.
* @GRL_RESOLVE_FAST_ONLY: Only resolve fast metadata keys.
*
- * GrlMetadata resolution flags
+ * Resolution flags
*/
typedef enum {
GRL_RESOLVE_NORMAL = 0, /* Normal mode */
GRL_RESOLVE_FULL = (1 << 0), /* Try other plugins if necessary */
GRL_RESOLVE_IDLE_RELAY = (1 << 1), /* Use idle loop to relay results */
GRL_RESOLVE_FAST_ONLY = (1 << 2) /* Only resolve fast metadata keys */
-} GrlMetadataResolutionFlags;
+} GrlResolutionFlags;
+
+/**
+ * GrlWriteFlags:
+ * @GRL_WRITE_NORMAL: Normal mode.
+ * @GRL_WRITE_FULL: Try other plugins if necessary.
+ *
+ * Flags for writing operations.
+ */
+typedef enum {
+ GRL_WRITE_NORMAL = 0, /* Normal mode */
+ GRL_WRITE_FULL = (1 << 0) /* Try other plugins if necessary */
+} GrlWriteFlags;
#define GRL_COUNT_INFINITY (-1)
@@ -93,8 +105,8 @@ gboolean grl_operation_options_set_count (GrlOperationOptions *options, gint cou
gint grl_operation_options_get_count (GrlOperationOptions *options);
gboolean grl_operation_options_set_flags (GrlOperationOptions *options,
- GrlMetadataResolutionFlags flags);
-GrlMetadataResolutionFlags
+ GrlResolutionFlags flags);
+GrlResolutionFlags
grl_operation_options_get_flags (GrlOperationOptions *options);
gboolean grl_operation_options_set_type_filter (GrlOperationOptions *options,
diff --git a/src/grl-plugin-registry.h b/src/grl-plugin-registry.h
index 8b8e890..7079523 100644
--- a/src/grl-plugin-registry.h
+++ b/src/grl-plugin-registry.h
@@ -31,7 +31,7 @@
#include <glib-object.h>
#include <gmodule.h>
-#include <grl-media-source.h>
+#include <grl-source.h>
#include <grl-metadata-key.h>
#include <grl-config.h>
#include <grl-definitions.h>
@@ -141,7 +141,7 @@ typedef enum {
GRL_RANK_LOW = -32,
GRL_RANK_DEFAULT = 0,
GRL_RANK_HIGH = 32,
- GRL_RANK_HIGHEST = 64,
+ GRL_RANK_HIGHEST = 64
} GrlRank;
/* GrlPluginRegistry object */
diff --git a/src/grl-plugin.c b/src/grl-plugin.c
index 4f70a63..aa8e7b8 100644
--- a/src/grl-plugin.c
+++ b/src/grl-plugin.c
@@ -51,7 +51,7 @@ GRL_LOG_DOMAIN(plugin_log_domain);
enum {
PROP_0,
PROP_LOADED,
- PROP_LAST,
+ PROP_LAST
};
static GParamSpec *properties[PROP_LAST] = { 0 };
diff --git a/src/grl-source.c b/src/grl-source.c
index d15dde9..71046dc 100644
--- a/src/grl-source.c
+++ b/src/grl-source.c
@@ -23,7 +23,7 @@
/**
* SECTION:grl-source
* @short_description: Abstract base class for sources
- * @see_also: #GrlPlugin, #GrlMediaSource, #GrlMetadataSource, #GrlMedia
+ * @see_also: #GrlPlugin, #GrlSource, #GrlMedia
*
* GrlSource is the abstract base class needed to construct a source providing
* multimedia information that can be used in a Grilo application.
@@ -33,10 +33,11 @@
*/
#include "grl-source.h"
-#include "grl-source-priv.h"
-#include "grl-metadata-source.h"
+
#include "grl-operation.h"
#include "grl-operation-priv.h"
+#include "grl-marshal.h"
+#include "grl-type-builtins.h"
#include "grl-sync-priv.h"
#include "grl-plugin-registry.h"
#include "grl-error.h"
@@ -48,9 +49,9 @@
#define GRL_LOG_DOMAIN_DEFAULT source_log_domain
GRL_LOG_DOMAIN(source_log_domain);
-#define GRL_SOURCE_GET_PRIVATE(object) \
- (G_TYPE_INSTANCE_GET_PRIVATE((object), \
- GRL_TYPE_SOURCE, \
+#define GRL_SOURCE_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((object), \
+ GRL_TYPE_SOURCE, \
GrlSourcePrivate))
enum {
@@ -60,28 +61,155 @@ enum {
PROP_DESC,
PROP_PLUGIN,
PROP_RANK,
+ PROP_AUTO_SPLIT_THRESHOLD
+};
+
+enum {
+ SIG_CONTENT_CHANGED,
+ SIG_LAST
};
+static gint registry_signals[SIG_LAST];
+
+typedef void (*MediaDecorateCb) (GrlMedia *media,
+ gpointer user_data,
+ const GError *error);
+
struct _GrlSourcePrivate {
gchar *id;
gchar *name;
gchar *desc;
gint rank;
+ guint auto_split_threshold;
GrlPlugin *plugin;
};
+typedef struct {
+ GrlMedia *media;
+ gboolean is_ready;
+ gint remaining;
+ GError *error;
+} QueueElement;
+
+typedef struct {
+ GrlSource *source;
+ GList *required_keys;
+ gboolean being_queried;
+} MapNode;
+
+struct AutoSplitCtl {
+ gboolean chunk_first;
+ guint chunk_requested;
+ guint chunk_consumed;
+ guint threshold;
+ guint count;
+ guint total_remaining;
+ guint chunk_remaining;
+};
+
struct OperationState {
GrlSource *source;
guint operation_id;
gboolean cancelled;
gboolean completed;
+ gboolean started;
+};
+
+struct ResolveRelayCb {
+ GrlSource *source;
+ GrlSupportedOps operation_type;
+ guint operation_id;
+ GrlMedia *media;
+ GList *keys;
+ GrlOperationOptions *options;
+ GrlSourceResolveCb user_callback;
+ gpointer user_data;
+ GHashTable *map;
+ GHashTable *resolve_specs;
+ GList *specs_to_invoke;
+ gboolean cancel_invoked;
+ GError *error;
+ union {
+ GrlSourceResolveSpec *res;
+ GrlSourceMediaFromUriSpec *mfu;
+ } spec;
+};
+
+struct BrowseRelayCb {
+ GrlSource *source;
+ GrlSupportedOps operation_type;
+ guint operation_id;
+ GList *keys;
+ GrlOperationOptions *options;
+ GrlSourceResultCb user_callback;
+ gpointer user_data;
+ union {
+ GrlSourceBrowseSpec *browse;
+ GrlSourceSearchSpec *search;
+ GrlSourceQuerySpec *query;
+ } spec;
+ GQueue *queue;
+ gboolean dispatcher_running;
+ struct AutoSplitCtl *auto_split;
+};
+
+struct RemoveRelayCb {
+ GrlSource *source;
+ GrlMedia *media;
+ GrlSourceRemoveCb user_callback;
+ gpointer user_data;
+ GrlSourceRemoveSpec *spec;
+ GError *error;
+};
+
+struct StoreRelayCb {
+ GrlWriteFlags flags;
+ GrlSourceStoreCb user_callback;
+ gpointer user_data;
+};
+
+struct StoreMetadataRelayCb {
+ GrlSource *source;
+ GrlMedia *media;
+ GHashTable *map;
+ GList *use_sources;
+ GList *failed_keys;
+ GList *specs;
+ GrlSourceStoreCb user_callback;
+ gpointer user_data;
+};
+
+struct ResolveFullResolutionCtlCb {
+ GrlSourceResolveCb user_callback;
+ gpointer user_data;
+ GList *keys;
+ GrlResolutionFlags flags;
+ guint operation_id;
+};
+
+struct ResolveFullResolutionDoneCb {
+ GrlSourceResolveCb user_callback;
+ gpointer user_data;
+ GHashTable *pending_callbacks;
+ gboolean cancelled;
+ GrlSource *source;
+ struct ResolveFullResolutionCtlCb *ctl_info;;
+};
+
+struct MediaDecorateData {
+ GrlSource *source;
+ guint operation_id;
+ GHashTable *pending_callbacks;
+ MediaDecorateCb callback;
+ gboolean cancelled;
+ gpointer user_data;
};
-static void grl_source_finalize (GObject *object);
+static void grl_source_finalize (GObject *plugin);
-static void grl_source_dispose (GObject *object);
+static void grl_source_dispose (GObject *objct);
-static void grl_source_get_property (GObject *object,
+static void grl_source_get_property (GObject *plugin,
guint prop_id,
GValue *value,
GParamSpec *pspec);
@@ -91,6 +219,33 @@ static void grl_source_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec);
+static gboolean browse_idle (gpointer user_data);
+
+static gboolean search_idle (gpointer user_data);
+
+static gboolean query_idle (gpointer user_data);
+
+static void run_store_metadata (GrlSource *source,
+ GrlMedia *media,
+ GList *keys,
+ GrlWriteFlags flags,
+ GrlSourceStoreCb callback,
+ gpointer user_data);
+
+static void map_list_nodes_free (GList *nodes);
+
+static void resolve_result_relay_cb (GrlSource *source,
+ guint operation_id,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error);
+
+static gboolean resolve_idle (gpointer user_data);
+
+static gboolean resolve_all_done (gpointer user_data);
+
+static void source_cancel_cb (struct OperationState *op_state);
+
/* ================ GrlSource GObject ================ */
G_DEFINE_ABSTRACT_TYPE (GrlSource,
@@ -181,6 +336,58 @@ grl_source_class_init (GrlSourceClass *source_class)
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
+ /**
+ * GrlSource:auto-split-threshold
+ *
+ * Transparently split queries with count requests
+ * bigger than a certain threshold into smaller queries.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_AUTO_SPLIT_THRESHOLD,
+ g_param_spec_uint ("auto-split-threshold",
+ "Auto-split threshold",
+ "Threshold to use auto-split of queries",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GrlSource::content-changed:
+ * @source: source that has changed
+ * @changed_medias: a #GPtrArray with the medias that changed or a common
+ * ancestor of them of type #GrlMediaBox.
+ * @change_type: the kind of change that ocurred
+ * @location_unknown: @TRUE if the change happened in @media itself or in one
+ * of its direct children (when @media is a #GrlMediaBox). @FALSE otherwise
+ *
+ * Signals that the content in the source has changed. @changed_medias is the
+ * list of elements that have changed. Usually these medias are of type
+ * #GrlMediaBox, meaning that the content of that box has changed.
+ *
+ * If @location_unknown is @TRUE it means the source cannot establish where the
+ * change happened: could be either in the box, in any child, or in any other
+ * descendant of the box in the hierarchy.
+ *
+ * Both @change_type and @location_unknown are applied to all elements in the
+ * list.
+ *
+ * For the cases where the source can only signal that a change happened, but
+ * not where, it would use a list with the the root box (@NULL id) and set
+ * location_unknown as @TRUE.
+ */
+ registry_signals[SIG_CONTENT_CHANGED] =
+ g_signal_new("content-changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ 0,
+ NULL,
+ NULL,
+ grl_marshal_VOID__BOXED_ENUM_BOOLEAN,
+ G_TYPE_NONE,
+ 3,
+ G_TYPE_PTR_ARRAY,
+ GRL_TYPE_SOURCE_CHANGE_TYPE,
+ G_TYPE_BOOLEAN);
g_type_class_add_private (source_class,
sizeof (GrlSourcePrivate));
@@ -253,6 +460,9 @@ grl_source_set_property (GObject *object,
case PROP_RANK:
source->priv->rank = g_value_get_int (value);
break;
+ case PROP_AUTO_SPLIT_THRESHOLD:
+ source->priv->auto_split_threshold = g_value_get_uint (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec);
break;
@@ -265,7 +475,9 @@ grl_source_get_property (GObject *object,
GValue *value,
GParamSpec *pspec)
{
- GrlSource *source = GRL_SOURCE (object);
+ GrlSource *source;
+
+ source = GRL_SOURCE (object);
switch (prop_id) {
case PROP_ID:
@@ -283,6 +495,9 @@ grl_source_get_property (GObject *object,
case PROP_RANK:
g_value_set_int (value, source->priv->rank);
break;
+ case PROP_AUTO_SPLIT_THRESHOLD:
+ g_value_set_uint (value, source->priv->auto_split_threshold);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec);
break;
@@ -291,6 +506,13 @@ grl_source_get_property (GObject *object,
/* ================ Utilities ================ */
+static void
+operation_state_free (struct OperationState *op_state)
+{
+ g_object_unref (op_state->source);
+ g_free (op_state);
+}
+
/*
* This method will _intersect two key lists_:
*
@@ -330,76 +552,66 @@ filter_key_list (GrlSource *source,
return g_list_reverse (out_source);
}
-/* ================ API ================ */
-
-/**
- * grl_source_supported_keys:
- * @source: a source
- *
- * Get a list of #GrlKeyID, which describe a metadata types that this
- * source can fetch and store.
- *
- * Returns: (element-type GrlKeyID) (transfer none): a #GList with the keys
- */
-const GList *
-grl_source_supported_keys (GrlSource *source)
+static GList *
+filter_known_keys (GrlMedia *media,
+ GList *keys)
{
- g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+ GList *unknown_keys = NULL;
+ GList *k;
- if (GRL_SOURCE_GET_CLASS (source)->supported_keys) {
- return GRL_SOURCE_GET_CLASS (source)->supported_keys (source);
- } else {
- return NULL;
+ if (!media) {
+ return g_list_copy (keys);
}
-}
-
-/**
- * grl_source_slow_keys:
- * @source: a source
- *
- * Similar to grl_source_supported_keys(), but these keys
- * are marked as slow because of the amount of traffic/processing needed
- * to fetch them.
- *
- * Returns: (element-type GrlKeyID) (transfer none): a #GList with the keys
- */
-const GList *
-grl_source_slow_keys (GrlSource *source)
-{
- g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
- if (GRL_SOURCE_GET_CLASS (source)->slow_keys) {
- return GRL_SOURCE_GET_CLASS (source)->slow_keys (source);
- } else {
- return NULL;
+ for (k = keys; k; k = g_list_next (k)) {
+ if (!grl_data_has_key (GRL_DATA (media),
+ GRLPOINTER_TO_KEYID (k->data))) {
+ unknown_keys = g_list_prepend (unknown_keys, k->data);
+ }
}
+
+ return unknown_keys;
}
-/**
- * grl_source_writable_keys:
- * @source: a source
- *
- * Similar to grl_source_supported_keys(), but these keys
- * are marked as writable, meaning the source allows the client
- * to provide new values for these keys that will be stored permanently.
- *
- * Returns: (element-type GrlKeyID) (transfer none):
- * a #GList with the keys
+/*
+ * Removes all keys from @keys that can't be resolved by any of the sources in
+ * @sourcelist.
*/
-const GList *
-grl_source_writable_keys (GrlSource *source)
+static GList *
+filter_unresolvable_keys (GList *sourcelist, GList **keys)
{
- g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+ GList *each_key;
+ GList *delete_key;
+ GList *each_source;
+ gboolean supported;
- if (GRL_SOURCE_GET_CLASS (source)->writable_keys) {
- return GRL_SOURCE_GET_CLASS (source)->writable_keys (source);
- } else {
- return NULL;
+ each_key = *keys;
+ while (each_key) {
+ supported = FALSE;
+
+ for (each_source = sourcelist;
+ each_source;
+ each_source = g_list_next (each_source)) {
+ if (g_list_find ((GList *) grl_source_supported_keys (each_source->data),
+ each_key->data)) {
+ supported = TRUE;
+ break;
+ }
+ }
+ if (!supported) {
+ delete_key = each_key;
+ each_key = g_list_next (each_key);
+ *keys = g_list_delete_link (*keys, delete_key);
+ } else {
+ each_key = g_list_next (each_key);
+ }
}
+
+ return *keys;
}
-/**
- * grl_source_filter_supported:
+/*
+ * filter_supported:
* @source: a source
* @keys: (element-type GrlKeyID) (transfer container) (allow-none) (inout):
* the list of keys to filter out
@@ -413,10 +625,10 @@ grl_source_writable_keys (GrlSource *source)
* if @return_filtered is %TRUE will return the list of removed keys;
* otherwise %NULL
*/
-GList *
-grl_source_filter_supported (GrlSource *source,
- GList **keys,
- gboolean return_filtered)
+static GList *
+filter_supported (GrlSource *source,
+ GList **keys,
+ gboolean return_filtered)
{
const GList *supported_keys;
@@ -427,8 +639,8 @@ grl_source_filter_supported (GrlSource *source,
return filter_key_list (source, keys, return_filtered, (GList *) supported_keys);
}
-/**
- * grl_source_filter_slow:
+/*
+ * filter_slow:
* @source: a source
* @keys: (element-type GrlKeyID) (transfer container) (allow-none) (inout):
* the list of keys to filter out
@@ -443,10 +655,10 @@ grl_source_filter_supported (GrlSource *source,
* @return_filtered is %TRUE will return the list of slow keys; otherwise
* %NULL
*/
-GList *
-grl_source_filter_slow (GrlSource *source,
- GList **keys,
- gboolean return_filtered)
+static GList *
+filter_slow (GrlSource *source,
+ GList **keys,
+ gboolean return_filtered)
{
const GList *slow_keys;
GList *fastest_keys, *tmp;
@@ -468,15 +680,15 @@ grl_source_filter_slow (GrlSource *source,
}
}
-/**
- * grl_source_filter_writable:
+/*
+ * filter_writable:
* @source: a source
* @keys: (element-type GrlKeyID) (transfer container) (allow-none) (inout):
* the list of keys to filter out
* @return_filtered: if %TRUE the return value shall be a new list with
* the non-writable keys
*
- * Similar to grl_source_filter_supported() but applied to the writable keys in
+ * Similar to filter_supported() but applied to the writable keys in
* grl_source_writable_keys().
*
* Filter the @keys list keeping only those keys that are writtable in
@@ -487,8 +699,8 @@ grl_source_filter_slow (GrlSource *source,
* if @return_filtered is %TRUE will return the list of non-writtable keys;
* otherwise %NULL
*/
-GList *
-grl_source_filter_writable (GrlSource *source,
+static GList *
+filter_writable (GrlSource *source,
GList **keys,
gboolean return_filtered)
{
@@ -502,137 +714,75 @@ grl_source_filter_writable (GrlSource *source,
return filter_key_list (source, keys, return_filtered, (GList *) writable_keys);
}
-/**
- * grl_source_get_id:
- * @source: a source
- *
- * Returns: the ID of the @source
- */
-const gchar *
-grl_source_get_id (GrlSource *source)
-{
- g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
-
- return source->priv->id;
-}
-
-/**
- * grl_source_get_name:
- * @source: a source
- *
- * Returns: the name of the @source
+/*
+ * Operation states:
+ * - started: The operation has been invoked, but not started (plugin has
+ * not been invoked) yet.
+ * - finished: We have already emitted the last result to the user
+ * - completed: We have already received the last result in the relay cb
+ * (If it is finished it is also completed).
+ * - cancelled: Operation valid (not finished) but was cancelled.
+ * - ongoing: if the operation is valid (not finished) and not cancelled.
*/
-const gchar *
-grl_source_get_name (GrlSource *source)
-{
- g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
- return source->priv->name;
-}
-/**
- * grl_source_get_description:
- * @source: a source
+/*
+ * operation_set_started:
*
- * Returns: the description of the @source
- */
-const gchar *
-grl_source_get_description (GrlSource *source)
+ * Sets operation as started (we have invoked the operation in the plugin).
+ **/
+static void
+operation_set_started (guint operation_id)
{
- g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+ struct OperationState *op_state;
- return source->priv->desc;
-}
+ GRL_DEBUG ("%s (%d)", __FUNCTION__, operation_id);
-/**
- * grl_source_get_plugin:
- * @source: a source
- *
- * Returns: (transfer none): the plugin this source belongs to
- **/
-GrlPlugin *
-grl_source_get_plugin (GrlSource *source)
-{
- g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+ op_state = grl_operation_get_private_data (operation_id);
- return source->priv->plugin;
+ if (op_state) {
+ op_state->started = TRUE;
+ }
}
-/**
- * grl_source_get_rank:
- * @source: a source
- *
- * Gets the source rank
+/*
+ * operation_is_started:
*
- * Returns: rank value
+ * Checks if operation has been started (the operation in plugin has been
+ * invoked).
**/
-gint
-grl_source_get_rank (GrlSource *source)
+static gboolean
+operation_is_started (guint operation_id)
{
- g_return_val_if_fail (GRL_IS_SOURCE (source), 0);
-
- return source->priv->rank;
-}
+ struct OperationState *op_state;
-/**
- * grl_source_supported_operations:
- * @source: a source
- *
- * By default the derived objects of #GrlSource can only resolve.
- *
- * Returns: (type uint): a bitwise mangle with the supported operations by
- * the source
- */
-GrlSupportedOps
-grl_source_supported_operations (GrlSource *source)
-{
- g_return_val_if_fail (GRL_IS_SOURCE (source), GRL_OP_NONE);
+ op_state = grl_operation_get_private_data (operation_id);
- if (GRL_SOURCE_GET_CLASS (source)->supported_operations) {
- return GRL_SOURCE_GET_CLASS (source)->supported_operations (source);
- } else {
- return GRL_OP_NONE;
- }
+ return op_state && op_state->started;
}
/*
- * Operation states:
- *
- * - finished: We have already emitted the last result to the user
- *
- * - completed: We have already received the last result in the relay
- * cb (If it is finished it is also completed).
- *
- * - cancelled: Operation valid (not finished) but was cancelled.
- *
- * - ongoing: if the operation is valid (not finished) and not
- * cancelled.
- */
-
-/*
- * grl_source_set_operation_finished:
+ * operation_set_finished:
*
* Sets operation as finished (we have already emitted the last result
* to the user).
*/
-void
-grl_source_set_operation_finished (GrlSource *source,
- guint operation_id)
+static void
+operation_set_finished (guint operation_id)
{
- GRL_DEBUG ("grl_source_set_operation_finished (%d)", operation_id);
+ GRL_DEBUG ("%s (%d)", __FUNCTION__, operation_id);
grl_operation_remove (operation_id);
}
/*
- * grl_source_operation_is_finished:
+ * operation_is_finished:
*
* Checks if operation is finished (we have already emitted the last
* result to the user).
*/
-gboolean
-grl_source_operation_is_finished (GrlSource *source,
- guint operation_id)
+G_GNUC_UNUSED static gboolean
+operation_is_finished (guint operation_id)
{
struct OperationState *op_state;
@@ -642,18 +792,17 @@ grl_source_operation_is_finished (GrlSource *source,
}
/*
- * grl_source_set_operation_completed:
+ * operation_set_completed:
*
* Sets the operation as completed (we have already received the last
* result in the relay cb. If it is finsihed it is also completed).
*/
-void
-grl_source_set_operation_completed (GrlSource *source,
- guint operation_id)
+static void
+operation_set_completed (guint operation_id)
{
struct OperationState *op_state;
- GRL_DEBUG ("grl_source_set_operation_completed (%d)", operation_id);
+ GRL_DEBUG ("%s (%d)", __FUNCTION__, operation_id);
op_state = grl_operation_get_private_data (operation_id);
@@ -663,15 +812,14 @@ grl_source_set_operation_completed (GrlSource *source,
}
/*
- * grl_source_operation_is_completed:
+ * operation_is_completed:
*
* Checks if operation is completed (we have already received the last
* result in the relay cb. A finished operation is also a completed
* operation).
*/
-gboolean
-grl_source_operation_is_completed (GrlSource *source,
- guint operation_id)
+static gboolean
+operation_is_completed (guint operation_id)
{
struct OperationState *op_state;
@@ -681,18 +829,17 @@ grl_source_operation_is_completed (GrlSource *source,
}
/*
- * grl_source_set_operation_cancelled:
+ * operation_set_cancelled:
*
* Sets the operation as cancelled (a valid operation, i.e., not
* finished, was cancelled)
*/
-void
-grl_source_set_operation_cancelled (GrlSource *source,
- guint operation_id)
+static void
+operation_set_cancelled (guint operation_id)
{
struct OperationState *op_state;
- GRL_DEBUG ("grl_source_set_operation_cancelled (%d)", operation_id);
+ GRL_DEBUG ("%s (%d)", __FUNCTION__, operation_id);
op_state = grl_operation_get_private_data (operation_id);
@@ -702,14 +849,13 @@ grl_source_set_operation_cancelled (GrlSource *source,
}
/*
- * grl_source_operation_is_cancelled:
+ * operation_is_cancelled:
*
* Checks if operation is cancelled (a valid operation that was
* cancelled).
*/
-gboolean
-grl_source_operation_is_cancelled (GrlSource *source,
- guint operation_id)
+static gboolean
+operation_is_cancelled (guint operation_id)
{
struct OperationState *op_state;
@@ -718,13 +864,51 @@ grl_source_operation_is_cancelled (GrlSource *source,
return op_state && op_state->cancelled;
}
+/*
+ * operation_set_ongoing:
+ *
+ * Sets the operation as ongoing (operation is valid, not finished, not started
+ * and not cancelled)
+ */
static void
-grl_source_cancel_cb (struct OperationState *op_state)
+operation_set_ongoing (GrlSource *source, guint operation_id)
{
- GrlSource *source = op_state->source;
+ struct OperationState *op_state;
- if (!grl_source_operation_is_ongoing (source,
- op_state->operation_id)) {
+ GRL_DEBUG ("%s (%d)", __FUNCTION__, operation_id);
+
+ op_state = g_new0 (struct OperationState, 1);
+ op_state->source = g_object_ref (source);
+ op_state->operation_id = operation_id;
+
+ grl_operation_set_private_data (operation_id,
+ op_state,
+ (GrlOperationCancelCb) source_cancel_cb,
+ (GDestroyNotify) operation_state_free);
+}
+
+/*
+ * operation_is_ongoing:
+ *
+ * Checks if operation is ongoing (operation is valid, and it is not
+ * finished nor cancelled).
+ */
+static gboolean
+operation_is_ongoing (guint operation_id)
+{
+ struct OperationState *op_state;
+
+ op_state = grl_operation_get_private_data (operation_id);
+
+ return op_state && !op_state->cancelled;
+}
+
+static void
+source_cancel_cb (struct OperationState *op_state)
+{
+ GrlSource *source = op_state->source;
+
+ if (!operation_is_ongoing (op_state->operation_id)) {
GRL_DEBUG ("Tried to cancel invalid or already cancelled operation. "
"Skipping...");
return;
@@ -738,8 +922,7 @@ grl_source_cancel_cb (struct OperationState *op_state)
search(), this will happen when it emits remaining = 0 (which can be
because it did not cancel the op or because it managed to cancel it and is
signaling so) */
- grl_source_set_operation_cancelled (source,
- op_state->operation_id);
+ operation_set_cancelled (op_state->operation_id);
/* If the source provides an implementation for operation cancellation,
let's use that to avoid further unnecessary processing in the plugin */
@@ -749,47 +932,3370 @@ grl_source_cancel_cb (struct OperationState *op_state)
}
}
+static void
+cancel_resolve (gpointer source, gpointer operation_id, gpointer user_data)
+{
+ struct OperationState *op_state;
+
+ op_state = grl_operation_get_private_data (GPOINTER_TO_UINT (operation_id));
+ if (op_state) {
+ source_cancel_cb (op_state);
+ }
+}
+
+static void
+resolve_spec_free (GrlSourceResolveSpec *spec)
+{
+ g_object_unref (spec->source);
+ g_object_unref (spec->media);
+ g_object_unref (spec->options);
+ g_free (spec);
+}
+
+static void
+browse_relay_spec_free (struct BrowseRelayCb *brc)
+{
+ switch (brc->operation_type) {
+ case GRL_OP_BROWSE:
+ g_object_unref (brc->spec.browse->source);
+ g_object_unref (brc->spec.browse->container);
+ g_object_unref (brc->spec.browse->options);
+ g_free (brc->spec.browse);
+ break;
+ case GRL_OP_SEARCH:
+ g_object_unref (brc->spec.search->source);
+ g_object_unref (brc->spec.search->options);
+ g_free (brc->spec.search->text);
+ g_free (brc->spec.search);
+ break;
+ case GRL_OP_QUERY:
+ g_object_unref (brc->spec.query->source);
+ g_object_unref (brc->spec.query->options);
+ g_free (brc->spec.query->query);
+ g_free (brc->spec.query);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+media_from_uri_spec_free (GrlSourceMediaFromUriSpec *spec)
+{
+ g_object_unref (spec->source);
+ g_free (spec->uri);
+ g_free (spec);
+}
+
+static void
+store_spec_free (GrlSourceStoreSpec *spec)
+{
+ g_object_unref (spec->source);
+ g_object_unref (spec->media);
+ if (spec->parent) {
+ g_object_unref (spec->parent);
+ }
+ g_free (spec);
+}
+
+static void
+store_metadata_spec_free (GrlSourceStoreMetadataSpec *spec)
+{
+ g_object_unref (spec->source);
+ g_object_unref (spec->media);
+ g_free (spec);
+}
+
+static void
+resolve_relay_free (struct ResolveRelayCb *rrc)
+{
+ GHashTableIter iter;
+ gpointer value;
+
+ g_object_unref (rrc->source);
+ g_object_unref (rrc->media);
+ g_object_unref (rrc->options);
+ g_list_free (rrc->keys);
+
+ g_hash_table_iter_init (&iter, rrc->map);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ map_list_nodes_free ((GList *) value);
+ }
+ g_hash_table_unref (rrc->map);
+ g_hash_table_unref (rrc->resolve_specs);
+
+ g_slice_free (struct ResolveRelayCb, rrc);
+}
+
+static void
+browse_relay_free (struct BrowseRelayCb *brc)
+{
+ g_object_unref (brc->source);
+ g_object_unref (brc->options);
+ g_list_free (brc->keys);
+ if (brc->auto_split) {
+ g_slice_free (struct AutoSplitCtl, brc->auto_split);
+ }
+ if (brc->queue) {
+ g_queue_free (brc->queue);
+ }
+ g_slice_free (struct BrowseRelayCb, brc);
+}
+
+static void
+remove_relay_free (struct RemoveRelayCb *rrc)
+{
+ g_object_unref (rrc->source);
+ g_object_unref (rrc->media);
+ if (rrc->spec) {
+ g_object_unref (rrc->spec->source);
+ g_object_unref (rrc->spec->media);
+ g_free (rrc->spec->media_id);
+ g_free (rrc->spec);
+ }
+ g_slice_free (struct RemoveRelayCb, rrc);
+}
+
+static void
+store_relay_free (struct StoreRelayCb *src)
+{
+ g_slice_free (struct StoreRelayCb, src);
+}
+
+static void
+store_metadata_relay_free (struct StoreMetadataRelayCb *smrc)
+{
+ g_object_unref (smrc->source);
+ g_object_unref (smrc->media);
+ g_list_free (smrc->failed_keys);
+ g_hash_table_unref (smrc->map);
+ g_list_free (smrc->use_sources);
+ g_list_foreach (smrc->specs, (GFunc) store_metadata_spec_free, NULL);
+
+ g_slice_free (struct StoreMetadataRelayCb, smrc);
+}
+
+/*
+ * Returns a list of all the keys that are in deps but are not defined in data
+ */
+static GList *
+missing_in_data (GrlData *data, const GList *deps)
+{
+ GList *iter, *result = NULL;
+ GRL_DEBUG ("missing_in_data");
+
+ if (!data)
+ return g_list_copy ((GList *) deps);
+
+ for (iter = (GList *)deps; iter; iter = g_list_next (iter)) {
+ if (!grl_data_has_key (data, GRLPOINTER_TO_KEYID (iter->data)))
+ result = g_list_append (result, iter->data);
+ }
+
+ return result;
+}
+
+/*
+ * TRUE iff source supports all those keys
+ */
+static gboolean
+source_supports (GrlSource *source,
+ const GList *keys)
+{
+ const GList *iter;
+ GList *supported;
+
+ supported = (GList *) grl_source_supported_keys (source);
+
+ for (iter = keys; iter; iter = g_list_next (iter)) {
+ if (!g_list_find (supported, iter->data)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
/*
- * grl_source_set_operation_ongoing:
+ * Find the source that should be queried to add @key to @media.
+ * If @additional_keys is provided, the result may include sources that need
+ * more metadata to be present in @media, the keys corresponding to that
+ * metadata will be put in @additional_keys.
+ * If @additional_keys is NULL, will only consider sources that can resolve
+ * @keys immediately
+ *
+ * If @main_source_is_only_resolver is TRUE and @additional_keys is not @NULL,
+ * only additional keys that can be resolved directly by @source will be
+ * considered. Sources that need other additional keys will not be put in the
+ * returned list.
*
- * Sets the operation as ongoing (operation is valid, not finished and
- * not cancelled)
+ * @source will never be considered as additional source.
+ *
+ * @source and @additional_keys may not be @NULL if
+ * @main_source_is_only_resolver is @TRUE.
+ *
+ * Assumes @key is not already in @media.
*/
-void
-grl_source_set_operation_ongoing (GrlSource *source,
- guint operation_id)
+static GrlSource *
+get_additional_source_for_key (GrlSource *source,
+ GList *sources,
+ GrlMedia *media,
+ GrlKeyID key,
+ GList **additional_keys,
+ gboolean main_source_is_only_resolver)
{
- struct OperationState *op_state;
+ GList *iter;
- GRL_DEBUG ("grl_source_set_operation_ongoing (%d)", operation_id);
+ g_return_val_if_fail (source || !main_source_is_only_resolver, NULL);
+ g_return_val_if_fail (additional_keys || !main_source_is_only_resolver, NULL);
- op_state = g_new0 (struct OperationState, 1);
- op_state->source = source;
- op_state->operation_id = operation_id;
+ for (iter = sources; iter; iter = g_list_next (iter)) {
+ GList *_additional_keys = NULL;
+ GrlSource *_source = (GrlSource *) iter->data;
- grl_operation_set_private_data (operation_id,
- op_state,
- (GrlOperationCancelCb) grl_source_cancel_cb,
- g_free);
+ if (grl_source_may_resolve (_source, media, key, &_additional_keys)) {
+ return _source;
+ }
+
+ if (additional_keys && _additional_keys) {
+ if (main_source_is_only_resolver &&
+ !source_supports (source, _additional_keys))
+ continue;
+
+ *additional_keys = _additional_keys;
+ return _source;
+ }
+ }
+
+ return NULL;
}
/*
- * grl_source_operation_is_ongoing:
+ * Does the same thing as g_list_concat(), except that elements from
+ * @additional_set that are already in @original_set are destroyed instead of
+ * being added to the result. The same happens for elements that are more than
+ * once in @additional_set.
+ * Because of that, if @original_set does not contain doubles, the result will
+ * not contain doubles.
*
- * Checks if operation is ongoing (operation is valid, and it is not
- * finished nor cancelled).
+ * You can also use this method to remove doubles from a list like that:
+ * my_list = list_union (NULL, my_list, free_func);
+ *
+ * Note that no elements are copied, elements of @additional_set are either
+ * moved to @original_set or destroyed.
+ * Therefore, both @original_set and @additional_set are modified.
+ *
+ * @free_func is optional.
*/
-gboolean
-grl_source_operation_is_ongoing (GrlSource *source,
- guint operation_id)
+static GList *
+list_union (GList *original_set, GList *additional_set, GDestroyNotify free_func)
{
- struct OperationState *op_state;
+ while (additional_set) {
+ /* these two lines pop the first element of additional_set into tmp */
+ GList *tmp = additional_set;
+ additional_set = g_list_remove_link (additional_set, tmp);
- op_state = grl_operation_get_private_data (operation_id);
+ if (NULL == g_list_find (original_set, tmp->data)) {
+ original_set = g_list_concat (original_set, tmp);
+ } else {
+ if (free_func)
+ free_func (tmp->data);
+ g_list_free_1 (tmp);
+ }
+ }
+ return original_set;
+}
- return op_state && !op_state->cancelled;
+/*
+ * Find the sources that should be queried to add @keys to @media.
+ * If @additional_keys is provided, the result may include sources that need
+ * more metadata to be present in @media, the keys corresponding to that
+ * metadata will be put in @additional_keys.
+ * If @additional_keys is NULL, will only consider sources that can resolve
+ * @keys immediately
+ *
+ * If @main_source_is_only_resolver is TRUE and @additional_keys is not @NULL,
+ * only additional keys that can be resolved directly by @source will be
+ * considered. Sources that need other additional keys will not be put in the
+ * returned list.
+ *
+ * Ignore elements of @keys that are already in @media.
+ */
+static GList *
+get_additional_sources (GrlSource *source,
+ GrlMedia *media,
+ GList *keys,
+ GList **additional_keys,
+ gboolean main_source_is_only_resolver)
+{
+ GList *missing_keys, *iter, *result = NULL, *sources;
+ GrlPluginRegistry *registry;
+
+ missing_keys = missing_in_data (GRL_DATA (media), keys);
+ if (!missing_keys)
+ return NULL;
+
+ registry = grl_plugin_registry_get_default ();
+ sources = grl_plugin_registry_get_sources_by_operations (registry,
+ GRL_OP_RESOLVE,
+ TRUE);
+
+ for (iter = missing_keys; iter; iter = g_list_next (iter)) {
+ GrlKeyID key = (GrlKeyID) iter->data;
+ GrlSource *_source;
+ GList *needed_keys = NULL;
+
+ _source = get_additional_source_for_key (source, sources, media, key,
+ additional_keys?&needed_keys:NULL,
+ main_source_is_only_resolver);
+ if (_source) {
+ result = g_list_append (result, _source);
+
+ if (needed_keys)
+ *additional_keys = list_union (*additional_keys, needed_keys, NULL);
+
+ GRL_INFO ("%s can resolve %s %s",
+ grl_source_get_name (_source),
+ GRL_METADATA_KEY_GET_NAME (key),
+ needed_keys? "with more keys" : "directly");
+
+ } else {
+ GRL_DEBUG ("Could not find a source for %s",
+ GRL_METADATA_KEY_GET_NAME (key));
+ }
+ }
+
+ /* list_union() is used to remove doubles */
+ return list_union (NULL, result, NULL);
+}
+
+/**
+ * Will add to @keys the keys that should be asked to @source when doing an
+ * operation with GRL_RESOLVE_FULL.
+ * The added keys are the keys that will be needed by other sources to obtain
+ * the ones that @source says it cannot resolve.
+ */
+static GList *
+expand_operation_keys (GrlSource *source,
+ GrlMedia *media,
+ GList *keys)
+{
+ GList *unsupported_keys = NULL,
+ *additional_keys = NULL,
+ *sources;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ if (!keys)
+ return NULL;
+
+ /* Get the list of keys not supported by the source; they will be queried to
+ other sources */
+ unsupported_keys = filter_supported (source, &keys, TRUE);
+
+ /* now, for each of the unsupported keys to solve
+ * (the ones we know @source cannot resolve), try to find a matching source.
+ * A matching source may need additional keys, but then these additional keys
+ * can be resolved by @source.
+ */
+
+ sources =
+ get_additional_sources (source, media, unsupported_keys,
+ &additional_keys, TRUE);
+ g_list_free (sources);
+
+ /* Merge back the supported and unsupported list, and add also the additional keys */
+ keys = g_list_concat (keys, unsupported_keys);
+ keys = list_union (keys, additional_keys, NULL);
+
+ return keys;
+}
+
+/*
+ * Returns %TRUE if @key is a slow key for @source
+ */
+static gboolean
+is_slow_key (GrlSource *source, GrlKeyID key)
+{
+ return (g_list_find ((GList *) grl_source_slow_keys (source),
+ GRLKEYID_TO_POINTER (key)) != NULL);
+}
+
+/*
+ * Create a node for the map keys
+ */
+static MapNode *
+map_node_new (GrlSource *source, GList *keys)
+{
+ MapNode *node = g_new (MapNode, 1);
+ node->source = g_object_ref (source);
+ node->required_keys = g_list_copy (keys);
+ node->being_queried = FALSE;
+
+ return node;
+}
+
+/*
+ * Free a MapNode
+ */
+static void
+map_node_free (MapNode *node)
+{
+ g_object_unref (node->source);
+ g_list_free (node->required_keys);
+ g_free (node);
+}
+
+/*
+ * Free a list of MapNodes
+ */
+static void
+map_list_nodes_free (GList *nodes)
+{
+ g_list_foreach (nodes, (GFunc) map_node_free, NULL);
+ g_list_free (nodes);
+}
+
+/*
+ * Create a new (key, [sources]) map
+ */
+static GHashTable *
+map_keys_new (void)
+{
+ return g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+/*
+ * Maps each key in @keys to the list of sources (from @sources) that can
+ * resolve that key. For each of those (key, source) pair, a list of keys
+ * dependencies is added
+ */
+static void
+map_keys_to_sources (GHashTable *map, GList *keys, GList *sources, GrlMedia *media, gboolean filter_slow_keys)
+{
+ GList *each_source;
+ GList *resolvable_sources;
+ GList *each_key;
+ GList *required_keys;
+ GList *keys_to_map_later = NULL;
+
+ for (each_key = keys;
+ each_key;
+ each_key = g_list_next (each_key)) {
+ if (g_hash_table_lookup_extended (map, each_key->data, NULL, NULL)) {
+ /* Key already in map; skip */
+ continue;
+ }
+ resolvable_sources = NULL;
+ for (each_source = sources;
+ each_source;
+ each_source = g_list_next (each_source)) {
+ if (filter_slow_keys &&
+ is_slow_key (each_source->data, GRLPOINTER_TO_KEYID (each_key->data))) {
+ continue;
+ }
+ required_keys = NULL;
+ if (grl_source_may_resolve (each_source->data,
+ media,
+ GRLPOINTER_TO_KEYID (each_key->data),
+ &required_keys)) {
+ resolvable_sources = g_list_prepend (resolvable_sources, map_node_new (each_source->data, NULL));
+ } else if (required_keys) {
+ resolvable_sources = g_list_prepend (resolvable_sources, map_node_new (each_source->data, required_keys));
+ keys_to_map_later = g_list_concat (keys_to_map_later, required_keys);
+ }
+ }
+
+ resolvable_sources = g_list_reverse (resolvable_sources);
+ g_hash_table_insert (map, each_key->data, resolvable_sources);
+ }
+
+ if (keys_to_map_later) {
+ map_keys_to_sources (map, keys_to_map_later, sources, media, filter_slow_keys);
+ g_list_free (keys_to_map_later);
+ }
+}
+
+/*
+ * Create a new (source, spec) map
+ */
+static GHashTable *
+map_sources_new (void)
+{
+ return g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ g_object_unref,
+ (GDestroyNotify) resolve_spec_free);
+}
+
+/*
+ * Given a (keys, [sources]) @map, builds a map of sources to
+ * GrlSourceResolveSpec that can solve @key in @media. Returns @FALSE if the
+ * key can't be mapped (could be, for instance, that it detects a loop)
+ */
+static gboolean
+map_sources_to_specs (GHashTable *specs,
+ GHashTable *map,
+ GrlMedia *media,
+ GrlKeyID key,
+ GrlOperationOptions *options,
+ gpointer user_data)
+{
+ GList *map_nodes;
+ MapNode *node;
+ GList *each_required_key;
+ GrlSourceResolveSpec *rs;
+ gboolean success;
+
+ /* Search the source candidate to solve the key */
+ map_nodes = g_hash_table_lookup (map, GRLKEYID_TO_POINTER (key));
+ while (map_nodes) {
+ node = (MapNode *) map_nodes->data;
+ if (node->being_queried) {
+ /* If it has required keys and it is marked as being queried, it means we
+ have enter in a loop */
+ return (node->required_keys == NULL);
+ }
+ /* Check if it has any dependency */
+ if (node->required_keys) {
+ /* Mark node to avoid endless loops */
+ node->being_queried = TRUE;
+ success = TRUE;
+ for (each_required_key = node->required_keys;
+ each_required_key;
+ each_required_key = g_list_next (each_required_key)) {
+ success = map_sources_to_specs (specs, map, media,
+ GRLPOINTER_TO_KEYID (each_required_key->data),
+ options, user_data);
+ if (!success) {
+ break;
+ }
+ }
+ node->being_queried = FALSE;
+ if (success) {
+ return TRUE;
+ } else {
+ /* Try next node */
+ map_nodes = g_list_next (map_nodes);
+ }
+ } else {
+ rs = g_hash_table_lookup (specs, node->source);
+ if (!rs) {
+ /* Build spec */
+ rs = g_new (GrlSourceResolveSpec, 1);
+ rs->source = g_object_ref (node->source);
+ rs->media = g_object_ref (media);
+ rs->operation_id = grl_operation_generate_id ();
+ rs->keys = g_list_prepend (NULL, GRLKEYID_TO_POINTER (key));
+ rs->options = g_object_ref (options);
+ rs->callback = resolve_result_relay_cb;
+ rs->user_data = user_data;
+ g_hash_table_insert (specs, g_object_ref (node->source), rs);
+ } else {
+ /* Put key in spec */
+ rs->keys = g_list_prepend (rs->keys, GRLKEYID_TO_POINTER (key));
+ }
+ node->being_queried = TRUE;
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Update @map knowing @key is known; means dropping the @key from the map and
+ * updating all keys that were depending on @key.
+ */
+static void
+map_update_known_key (GHashTable *map, GrlKeyID key, GrlMedia *media)
+{
+ GList *keylist;
+ GList *each_key;
+ GList *map_nodes;
+ GList *each_node;
+ MapNode *node;
+
+ map_list_nodes_free (g_hash_table_lookup (map, GRLKEYID_TO_POINTER (key)));
+ g_hash_table_remove (map, GRLKEYID_TO_POINTER (key));
+
+ keylist = g_hash_table_get_keys (map);
+ for (each_key = keylist;
+ each_key;
+ each_key = g_list_next (each_key)) {
+ map_nodes = g_hash_table_lookup (map, each_key->data);
+ for (each_node = map_nodes;
+ each_node;
+ each_node = g_list_next (each_node)) {
+ node = (MapNode *) each_node->data;
+ if (g_list_find (node->required_keys, (gconstpointer) GRLKEYID_TO_POINTER (key))) {
+ /* Let's recompute the required keys */
+ g_list_free (node->required_keys);
+ node->required_keys = NULL;
+ grl_source_may_resolve (node->source,
+ media,
+ GRLPOINTER_TO_KEYID (each_key->data),
+ &(node->required_keys));
+ }
+ }
+ }
+ g_list_free (keylist);
+}
+
+/*
+ * Update @map knowing that @key could not be resolved by @source.
+ */
+static void
+map_update_unknown_key (GHashTable *map, GrlKeyID key, GrlSource *source)
+{
+ GList *unsolvable_keys = NULL;
+ GList *each_unsolvable_key;
+ GList *each_key;
+ GList *map_nodes;
+ GList *delete_nodes = NULL;
+ GList *each_node;
+ GList *keylist;
+ MapNode *node;
+
+ map_nodes = g_hash_table_lookup (map, GRLKEYID_TO_POINTER (key));
+ each_node = map_nodes;
+ while (each_node) {
+ node = (MapNode *) each_node->data;
+ if (node->being_queried && node->source == source) {
+ map_nodes = g_list_delete_link (map_nodes, each_node);
+ map_node_free (node);
+ g_hash_table_insert (map, GRLKEYID_TO_POINTER (key), map_nodes);
+ each_node = NULL;
+ } else {
+ each_node = g_list_next (each_node);
+ }
+ }
+
+ /* If @map_nodes is empty, means no source is able to solve this key; so any
+ other (key, source) depending on this key can't neither be solved; so
+ remove them from the map */
+ if (!map_nodes) {
+ unsolvable_keys = g_list_prepend (unsolvable_keys,
+ GRLKEYID_TO_POINTER (key));
+ for (each_unsolvable_key = g_list_last (unsolvable_keys);
+ each_unsolvable_key;
+ each_unsolvable_key = g_list_previous (each_unsolvable_key)) {
+ keylist = g_hash_table_get_keys (map);
+ for (each_key = keylist;
+ each_key;
+ each_key = g_list_next (each_key)) {
+ map_nodes = g_hash_table_lookup (map, each_key->data);
+ if (map_nodes) {
+ for (each_node = map_nodes;
+ each_node;
+ each_node = g_list_next (each_node)) {
+ node = (MapNode *) each_node->data;
+ if (g_list_find (node->required_keys, each_unsolvable_key->data)) {
+ /* Put this node for further deletion, as it can't be solved */
+ delete_nodes = g_list_prepend (delete_nodes, node);
+ }
+ }
+ /* Delete nodes */
+ for (each_node = delete_nodes;
+ each_node;
+ each_node = g_list_next (each_node)) {
+ map_nodes = g_list_remove (map_nodes, each_node->data);
+ }
+ g_list_free (delete_nodes);
+ delete_nodes = NULL;
+
+ g_hash_table_insert (map, each_key->data, map_nodes);
+ /* If this key can't be resolved neither, mark it */
+ if (!map_nodes) {
+ unsolvable_keys = g_list_prepend (unsolvable_keys, each_key->data);
+ }
+ }
+ }
+ g_list_free (keylist);
+ }
+ g_list_free (unsolvable_keys);
+ }
}
+static void
+send_decorated_media (GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
+{
+ struct ResolveRelayCb *mrc = (struct ResolveRelayCb *) user_data;
+
+ mrc->user_callback (mrc->spec.res->source, mrc->spec.res->operation_id,
+ media, mrc->user_data, error);
+ resolve_relay_free (mrc);
+}
+
+static void
+media_decorate_cb (GrlSource *source,
+ guint operation_id,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
+{
+ struct MediaDecorateData *mdd = (struct MediaDecorateData *) user_data;
+ GError *_error = NULL;
+ GRL_DEBUG (__FUNCTION__);
+
+ if (operation_id > 0) {
+ g_hash_table_remove (mdd->pending_callbacks, source);
+ }
+
+ /* Check if pending resolutions must be cancelled */
+ if (!mdd->cancelled &&
+ operation_is_cancelled (mdd->operation_id)) {
+ mdd->cancelled = TRUE;
+ g_hash_table_foreach (mdd->pending_callbacks, cancel_resolve, NULL);
+ }
+
+ /* If all operations are complete, send the element */
+ if (g_hash_table_size (mdd->pending_callbacks) == 0) {
+ if (mdd->cancelled) {
+ _error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_OPERATION_CANCELLED,
+ "Operation was cancelled");
+ }
+ mdd->callback (media, mdd->user_data, _error);
+ if (_error) {
+ g_error_free (_error);
+ }
+ g_object_unref (mdd->source);
+ g_hash_table_unref (mdd->pending_callbacks);
+ g_slice_free (struct MediaDecorateData, mdd);
+ }
+}
+
+static void
+media_decorate (GrlSource *main_source,
+ guint main_operation_id,
+ GrlMedia *media,
+ GList *keys,
+ GrlOperationOptions *options,
+ MediaDecorateCb callback,
+ gpointer user_data)
+{
+ struct MediaDecorateData *mdd;
+ GList *s, *sources;
+ guint operation_id;
+ GrlOperationOptions *decorate_options;
+ GrlResolutionFlags flags;
+
+ flags = grl_operation_options_get_flags (options);
+ if (flags & GRL_RESOLVE_FULL) {
+ decorate_options = grl_operation_options_copy (options);
+ grl_operation_options_set_flags (decorate_options,
+ flags & ~GRL_RESOLVE_FULL);
+ } else {
+ decorate_options = g_object_ref (options);
+ }
+
+ sources = get_additional_sources (main_source, media,
+ keys, NULL, FALSE);
+
+ mdd = g_slice_new (struct MediaDecorateData);
+ mdd->source = g_object_ref (main_source);
+ mdd->operation_id = main_operation_id;
+ mdd->callback = callback;
+ mdd->user_data = user_data;
+ mdd->pending_callbacks = g_hash_table_new (g_direct_hash, g_direct_equal);
+ mdd->cancelled = FALSE;
+
+ for (s = sources; s; s = g_list_next (s)) {
+ if (grl_source_supported_operations (s->data) & GRL_OP_RESOLVE) {
+ operation_id = grl_source_resolve (s->data, media, keys, decorate_options,
+ media_decorate_cb, mdd);
+ if (operation_id > 0) {
+ g_hash_table_insert (mdd->pending_callbacks,
+ s->data,
+ GUINT_TO_POINTER (operation_id));
+ }
+ }
+ }
+
+ /* Check if nobody can solve the keys */
+ if (g_hash_table_size (mdd->pending_callbacks) == 0) {
+ media_decorate_cb (NULL, 0, media, mdd, NULL);
+ }
+
+ g_object_unref (decorate_options);
+ g_list_free (sources);
+}
+
+static void
+media_from_uri_result_relay_cb (GrlSource *source,
+ guint operation_id,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
+{
+ struct ResolveRelayCb *rrc = (struct ResolveRelayCb *) user_data;
+ GError *_error = (GError *) error;
+ GList *unknown_keys;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ /* Free specs */
+ media_from_uri_spec_free (rrc->spec.mfu);
+
+ /* Check if cancelled */
+ if (operation_is_cancelled (rrc->operation_id)) {
+ /* if the plugin already set an error, we don't care because we're
+ * cancelled */
+ GRL_DEBUG ("operation was cancelled");
+ _error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_OPERATION_CANCELLED,
+ "Operation was cancelled");
+ }
+
+ if (_error) {
+ rrc->user_callback (source, rrc->operation_id, media, rrc->user_data, _error);
+ if (_error != error) {
+ g_error_free (_error);
+ }
+ resolve_relay_free (rrc);
+ return;
+ }
+
+ if (grl_operation_options_get_flags (rrc->options) & GRL_RESOLVE_FULL) {
+ /* Check if there are unsolved keys that need to be solved by other
+ sources */
+ unknown_keys = filter_known_keys (media, rrc->keys);
+ if (unknown_keys) {
+ media_decorate (source, operation_id, media, unknown_keys, rrc->options,
+ send_decorated_media, rrc);
+ g_list_free (unknown_keys);
+ return;
+ }
+ }
+
+ rrc->user_callback (source, rrc->operation_id, media, rrc->user_data, error);
+ resolve_relay_free (rrc);
+}
+
+static void
+cancel_resolve_spec (GrlSource *source, GrlSourceResolveSpec *spec)
+{
+ struct OperationState *op_state;
+
+ op_state = grl_operation_get_private_data (spec->operation_id);
+ if (op_state) {
+ source_cancel_cb (op_state);
+ }
+}
+
+static void
+resolve_result_relay_cb (GrlSource *source,
+ guint operation_id,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
+{
+ struct ResolveRelayCb *rrc = (struct ResolveRelayCb *) user_data;
+ GList *each_key;
+ GList *delete_key;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ if (!operation_is_cancelled (operation_id)) {
+ /* Check which keys are now known */
+ each_key = rrc->keys;
+ while (each_key) {
+ if (grl_data_has_key (GRL_DATA (media), GRLPOINTER_TO_KEYID (each_key->data))) {
+ map_update_known_key (rrc->map, GRLPOINTER_TO_KEYID (each_key->data), media);
+ delete_key = each_key;
+ each_key = g_list_next (each_key);
+ rrc->keys = g_list_delete_link (rrc->keys, delete_key);
+ } else {
+ map_update_unknown_key (rrc->map, GRLPOINTER_TO_KEYID (each_key->data), source);
+ each_key = g_list_next (each_key);
+ }
+ }
+
+ g_hash_table_remove (rrc->resolve_specs, source);
+ }
+
+ operation_set_finished (operation_id);
+
+ if (operation_is_cancelled (rrc->operation_id) &&
+ !rrc->cancel_invoked) {
+ rrc->cancel_invoked = TRUE;
+ g_hash_table_foreach (rrc->resolve_specs, (GHFunc) cancel_resolve_spec, NULL);
+ }
+
+ if (error && source == rrc->source && !rrc->error) {
+ /* Save error for further sending */
+ rrc->error = g_error_copy (error);
+ }
+
+ if (g_hash_table_size (rrc->resolve_specs) == 0 && !rrc->specs_to_invoke) {
+ /* All sources have replied. Let's run another round if not cancelled */
+ if (!operation_is_cancelled (rrc->operation_id)) {
+ each_key = rrc->keys;
+ while (each_key) {
+ if (map_sources_to_specs (rrc->resolve_specs, rrc->map, media,
+ GRLPOINTER_TO_KEYID (each_key->data),
+ rrc->options, rrc)) {
+ each_key = g_list_next (each_key);
+ } else {
+ delete_key = each_key;
+ each_key = g_list_next (each_key);
+ rrc->keys = g_list_delete_link (rrc->keys, delete_key);
+ }
+ }
+
+ }
+
+ rrc->specs_to_invoke = g_hash_table_get_values (rrc->resolve_specs);
+ if (rrc->specs_to_invoke) {
+ g_idle_add_full (grl_operation_options_get_flags (rrc->options) & GRL_RESOLVE_IDLE_RELAY?
+ G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
+ resolve_idle,
+ rrc,
+ NULL);
+ } else {
+ g_idle_add_full (grl_operation_options_get_flags (rrc->options) & GRL_RESOLVE_IDLE_RELAY?
+ G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
+ resolve_all_done,
+ rrc,
+ NULL);
+ }
+ }
+}
+
+static gboolean
+queue_process (gpointer user_data)
+{
+ QueueElement *qelement;
+ GError *error;
+ gint remaining;
+ struct BrowseRelayCb *brc = (struct BrowseRelayCb *) user_data;
+
+ /* Check if operation is cancelled */
+ if (operation_is_cancelled (brc->operation_id)) {
+ /* This is how this works: if operation is cancelled, no one will add more
+ elements to queue. If one with remaining=0 is found, means that the
+ browse/search/query operation finished before operation is cancelled. So we
+ need to emit this result to user to tell him that operation is cancelled. For
+ elements that are not ready, means that a source_resolve() was run to get
+ solve more keys. So the algorithm is freeing all elements that are ready, and
+ if some of them has remaining==0, sending the cancel signal to user */
+ while ((qelement = (QueueElement *) g_queue_peek_head (brc->queue)) &&
+ qelement->is_ready) {
+ g_queue_pop_head (brc->queue);
+ if (qelement->remaining == 0) {
+ error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_OPERATION_CANCELLED,
+ "Operation was cancelled");
+ brc->user_callback (brc->source, brc->operation_id, NULL,
+ 0, brc->user_data, error);
+ g_error_free (error);
+ }
+ if (qelement->error) {
+ g_error_free (qelement->error);
+ }
+ g_free (qelement);
+ }
+ if (g_queue_is_empty (brc->queue)) {
+ operation_set_finished (brc->operation_id);
+ browse_relay_free (brc);
+ return FALSE;
+ }
+ brc->dispatcher_running = FALSE;
+ return FALSE;
+ }
+
+ /* Send the last element */
+ qelement = (QueueElement *) g_queue_pop_head (brc->queue);
+ remaining = qelement->remaining;
+ brc->user_callback (brc->source, brc->operation_id, qelement->media,
+ remaining, brc->user_data, qelement->error);
+ if (qelement->error) {
+ g_error_free (qelement->error);
+ }
+ g_free (qelement);
+
+ if (remaining == 0) {
+ operation_set_finished (brc->operation_id);
+ browse_relay_free (brc);
+ return FALSE;
+ }
+
+ /* Check if should keep running */
+ qelement = (QueueElement *) g_queue_peek_head (brc->queue);
+ brc->dispatcher_running = qelement && qelement->is_ready;
+
+ return brc->dispatcher_running;
+}
+
+static void
+queue_start_process (struct BrowseRelayCb *brc)
+{
+ QueueElement *qelement;
+
+ if (!brc->dispatcher_running) {
+ qelement = g_queue_peek_head (brc->queue);
+ if (qelement && qelement->is_ready) {
+ g_idle_add (queue_process, brc);
+ brc->dispatcher_running = TRUE;
+ }
+ }
+}
+
+static gint
+compare_queue_element (QueueElement *qelement,
+ GrlMedia *media)
+{
+ return qelement->media < media;
+}
+
+static void
+media_ready_cb (GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
+{
+ GList *element;
+ QueueElement *qelement;
+ struct BrowseRelayCb *brc = (struct BrowseRelayCb *) user_data;
+
+ /* Mark element as ready */
+ element = g_queue_find_custom (brc->queue, media,
+ (GCompareFunc) compare_queue_element);
+ if (!element) {
+ GRL_ERROR ("Media not found in the queue!");
+ return;
+ }
+
+ qelement = (QueueElement *) element->data;
+ qelement->is_ready = TRUE;
+ queue_start_process (brc);
+}
+
+static void
+queue_add_media (struct BrowseRelayCb *brc,
+ GrlMedia *media,
+ guint remaining,
+ const GError *error)
+{
+ QueueElement *qelement;
+ GList *unknown_keys;
+
+ if (!brc->queue) {
+ brc->queue = g_queue_new ();
+ }
+
+ /* Add element */
+ qelement = g_new (QueueElement, 1);
+ qelement->media = media;
+ qelement->remaining = remaining;
+ /* Media is ready if we do not need to ask other sources to complete it */
+ qelement->is_ready = TRUE;
+ if (grl_operation_options_get_flags (brc->options) & GRL_RESOLVE_FULL) {
+ unknown_keys = filter_known_keys (media, brc->keys);
+ if (unknown_keys) {
+ qelement->is_ready = FALSE;
+ }
+ }
+ if (error) {
+ qelement->error = g_error_copy (error);
+ } else {
+ qelement->error = NULL;
+ }
+ g_queue_push_tail (brc->queue, qelement);
+
+ if (!qelement->is_ready) {
+ media_decorate (brc->source, brc->operation_id, media, unknown_keys,
+ brc->options, media_ready_cb, brc);
+ }
+
+ queue_start_process (brc);
+}
+
+static struct AutoSplitCtl *
+auto_split_setup (GrlSource *source,
+ GrlOperationOptions *options)
+{
+ struct AutoSplitCtl *as_ctl = NULL;
+ gint count = grl_operation_options_get_count (options);
+
+ if (source->priv->auto_split_threshold > 0 &&
+ count > source->priv->auto_split_threshold) {
+ GRL_DEBUG ("auto-split: enabled");
+
+ as_ctl = g_slice_new (struct AutoSplitCtl);
+ as_ctl->threshold = source->priv->auto_split_threshold;
+ as_ctl->total_remaining = count;
+ as_ctl->chunk_remaining = as_ctl->threshold;
+ count = as_ctl->chunk_remaining;
+ grl_operation_options_set_count (options, count);
+ GRL_DEBUG ("auto-split: requesting chunk (skip=%u, count=%u)",
+ grl_operation_options_get_skip (options),
+ count);
+ }
+
+ return as_ctl;
+}
+
+static void
+auto_split_run_next_chunk (struct BrowseRelayCb *brc)
+{
+ brc->auto_split->chunk_remaining = MIN (brc->auto_split->threshold,
+ brc->auto_split->total_remaining);
+
+ switch (brc->operation_type) {
+ case GRL_OP_BROWSE:
+ grl_operation_options_set_skip (brc->spec.browse->options,
+ grl_operation_options_get_skip (brc->spec.browse->options) +
+ brc->auto_split->threshold);
+ grl_operation_options_set_count (brc->spec.browse->options,
+ brc->auto_split->chunk_remaining);
+ GRL_DEBUG ("auto-split: requesting chunk (skip=%u, count=%u)",
+ grl_operation_options_get_skip (brc->spec.browse->options),
+ grl_operation_options_get_count (brc->spec.browse->options));
+ g_idle_add_full (grl_operation_options_get_flags (brc->options) & GRL_RESOLVE_IDLE_RELAY?
+ G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
+ browse_idle,
+ brc->spec.browse,
+ NULL);
+ break;
+ case GRL_OP_SEARCH:
+ grl_operation_options_set_skip (brc->spec.search->options,
+ grl_operation_options_get_skip (brc->spec.search->options) +
+ brc->auto_split->threshold);
+ grl_operation_options_set_count (brc->spec.search->options,
+ brc->auto_split->chunk_remaining);
+ GRL_DEBUG ("auto-split: requesting chunk (skip=%u, count=%u)",
+ grl_operation_options_get_skip (brc->spec.search->options),
+ grl_operation_options_get_count (brc->spec.search->options));
+ g_idle_add_full (grl_operation_options_get_flags (brc->options) & GRL_RESOLVE_IDLE_RELAY?
+ G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
+ search_idle,
+ brc->spec.search,
+ NULL);
+ break;
+ case GRL_OP_QUERY:
+ grl_operation_options_set_skip (brc->spec.query->options,
+ grl_operation_options_get_skip (brc->spec.query->options) +
+ brc->auto_split->threshold);
+ grl_operation_options_set_count (brc->spec.query->options,
+ brc->auto_split->chunk_remaining);
+ GRL_DEBUG ("auto-split: requesting chunk (skip=%u, count=%u)",
+ grl_operation_options_get_skip (brc->spec.query->options),
+ grl_operation_options_get_count (brc->spec.query->options));
+ g_idle_add_full (grl_operation_options_get_flags (brc->options) & GRL_RESOLVE_IDLE_RELAY?
+ G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
+ query_idle,
+ brc->spec.query,
+ NULL);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+browse_result_relay_cb (GrlSource *source,
+ guint operation_id,
+ GrlMedia *media,
+ guint remaining,
+ gpointer user_data,
+ const GError *error)
+{
+ GError *_error;
+ struct BrowseRelayCb *brc = (struct BrowseRelayCb *) user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ /* Ignore elements after operation has completed */
+ if (operation_is_completed (operation_id)) {
+ GRL_WARNING ("Source '%s' emitted 'remaining=0' more than once "
+ "for operation %d",
+ grl_source_get_id (source), operation_id);
+ if (media) {
+ g_object_unref (media);
+ }
+ return;
+ }
+
+ /* Check if cancelled */
+ if (operation_is_cancelled (operation_id)) {
+ GRL_DEBUG ("Operation is cancelled, skipping result until getting the last one");
+ if (media) {
+ g_object_unref (media);
+ }
+ /* Wait for the last element */
+ if (remaining > 0) {
+ return;
+ } else {
+ _error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_OPERATION_CANCELLED,
+ "Operation was cancelled");
+ brc->user_callback (source, operation_id, NULL, 0,
+ brc->user_data, _error);
+ g_error_free (_error);
+ goto free_resources;
+ }
+ }
+
+ /* Auto-split management */
+ if (brc->auto_split) {
+ brc->auto_split->chunk_remaining--;
+ brc->auto_split->total_remaining--;
+ /* On last element, check if more elements should be asked: if source
+ satisfied all requested elements, but we need to get more */
+ if (remaining == 0) {
+ if (brc->auto_split->chunk_remaining == 0 &&
+ brc->auto_split->total_remaining > 0) {
+ auto_split_run_next_chunk (brc);
+ remaining = brc->auto_split->total_remaining;
+ }
+ } else {
+ remaining = brc->auto_split->total_remaining;
+ }
+ }
+
+ /* Set the source */
+ if (media) {
+ grl_media_set_source (media, grl_source_get_id (source));
+ }
+
+ /* If we need further processing of media, put it in a queue */
+ if (grl_operation_options_get_flags (brc->options) &
+ (GRL_RESOLVE_FULL | GRL_RESOLVE_IDLE_RELAY)) {
+ queue_add_media (brc, media, remaining, error);
+ } else {
+ brc->user_callback (source, operation_id, media, remaining,
+ brc->user_data, error);
+ }
+
+ if (remaining == 0) {
+ free_resources:
+ browse_relay_spec_free (brc);
+ if (!brc->queue || g_queue_is_empty (brc->queue)) {
+ operation_set_finished (operation_id);
+ browse_relay_free (brc);
+ } else {
+ /* There are elements pending to be processed; let's wait to free it in
+ the queue */
+ operation_set_completed (operation_id);
+ }
+ }
+}
+
+static void
+remove_result_relay_cb (GrlSource *source,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
+{
+ struct RemoveRelayCb *rrc = (struct RemoveRelayCb *) user_data;
+
+ rrc->user_callback (source, media, rrc->user_data, error);
+ remove_relay_free (rrc);
+}
+
+static gboolean
+resolve_idle (gpointer user_data)
+{
+ struct ResolveRelayCb *rrc = (struct ResolveRelayCb *) user_data;
+ GrlSourceResolveSpec *rs;
+ GList *spec;
+ GList *key;
+ gboolean run_next;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ /* Abort if operation was cancelled */
+ if (operation_is_cancelled (rrc->operation_id)) {
+ for (spec = rrc->specs_to_invoke;
+ spec;
+ spec = g_list_next (rs)) {
+ rs = (GrlSourceResolveSpec *) spec->data;
+ g_hash_table_remove (rrc->resolve_specs, rs->source);
+ }
+ g_list_free (rrc->specs_to_invoke);
+ rrc->specs_to_invoke = NULL;
+ run_next = FALSE;
+ resolve_result_relay_cb (rrc->source, rrc->operation_id, rrc->media, rrc, NULL);
+ } else {
+ rs = rrc->specs_to_invoke->data;
+ rrc->specs_to_invoke = g_list_next (rrc->specs_to_invoke);
+ run_next = (rrc->specs_to_invoke != NULL);
+
+ /* Put the specific keys in rs also into rrc */
+ for (key = rs->keys; key; key = g_list_next (key)) {
+ if (!g_list_find (rrc->keys, key->data)) {
+ rrc->keys = g_list_prepend (rrc->keys, key->data);
+ }
+ }
+
+ operation_set_ongoing (rs->source, rs->operation_id);
+ operation_set_started (rs->operation_id);
+ GRL_SOURCE_GET_CLASS (rs->source)->resolve (rs->source, rs);
+ }
+
+ return run_next;
+}
+
+static gboolean
+resolve_all_done (gpointer user_data)
+{
+ struct ResolveRelayCb *rrc = (struct ResolveRelayCb *) user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ if (operation_is_cancelled (rrc->operation_id)) {
+ if (rrc->error) {
+ g_error_free (rrc->error);
+ }
+ rrc->error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_OPERATION_CANCELLED,
+ "Operation was cancelled");
+ }
+
+ rrc->user_callback (rrc->source, rrc->operation_id, rrc->media, rrc->user_data, rrc->error);
+ operation_set_finished (rrc->operation_id);
+ resolve_relay_free (rrc);
+
+ return FALSE;
+}
+
+static gboolean
+media_from_uri_idle (gpointer user_data)
+{
+ GrlSourceMediaFromUriSpec *mfus = (GrlSourceMediaFromUriSpec *) user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ /* Abort if operation is cancelled */
+ if (operation_is_cancelled (mfus->operation_id)) {
+ mfus->callback (mfus->source, mfus->operation_id,
+ NULL, mfus->user_data, NULL);
+ } else {
+ operation_set_started (mfus->operation_id);
+ GRL_SOURCE_GET_CLASS (mfus->source)->media_from_uri (mfus->source, mfus);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+browse_idle (gpointer user_data)
+{
+ GrlSourceBrowseSpec *bs = (GrlSourceBrowseSpec *) user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ /* Abort if operation is cancelled */
+ if (operation_is_cancelled (bs->operation_id)) {
+ bs->callback (bs->source, bs->operation_id, NULL, 0, bs->user_data, NULL);
+ } else {
+ operation_set_started (bs->operation_id);
+ GRL_SOURCE_GET_CLASS (bs->source)->browse (bs->source, bs);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+search_idle (gpointer user_data)
+{
+ GrlSourceSearchSpec *ss = (GrlSourceSearchSpec *) user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ /* Abort if operation is cancelled */
+ if (operation_is_cancelled (ss->operation_id)) {
+ ss->callback (ss->source, ss->operation_id, NULL, 0, ss->user_data, NULL);
+ } else {
+ operation_set_started (ss->operation_id);
+ GRL_SOURCE_GET_CLASS (ss->source)->search (ss->source, ss);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+query_idle (gpointer user_data)
+{
+ GrlSourceQuerySpec *qs = (GrlSourceQuerySpec *) user_data;
+ GRL_DEBUG (__FUNCTION__);
+
+ /* Abort if operation is cancelled */
+ if (operation_is_cancelled (qs->operation_id)) {
+ qs->callback (qs->source, qs->operation_id, NULL, 0, qs->user_data, NULL);
+ } else {
+ operation_set_started (qs->operation_id);
+ GRL_SOURCE_GET_CLASS (qs->source)->query (qs->source, qs);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+remove_idle (gpointer user_data)
+{
+ struct RemoveRelayCb *rrc = (struct RemoveRelayCb *) user_data;
+ GRL_DEBUG (__FUNCTION__);
+
+ if (rrc->error) {
+ rrc->user_callback (rrc->source, rrc->media, rrc->user_data, rrc->error);
+ remove_relay_free (rrc);
+ } else {
+ GRL_SOURCE_GET_CLASS (rrc->source)->remove (rrc->source, rrc->spec);
+ }
+
+ return FALSE;
+}
+
+static void
+resolve_result_async_cb (GrlSource *source,
+ guint operation_id,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
+{
+ GrlDataSync *ds = (GrlDataSync *) user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ if (error) {
+ ds->error = g_error_copy (error);
+ }
+
+ ds->data = media;
+ ds->complete = TRUE;
+}
+
+static void
+multiple_result_async_cb (GrlSource *source,
+ guint op_id,
+ GrlMedia *media,
+ guint remaining,
+ gpointer user_data,
+ const GError *error)
+{
+ GrlDataSync *ds = (GrlDataSync *) user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ if (error) {
+ ds->error = g_error_copy (error);
+
+ /* Free previous results */
+ g_list_foreach (ds->data, (GFunc) g_object_unref, NULL);
+ g_list_free (ds->data);
+
+ ds->data = NULL;
+ ds->complete = TRUE;
+ return;
+ }
+
+ if (media) {
+ ds->data = g_list_prepend (ds->data, media);
+ }
+
+ if (remaining == 0) {
+ ds->data = g_list_reverse (ds->data);
+ ds->complete = TRUE;
+ }
+}
+
+static void
+remove_async_cb (GrlSource *source,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
+{
+ GrlDataSync *ds = (GrlDataSync *) user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ if (error) {
+ ds->error = g_error_copy (error);
+ }
+
+ ds->complete = TRUE;
+}
+
+static void
+store_result_async_cb (GrlSource *source,
+ GrlMedia *media,
+ GList *failed_keys,
+ gpointer user_data,
+ const GError *error)
+{
+ GrlDataSync *ds = (GrlDataSync *) user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ if (error) {
+ ds->error = g_error_copy (error);
+ }
+
+ ds->data = g_list_copy (failed_keys);
+ ds->complete = TRUE;
+}
+
+
+static void
+store_metadata_result_async_cb (GrlSource *source,
+ GrlMedia *media,
+ GList *failed_keys,
+ gpointer user_data,
+ const GError *error)
+{
+ GrlDataSync *ds = (GrlDataSync *) user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ if (error) {
+ ds->error = g_error_copy (error);
+ }
+
+ ds->data = g_list_copy (failed_keys);
+ ds->complete = TRUE;
+}
+
+static GHashTable *
+map_writable_keys (GrlSource *source,
+ GList *keys,
+ GrlWriteFlags flags,
+ GList **failed_keys)
+{
+ GHashTable *map;
+ GrlPluginRegistry *registry;
+ GList *sources = NULL;
+ GList *sources_iter;
+ GrlSource *_source;
+
+ map = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ g_object_unref,
+ (GDestroyNotify) g_list_free);
+
+ /* 'key_list' holds keys that can be written by this source
+ 'unsupportedy_keys' holds those that must be handled by other sources */
+ GList *key_list = g_list_copy (keys);
+ GList *unsupported_keys = filter_writable (source, &key_list, TRUE);
+
+ if (key_list) {
+ g_hash_table_insert (map, g_object_ref (source), key_list);
+ }
+
+ if (!unsupported_keys || !(flags & GRL_WRITE_FULL)) {
+ /* We are done! */
+ goto done;
+ }
+
+ /* Check if other sources can write the missing keys */
+ registry = grl_plugin_registry_get_default ();
+ sources =
+ grl_plugin_registry_get_sources_by_operations (registry,
+ GRL_OP_STORE_METADATA,
+ TRUE);
+
+ for (sources_iter = sources; unsupported_keys && sources_iter;
+ sources_iter = g_list_next (sources_iter)) {
+ _source = GRL_SOURCE (sources_iter->data);
+
+ if (_source == source) {
+ continue;
+ }
+
+ key_list = unsupported_keys;
+ unsupported_keys = filter_writable (_source, &key_list, TRUE);
+
+ if (!key_list) {
+ continue;
+ }
+
+ g_hash_table_insert (map, g_object_ref (_source), key_list);
+
+ if (!unsupported_keys) {
+ break;
+ }
+ }
+
+ g_list_free (sources);
+
+ done:
+ *failed_keys = unsupported_keys;
+ return map;
+}
+
+static void
+store_relay_cb (GrlSource *source,
+ GrlMedia *media,
+ GList *failed_keys,
+ gpointer user_data,
+ const GError *error)
+{
+ GrlSourceStoreSpec *ss = (GrlSourceStoreSpec *) user_data;
+ struct StoreRelayCb *src = (struct StoreRelayCb *) ss->user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ if (error || src->flags & GRL_WRITE_NORMAL) {
+ src->user_callback (source, media, failed_keys, src->user_data, error);
+ } else {
+ run_store_metadata (source, media, failed_keys, GRL_WRITE_FULL,
+ src->user_callback, src->user_data);
+ }
+ store_relay_free (src);
+ store_spec_free (ss);
+}
+
+static void
+store_metadata_ctl_cb (GrlSource *source,
+ GrlMedia *media,
+ GList *failed_keys,
+ gpointer user_data,
+ const GError *error)
+{
+ struct StoreMetadataRelayCb *smrc;
+ GError *own_error = NULL;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ smrc = (struct StoreMetadataRelayCb *) user_data;
+
+ if (failed_keys) {
+ smrc->failed_keys = g_list_concat (smrc->failed_keys, failed_keys);
+ }
+
+ g_hash_table_remove (smrc->map, source);
+
+ /* If we all sources have answered */
+ if (g_hash_table_size (smrc->map) == 0) {
+ /* We ignore the plugin errors, instead we create an own error
+ if some keys were not written */
+ if (smrc->user_callback) {
+ if (smrc->failed_keys) {
+ own_error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_STORE_METADATA_FAILED,
+ "Some keys could not be written");
+ }
+ smrc->user_callback (smrc->source,
+ media,
+ smrc->failed_keys,
+ smrc->user_data,
+ own_error);
+ if (own_error) {
+ g_error_free (own_error);
+ }
+ }
+ store_metadata_relay_free (smrc);
+ }
+}
+
+static gboolean
+store_idle (gpointer user_data)
+{
+ GrlSourceStoreSpec *ss = (GrlSourceStoreSpec *) user_data;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ GRL_SOURCE_GET_CLASS (ss->source)->store(ss->source, ss);
+
+ return FALSE;
+}
+
+static gboolean
+store_metadata_idle (gpointer user_data)
+{
+ GrlSourceStoreMetadataSpec *sms;
+ struct StoreMetadataRelayCb *smrc;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ smrc = (struct StoreMetadataRelayCb *) user_data;
+
+ sms = g_new (GrlSourceStoreMetadataSpec, 1);
+
+ sms->source = g_object_ref (g_list_first (smrc->use_sources));
+ sms->keys = g_hash_table_lookup (smrc->map, sms->source);
+ sms->media = g_object_ref (smrc->media);
+ sms->callback = store_metadata_ctl_cb;
+ sms->user_data = smrc;
+
+ /* Remove list header */
+ smrc->use_sources = g_list_remove_link (smrc->use_sources, smrc->use_sources);
+ smrc->specs = g_list_prepend (smrc->specs, sms);
+
+ GRL_SOURCE_GET_CLASS (sms->source)->store_metadata (sms->source, sms);
+
+ return (smrc->use_sources != NULL);
+}
+
+static void
+run_store_metadata (GrlSource *source,
+ GrlMedia *media,
+ GList *keys,
+ GrlWriteFlags flags,
+ GrlSourceStoreCb callback,
+ gpointer user_data)
+{
+ GHashTable *map;
+ GList *failed_keys = NULL;
+ GError *error;
+ struct StoreMetadataRelayCb *smrc;
+
+ map = map_writable_keys (source, keys, flags, &failed_keys);
+
+ if (g_hash_table_size (map) == 0) {
+ error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_STORE_METADATA_FAILED,
+ "None of the specified keys is writable");
+ if (callback) {
+ callback (source, media, failed_keys, user_data, error);
+ }
+
+ g_error_free (error);
+ g_list_free (failed_keys);
+ g_hash_table_unref (map);
+
+ return;
+ }
+
+ smrc = g_slice_new (struct StoreMetadataRelayCb);
+ smrc->source = g_object_ref (source);
+ smrc->media = g_object_ref (media);
+ smrc->map = map;
+ smrc->use_sources = g_hash_table_get_keys (map);
+ smrc->failed_keys = failed_keys;
+ smrc->specs = NULL;
+ smrc->user_callback = callback;
+ smrc->user_data = user_data;
+
+ g_idle_add (store_metadata_idle, smrc);
+}
+
+static gboolean
+check_options (GrlSource *source,
+ GrlSupportedOps operation,
+ GrlOperationOptions *options)
+{
+ GrlCaps *caps;
+
+ /* FIXME: that check should be in somewhere in GrlOperationOptions */
+ if (grl_operation_options_get_count (options) == 0)
+ return FALSE;
+
+ caps = grl_source_get_caps (source, operation);
+
+ return grl_operation_options_obey_caps (options, caps, NULL, NULL);
+}
+
+/* ============= API ============= */
+
+/**
+ * grl_source_supported_keys:
+ * @source: a source
+ *
+ * Get a list of #GrlKeyID, which describe a metadata types that this
+ * source can fetch and store.
+ *
+ * Returns: (element-type GrlKeyID) (transfer none): a #GList with the keys
+ */
+const GList *
+grl_source_supported_keys (GrlSource *source)
+{
+ g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+
+ if (GRL_SOURCE_GET_CLASS (source)->supported_keys) {
+ return GRL_SOURCE_GET_CLASS (source)->supported_keys (source);
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * grl_source_slow_keys:
+ * @source: a source
+ *
+ * Similar to grl_source_supported_keys(), but these keys
+ * are marked as slow because of the amount of traffic/processing needed
+ * to fetch them.
+ *
+ * Returns: (element-type GrlKeyID) (transfer none): a #GList with the keys
+ */
+const GList *
+grl_source_slow_keys (GrlSource *source)
+{
+ g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+
+ if (GRL_SOURCE_GET_CLASS (source)->slow_keys) {
+ return GRL_SOURCE_GET_CLASS (source)->slow_keys (source);
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * grl_source_writable_keys:
+ * @source: a source
+ *
+ * Similar to grl_source_supported_keys(), but these keys
+ * are marked as writable, meaning the source allows the client
+ * to provide new values for these keys that will be stored permanently.
+ *
+ * Returns: (element-type GrlKeyID) (transfer none):
+ * a #GList with the keys
+ */
+const GList *
+grl_source_writable_keys (GrlSource *source)
+{
+ g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+
+ if (GRL_SOURCE_GET_CLASS (source)->writable_keys) {
+ return GRL_SOURCE_GET_CLASS (source)->writable_keys (source);
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * grl_source_get_id:
+ * @source: a source
+ *
+ * Returns: the ID of the @source
+ */
+const gchar *
+grl_source_get_id (GrlSource *source)
+{
+ g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+
+ return source->priv->id;
+}
+
+/**
+ * grl_source_get_name:
+ * @source: a source
+ *
+ * Returns: the name of the @source
+ */
+const gchar *
+grl_source_get_name (GrlSource *source)
+{
+ g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+
+ return source->priv->name;
+}
+
+/**
+ * grl_source_get_description:
+ * @source: a source
+ *
+ * Returns: the description of the @source
+ */
+const gchar *
+grl_source_get_description (GrlSource *source)
+{
+ g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+
+ return source->priv->desc;
+}
+
+/**
+ * grl_source_get_plugin:
+ * @source: a source
+ *
+ * Returns: (transfer none): the plugin this source belongs to
+ **/
+GrlPlugin *
+grl_source_get_plugin (GrlSource *source)
+{
+ g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+
+ return source->priv->plugin;
+}
+
+/**
+ * grl_source_get_rank:
+ * @source: a source
+ *
+ * Gets the source rank
+ *
+ * Returns: rank value
+ **/
+gint
+grl_source_get_rank (GrlSource *source)
+{
+ g_return_val_if_fail (GRL_IS_SOURCE (source), 0);
+
+ return source->priv->rank;
+}
+
+/**
+ * grl_source_cancel:
+ * @source: a source
+ * @operation_id: the identifier of the running operation, as returned by the
+ * function that started it
+ *
+ * Cancel a running method.
+ *
+ * The derived class must implement the cancel vmethod in order to honour the
+ * request correctly. Otherwise, the operation will not be interrupted.
+ *
+ * In all cases, if this function is called on an ongoing operation, the
+ * corresponding callback will be called with the
+ * @GRL_CORE_ERROR_OPERATION_CANCELLED error set, and no more action will be
+ * taken for that operation after the said callback with error has been called.
+ */
+void
+grl_source_cancel (GrlSource *source, guint operation_id)
+{
+ GRL_DEBUG (__FUNCTION__);
+
+ g_return_if_fail (GRL_IS_SOURCE (source));
+
+ if (!operation_is_ongoing (operation_id)) {
+ GRL_DEBUG ("Tried to cancel invalid or already cancelled operation. "
+ "Skipping...");
+ return;
+ }
+
+ /* Mark the operation as finished, if the source does not implement
+ cancellation or it did not make it in time, we will not emit the results
+ for this operation in any case. At any rate, we will not free the
+ operation data until we are sure the plugin won't need it any more. In the
+ case of operations dealing with multiple results, like browse() or
+ search(), this will happen when it emits remaining = 0 (which can be
+ because it did not cancel the op or because it managed to cancel it and is
+ signaling so) */
+ operation_set_cancelled (operation_id);
+
+ /* If the source provides an implementation for operation cancellation,
+ let's use that to avoid further unnecessary processing in the plugin */
+ if (!operation_is_started (operation_id) &&
+ GRL_SOURCE_GET_CLASS (source)->cancel) {
+ GRL_SOURCE_GET_CLASS (source)->cancel (source, operation_id);
+ }
+}
+
+/**
+ * grl_source_supported_operations:
+ * @source: a source
+ *
+ * By default the derived objects of #GrlSource can only resolve.
+ *
+ * Returns: (type uint): a bitwise mangle with the supported operations by
+ * the source
+ */
+GrlSupportedOps
+grl_source_supported_operations (GrlSource *source)
+{
+ GrlSupportedOps ops = GRL_OP_NONE;
+ GrlSourceClass *source_class;
+
+ g_return_val_if_fail (GRL_IS_SOURCE (source), GRL_OP_NONE);
+
+ source_class = GRL_SOURCE_GET_CLASS (source);
+
+ if (source_class->supported_operations) {
+ return source_class->supported_operations (source);
+ }
+
+ if (source_class->resolve) {
+ ops |= GRL_OP_RESOLVE;
+ }
+ if (source_class->test_media_from_uri &&
+ source_class->media_from_uri) {
+ ops |= GRL_OP_MEDIA_FROM_URI;
+ }
+ if (source_class->browse) {
+ ops |= GRL_OP_BROWSE;
+ }
+ if (source_class->search) {
+ ops |= GRL_OP_SEARCH;
+ }
+ if (source_class->query) {
+ ops |= GRL_OP_QUERY;
+ }
+ if (source_class->remove) {
+ ops |= GRL_OP_REMOVE;
+ }
+ if (source_class->store_metadata) {
+ ops |= GRL_OP_STORE_METADATA;
+ }
+
+ if (source_class->notify_change_start &&
+ source_class->notify_change_stop) {
+ ops |= GRL_OP_NOTIFY_CHANGE;
+ }
+
+ return ops;
+}
+
+/**
+ * grl_source_get_auto_split_threshold:
+ * @source: a source
+ *
+ * Gets how much elements the source is able to handle in a single request.
+ *
+ * See #grilo_source_set_auto_split_threshold()
+ *
+ * Returns: the assigned threshold, or 0 if there is no threshold
+ */
+guint
+grl_source_get_auto_split_threshold (GrlSource *source)
+{
+ g_return_val_if_fail (GRL_IS_SOURCE (source), 0);
+
+ return source->priv->auto_split_threshold;
+}
+
+/**
+ * grl_source_set_auto_split_threshold:
+ * @source: a source
+ * @threshold: the threshold to set
+ *
+ * Sets how much elements the source is able to handle in a single request.
+ *
+ * If user, during a search or browsing operation, asks for more elements than
+ * the threshold, the request will be automatically splitted in chunks, so up to
+ * @threshold elements will be asked in each request.
+ *
+ * Source will act as if user were asking just a chunk, and user won't notice
+ * that the request was chunked.
+ *
+ * <note>
+ * <para>
+ * This function is intended to be used only by plugins.
+ * </para>
+ * </note>
+ *
+ */
+void
+grl_source_set_auto_split_threshold (GrlSource *source,
+ guint threshold)
+{
+ g_return_if_fail (GRL_IS_SOURCE (source));
+
+ source->priv->auto_split_threshold = threshold;
+}
+
+/**
+ * grl_source_resolve:
+ * @source: a source
+ * @media: (allow-none): a data transfer object
+ * @keys: (element-type GrlKeyID): the #GList of
+ * #GrlKeyID<!-- -->s to request
+ * @options: options to pass to this operation
+ * @callback: (scope notified): the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * This method is intended to fetch the requested keys of metadata of
+ * a given @media to the media source.
+ *
+ * This method is asynchronous.
+ *
+ * Returns: the operation identifier
+ */
+guint
+grl_source_resolve (GrlSource *source,
+ GrlMedia *media,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GrlSourceResolveCb callback,
+ gpointer user_data)
+{
+ GList *_keys;
+ GList *each_key;
+ GList *delete_key;
+ struct ResolveRelayCb *rrc;
+ guint operation_id;
+ GList *sources;
+ GrlResolutionFlags flags;
+ GrlOperationOptions *resolve_options;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ g_return_val_if_fail (GRL_IS_SOURCE (source), 0);
+ g_return_val_if_fail (keys != NULL, 0);
+ g_return_val_if_fail (callback != NULL, 0);
+ g_return_val_if_fail (grl_source_supported_operations (source) &
+ GRL_OP_RESOLVE, 0);
+ g_return_val_if_fail (check_options (source, GRL_OP_RESOLVE, options), 0);
+
+ if (!media) {
+ /* Special case, NULL media ==> root container */
+ media = grl_media_box_new ();
+ grl_media_set_id (media, NULL);
+ grl_media_set_source (media, grl_source_get_id (source));
+ } else if (!grl_media_get_source (media)) {
+ grl_media_set_source (media, grl_source_get_id (source));
+ }
+
+ /* By default assume we will use the parameters specified by the user */
+ _keys = filter_known_keys (media, (GList *) keys);
+
+ flags = grl_operation_options_get_flags (options);
+
+ if (flags & GRL_RESOLVE_FULL) {
+ GRL_DEBUG ("requested full metadata");
+ sources = grl_plugin_registry_get_sources_by_operations (grl_plugin_registry_get_default (),
+ GRL_OP_RESOLVE,
+ TRUE);
+ /* Put current source on top */
+ sources = g_list_remove (sources, source);
+ sources = g_list_prepend (sources, source);
+ flags &= ~GRL_RESOLVE_FULL;
+ resolve_options = grl_operation_options_copy (options);
+ grl_operation_options_set_flags (resolve_options, flags);
+ } else {
+ /* Consider only this source */
+ sources = g_list_prepend (NULL, source);
+ resolve_options = g_object_ref (options);
+ }
+
+ if (flags & GRL_RESOLVE_FAST_ONLY) {
+ GRL_DEBUG ("requested fast keys");
+ }
+
+ _keys = filter_unresolvable_keys (sources, &_keys);
+
+ operation_id = grl_operation_generate_id ();
+
+ operation_set_ongoing (source, operation_id);
+
+ /* Always hook an own relay callback so we can do some
+ post-processing before handing out the results
+ to the user */
+ rrc = g_slice_new (struct ResolveRelayCb);
+ rrc->source = g_object_ref (source);
+ rrc->operation_type = GRL_OP_RESOLVE;
+ rrc->operation_id = operation_id;
+ rrc->media = g_object_ref (media);
+ rrc->keys = _keys;
+ rrc->options = resolve_options;
+ rrc->user_callback = callback;
+ rrc->user_data = user_data;
+ rrc->cancel_invoked = FALSE;
+ rrc->map = map_keys_new ();
+ rrc->resolve_specs = map_sources_new ();
+ rrc->error = NULL;
+ rrc->specs_to_invoke = NULL;
+
+ map_keys_to_sources (rrc->map, _keys, sources, media, flags & GRL_RESOLVE_FAST_ONLY);
+ g_list_free (sources);
+
+ each_key = rrc->keys;
+ while (each_key) {
+ if (map_sources_to_specs (rrc->resolve_specs, rrc->map, media,
+ GRLPOINTER_TO_KEYID (each_key->data),
+ resolve_options, rrc)) {
+ each_key = g_list_next (each_key);
+ } else {
+ delete_key = each_key;
+ each_key = g_list_next (each_key);
+ rrc->keys = g_list_delete_link (rrc->keys, delete_key);
+ }
+ }
+
+ rrc->specs_to_invoke = g_hash_table_get_values (rrc->resolve_specs);
+ if (rrc->specs_to_invoke) {
+ g_idle_add_full (flags & GRL_RESOLVE_IDLE_RELAY?
+ G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
+ resolve_idle,
+ rrc,
+ NULL);
+ } else {
+ g_idle_add_full (flags & GRL_RESOLVE_IDLE_RELAY?
+ G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
+ resolve_all_done,
+ rrc,
+ NULL);
+ }
+
+ return operation_id;
+}
+
+/**
+ * grl_source_resolve_sync:
+ * @source: a source
+ * @media: (allow-none): a data transfer object
+ * @keys: (element-type GrlKeyID): the #GList of
+ * #GrlKeyID<!-- -->s to request
+ * @options: options to pass to this operation
+ * @error: a #GError, or @NULL
+ *
+ * This method is intended to fetch the requested keys of metadata of
+ * a given @media to the media source.
+ *
+ * This method is synchronous.
+ *
+ * Returns: (transfer full): a filled #GrlMedia
+ */
+GrlMedia *
+grl_source_resolve_sync (GrlSource *source,
+ GrlMedia *media,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GError **error)
+{
+ GrlDataSync *ds;
+
+ ds = g_slice_new0 (GrlDataSync);
+
+ grl_source_resolve (source,
+ media,
+ keys,
+ options,
+ resolve_result_async_cb,
+ ds);
+
+ grl_wait_for_async_operation_complete (ds);
+
+ if (ds->error) {
+ if (error) {
+ *error = ds->error;
+ } else {
+ g_error_free (ds->error);
+ }
+ }
+
+ g_slice_free (GrlDataSync, ds);
+
+ return media;
+}
+
+/**
+ * grl_source_may_resolve:
+ * @source: a source
+ * @media: a media on which we want more metadata
+ * @key_id: the key corresponding to a metadata we might want
+ * @missing_keys: an optional originally empty list
+ *
+ * Checks whether @key_id may be resolved with @source for @media, so that the
+ * caller can avoid calling grl_source_resolve() if it can be known in
+ * advance it will fail.
+ *
+ * If the resolution is known to be impossible because more keys are needed in
+ * @media, and @missing_keys is not @NULL, it is populated with the list of
+ * GrlKeyID that would be needed.
+ *
+ * This function is synchronous and should not block.
+ *
+ * Returns: @TRUE if there's a possibility that @source resolves @key_id for
+ * @media, @FALSE otherwise.
+ */
+gboolean
+grl_source_may_resolve (GrlSource *source,
+ GrlMedia *media,
+ GrlKeyID key_id,
+ GList **missing_keys)
+{
+ GrlSourceClass *klass;
+ const GList *supported_keys;
+ const gchar *media_source;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ g_return_val_if_fail (GRL_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (!missing_keys || !*missing_keys, FALSE);
+
+ klass = GRL_SOURCE_GET_CLASS (source);
+
+ if (klass->may_resolve) {
+ return klass->may_resolve (source, media, key_id, missing_keys);
+ }
+
+ /* Default behaviour is to assume that if source implements resolve, then any
+ supported key for its own content is resolved */
+ if (klass->resolve) {
+ GRL_DEBUG ("Using default may_resolve()");
+ /* We need to know the media source */
+ if (media == NULL ||
+ (media_source = grl_media_get_source (media)) == NULL) {
+ if (missing_keys) {
+ *missing_keys = NULL;
+ *missing_keys =
+ g_list_prepend (*missing_keys,
+ GRLKEYID_TO_POINTER (GRL_METADATA_KEY_SOURCE));
+ }
+ return FALSE;
+ }
+ /* Content is from different source */
+ if (g_strcmp0 (grl_source_get_id (source), media_source) != 0) {
+ return FALSE;
+ }
+ /* Check if the key is supported */
+ supported_keys = grl_source_supported_keys (source);
+ return (g_list_find ((GList *) supported_keys,
+ GRLKEYID_TO_POINTER (key_id)) != NULL);
+ } else {
+ GRL_WARNING ("Source %s does not implement may_resolve()",
+ grl_source_get_id (source));
+ return FALSE;
+ }
+}
+
+/**
+ * grl_source_test_media_from_uri:
+ * @source: a source
+ * @uri: A URI that can be used to identify a media resource
+ *
+ * Tests whether @source can instantiate a #GrlMedia object representing
+ * the media resource exposed at @uri.
+ *
+ * Returns: %TRUE if it can, %FALSE otherwise.
+ *
+ * This method is synchronous.
+ */
+gboolean
+grl_source_test_media_from_uri (GrlSource *source,
+ const gchar *uri)
+{
+ GRL_DEBUG (__FUNCTION__);
+
+ g_return_val_if_fail (GRL_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ if (GRL_SOURCE_GET_CLASS (source)->test_media_from_uri) {
+ return GRL_SOURCE_GET_CLASS (source)->test_media_from_uri (source, uri);
+ } else {
+ return FALSE;
+ }
+}
+
+/**
+ * grl_source_get_media_from_uri:
+ * @source: a source
+ * @uri: A URI that can be used to identify a media resource
+ * @keys: A list of keys to resolve
+ * @flags: the resolution mode
+ * @callback: (scope notified): the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Creates an instance of #GrlMedia representing the media resource
+ * exposed at @uri.
+ *
+ * It is recommended to call grl_source_test_media_from_uri() before invoking
+ * this to check whether the target source can theoretically do the resolution.
+ *
+ * This method is asynchronous.
+ *
+ * Returns: the operation identifier
+ */
+guint
+grl_source_get_media_from_uri (GrlSource *source,
+ const gchar *uri,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GrlSourceResolveCb callback,
+ gpointer user_data)
+{
+ GList *_keys;
+ GrlSourceMediaFromUriSpec *mfus;
+ struct ResolveRelayCb *rrc;
+ guint operation_id;
+ GrlResolutionFlags flags;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ g_return_val_if_fail (GRL_IS_SOURCE (source), 0);
+ g_return_val_if_fail (uri != NULL, 0);
+ g_return_val_if_fail (keys != NULL, 0);
+ g_return_val_if_fail (callback != NULL, 0);
+ g_return_val_if_fail (grl_source_supported_operations (source) &
+ GRL_OP_MEDIA_FROM_URI, 0);
+ g_return_val_if_fail (check_options (source, GRL_OP_MEDIA_FROM_URI, options), 0);
+
+ _keys = g_list_copy ((GList *) keys);
+ flags = grl_operation_options_get_flags (options);
+
+ if (flags & GRL_RESOLVE_FAST_ONLY) {
+ filter_slow (source, &_keys, FALSE);
+ }
+
+ if (flags & GRL_RESOLVE_FULL) {
+ _keys = expand_operation_keys (source, NULL, _keys);
+ }
+
+ operation_id = grl_operation_generate_id ();
+
+ /* We cannot prepare for full resolution yet because we don't
+ have a GrlMedia t operate with.
+ TODO: full resolution could be added in the relay calback
+ when we get the GrlMedia object */
+
+ /* Always hook an own relay callback so we can do some
+ post-processing before handing out the results
+ to the user */
+ rrc = g_slice_new (struct ResolveRelayCb);
+ rrc->source = g_object_ref (source);
+ rrc->operation_type = GRL_OP_MEDIA_FROM_URI;
+ rrc->operation_id = operation_id;
+ rrc->keys = _keys;
+ rrc->options = g_object_ref (options);
+ rrc->user_callback = callback;
+ rrc->user_data = user_data;
+
+ mfus = g_new0 (GrlSourceMediaFromUriSpec, 1);
+ mfus->source = g_object_ref (source);
+ mfus->operation_id = operation_id;
+ mfus->uri = g_strdup (uri);
+ mfus->keys = _keys;
+ mfus->options = grl_operation_options_copy (options);
+ mfus->callback = media_from_uri_result_relay_cb;
+ mfus->user_data = rrc;
+
+ /* Save a reference to the operaton spec in the relay-cb's
+ user_data so that we can free the spec there */
+ rrc->spec.mfu = mfus;
+
+ operation_set_ongoing (source, operation_id);
+
+ g_idle_add_full (flags & GRL_RESOLVE_IDLE_RELAY?
+ G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
+ media_from_uri_idle,
+ mfus,
+ NULL);
+
+ return operation_id;
+}
+
+/**
+ * grl_source_get_media_from_uri_sync:
+ * @source: a source
+ * @uri: A URI that can be used to identify a media resource
+ * @keys: A list of keys to resolve
+ * @options: options wanted for that operation
+ * @error: a #GError, or @NULL
+ *
+ * Creates an instance of #GrlMedia representing the media resource
+ * exposed at @uri.
+ *
+ * It is recommended to call grl_source_test_media_from_uri() before
+ * invoking this to check whether the target source can theoretically do the
+ * resolution.
+ *
+ * This method is synchronous.
+ *
+ * Returns: (transfer full): a filled #GrlMedia
+ */
+GrlMedia *
+grl_source_get_media_from_uri_sync (GrlSource *source,
+ const gchar *uri,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GError **error)
+{
+ GrlDataSync *ds;
+ GrlMedia *result;
+
+ ds = g_slice_new0 (GrlDataSync);
+
+ grl_source_get_media_from_uri (source,
+ uri,
+ keys,
+ options,
+ resolve_result_async_cb,
+ ds);
+
+ grl_wait_for_async_operation_complete (ds);
+
+ if (ds->error) {
+ if (error) {
+ *error = ds->error;
+ } else {
+ g_error_free (ds->error);
+ }
+ }
+
+ result = (GrlMedia *) ds->data;
+ g_slice_free (GrlDataSync, ds);
+
+ return result;
+}
+
+/**
+ * grl_source_browse:
+ * @source: a source
+ * @container: (allow-none): a container of data transfer objects
+ * @keys: (element-type GrlKeyID): the #GList of
+ * #GrlKeyID<!-- -->s to request
+ * @options: options wanted for that operation
+ * @callback: (scope notified): the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Browse from media elements through an available list.
+ *
+ * This method is asynchronous.
+ *
+ * Returns: the operation identifier
+ */
+guint
+grl_source_browse (GrlSource *source,
+ GrlMedia *container,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GrlSourceResultCb callback,
+ gpointer user_data)
+{
+ GList *_keys;
+ GrlSourceBrowseSpec *bs;
+ guint operation_id;
+ struct BrowseRelayCb *brc;
+ GrlResolutionFlags flags;
+
+ g_return_val_if_fail (GRL_IS_SOURCE (source), 0);
+ g_return_val_if_fail (callback != NULL, 0);
+ g_return_val_if_fail (grl_source_supported_operations (source) &
+ GRL_OP_BROWSE, 0);
+ g_return_val_if_fail (check_options (source, GRL_OP_BROWSE, options), 0);
+
+ /* By default assume we will use the parameters specified by the user */
+ _keys = g_list_copy ((GList *) keys);
+
+ flags = grl_operation_options_get_flags (options);
+
+ if (flags & GRL_RESOLVE_FAST_ONLY) {
+ GRL_DEBUG ("requested fast keys");
+ filter_slow (source, &_keys, FALSE);
+ }
+
+ /* Setup full resolution mode if requested */
+ if (flags & GRL_RESOLVE_FULL) {
+ GRL_DEBUG ("requested full metadata");
+ _keys = expand_operation_keys (source, NULL, _keys);
+ }
+
+ operation_id = grl_operation_generate_id ();
+
+ /* Always hook an own relay callback so we can do some
+ post-processing before handing out the results
+ to the user */
+ brc = g_slice_new (struct BrowseRelayCb);
+ brc->source = g_object_ref (source);
+ brc->operation_type = GRL_OP_BROWSE;
+ brc->operation_id = operation_id;
+ brc->keys = _keys;
+ brc->options = g_object_ref (options);
+ brc->user_callback = callback;
+ brc->user_data = user_data;
+ brc->queue = NULL;
+ brc->dispatcher_running = FALSE;
+
+ bs = g_new (GrlSourceBrowseSpec, 1);
+ bs->source = g_object_ref (source);
+ bs->operation_id = operation_id;
+ /* _keys is already a copy */
+ bs->keys = _keys;
+ bs->options = grl_operation_options_copy (options);
+ bs->callback = browse_result_relay_cb;
+ bs->user_data = brc;
+
+ if (!container) {
+ /* Special case: NULL container ==> NULL id */
+ bs->container = grl_media_box_new ();
+ grl_media_set_source (bs->container,
+ grl_source_get_id (source));
+ } else {
+ bs->container = g_object_ref (container);
+ }
+
+ /* Save a reference to the operaton spec in the relay-cb's
+ user_data so that we can free the spec there when we get
+ the last result */
+ brc->spec.browse = bs;
+
+ /* Setup auto-split management if requested */
+ brc->auto_split = auto_split_setup (source, bs->options);
+
+ operation_set_ongoing (source, operation_id);
+
+ g_idle_add_full (flags & GRL_RESOLVE_IDLE_RELAY? G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
+ browse_idle,
+ bs,
+ NULL);
+
+ return operation_id;
+}
+
+/**
+ * grl_source_browse_sync:
+ * @source: a source
+ * @container: (allow-none): a container of data transfer objects
+ * @keys: (element-type GrlKeyID): the #GList of
+ * #GrlKeyID<!-- -->s to request
+ * @options: options wanted for that operation
+ * @error: a #GError, or @NULL
+ *
+ * Browse media elements through an available
+ * list.
+ *
+ * This method is synchronous.
+ *
+ * Returns: (element-type Grl.Media) (transfer full): a #GList with #GrlMedia
+ * elements. After use g_object_unref() every element and g_list_free() the
+ * list.
+ */
+GList *
+grl_source_browse_sync (GrlSource *source,
+ GrlMedia *container,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GError **error)
+{
+ GrlDataSync *ds;
+ GList *result;
+
+ ds = g_slice_new0 (GrlDataSync);
+
+ grl_source_browse (source,
+ container,
+ keys,
+ options,
+ multiple_result_async_cb,
+ ds);
+
+ grl_wait_for_async_operation_complete (ds);
+
+ if (ds->error) {
+ if (error) {
+ *error = ds->error;
+ } else {
+ g_error_free (ds->error);
+ }
+ }
+
+ result = (GList *) ds->data;
+ g_slice_free (GrlDataSync, ds);
+
+ return result;
+}
+
+/**
+ * grl_source_search:
+ * @source: a source
+ * @text: the text to search
+ * @keys: (element-type GrlKeyID): the #GList of
+ * #GrlKeyID<!-- -->s to request
+ * @options: options wanted for that operation
+ * @callback: (scope notified): the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Search for the @text string in a source for data identified with that string.
+ *
+ * If @text is @NULL then no text filter will be applied, and thus, no media
+ * items from @source will be filtered. If @source does not support NULL-text
+ * search operations it should notiy the client by setting
+ * @GRL_CORE_ERROR_SEARCH_NULL_UNSUPPORTED in @callback's error parameter.
+ *
+ * This method is asynchronous.
+ *
+ * Returns: the operation identifier
+ */
+guint
+grl_source_search (GrlSource *source,
+ const gchar *text,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GrlSourceResultCb callback,
+ gpointer user_data)
+{
+ GList *_keys;
+ GrlSourceSearchSpec *ss;
+ guint operation_id;
+ struct BrowseRelayCb *brc;
+ GrlResolutionFlags flags;
+
+ g_return_val_if_fail (GRL_IS_SOURCE (source), 0);
+ g_return_val_if_fail (callback != NULL, 0);
+ g_return_val_if_fail (grl_source_supported_operations (source) &
+ GRL_OP_SEARCH, 0);
+ g_return_val_if_fail (check_options (source, GRL_OP_SEARCH, options), 0);
+
+ /* By default assume we will use the parameters specified by the user */
+ _keys = g_list_copy ((GList *) keys);
+
+ flags = grl_operation_options_get_flags (options);
+
+ if (flags & GRL_RESOLVE_FAST_ONLY) {
+ GRL_DEBUG ("requested fast keys");
+ filter_slow (source, &_keys, FALSE);
+ }
+
+ /* Setup full resolution mode if requested */
+ if (flags & GRL_RESOLVE_FULL) {
+ GRL_DEBUG ("requested full metadata");
+ _keys = expand_operation_keys (source, NULL, _keys);
+ }
+
+ operation_id = grl_operation_generate_id ();
+
+ /* Always hook an own relay callback so we can do some
+ post-processing before handing out the results
+ to the user */
+ brc = g_slice_new (struct BrowseRelayCb);
+ brc->source = g_object_ref (source);
+ brc->operation_type = GRL_OP_SEARCH;
+ brc->operation_id = operation_id;
+ brc->keys = _keys;
+ brc->options = g_object_ref (options);
+ brc->user_callback = callback;
+ brc->user_data = user_data;
+ brc->queue = NULL;
+ brc->dispatcher_running = FALSE;
+
+ ss = g_new (GrlSourceSearchSpec, 1);
+ ss->source = g_object_ref (source);
+ ss->operation_id = operation_id;
+ ss->text = g_strdup (text);
+ ss->keys = _keys;
+ ss->options = grl_operation_options_copy (options);
+ ss->callback = browse_result_relay_cb;
+ ss->user_data = brc;
+
+ /* Save a reference to the operaton spec in the relay-cb's
+ user_data so that we can free the spec there when we get
+ the last result */
+ brc->spec.search = ss;
+
+ /* Setup auto-split management if requested */
+ brc->auto_split = auto_split_setup (source, ss->options);
+
+ operation_set_ongoing (source, operation_id);
+
+ g_idle_add_full (flags & GRL_RESOLVE_IDLE_RELAY? G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
+ search_idle,
+ ss,
+ NULL);
+
+ return operation_id;
+}
+
+/**
+ * grl_source_search_sync:
+ * @source: a source
+ * @text: the text to search
+ * @keys: (element-type GrlKeyID): the #GList of
+ * #GrlKeyID<!-- -->s to request
+ * @options: options wanted for that operation
+ * @error: a #GError, or @NULL
+ *
+ * Search for the @text string in a source for data identified with that string.
+ *
+ * If @text is @NULL then no text filter will be applied, and thus, no media
+ * items from @source will be filtered. If @source does not support NULL-text
+ * search operations it should notiy the client by setting
+ * @GRL_CORE_ERROR_SEARCH_NULL_UNSUPPORTED in the error parameter.
+ *
+ * This method is synchronous.
+ *
+ * Returns: (element-type Grl.Media) (transfer full): a #GList with #GrlMedia
+ * elements. After use g_object_unref() every element and g_list_free() the
+ * list.
+ */
+GList *
+grl_source_search_sync (GrlSource *source,
+ const gchar *text,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GError **error)
+{
+ GrlDataSync *ds;
+ GList *result;
+
+ ds = g_slice_new0 (GrlDataSync);
+
+ grl_source_search (source,
+ text,
+ keys,
+ options,
+ multiple_result_async_cb,
+ ds);
+
+ grl_wait_for_async_operation_complete (ds);
+
+ if (ds->error) {
+ if (error) {
+ *error = ds->error;
+ } else {
+ g_error_free (ds->error);
+ }
+ }
+
+ result = (GList *) ds->data;
+ g_slice_free (GrlDataSync, ds);
+
+ return result;
+}
+
+/**
+ * grl_source_query:
+ * @source: a source
+ * @query: the query to process
+ * @keys: (element-type GrlKeyID): the #GList of
+ * #GrlKeyID<!-- -->s to request
+ * @options: options wanted for that operation
+ * @callback: (scope notified): the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Execute a specialized query (specific for each provider) on a media
+ * repository.
+ *
+ * It is different from grl_source_search() semantically, because the query
+ * implies a carefully crafted string, rather than a simple string to search.
+ *
+ * This method is asynchronous.
+ *
+ * Returns: the operation identifier
+ */
+guint
+grl_source_query (GrlSource *source,
+ const gchar *query,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GrlSourceResultCb callback,
+ gpointer user_data)
+{
+ GList *_keys;
+ GrlSourceQuerySpec *qs;
+ guint operation_id;
+ struct BrowseRelayCb *brc;
+ GrlResolutionFlags flags;
+
+ g_return_val_if_fail (GRL_IS_SOURCE (source), 0);
+ g_return_val_if_fail (query != NULL, 0);
+ g_return_val_if_fail (callback != NULL, 0);
+ g_return_val_if_fail (grl_source_supported_operations (source) &
+ GRL_OP_QUERY, 0);
+ g_return_val_if_fail (check_options (source, GRL_OP_QUERY, options), 0);
+
+ /* By default assume we will use the parameters specified by the user */
+ _keys = g_list_copy ((GList *) keys);
+
+ flags = grl_operation_options_get_flags (options);
+
+ if (flags & GRL_RESOLVE_FAST_ONLY) {
+ GRL_DEBUG ("requested fast keys");
+ filter_slow (source, &_keys, FALSE);
+ }
+
+ if (flags & GRL_RESOLVE_FULL) {
+ GRL_DEBUG ("requested full metadata");
+ _keys = expand_operation_keys (source, NULL, _keys);
+ }
+
+ operation_id = grl_operation_generate_id ();
+
+ /* Always hook an own relay callback so we can do some
+ post-processing before handing out the results
+ to the user */
+ brc = g_slice_new (struct BrowseRelayCb);
+ brc->source = g_object_ref (source);
+ brc->operation_type = GRL_OP_QUERY;
+ brc->operation_id = operation_id;
+ brc->keys = _keys;
+ brc->options = g_object_ref (options);
+ brc->user_callback = callback;
+ brc->user_data = user_data;
+ brc->queue = NULL;
+ brc->dispatcher_running = FALSE;
+
+ qs = g_new (GrlSourceQuerySpec, 1);
+ qs->source = g_object_ref (source);
+ qs->operation_id = operation_id;
+ qs->query = g_strdup (query);
+ qs->keys = _keys;
+ qs->options = grl_operation_options_copy (options);
+ qs->callback = browse_result_relay_cb;
+ qs->user_data = brc;
+
+ /* Save a reference to the operaton spec in the relay-cb's
+ user_data so that we can free the spec there when we get
+ the last result */
+ brc->spec.query = qs;
+
+ /* Setup auto-split management if requested */
+ brc->auto_split = auto_split_setup (source, qs->options);
+
+ operation_set_ongoing (source, operation_id);
+
+ g_idle_add_full (flags & GRL_RESOLVE_IDLE_RELAY? G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
+ query_idle,
+ qs,
+ NULL);
+
+ return operation_id;
+}
+
+/**
+ * grl_source_query_sync:
+ * @source: a source
+ * @query: the query to process
+ * @keys: (element-type GrlKeyID): the #GList of
+ * #GrlKeyID<!-- -->s to request
+ * @options: options wanted for that operation
+ * @error: a #GError, or @NULL
+ *
+ * Execute a specialized query (specific for each provider) on a media
+ * repository.
+ *
+ * This method is synchronous.
+ *
+ * Returns: (element-type Grl.Media) (transfer full): a #GList with #GrlMedia
+ * elements. After use g_object_unref() every element and g_list_free() the
+ * list.
+ */
+GList *
+grl_source_query_sync (GrlSource *source,
+ const gchar *query,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GError **error)
+{
+ GrlDataSync *ds;
+ GList *result;
+
+ ds = g_slice_new0 (GrlDataSync);
+
+ grl_source_query (source,
+ query,
+ keys,
+ options,
+ multiple_result_async_cb,
+ ds);
+
+ grl_wait_for_async_operation_complete (ds);
+
+ if (ds->error) {
+ if (error) {
+ *error = ds->error;
+ } else {
+ g_error_free (ds->error);
+ }
+ }
+
+ result = (GList *) ds->data;
+ g_slice_free (GrlDataSync, ds);
+
+ return result;
+}
+
+/**
+ * grl_source_remove:
+ * @source: a source
+ * @media: a data transfer object
+ * @callback: (scope notified): the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Remove a @media from the @source repository.
+ *
+ * This method is asynchronous.
+ */
+void
+grl_source_remove (GrlSource *source,
+ GrlMedia *media,
+ GrlSourceRemoveCb callback,
+ gpointer user_data)
+{
+ const gchar *id;
+ struct RemoveRelayCb *rrc;
+ GrlSourceRemoveSpec *rs;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ g_return_if_fail (GRL_IS_SOURCE (source));
+ g_return_if_fail (GRL_IS_MEDIA (media));
+ g_return_if_fail (callback != NULL);
+ g_return_if_fail (grl_source_supported_operations (source) &
+ GRL_OP_REMOVE);
+
+ rrc = g_slice_new (struct RemoveRelayCb);
+ rrc->source = g_object_ref (source);
+ rrc->media = g_object_ref (media);
+ rrc->user_callback = callback;
+ rrc->user_data = user_data;
+
+ /* Check that we have the minimum information we need */
+ id = grl_media_get_id (media);
+ if (!id) {
+ rrc->error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_REMOVE_FAILED,
+ "Media has no id, cannot remove");
+ rrc->spec = NULL;
+ } else {
+ rrc->error = NULL;
+ rs = g_new0 (GrlSourceRemoveSpec, 1);
+ rs->source = g_object_ref (source);
+ rs->media_id = g_strdup (id);
+ rs->media = g_object_ref (media);
+ rs->callback = remove_result_relay_cb;
+ rs->user_data = rrc;
+ rrc->spec = rs;
+ }
+
+ g_idle_add (remove_idle, rrc);
+}
+
+/**
+ * grl_source_remove_sync:
+ * @source: a source
+ * @media: a data transfer object
+ * @error: a #GError, or @NULL
+ *
+ * Remove a @media from the @source repository.
+ *
+ * This method is synchronous.
+ *
+ * Since: 0.1.6
+ */
+void
+grl_source_remove_sync (GrlSource *source,
+ GrlMedia *media,
+ GError **error)
+{
+ GrlDataSync *ds;
+
+ ds = g_slice_new0 (GrlDataSync);
+
+ grl_source_remove (source,
+ media,
+ remove_async_cb,
+ ds);
+
+ grl_wait_for_async_operation_complete (ds);
+
+ if (ds->error) {
+ if (error) {
+ *error = ds->error;
+ } else {
+ g_error_free (ds->error);
+ }
+ }
+
+ g_slice_free (GrlDataSync, ds);
+}
+
+/**
+ * grl_source_store:
+ * @source: a source
+ * @parent: (allow-none): a parent to store the data transfer objects
+ * @media: a data transfer object
+ * @flags: flags to configure specific behaviour of the operation
+ * @callback: (scope notified): the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Store the @media into the @parent container
+ *
+ * This method is asynchronous.
+ */
+void
+grl_source_store (GrlSource *source,
+ GrlMediaBox *parent,
+ GrlMedia *media,
+ GrlWriteFlags flags,
+ GrlSourceStoreCb callback,
+ gpointer user_data)
+{
+ struct StoreRelayCb *src;
+ GrlSourceStoreSpec *ss;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ g_return_if_fail (GRL_IS_SOURCE (source));
+ g_return_if_fail (!parent || GRL_IS_MEDIA_BOX (parent));
+ g_return_if_fail (GRL_IS_MEDIA (media));
+
+ g_return_if_fail ((!parent &&
+ grl_source_supported_operations (source) & GRL_OP_STORE) ||
+ (parent &&
+ grl_source_supported_operations (source) & GRL_OP_STORE_PARENT));
+
+ src = g_slice_new (struct StoreRelayCb);
+ src->flags = flags;
+ src->user_callback = callback;
+ src->user_data = user_data;
+
+ ss = g_new (GrlSourceStoreSpec, 1);
+ ss->source = g_object_ref (source);
+ ss->parent = parent? g_object_ref (parent): NULL;
+ ss->media = g_object_ref (media);
+ ss->callback = store_relay_cb;
+ ss->user_data = src;
+
+ g_idle_add (store_idle, ss);
+}
+
+/**
+ * grl_source_store_sync:
+ * @source: a source
+ * @parent: (allow-none): a #GrlMediaBox to store the data transfer objects
+ * @media: a #GrlMedia data transfer object
+ * @error: a #GError, or @NULL
+ *
+ * Store the @media into the @parent container.
+ *
+ * This method is synchronous.
+ */
+void
+grl_source_store_sync (GrlSource *source,
+ GrlMediaBox *parent,
+ GrlMedia *media,
+ GrlWriteFlags flags,
+ GError **error)
+{
+ GrlDataSync *ds;
+
+ ds = g_slice_new0 (GrlDataSync);
+
+ grl_source_store (source,
+ parent,
+ media,
+ flags,
+ store_result_async_cb,
+ ds);
+
+ grl_wait_for_async_operation_complete (ds);
+
+ if (ds->error) {
+ if (error) {
+ *error = ds->error;
+ } else {
+ g_error_free (ds->error);
+ }
+ }
+
+ g_slice_free (GrlDataSync, ds);
+}
+
+/**
+ * grl_source_store_metadata:
+ * @source: a metadata source
+ * @media: the #GrlMedia object that we want to operate on.
+ * @keys: (element-type GObject.ParamSpec) (allow-none): a list
+ * of #GrlKeyID whose values we want to change.
+ * @flags: Flags to configure specific behaviors of the operation.
+ * @callback: (scope notified): the callback to execute when the operation is finished.
+ * @user_data: user data set for the @callback
+ *
+ * This is the main method of the #GrlMetadataSource class. It will
+ * get the values for @keys from @media and store it permanently. After
+ * calling this method, future queries that return this media object
+ * shall return this new values for the selected keys.
+ *
+ * This function is asynchronous and uses the Glib's main loop.
+ */
+void
+grl_source_store_metadata (GrlSource *source,
+ GrlMedia *media,
+ GList *keys,
+ GrlWriteFlags flags,
+ GrlSourceStoreCb callback,
+ gpointer user_data)
+{
+ GRL_DEBUG (__FUNCTION__);
+
+ g_return_if_fail (GRL_IS_SOURCE (source));
+ g_return_if_fail (GRL_IS_MEDIA (media));
+ g_return_if_fail (keys != NULL);
+ g_return_if_fail (grl_source_supported_operations (source) &
+ GRL_OP_STORE_METADATA);
+
+ run_store_metadata (source, media, keys, flags, callback, user_data);
+}
+
+/**
+ * grl_source_store_metadata_sync:
+ * @source: a source
+ * @media: the #GrlMedia object that we want to operate on
+ * @keys: (element-type GrlKeyID) (allow-none): a list of
+ * #GrlKeyID whose values we want to change
+ * @flags: Flags to configure specific behaviors of the operation.
+ * @error: a #GError, or @NULL
+ *
+ * Update @keys values from @media in the @source. After calling this method,
+ * future queries that return this media object shall return this new value for
+ * the selected key.
+ *
+ * This function is synchronous.
+ *
+ * Returns: (element-type GrlKeyID) (transfer container):
+ * a #GList of keys that could not be updated, or @NULL
+ */
+GList *
+grl_source_store_metadata_sync (GrlSource *source,
+ GrlMedia *media,
+ GList *keys,
+ GrlWriteFlags flags,
+ GError **error)
+{
+ GrlDataSync *ds;
+ GList *failed;
+
+ ds = g_slice_new0 (GrlDataSync);
+
+ grl_source_store_metadata (source,
+ media,
+ keys,
+ flags,
+ store_metadata_result_async_cb,
+ ds);
+
+ grl_wait_for_async_operation_complete (ds);
+
+ if (ds->error) {
+ if (error) {
+ *error = ds->error;
+ } else {
+ g_error_free (ds->error);
+ }
+ }
+
+ failed = ds->data;
+
+ g_slice_free (GrlDataSync, ds);
+
+ return failed;
+}
+
+/**
+ * grl_source_notify_change_start:
+ * @source: a source
+ * @error: a #GError, or @NULL
+ *
+ * Starts emitting ::content-changed signals when @source discovers changes in
+ * the content. This instructs @source to setup the machinery needed to be aware
+ * of changes in the content.
+ *
+ * Returns: @TRUE if initialization has succeed.
+ */
+gboolean
+grl_source_notify_change_start (GrlSource *source,
+ GError **error)
+{
+ GRL_DEBUG (__FUNCTION__);
+
+ g_return_val_if_fail (GRL_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (grl_source_supported_operations (source) &
+ GRL_OP_NOTIFY_CHANGE, FALSE);
+
+ return GRL_SOURCE_GET_CLASS (source)->notify_change_start (source, error);
+}
+
+/**
+ * grl_source_notify_change_stop:
+ * @source: a source
+ * @error: a #GError, or @NULL
+ *
+ * This will drop emission of ::content-changed signals from @source. When this
+ * is done @source should stop the machinery required for it to track changes in
+ * the content.
+ *
+ * Returns: @TRUE if stop has succeed.
+ */
+gboolean
+grl_source_notify_change_stop (GrlSource *source,
+ GError **error)
+{
+ GRL_DEBUG (__FUNCTION__);
+
+ g_return_val_if_fail (GRL_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (grl_source_supported_operations (source) &
+ GRL_OP_NOTIFY_CHANGE, FALSE);
+
+ return GRL_SOURCE_GET_CLASS (source)->notify_change_stop (source, error);
+}
+
+/**
+ * grl_source_notify_change_list:
+ * @source: a source
+ * @changed_medias: (element-type Grl.Media) (transfer full):: the list of
+ * medias that have changed
+ * @change_type: the type of change
+ * @location_unknown: if change has happpened in @media or any descendant
+ *
+ * Emits "content-changed" signal to notify subscribers that a change ocurred
+ * in @source.
+ *
+ * The function will take ownership of @changed medias and it should not be
+ * manipulated in any way by the caller after invoking this function. If that is
+ * needed, the caller must ref the array in advance.
+ *
+ * See GrlSource::content-changed signal.
+ *
+ * <note>
+ * <para>
+ * This function is intended to be used only by plugins.
+ * </para>
+ * </note>
+ */
+void grl_source_notify_change_list (GrlSource *source,
+ GPtrArray *changed_medias,
+ GrlSourceChangeType change_type,
+ gboolean location_unknown)
+{
+ const gchar *source_id;
+
+ g_return_if_fail (GRL_IS_SOURCE (source));
+ g_return_if_fail (changed_medias);
+
+ /* Set the source */
+ source_id = grl_source_get_id (source);
+ g_ptr_array_foreach (changed_medias,
+ (GFunc) grl_media_set_source,
+ (gpointer) source_id);
+
+ /* Add hook to free content when freeing the array */
+ g_ptr_array_set_free_func (changed_medias, (GDestroyNotify) g_object_unref);
+
+ g_signal_emit (source,
+ registry_signals[SIG_CONTENT_CHANGED],
+ 0,
+ changed_medias,
+ change_type,
+ location_unknown);
+
+ g_ptr_array_unref (changed_medias);
+}
+
+/**
+ * grl_source_notify_change:
+ * @source: a source
+ * @media: (allow-none): the media which has changed, or @NULL to use the root box.
+ * @change_type: the type of change
+ * @location_unknown: if change has happened in @media or any descendant
+ *
+ * Emits "content-changed" signal to notify subscribers that a change ocurred
+ * in @source.
+ *
+ * See #grl_source_notify_change_list() function.
+ *
+ * <note>
+ * <para>
+ * This function is intended to be used only by plugins.
+ * </para>
+ * </note>
+ */
+void grl_source_notify_change (GrlSource *source,
+ GrlMedia *media,
+ GrlSourceChangeType change_type,
+ gboolean location_unknown)
+{
+ GPtrArray *ptr_array;
+
+ g_return_if_fail (GRL_IS_SOURCE (source));
+
+ if (!media) {
+ media = grl_media_box_new ();
+ } else {
+ g_object_ref (media);
+ }
+
+ ptr_array = g_ptr_array_sized_new (1);
+ g_ptr_array_add (ptr_array, media);
+ grl_source_notify_change_list (source, ptr_array,
+ change_type, location_unknown);
+}
+
+/******************************************************************************/
+
/**
* grl_source_get_caps:
* @source: a source
diff --git a/src/grl-source.h b/src/grl-source.h
index e317e8e..c73c21b 100644
--- a/src/grl-source.h
+++ b/src/grl-source.h
@@ -29,6 +29,7 @@
#include <grl-metadata-key.h>
#include <grl-media.h>
+#include <grl-media-box.h>
#include <grl-definitions.h>
#include <grl-plugin.h>
#include <grl-operation-options.h>
@@ -80,15 +81,14 @@ struct _GrlSource {
/**
* GrlSupportedOps:
* @GRL_OP_NONE: no operation is supported
- * @GRL_OP_METADATA: Fetch specific keys of metadata based on the media id.
* @GRL_OP_RESOLVE: Fetch specific keys of metadata based on other metadata.
* @GRL_OP_BROWSE: Retrieve complete sets of #GrlMedia
* @GRL_OP_SEARCH: Look up for #GrlMedia given a search text
* @GRL_OP_QUERY: Look up for #GrlMedia give a service specific query
* @GRL_OP_STORE: Store content in a service
* @GRL_OP_STORE_PARENT: Store content as child of a certian parent category.
+ * @GRL_OP_STORE_METADATA: Update metadata of a #GrlMedia in a service.
* @GRL_OP_REMOVE: Remove content from a service.
- * @GRL_OP_SET_METADATA: Update metadata of a #GrlMedia in a service.
* @GRL_OP_MEDIA_FROM_URI: Create a #GrlMedia instance from an URI
* representing a media resource.
* @GRL_OP_NOTIFY_CHANGE: Notify about changes in the #GrlMediaSource.
@@ -98,19 +98,300 @@ struct _GrlSource {
*/
typedef enum {
GRL_OP_NONE = 0,
- GRL_OP_METADATA = 1,
- GRL_OP_RESOLVE = 1 << 1,
- GRL_OP_BROWSE = 1 << 2,
- GRL_OP_SEARCH = 1 << 3,
- GRL_OP_QUERY = 1 << 4,
- GRL_OP_STORE = 1 << 5,
- GRL_OP_STORE_PARENT = 1 << 6,
+ GRL_OP_RESOLVE = 1,
+ GRL_OP_BROWSE = 1 << 1,
+ GRL_OP_SEARCH = 1 << 2,
+ GRL_OP_QUERY = 1 << 3,
+ GRL_OP_STORE = 1 << 4,
+ GRL_OP_STORE_PARENT = 1 << 5,
+ GRL_OP_STORE_METADATA = 1 << 6,
GRL_OP_REMOVE = 1 << 7,
- GRL_OP_SET_METADATA = 1 << 8,
- GRL_OP_MEDIA_FROM_URI = 1 << 9,
- GRL_OP_NOTIFY_CHANGE = 1 << 10,
+ GRL_OP_MEDIA_FROM_URI = 1 << 8,
+ GRL_OP_NOTIFY_CHANGE = 1 << 9
} GrlSupportedOps;
+/**
+ * GrlSourceChangeType:
+ * @GRL_CONTENT_CHANGED: content has changed. It is used when any property of
+ * #GrlMedia has changed, or in case of #GrlMediaBox, if several children have
+ * been added and removed.
+ * @GRL_CONTENT_ADDED: new content has been added.
+ * @GRL_CONTENT_REMOVED: content has been removed
+ *
+ * Specifies which kind of change has happened in the plugin
+ */
+typedef enum {
+ GRL_CONTENT_CHANGED,
+ GRL_CONTENT_ADDED,
+ GRL_CONTENT_REMOVED
+} GrlSourceChangeType;
+
+/**
+ * GrlSourceResolveCb:
+ * @source: a source
+ * @operation_id: operation identifier
+ * @media: (transfer full): a data transfer object
+ * @user_data: user data passed to grl_source_resolve()
+ * @error: (type uint): possible #GError generated at processing
+ *
+ * Prototype for the callback passed to grl_source_resolve()
+ */
+typedef void (*GrlSourceResolveCb) (GrlSource *source,
+ guint operation_id,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error);
+
+/**
+ * GrlSourceResultCb:
+ * @source: a source
+ * @operation_id: operation identifier
+ * @media: (transfer full): a data transfer object
+ * @remaining: the number of remaining #GrlMedia to process, or
+ * GRL_SOURCE_REMAINING_UNKNOWN if it is unknown
+ * @user_data: user data passed to the used method
+ * @error: (type uint): possible #GError generated at processing
+ *
+ * Prototype for the callback passed to the media sources' methods
+ */
+typedef void (*GrlSourceResultCb) (GrlSource *source,
+ guint operation_id,
+ GrlMedia *media,
+ guint remaining,
+ gpointer user_data,
+ const GError *error);
+
+/**
+ * GrlSourceRemoveCb:
+ * @source: a source
+ * @media: (transfer full): a data transfer object
+ * @user_data: user data passed to grl_source_remove()
+ * @error: (type uint): possible #GError generated at processing
+ *
+ * Prototype for the callback passed to grl_source_remove()
+ */
+typedef void (*GrlSourceRemoveCb) (GrlSource *source,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error);
+
+/**
+ * GrlSourceStoreCb:
+ * @source: a source
+ * @media: (transfer full): a #GrlMedia transfer object
+ * @failed_keys: (element-type GrlKeyID) (transfer container): #GList of
+ * keys that could not be updated, if any
+ * @user_data: user data
+ * @error: (type uint): possible #GError generated
+ *
+ * Prototype for the callback passed to grl_source_store_foo functions
+ */
+typedef void (*GrlSourceStoreCb) (GrlSource *source,
+ GrlMedia *media,
+ GList *failed_keys,
+ gpointer user_data,
+ const GError *error);
+
+/**
+ * GrlSourceResolveSpec:
+ * @source: a source
+ * @operation_id: operation identifier
+ * @media: a data transfer object
+ * @keys: the #GList of #GrlKeyID<!-- -->s to request
+ * @options: options wanted for that operation
+ * @callback: the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Data transport structure used internally by the plugins which support
+ * resolve vmethod.
+ */
+typedef struct {
+ GrlSource *source;
+ guint operation_id;
+ GrlMedia *media;
+ GList *keys;
+ GrlOperationOptions *options;
+ GrlSourceResolveCb callback;
+ gpointer user_data;
+
+ /*< private >*/
+ gpointer _grl_reserved[GRL_PADDING];
+} GrlSourceResolveSpec;
+
+/**
+ * GrlSourceMediaFromUriSpec:
+ * @source: a source
+ * @operation_id: operation identifier
+ * @uri: A URI that can be used to identify a media resource
+ * @keys: Metadata keys to resolve
+ * @options: options wanted for that operation
+ * @callback: the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Data transport structure used internally by the plugins which support
+ * media_from_uri vmethod.
+ */
+typedef struct {
+ GrlSource *source;
+ guint operation_id;
+ gchar *uri;
+ GList *keys;
+ GrlOperationOptions *options;
+ GrlSourceResolveCb callback;
+ gpointer user_data;
+
+ /*< private >*/
+ gpointer _grl_reserved[GRL_PADDING];
+} GrlSourceMediaFromUriSpec;
+
+/**
+ * GrlSourceBrowseSpec:
+ * @source: a source
+ * @operation_id: operation identifier
+ * @container: a container of data transfer objects
+ * @keys: the #GList of #GrlKeyID<!-- -->s to request
+ * @options: options wanted for that operation
+ * @callback: the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Data transport structure used internally by the plugins which support
+ * browse vmethod.
+ */
+typedef struct {
+ GrlSource *source;
+ guint operation_id;
+ GrlMedia *container;
+ GList *keys;
+ GrlOperationOptions *options;
+ GrlSourceResultCb callback;
+ gpointer user_data;
+
+ /*< private >*/
+ gpointer _grl_reserved[GRL_PADDING];
+} GrlSourceBrowseSpec;
+
+/**
+ * GrlSourceSearchSpec:
+ * @source: a source
+ * @operation_id: operation identifier
+ * @text: the text to search
+ * @keys: the #GList of #GrlKeyID<!-- -->s to request
+ * @options: options wanted for that operation
+ * @callback: the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Data transport structure used internally by the plugins which support
+ * search vmethod.
+ */
+typedef struct {
+ GrlSource *source;
+ guint operation_id;
+ gchar *text;
+ GList *keys;
+ GrlOperationOptions *options;
+ GrlSourceResultCb callback;
+ gpointer user_data;
+
+ /*< private >*/
+ gpointer _grl_reserved[GRL_PADDING];
+} GrlSourceSearchSpec;
+
+/**
+ * GrlSourceQuerySpec:
+ * @source: a source
+ * @query_id: operation identifier
+ * @query: the query to process
+ * @keys: the #GList of #GrlKeyID<!-- -->s to request
+ * @options: options wanted for that operation
+ * @callback: the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Data transport structure used internally by the plugins which support
+ * query vmethod.
+ */
+typedef struct {
+ GrlSource *source;
+ guint operation_id;
+ gchar *query;
+ GList *keys;
+ GrlOperationOptions *options;
+ GrlSourceResultCb callback;
+ gpointer user_data;
+
+ /*< private >*/
+ gpointer _grl_reserved[GRL_PADDING];
+} GrlSourceQuerySpec;
+
+/**
+ * GrlSourceRemoveSpec:
+ * @source: a source
+ * @media_id: media identifier to remove
+ * @media: a data transfer object
+ * @callback: the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Data transport structure used internally by the plugins which support
+ * store vmethod.
+ */
+typedef struct {
+ GrlSource *source;
+ gchar *media_id;
+ GrlMedia *media;
+ GrlSourceRemoveCb callback;
+ gpointer user_data;
+
+ /*< private >*/
+ gpointer _grl_reserved[GRL_PADDING];
+} GrlSourceRemoveSpec;
+
+/**
+ * GrlSourceStoreSpec:
+ * @source: a media source
+ * @parent: a parent to store the data transfer objects
+ * @media: a data transfer object
+ * @callback: the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Data transport structure used internally by the plugins which support
+ * store vmethod.
+ */
+typedef struct {
+ GrlSource *source;
+ GrlMediaBox *parent;
+ GrlMedia *media;
+ GrlSourceStoreCb callback;
+ gpointer user_data;
+
+ /*< private >*/
+ gpointer _grl_reserved[GRL_PADDING];
+} GrlSourceStoreSpec;
+
+/**
+ * GrlSourceStoreMetadataSpec:
+ * @source: a source
+ * @media: a #GrlMedia transfer object
+ * @keys: List of keys to be stored/updated.
+ * @flags: Flags to control specific bahviors of the set metadata operation.
+ * @callback: the callback passed to grl_source_store_metadata()
+ * @user_data: user data passed to grl_source_store_metadata()
+ * @failed_keys: for internal use of the framework only.
+ *
+ * Data transport structure used internally by the plugins which support
+ * store_metadata vmethod.
+ */
+typedef struct {
+ GrlSource *source;
+ GrlMedia *media;
+ GList *keys;
+ GrlWriteFlags flags;
+ GrlSourceStoreCb callback;
+ gpointer user_data;
+ GList *failed_keys;
+
+ /*< private >*/
+ gpointer _grl_reserved[GRL_PADDING];
+} GrlSourceStoreMetadataSpec;
+
/* GrlSource class */
typedef struct _GrlSourceClass GrlSourceClass;
@@ -123,8 +404,20 @@ typedef struct _GrlSourceClass GrlSourceClass;
* @slow_keys: the list of slow keys that can be fetched
* @writable_keys: the list of keys which value can be written
* @get_caps: the capabilities that @source supports for @operation
+ * @resolve: resolve the metadata of a given transfer object
+ * @may_resolve: return FALSE if it can be known without blocking that @key_id
+ * @test_media_from_uri: tests if this source can create #GrlMedia
+ * instances from a given URI.
+ * @browse: browse through a list of media
+ * @search: search for media
+ * @query: query for a specific media
+ * @store: store a media in a container
+ * @store_metadata: update metadata values for a given object in a
+ * permanent fashion
* @cancel: cancel the current operation
-
+ * @notify_change_start: start emitting signals about changes in content
+ * @notify_change_stop: stop emitting signals about changes in content
+ *
* Grilo Source class. Override the vmethods to implement the
* element functionality.
*/
@@ -142,8 +435,37 @@ struct _GrlSourceClass {
GrlCaps * (*get_caps) (GrlSource *source, GrlSupportedOps operation);
+ void (*resolve) (GrlSource *source, GrlSourceResolveSpec *ms);
+
+ gboolean (*may_resolve) (GrlSource *source, GrlMedia *media,
+ GrlKeyID key_id, GList **missing_keys);
+
+ gboolean (*test_media_from_uri) (GrlSource *source,
+ const gchar *uri);
+
+ void (*media_from_uri) (GrlSource *source,
+ GrlSourceMediaFromUriSpec *mfus);
+
+ void (*browse) (GrlSource *source, GrlSourceBrowseSpec *bs);
+
+ void (*search) (GrlSource *source, GrlSourceSearchSpec *ss);
+
+ void (*query) (GrlSource *source, GrlSourceQuerySpec *qs);
+
+ void (*remove) (GrlSource *source, GrlSourceRemoveSpec *ss);
+
+ void (*store) (GrlSource *source, GrlSourceStoreSpec *ss);
+
+ void (*store_metadata) (GrlSource *source, GrlSourceStoreMetadataSpec *sms);
+
void (*cancel) (GrlSource *source, guint operation_id);
+ gboolean (*notify_change_start) (GrlSource *source,
+ GError **error);
+
+ gboolean (*notify_change_stop) (GrlSource *source,
+ GError **error);
+
/*< private >*/
gpointer _grl_reserved[GRL_PADDING];
};
@@ -163,6 +485,138 @@ const GList *grl_source_writable_keys (GrlSource *source);
GrlCaps *grl_source_get_caps (GrlSource *source,
GrlSupportedOps operation);
+void grl_source_set_auto_split_threshold (GrlSource *source,
+ guint threshold);
+
+guint grl_source_get_auto_split_threshold (GrlSource *source);
+
+
+guint grl_source_resolve (GrlSource *source,
+ GrlMedia *media,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GrlSourceResolveCb callback,
+ gpointer user_data);
+
+GrlMedia *grl_source_resolve_sync (GrlSource *source,
+ GrlMedia *media,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GError **error);
+
+gboolean grl_source_may_resolve (GrlSource *source,
+ GrlMedia *media,
+ GrlKeyID key_id,
+ GList **missing_keys);
+
+gboolean grl_source_test_media_from_uri (GrlSource *source,
+ const gchar *uri);
+
+guint grl_source_get_media_from_uri (GrlSource *source,
+ const gchar *uri,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GrlSourceResolveCb callback,
+ gpointer user_data);
+
+GrlMedia *grl_source_get_media_from_uri_sync (GrlSource *source,
+ const gchar *uri,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GError **error);
+
+guint grl_source_browse (GrlSource *source,
+ GrlMedia *container,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GrlSourceResultCb callback,
+ gpointer user_data);
+
+GList *grl_source_browse_sync (GrlSource *source,
+ GrlMedia *container,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GError **error);
+
+guint grl_source_search (GrlSource *source,
+ const gchar *text,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GrlSourceResultCb callback,
+ gpointer user_data);
+
+GList *grl_source_search_sync (GrlSource *source,
+ const gchar *text,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GError **error);
+
+guint grl_source_query (GrlSource *source,
+ const gchar *query,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GrlSourceResultCb callback,
+ gpointer user_data);
+
+GList *grl_source_query_sync (GrlSource *source,
+ const gchar *query,
+ const GList *keys,
+ GrlOperationOptions *options,
+ GError **error);
+
+void grl_source_remove (GrlSource *source,
+ GrlMedia *media,
+ GrlSourceRemoveCb callback,
+ gpointer user_data);
+
+void grl_source_remove_sync (GrlSource *source,
+ GrlMedia *media,
+ GError **error);
+
+void grl_source_store (GrlSource *source,
+ GrlMediaBox *parent,
+ GrlMedia *media,
+ GrlWriteFlags flags,
+ GrlSourceStoreCb callback,
+ gpointer user_data);
+
+void grl_source_store_sync (GrlSource *source,
+ GrlMediaBox *parent,
+ GrlMedia *media,
+ GrlWriteFlags flags,
+ GError **error);
+
+void grl_source_store_metadata (GrlSource *source,
+ GrlMedia *media,
+ GList *keys,
+ GrlWriteFlags flags,
+ GrlSourceStoreCb callback,
+ gpointer user_data);
+
+GList *grl_source_store_metadata_sync (GrlSource *source,
+ GrlMedia *media,
+ GList *keys,
+ GrlWriteFlags flags,
+ GError **error);
+
+gboolean grl_source_notify_change_start (GrlSource *source,
+ GError **error);
+
+gboolean grl_source_notify_change_stop (GrlSource *source,
+ GError **error);
+
+void grl_source_notify_change_list (GrlSource *source,
+ GPtrArray *changed_medias,
+ GrlSourceChangeType change_type,
+ gboolean location_unknown);
+
+void grl_source_notify_change (GrlSource *source,
+ GrlMedia *media,
+ GrlSourceChangeType change_type,
+ gboolean location_unknown);
+
+void grl_source_cancel (GrlSource *source, guint operation_id);
+
const gchar *grl_source_get_id (GrlSource *source);
const gchar *grl_source_get_name (GrlSource *source);
diff --git a/tools/grilo-inspect/grl-inspect.c b/tools/grilo-inspect/grl-inspect.c
index abe32c6..b054cc6 100644
--- a/tools/grilo-inspect/grl-inspect.c
+++ b/tools/grilo-inspect/grl-inspect.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, 2011 Igalia S.L.
+ * Copyright (C) 2010-2012 Igalia S.L.
*
* Contact: Iago Toral Quiroga <itoral igalia com>
*
@@ -136,8 +136,6 @@ introspect_source (const gchar *source_id)
g_print ("Source Details:\n");
g_print (" %-20s %s\n", "Identifier:",
grl_source_get_id (source));
- g_print (" %-20s %s\n", "Type:",
- GRL_IS_MEDIA_SOURCE (source)? "Media Provider": "Metadata Provider");
g_print (" %-20s %s\n", "Name:",
grl_source_get_name (source));
g_print (" %-20s %s\n", "Description:",
@@ -151,12 +149,6 @@ introspect_source (const gchar *source_id)
if (supported_ops & GRL_OP_RESOLVE) {
g_print (" grl_metadata_source_resolve():\tResolve Metadata\n");
}
- if (supported_ops & GRL_OP_SET_METADATA) {
- g_print (" grl_metadata_source_set_metadata():\tSet Metadata\n");
- }
- if (supported_ops & GRL_OP_METADATA) {
- g_print (" grl_media_source_metadata():\t\tRetrieve Metadata\n");
- }
if (supported_ops & GRL_OP_BROWSE) {
g_print (" grl_media_source_browse():\t\tBrowse\n");
}
@@ -172,6 +164,9 @@ introspect_source (const gchar *source_id)
if (supported_ops & GRL_OP_STORE_PARENT) {
g_print (" grl_media_source_store():\t\tAdd New Media\n");
}
+ if (supported_ops & GRL_OP_STORE_METADATA) {
+ g_print (" grl_metadata_source_store_metadata():\tStore Metadata\n");
+ }
if (supported_ops & GRL_OP_REMOVE) {
g_print (" grl_media_source_remove():\t\tRemove Media\n");
}
diff --git a/tools/grilo-test-ui/main.c b/tools/grilo-test-ui/main.c
index b46ef63..a19c29e 100644
--- a/tools/grilo-test-ui/main.c
+++ b/tools/grilo-test-ui/main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, 2011 Igalia S.L.
+ * Copyright (C) 2010-2012 Igalia S.L.
* Copyright (C) 2011 Intel Corporation.
*
* Contact: Iago Toral Quiroga <itoral igalia com>
@@ -63,7 +63,7 @@ GRL_LOG_DOMAIN_STATIC(test_ui_log_domain);
/* ----- Other ----- */
#define BROWSE_FLAGS (GRL_RESOLVE_FAST_ONLY | GRL_RESOLVE_IDLE_RELAY)
-#define METADATA_FLAGS (GRL_RESOLVE_FULL | GRL_RESOLVE_IDLE_RELAY)
+#define RESOLVE_FLAGS (GRL_RESOLVE_FULL | GRL_RESOLVE_IDLE_RELAY)
#define WINDOW_TITLE "Grilo Test UI (v." VERSION ")"
@@ -140,16 +140,16 @@ typedef struct {
/* Keeps track of our browsing position and history */
GList *source_stack;
GList *container_stack;
- GrlMediaSource *cur_source;
+ GrlSource *cur_source;
GrlMedia *cur_container;
/* Keeps track of the last element we showed metadata for */
- GrlMediaSource *cur_md_source;
+ GrlSource *cur_md_source;
GrlMedia *cur_md_media;
/* Keeps track of browse/search state */
gboolean op_ongoing;
- GrlMediaSource *cur_op_source;
+ GrlSource *cur_op_source;
guint cur_op_id;
gboolean multiple;
@@ -193,7 +193,7 @@ static const gchar *ui_definition =
"</ui>";
static GrlOperationOptions *default_options = NULL;
-static GrlOperationOptions *default_metadata_options = NULL;
+static GrlOperationOptions *default_resolve_options = NULL;
static void show_browsable_sources (void);
static void quit_cb (GtkAction *action);
@@ -208,9 +208,9 @@ static void load_all_plugins_cb (GtkAction *action);
static void load_all_plugins (void);
static void changes_notification_cb (GtkToggleAction *action);
-static void content_changed_cb (GrlMediaSource *source,
+static void content_changed_cb (GrlSource *source,
GPtrArray *changed_medias,
- GrlMediaSourceChangeType change_type,
+ GrlSourceChangeType change_type,
gboolean location_unknown,
gpointer data);
@@ -269,15 +269,13 @@ changes_notification_cb (GtkToggleAction *action)
if (grl_source_supported_operations (GRL_SOURCE (source->data)) &
GRL_OP_NOTIFY_CHANGE) {
if (ui_state->changes_notification) {
- grl_media_source_notify_change_start (GRL_MEDIA_SOURCE (source->data),
- NULL);
- g_signal_connect (GRL_MEDIA_SOURCE (source->data),
+ grl_source_notify_change_start (GRL_SOURCE (source->data), NULL);
+ g_signal_connect (GRL_SOURCE (source->data),
"content-changed",
G_CALLBACK (content_changed_cb),
NULL);
} else {
- grl_media_source_notify_change_stop (GRL_MEDIA_SOURCE (source->data),
- NULL);
+ grl_source_notify_change_stop (GRL_SOURCE (source->data), NULL);
g_signal_handlers_disconnect_by_func (source->data,
content_changed_cb,
NULL);
@@ -299,7 +297,7 @@ create_browser_model (void)
}
static GtkTreeModel *
-create_metadata_model (void)
+create_resolve_model (void)
{
return GTK_TREE_MODEL (gtk_list_store_new (2,
G_TYPE_STRING, /* name */
@@ -373,7 +371,7 @@ all_keys (void)
}
static void
-browse_history_push (GrlMediaSource *source, GrlMedia *media)
+browse_history_push (GrlSource *source, GrlMedia *media)
{
if (source)
g_object_ref (source);
@@ -385,12 +383,12 @@ browse_history_push (GrlMediaSource *source, GrlMedia *media)
}
static void
-browse_history_pop (GrlMediaSource **source, GrlMedia **media)
+browse_history_pop (GrlSource **source, GrlMedia **media)
{
GList *tmp;
tmp = g_list_last (ui_state->source_stack);
if (tmp) {
- *source = GRL_MEDIA_SOURCE (tmp->data);
+ *source = GRL_SOURCE (tmp->data);
ui_state->source_stack = g_list_delete_link (ui_state->source_stack, tmp);
}
tmp = g_list_last (ui_state->container_stack);
@@ -402,7 +400,7 @@ browse_history_pop (GrlMediaSource **source, GrlMedia **media)
}
static void
-set_cur_browse (GrlMediaSource *source, GrlMedia *media)
+set_cur_browse (GrlSource *source, GrlMedia *media)
{
if (ui_state->cur_source)
g_object_unref (ui_state->cur_source);
@@ -419,7 +417,7 @@ set_cur_browse (GrlMediaSource *source, GrlMedia *media)
}
static void
-set_cur_metadata (GrlMediaSource *source, GrlMedia *media)
+set_cur_resolve (GrlSource *source, GrlMedia *media)
{
if (ui_state->cur_md_source)
g_object_unref (ui_state->cur_md_source);
@@ -450,7 +448,7 @@ clear_panes (void)
gtk_list_store_clear (GTK_LIST_STORE (view->metadata_model));
g_object_unref (view->metadata_model);
}
- view->metadata_model = create_metadata_model ();
+ view->metadata_model = create_resolve_model ();
gtk_tree_view_set_model (GTK_TREE_VIEW (view->metadata),
view->metadata_model);
@@ -518,11 +516,11 @@ value_description (const GValue *value)
}
static void
-metadata_cb (GrlMediaSource *source,
- guint operation_id,
- GrlMedia *media,
- gpointer user_data,
- const GError *error)
+resolve_cb (GrlSource *source,
+ guint operation_id,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
{
GList *keys, *i;
GtkTreeIter iter;
@@ -539,7 +537,7 @@ metadata_cb (GrlMediaSource *source,
gtk_list_store_clear (GTK_LIST_STORE (view->metadata_model));
g_object_unref (view->metadata_model);
}
- view->metadata_model = create_metadata_model ();
+ view->metadata_model = create_resolve_model ();
gtk_tree_view_set_model (GTK_TREE_VIEW (view->metadata),
view->metadata_model);
@@ -578,7 +576,7 @@ metadata_cb (GrlMediaSource *source,
g_list_free (keys);
- /* Don't free media (we do not ref it when issuing metadata(),
+ /* Don't free media (we do not ref it when issuing resolve(),
so its reference comes from the treeview and that's freed
when the treeview is cleared */
@@ -596,7 +594,7 @@ metadata_cb (GrlMediaSource *source,
}
static void
-operation_started (GrlMediaSource *source, guint operation_id,
+operation_started (GrlSource *source, guint operation_id,
gboolean multiple)
{
ui_state->op_ongoing = TRUE;
@@ -621,12 +619,12 @@ operation_finished (void)
}
static void
-browse_search_query_cb (GrlMediaSource *source,
- guint op_id,
- GrlMedia *media,
- guint remaining,
- gpointer user_data,
- const GError *error)
+browse_search_query_cb (GrlSource *source,
+ guint op_id,
+ GrlMedia *media,
+ guint remaining,
+ gpointer user_data,
+ const GError *error)
{
gint type;
const gchar *name;
@@ -710,13 +708,13 @@ browse_search_query_cb (GrlMediaSource *source,
GrlOperationOptions *supported_options = NULL;
grl_operation_options_obey_caps (options,
- grl_source_get_caps (GRL_SOURCE (source), GRL_OP_SEARCH),
+ grl_source_get_caps (source, GRL_OP_SEARCH),
&supported_options,
NULL);
switch (state->type) {
case OP_TYPE_BROWSE:
next_op_id =
- grl_media_source_browse (source,
+ grl_source_browse (source,
ui_state->cur_container,
all_keys (),
supported_options,
@@ -725,7 +723,7 @@ browse_search_query_cb (GrlMediaSource *source,
break;
case OP_TYPE_SEARCH:
next_op_id =
- grl_media_source_search (source,
+ grl_source_search (source,
state->text,
all_keys (),
options,
@@ -734,7 +732,7 @@ browse_search_query_cb (GrlMediaSource *source,
break;
case OP_TYPE_QUERY:
next_op_id =
- grl_media_source_query (source,
+ grl_source_query (source,
state->text,
all_keys (),
options,
@@ -771,7 +769,7 @@ browse_search_query_cb (GrlMediaSource *source,
}
static void
-browse (GrlMediaSource *source, GrlMedia *container)
+browse (GrlSource *source, GrlMedia *container)
{
guint browse_id;
if (source) {
@@ -781,19 +779,19 @@ browse (GrlMediaSource *source, GrlMedia *container)
OperationState *state = g_new0 (OperationState, 1);
state->type = OP_TYPE_BROWSE;
- browse_id = grl_media_source_browse (source,
- container,
- all_keys (),
- default_options,
- browse_search_query_cb,
- state);
+ browse_id = grl_source_browse (source,
+ container,
+ all_keys (),
+ default_options,
+ browse_search_query_cb,
+ state);
operation_started (source, browse_id, FALSE);
} else {
show_browsable_sources ();
}
set_cur_browse (source, container);
- set_cur_metadata (NULL, NULL);
+ set_cur_resolve (NULL, NULL);
}
static void
@@ -806,7 +804,7 @@ browser_activated_cb (GtkTreeView *tree_view,
GtkTreeIter iter;
GrlMedia *content;
gint type;
- GrlMediaSource *source;
+ GrlSource *source;
GrlMedia *container;
model = gtk_tree_view_get_model (tree_view);
@@ -841,21 +839,21 @@ browser_activated_cb (GtkTreeView *tree_view,
}
static void
-metadata (GrlMediaSource *source, GrlMedia *media)
+resolve (GrlSource *source, GrlMedia *media)
{
if (source) {
- /* If source does not support metadata() operation, then use the current
+ /* If source does not support resolve() operation, then use the current
media */
- if ((grl_source_supported_operations (GRL_SOURCE (source)) &
- GRL_OP_METADATA)) {
- grl_media_source_metadata (source,
- media,
- all_keys (),
- default_metadata_options,
- metadata_cb,
- NULL);
+ if ((grl_source_supported_operations (source) &
+ GRL_OP_RESOLVE)) {
+ grl_source_resolve (source,
+ media,
+ all_keys (),
+ default_resolve_options,
+ resolve_cb,
+ NULL);
} else {
- metadata_cb (source, 0, media, NULL, NULL);
+ resolve_cb (source, 0, media, NULL, NULL);
}
}
}
@@ -866,7 +864,7 @@ browser_row_selected_cb (GtkTreeView *tree_view,
{
GtkTreePath *path = NULL;
GtkTreeIter iter;
- GrlMediaSource *source;
+ GrlSource *source;
GrlMedia *content;
gtk_tree_view_get_cursor (tree_view, &path, NULL);
@@ -882,8 +880,8 @@ browser_row_selected_cb (GtkTreeView *tree_view,
if (source != ui_state->cur_md_source ||
content != ui_state->cur_md_media) {
- set_cur_metadata (source, content);
- metadata (source, content);
+ set_cur_resolve (source, content);
+ resolve (source, content);
}
/* Check if we can store content in the selected item */
@@ -959,7 +957,7 @@ show_btn_clicked_cb (GtkButton *btn, gpointer user_data)
static void
back_btn_clicked_cb (GtkButton *btn, gpointer user_data)
{
- GrlMediaSource *prev_source = NULL;
+ GrlSource *prev_source = NULL;
GrlMedia *prev_container = NULL;
/* TODO: when using dynamic sources this will break
@@ -980,11 +978,11 @@ back_btn_clicked_cb (GtkButton *btn, gpointer user_data)
}
static void
-store_cb (GrlMediaSource *source,
- GrlMediaBox *box,
- GrlMedia *media,
- gpointer user_data,
- const GError *error)
+store_cb (GrlSource *source,
+ GrlMedia *media,
+ GList *failed_keys,
+ gpointer user_data,
+ const GError *error)
{
if (error) {
GRL_WARNING ("Error storing media: %s", error->message);
@@ -1001,7 +999,7 @@ store_btn_clicked_cb (GtkButton *btn, gpointer user_data)
GtkTreeSelection *sel;
GtkTreeModel *model = NULL;
GtkTreeIter iter;
- GrlMediaSource *source;
+ GrlSource *source;
GrlMedia *container;
sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->browser));
@@ -1054,8 +1052,8 @@ store_btn_clicked_cb (GtkButton *btn, gpointer user_data)
grl_media_set_title (media, gtk_entry_get_text (GTK_ENTRY (e1)));
grl_media_set_description (media,
gtk_entry_get_text (GTK_ENTRY (e3)));
- grl_media_source_store (source, GRL_MEDIA_BOX (container),
- media, store_cb, NULL);
+ grl_source_store (source, GRL_MEDIA_BOX (container),
+ media, GRL_WRITE_FULL, store_cb, NULL);
}
gtk_widget_destroy (dialog);
@@ -1069,10 +1067,10 @@ store_btn_clicked_cb (GtkButton *btn, gpointer user_data)
}
static void
-remove_item_from_view (GrlMediaSource *source, GrlMedia *media)
+remove_item_from_view (GrlSource *source, GrlMedia *media)
{
GtkTreeIter iter;
- GrlMediaSource *iter_source;
+ GrlSource *iter_source;
GrlMedia *iter_media;
gboolean found = FALSE;
gboolean more;
@@ -1099,10 +1097,10 @@ remove_item_from_view (GrlMediaSource *source, GrlMedia *media)
}
static void
-remove_cb (GrlMediaSource *source,
- GrlMedia *media,
- gpointer user_data,
- const GError *error)
+remove_cb (GrlSource *source,
+ GrlMedia *media,
+ gpointer user_data,
+ const GError *error)
{
if (error) {
GRL_WARNING ("Error removing media: %s", error->message);
@@ -1119,7 +1117,7 @@ remove_btn_clicked_cb (GtkButton *btn, gpointer user_data)
GtkTreeSelection *sel;
GtkTreeModel *model = NULL;
GtkTreeIter iter;
- GrlMediaSource *source;
+ GrlSource *source;
GrlMedia *media;
sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->browser));
@@ -1129,7 +1127,7 @@ remove_btn_clicked_cb (GtkButton *btn, gpointer user_data)
BROWSER_MODEL_CONTENT, &media,
-1);
- grl_media_source_remove (source, media, remove_cb, NULL);
+ grl_source_remove (source, media, remove_cb, NULL);
if (source) {
g_object_unref (source);
@@ -1140,7 +1138,7 @@ remove_btn_clicked_cb (GtkButton *btn, gpointer user_data)
}
static void
-search (GrlMediaSource *source, const gchar *text)
+search (GrlSource *source, const gchar *text)
{
OperationState *state;
guint search_id;
@@ -1175,7 +1173,7 @@ search (GrlMediaSource *source, const gchar *text)
&supported_options,
NULL);
g_object_unref (options);
- search_id = grl_media_source_search (source,
+ search_id = grl_source_search (source,
text,
all_keys (),
supported_options,
@@ -1204,7 +1202,7 @@ search_btn_clicked_cb (GtkButton *btn, gpointer user_data)
if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (view->search_combo),
&iter)) {
- GrlMediaSource *source;
+ GrlSource *source;
const gchar *text;
gtk_tree_model_get (view->search_combo_model, &iter,
SEARCH_MODEL_SOURCE, &source,
@@ -1224,7 +1222,7 @@ search_btn_clicked_cb (GtkButton *btn, gpointer user_data)
}
static void
-query (GrlMediaSource *source, const gchar *text)
+query (GrlSource *source, const gchar *text)
{
OperationState *state;
guint query_id;
@@ -1235,12 +1233,12 @@ query (GrlMediaSource *source, const gchar *text)
state = g_new0 (OperationState, 1);
state->text = (gchar *) text;
state->type = OP_TYPE_QUERY;
- query_id = grl_media_source_query (source,
- text,
- all_keys (),
- default_options,
- browse_search_query_cb,
- state);
+ query_id = grl_source_query (source,
+ text,
+ all_keys (),
+ default_options,
+ browse_search_query_cb,
+ state);
clear_panes ();
operation_started (source, query_id, FALSE);
}
@@ -1252,7 +1250,7 @@ query_btn_clicked_cb (GtkButton *btn, gpointer user_data)
if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (view->query_combo),
&iter)) {
- GrlMediaSource *source;
+ GrlSource *source;
const gchar *text;
gtk_tree_model_get (view->query_combo_model, &iter,
QUERY_MODEL_SOURCE, &source,
@@ -1272,7 +1270,7 @@ set_filter_cb (GtkComboBox *widget,
{
GrlCaps *caps;
GrlTypeFilter filter;
- GrlMediaSource *source;
+ GrlSource *source;
GtkTreeIter iter;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (view->filter_audio), TRUE);
@@ -1682,8 +1680,8 @@ options_setup (void)
grl_operation_options_set_skip (default_options, 0);
grl_operation_options_set_count (default_options, BROWSE_CHUNK_SIZE);
- default_metadata_options = grl_operation_options_new (NULL);
- grl_operation_options_set_flags (default_metadata_options, METADATA_FLAGS);
+ default_resolve_options = grl_operation_options_new (NULL);
+ grl_operation_options_set_flags (default_resolve_options, RESOLVE_FLAGS);
}
static void
@@ -1996,7 +1994,7 @@ reset_browse_history (void)
free_stack (&ui_state->source_stack);
free_stack (&ui_state->container_stack);
set_cur_browse (NULL, NULL);
- set_cur_metadata (NULL, NULL);
+ set_cur_resolve (NULL, NULL);
}
static void
@@ -2019,9 +2017,9 @@ remove_notification (gpointer data)
}
static void
-content_changed_cb (GrlMediaSource *source,
+content_changed_cb (GrlSource *source,
GPtrArray *changed_medias,
- GrlMediaSourceChangeType change_type,
+ GrlSourceChangeType change_type,
gboolean location_unknown,
gpointer data)
{
@@ -2054,7 +2052,7 @@ content_changed_cb (GrlMediaSource *source,
if (GRL_IS_MEDIA_BOX (media)) {
message =
g_strdup_printf ("%s: container '%s' has %s%s",
- grl_source_get_name (GRL_SOURCE (source)),
+ grl_source_get_name (source),
media_id? media_id: "root",
change_type_string,
location_string);
@@ -2085,8 +2083,8 @@ source_added_cb (GrlPluginRegistry *registry,
GRL_DEBUG ("Detected new source available: '%s'",
grl_source_get_name (source));
- GRL_DEBUG ("\tPlugin's name: %s", grl_source_get_name (source));
- GRL_DEBUG ("\tPlugin's description: %s", grl_source_get_description (source));
+ GRL_DEBUG ("\tSource's name: %s", grl_source_get_name (source));
+ GRL_DEBUG ("\tSource's description: %s", grl_source_get_description (source));
/* If showing the plugin list, refresh it */
if (!ui_state->cur_source && !ui_state->cur_container) {
@@ -2101,13 +2099,11 @@ source_added_cb (GrlPluginRegistry *registry,
if (ui_state->changes_notification &&
(grl_source_supported_operations (source) &
GRL_OP_NOTIFY_CHANGE)) {
- if (grl_media_source_notify_change_start (GRL_MEDIA_SOURCE (source), NULL)) {
- g_signal_connect (GRL_MEDIA_SOURCE (source), "content-changed",
+ if (grl_source_notify_change_start (GRL_SOURCE (source), NULL)) {
+ g_signal_connect (GRL_SOURCE (source), "content-changed",
G_CALLBACK (content_changed_cb), NULL);
}
}
-
- /* Activate filters by type (if supported) */
}
static void
diff --git a/tools/vala/grilo-test.vala b/tools/vala/grilo-test.vala
index c7bee94..be0b242 100644
--- a/tools/vala/grilo-test.vala
+++ b/tools/vala/grilo-test.vala
@@ -1,7 +1,7 @@
using Grl;
public class SimplePlaylist : Object {
- private GLib.List<MediaSource> source_list;
+ private GLib.List<Grl.Source> source_list;
MainLoop main_loop = new MainLoop (null, false);
int processed_sources = 0;
@@ -21,7 +21,7 @@ public class SimplePlaylist : Object {
var ops = source.supported_operations ();
if ((ops & Grl.SupportedOps.SEARCH) != 0) {
debug ("Detected new source availabe: '%s' and it supports search", source.get_name ());
- source_list.append (source as MediaSource);
+ source_list.append (source as Grl.Source);
debug ("source list size = %u", source_list.length ());
}
}
@@ -33,7 +33,7 @@ public class SimplePlaylist : Object {
public SimplePlaylist () {
}
- private void search_cb (Grl.MediaSource source,
+ private void search_cb (Grl.Source source,
uint browse_id,
Grl.Media? media,
uint remaining,
@@ -63,13 +63,9 @@ public class SimplePlaylist : Object {
public void search (string q) {
unowned GLib.List keys = Grl.MetadataKey.list_new (Grl.MetadataKey.ID, Grl.MetadataKey.TITLE, Grl.MetadataKey.URL);
- foreach (MediaSource source in source_list) {
+ foreach (Grl.Source source in source_list) {
debug ("%s - %s", source.get_name (), q);
- var caps = source.get_caps (Grl.SupportedOps.SEARCH);
- var options = new Grl.OperationOptions (caps);
- options.set_count (100);
- options.set_flags (Grl.MetadataResolutionFlags.FULL | Grl.MetadataResolutionFlags.IDLE_RELAY);
- source.search (q, keys, options, search_cb);
+ source.search (q, keys, 0, 100, Grl.ResolutionFlags.FULL | Grl.ResolutionFlags.IDLE_RELAY, search_cb);
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]