[grilo] core: Redesign source/plugin hierarchy



commit ae22e96590eed29b2a8d1c4b4bfce6fb11a1d822
Author: Juan A. Suarez Romero <jasuarez igalia com>
Date:   Tue May 3 16:19:09 2011 +0000

    core: Redesign source/plugin hierarchy
    
    Previously, hierarchy was:
    
          +----------------+
          | GrlMediaPlugin |
          +-------.--------+
                 /_\
                  |
        +-------------------+
        | GrlMetadataSource |
        +---------.---------+
                 /_\
                  |
          +----------------+
          | GrlMediaSource |
          +----------------+
    
    With the redesign, we have the new hierarchy:
    
        +-----------+        +-----------+
        | GrlPlugin |<>-----*| GrlSource |
        +-----------+        +-----.-----+
                                  /_\
                                   |
                      +------------+----------+
                      |                       |
            +-------------------+    +----------------+
            | GrlMetadataSource |    | GrlMediaSource |
            +-------------------+    +----------------+
    
    Signed-off-by: Juan A. Suarez Romero <jasuarez igalia com>

 bindings/vala/grilo-0.2.metadata         |   22 +-
 bindings/vala/grilo-uninstalled.files.in |    3 +-
 doc/grilo/grilo-sections.txt             |  106 ++--
 doc/grilo/grilo.types                    |    3 +-
 examples/browsing.c                      |   11 +-
 examples/configuring-plugins.c           |    4 +-
 examples/efficient-metadata-resolution.c |   12 +-
 examples/loading-plugins.c               |    8 +-
 examples/multivalues.c                   |   11 +-
 examples/searching.c                     |   11 +-
 src/Makefile.am                          |   12 +-
 src/grilo.h                              |    2 +-
 src/grl-log-priv.h                       |    3 +-
 src/grl-log.c                            |    6 +-
 src/grl-media-plugin-priv.h              |   47 --
 src/grl-media-plugin.c                   |  284 ---------
 src/grl-media-plugin.h                   |  134 ----
 src/grl-media-source.c                   |  488 ++++++++++-----
 src/grl-media-source.h                   |   12 +-
 src/grl-metadata-source-priv.h           |   51 +--
 src/grl-metadata-source.c                |  997 +-----------------------------
 src/grl-metadata-source.h                |   96 +---
 src/grl-multiple.c                       |   11 +-
 src/grl-plugin-priv.h                    |   70 +++
 src/grl-plugin-registry.c                |  578 +++++++++---------
 src/grl-plugin-registry.h                |   75 +--
 src/grl-plugin.c                         |  494 +++++++++++++++
 src/grl-plugin.h                         |  135 ++++
 src/grl-source-priv.h                    |   75 +++
 src/grl-source.c                         |  817 ++++++++++++++++++++++++
 src/grl-source.h                         |  178 ++++++
 tools/grilo-inspect/grl-inspect.c        |   59 +-
 tools/grilo-test-ui/main.c               |   81 ++--
 tools/vala/grilo-test.vala               |    8 +-
 34 files changed, 2667 insertions(+), 2237 deletions(-)
---
diff --git a/bindings/vala/grilo-0.2.metadata b/bindings/vala/grilo-0.2.metadata
index 578bb50..2c7de4f 100644
--- a/bindings/vala/grilo-0.2.metadata
+++ b/bindings/vala/grilo-0.2.metadata
@@ -7,11 +7,16 @@ grl_config_new.source nullable="1"
 # GrlPluginRegistry
 grl_plugin_registry_add_config.config transfer_ownership="1"
 grl_plugin_registry_get_metadata_keys type_arguments="unowned KeyID" transfer_ownership="1"
-grl_plugin_registry_get_sources type_arguments="unowned MediaPlugin" transfer_ownership="1"
-grl_plugin_registry_get_sources_by_operations type_arguments="unowned MediaPlugin" transfer_ownership="1"
+grl_plugin_registry_get_sources type_arguments="unowned Plugin" transfer_ownership="1"
+grl_plugin_registry_get_sources_by_operations type_arguments="unowned Plugin" transfer_ownership="1"
 
-# GrlMediaPlugin
-grl_media_plugin_get_info_keys type_arguments="unowned string" transfer_ownership="1"
+# GrlPlugin
+grl_plugin_get_info_keys type_arguments="unowned string" transfer_ownership="1"
+
+#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"
@@ -28,21 +33,12 @@ grl_media_source_search_sync type_arguments="Media" transfer_ownership="1"
 grl_media_source_search_sync.keys type_arguments="KeyID"
 
 # GrlMetadataSource
-grl_metadata_source_filter_slow type_arguments="unowned KeyID" transfer_ownership="1"
-grl_metadata_source_filter_slow.keys type_arguments="unowned KeyID" is_ref="1"
-grl_metadata_source_filter_supported type_arguments="unowned KeyID" transfer_ownership="1"
-grl_metadata_source_filter_supported.keys type_arguments="unowned KeyID" is_ref="1"
-grl_metadata_source_filter_writable type_arguments="unowned KeyID" transfer_ownership="1"
-grl_metadata_source_filter_writable.keys type_arguments="unowned KeyID" is_ref="1"
 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_metadata_source_slow_keys type_arguments="unowned KeyID" transfer_ownership="0"
-grl_metadata_source_supported_keys type_arguments="unowned KeyID" transfer_ownership="0"
-grl_metadata_source_writable_keys type_arguments="unowned KeyID" transfer_ownership="0"
 
 # GrlData
 grl_data_get_keys type_arguments="unowned KeyID" transfer_ownership="1"
diff --git a/bindings/vala/grilo-uninstalled.files.in b/bindings/vala/grilo-uninstalled.files.in
index 9215fe9..e1e99f2 100644
--- a/bindings/vala/grilo-uninstalled.files.in
+++ b/bindings/vala/grilo-uninstalled.files.in
@@ -1,7 +1,8 @@
 @top_builddir@/src/grilo.h
 @top_builddir@/src/grl-error.h
 @top_builddir@/src/grl-log.h
- top_builddir@/src/grl-media-plugin.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
diff --git a/doc/grilo/grilo-sections.txt b/doc/grilo/grilo-sections.txt
index 0dd312c..c619462 100644
--- a/doc/grilo/grilo-sections.txt
+++ b/doc/grilo/grilo-sections.txt
@@ -16,6 +16,66 @@
 <INCLUDE>grilo.h</INCLUDE>
 
 <SECTION>
+<FILE>grl-plugin</FILE>
+<TITLE>GrlPlugin</TITLE>
+GRL_PLUGIN_NAME
+GRL_PLUGIN_DESCRIPTION
+GRL_PLUGIN_VERSION
+GRL_PLUGIN_LICENSE
+GRL_PLUGIN_AUTHOR
+GRL_PLUGIN_SITE
+GrlPlugin
+GrlPluginClass
+grl_plugin_get_name
+grl_plugin_get_description
+grl_plugin_get_version
+grl_plugin_get_license
+grl_plugin_get_author
+grl_plugin_get_site
+grl_plugin_get_id
+grl_plugin_get_filename
+grl_plugin_get_rank
+grl_plugin_get_info_keys
+grl_plugin_get_info
+<SUBSECTION Standard>
+GRL_PLUGIN
+GRL_IS_PLUGIN
+GRL_TYPE_PLUGIN
+grl_plugin_get_type
+GRL_PLUGIN_CLASS
+GRL_IS_PLUGIN_CLASS
+GRL_PLUGIN_GET_CLASS
+<SUBSECTION Private>
+GrlPluginPrivate
+</SECTION>
+
+<SECTION>
+<FILE>grl-source</FILE>
+<TITLE>GrlSource</TITLE>
+GrlSource
+GrlSupportedOps
+GrlSourceClass
+grl_source_supported_operations
+grl_source_supported_keys
+grl_source_slow_keys
+grl_source_writable_keys
+grl_source_get_id
+grl_source_get_name
+grl_source_get_description
+grl_source_get_caps
+<SUBSECTION Standard>
+GRL_SOURCE
+GRL_IS_SOURCE
+GRL_TYPE_SOURCE
+grl_source_get_type
+GRL_SOURCE_CLASS
+GRL_IS_SOURCE_CLASS
+GRL_SOURCE_GET_CLASS
+<SUBSECTION Private>
+GrlSourcePrivate
+</SECTION>
+
+<SECTION>
 <FILE>grl-metadata-source</FILE>
 <TITLE>GrlMetadataSource</TITLE>
 GrlMetadataResolutionFlags
@@ -25,24 +85,12 @@ GrlMetadataSourceResolveCb
 GrlMetadataSourceSetMetadataCb
 GrlMetadataSourceResolveSpec
 GrlMetadataSourceSetMetadataSpec
-GrlSupportedOps
 GrlMetadataSourceClass
-grl_metadata_source_supported_operations
-grl_metadata_source_supported_keys
-grl_metadata_source_slow_keys
-grl_metadata_source_filter_supported
-grl_metadata_source_filter_slow
-grl_metadata_source_filter_writable
-grl_metadata_source_writable_keys
 grl_metadata_source_may_resolve
 grl_metadata_source_resolve
 grl_metadata_source_resolve_sync
 grl_metadata_source_set_metadata
 grl_metadata_source_set_metadata_sync
-grl_metadata_source_get_id
-grl_metadata_source_get_name
-grl_metadata_source_get_description
-grl_metadata_source_get_caps
 <SUBSECTION Standard>
 GRL_METADATA_SOURCE
 GRL_IS_METADATA_SOURCE
@@ -56,40 +104,6 @@ GrlMetadataSourcePrivate
 </SECTION>
 
 <SECTION>
-<FILE>grl-media-plugin</FILE>
-<TITLE>GrlMediaPlugin</TITLE>
-GRL_MEDIA_PLUGIN_NAME
-GRL_MEDIA_PLUGIN_DESCRIPTION
-GRL_MEDIA_PLUGIN_VERSION
-GRL_MEDIA_PLUGIN_LICENSE
-GRL_MEDIA_PLUGIN_AUTHOR
-GRL_MEDIA_PLUGIN_SITE
-GrlMediaPlugin
-GrlMediaPluginClass
-grl_media_plugin_get_name
-grl_media_plugin_get_description
-grl_media_plugin_get_version
-grl_media_plugin_get_license
-grl_media_plugin_get_author
-grl_media_plugin_get_site
-grl_media_plugin_get_id
-grl_media_plugin_get_filename
-grl_media_plugin_get_rank
-grl_media_plugin_get_info_keys
-grl_media_plugin_get_info
-<SUBSECTION Standard>
-GRL_MEDIA_PLUGIN
-GRL_IS_MEDIA_PLUGIN
-GRL_TYPE_MEDIA_PLUGIN
-grl_media_plugin_get_type
-GRL_MEDIA_PLUGIN_CLASS
-GRL_IS_MEDIA_PLUGIN_CLASS
-GRL_MEDIA_PLUGIN_GET_CLASS
-<SUBSECTION Private>
-GrlMediaPluginPrivate
-</SECTION>
-
-<SECTION>
 <FILE>grl-media-source</FILE>
 <TITLE>GrlMediaSource</TITLE>
 GrlMediaSourceChangeType
diff --git a/doc/grilo/grilo.types b/doc/grilo/grilo.types
index 68ce448..0831015 100644
--- a/doc/grilo/grilo.types
+++ b/doc/grilo/grilo.types
@@ -16,7 +16,8 @@ grl_media_box_get_type
 grl_media_audio_get_type
 grl_media_video_get_type
 grl_media_image_get_type
-grl_media_plugin_get_type
+grl_plugin_get_type
+grl_source_get_type
 grl_media_source_get_type
 grl_metadata_source_get_type
 grl_plugin_registry_get_type
diff --git a/examples/browsing.c b/examples/browsing.c
index 674ccb4..d9459da 100644
--- a/examples/browsing.c
+++ b/examples/browsing.c
@@ -62,26 +62,25 @@ browse_cb (GrlMediaSource *source,
 }
 
 static void
-source_added_cb (GrlPluginRegistry *registry, gpointer user_data)
+source_added_cb (GrlPluginRegistry *registry, GrlSource *source, gpointer user_data)
 {
   static gboolean first = TRUE;
-  GrlMetadataSource *source = GRL_METADATA_SOURCE (user_data);
   GList * keys = grl_metadata_key_list_new (GRL_METADATA_KEY_TITLE,
 					    GRL_METADATA_KEY_DURATION,
 					    GRL_METADATA_KEY_URL,
 					    GRL_METADATA_KEY_CHILDCOUNT,
 					    NULL);
   g_debug ("Detected new source available: '%s'",
-	   grl_metadata_source_get_name (source));
+           grl_source_get_name (source));
 
   /* We will just issue a browse operation on the first browseble
      source we find */
   if (first &&
-      grl_metadata_source_supported_operations (source) & GRL_OP_BROWSE) {
+      grl_source_supported_operations (source) & GRL_OP_BROWSE) {
     GrlOperationOptions *options;
     GrlCaps *caps;
     first = FALSE;
-    g_debug ("Browsing source: %s", grl_metadata_source_get_name (source));
+    g_debug ("Browsing source: %s", grl_source_get_name (source));
     /* Here is how you can browse a source, you have to provide:
        1) The source you want to browse contents from.
        2) The container object you want to browse (NULL for the root container)
@@ -92,7 +91,7 @@ source_added_cb (GrlPluginRegistry *registry, gpointer user_data)
        It returns an operation identifier that you can use to match results
        with the corresponding request (we ignore it here) */
 
-    caps = grl_metadata_source_get_caps (source, GRL_OP_BROWSE);
+    caps = grl_source_get_caps (source, GRL_OP_BROWSE);
     options = grl_operation_options_new (caps);
     grl_operation_options_set_count (options, 5);
     grl_operation_options_set_flags (options, GRL_RESOLVE_IDLE_RELAY);
diff --git a/examples/configuring-plugins.c b/examples/configuring-plugins.c
index 6e75427..fd93444 100644
--- a/examples/configuring-plugins.c
+++ b/examples/configuring-plugins.c
@@ -10,11 +10,11 @@
 GRL_LOG_DOMAIN_STATIC(example_log_domain);
 
 static void
-source_added_cb (GrlPluginRegistry *registry, gpointer user_data)
+source_added_cb (GrlPluginRegistry *registry, GrlSource *source, gpointer user_data)
 {
   /* If the Youtube plugin is installed, you should see it here now! */
   g_debug ("Detected new source available: '%s'",
-           grl_metadata_source_get_name (GRL_METADATA_SOURCE (user_data)));
+           grl_source_get_name (source));
 }
 
 static void
diff --git a/examples/efficient-metadata-resolution.c b/examples/efficient-metadata-resolution.c
index c407fcf..79a6b06 100644
--- a/examples/efficient-metadata-resolution.c
+++ b/examples/efficient-metadata-resolution.c
@@ -59,8 +59,7 @@ search_cb (GrlMediaSource *source,
     GrlCaps *caps;
     GList *keys = grl_metadata_key_list_new (GRL_METADATA_KEY_URL, NULL);
 
-    caps = grl_metadata_source_get_caps (GRL_METADATA_SOURCE (source),
-                                         GRL_OP_METADATA);
+    caps = grl_source_get_caps (GRL_SOURCE (source), GRL_OP_METADATA);
     options = grl_operation_options_new (caps);
     grl_operation_options_set_flags (options, GRL_RESOLVE_IDLE_RELAY);
     grl_media_source_metadata (source,
@@ -76,10 +75,9 @@ search_cb (GrlMediaSource *source,
 }
 
 static void
-source_added_cb (GrlPluginRegistry *registry, gpointer user_data)
+source_added_cb (GrlPluginRegistry *registry, GrlSource *source, gpointer user_data)
 {
-  GrlMetadataSource *source = GRL_METADATA_SOURCE (user_data);
-  const gchar *source_id = grl_metadata_source_get_id (source);
+  const gchar *source_id = grl_source_get_id (source);
   GrlCaps *caps;
   GrlOperationOptions *options;
 
@@ -92,10 +90,10 @@ source_added_cb (GrlPluginRegistry *registry, gpointer user_data)
 					   NULL);
 
   /* The source must be searchable */
-  if (!(grl_metadata_source_supported_operations (source) & GRL_OP_SEARCH))
+  if (!(grl_source_supported_operations (source) & GRL_OP_SEARCH))
     g_error ("Source %s is not searchable!", source_id);
 
-  caps = grl_metadata_source_get_caps (source, GRL_OP_SEARCH);
+  caps = grl_source_get_caps (source, GRL_OP_SEARCH);
   options = grl_operation_options_new (caps);
   grl_operation_options_set_count (options, 5);
   grl_operation_options_set_flags (options,
diff --git a/examples/loading-plugins.c b/examples/loading-plugins.c
index b23e04d..9169ee4 100644
--- a/examples/loading-plugins.c
+++ b/examples/loading-plugins.c
@@ -10,20 +10,20 @@
 GRL_LOG_DOMAIN_STATIC(example_log_domain);
 
 static void
-source_added_cb (GrlPluginRegistry *registry, gpointer user_data)
+source_added_cb (GrlPluginRegistry *registry, GrlSource *source, gpointer user_data)
 {
   g_debug ("Detected new source available: '%s'",
-           grl_metadata_source_get_name (GRL_METADATA_SOURCE (user_data)));
+           grl_source_get_name (source));
 
   /* Usually you may add the new service to the user interface so the user
      can interact with it (browse, search, etc) */
 }
 
 static void
-source_removed_cb (GrlPluginRegistry *registry, gpointer user_data)
+source_removed_cb (GrlPluginRegistry *registry, GrlSource *source, gpointer user_data)
 {
   g_debug ("Source '%s' is gone",
-           grl_metadata_source_get_name (GRL_METADATA_SOURCE (user_data)));
+           grl_source_get_name (source));
 
   /* Usually you would inform the user that this service is no longer
      available (for example a UPnP server was shutdown) and remove it
diff --git a/examples/multivalues.c b/examples/multivalues.c
index e179097..0043afb 100644
--- a/examples/multivalues.c
+++ b/examples/multivalues.c
@@ -48,10 +48,9 @@ search_cb (GrlMediaSource *source,
 }
 
 static void
-source_added_cb (GrlPluginRegistry *registry, gpointer user_data)
+source_added_cb (GrlPluginRegistry *registry, GrlSource *source, gpointer user_data)
 {
   const gchar *id;
-  GrlMetadataSource *source = GRL_METADATA_SOURCE (user_data);
   GrlCaps *caps;
   GrlOperationOptions *options;
   GList * keys = grl_metadata_key_list_new (GRL_METADATA_KEY_TITLE,
@@ -60,18 +59,18 @@ source_added_cb (GrlPluginRegistry *registry, gpointer user_data)
 					    NULL);
 
   /* Not interested if not searchable */
-  if (!(grl_metadata_source_supported_operations (source) & GRL_OP_SEARCH))
+  if (!(grl_source_supported_operations (source) & GRL_OP_SEARCH))
     return;
 
   g_debug ("Detected new searchable source available: '%s'",
-	   grl_metadata_source_get_name (source));
+	   grl_source_get_name (source));
 
   /* Only interested in Youtube */
-  id = grl_metadata_source_get_id (source);
+  id = grl_source_get_id (source);
   if (strcmp (id, "grl-youtube"))
     return;
 
-  caps = grl_metadata_source_get_caps (source, GRL_OP_SEARCH);
+  caps = grl_source_get_caps (source, GRL_OP_SEARCH);
   options = grl_operation_options_new (caps);
   grl_operation_options_set_skip (options, 0);
   grl_operation_options_set_count (options, 5);
diff --git a/examples/searching.c b/examples/searching.c
index 3c56e00..45b4ce7 100644
--- a/examples/searching.c
+++ b/examples/searching.c
@@ -45,10 +45,9 @@ search_cb (GrlMediaSource *source,
 }
 
 static void
-source_added_cb (GrlPluginRegistry *registry, gpointer user_data)
+source_added_cb (GrlPluginRegistry *registry, GrlSource *source, gpointer user_data)
 {
   const gchar *id;
-  GrlMetadataSource *source = GRL_METADATA_SOURCE (user_data);
   GrlCaps *caps;
   GrlOperationOptions *options;
   GList * keys = grl_metadata_key_list_new (GRL_METADATA_KEY_TITLE,
@@ -57,18 +56,18 @@ source_added_cb (GrlPluginRegistry *registry, gpointer user_data)
 					    NULL);
 
   /* Not interested if not searchable */
-  if (!(grl_metadata_source_supported_operations (source) & GRL_OP_SEARCH))
+  if (!(grl_source_supported_operations (source) & GRL_OP_SEARCH))
     return;
 
   g_debug ("Detected new searchable source available: '%s'",
-	   grl_metadata_source_get_name (source));
+	   grl_source_get_name (source));
 
   /* Only interested in Jamendo */
-  id = grl_metadata_source_get_id (source);
+  id = grl_source_get_id (source);
   if (strcmp (id, "grl-jamendo"))
     return;
 
-  caps = grl_metadata_source_get_caps (source, GRL_OP_SEARCH);
+  caps = grl_source_get_caps (source, GRL_OP_SEARCH);
   options = grl_operation_options_new (caps);
   grl_operation_options_set_count (options, 5);
   grl_operation_options_set_flags (options, GRL_RESOLVE_IDLE_RELAY);
diff --git a/src/Makefile.am b/src/Makefile.am
index 7d9f604..4a6bc7e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -41,10 +41,11 @@ lib GRL_NAME@_la_LDFLAGS =	\
 	-no-undefined
 
 lib GRL_NAME@_la_SOURCES =					\
-	grl-media-plugin.c grl-media-plugin-priv.h		\
+	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-metadata-source-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				\
@@ -76,9 +77,10 @@ lib GRL_NAME@incdir =	\
 lib GRL_NAME@inc_HEADERS =	\
 	grilo.h			\
 	grl-error.h		\
-	grl-media-plugin.h	\
+	grl-plugin.h	\
 	grl-plugin-registry.h	\
 	grl-metadata-key.h	\
+	grl-source.h	\
 	grl-metadata-source.h	\
 	grl-media-source.h	\
 	grl-log.h 		\
@@ -105,8 +107,8 @@ lib GRL_NAME@inc_HEADERS += $(data_h_headers)
 
 noinst_HEADERS =			\
 	grl-plugin-registry-priv.h	\
-	grl-media-plugin-priv.h		\
-	grl-metadata-source-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 721031b..b424571 100644
--- a/src/grilo.h
+++ b/src/grilo.h
@@ -30,7 +30,7 @@
 #include <grl-error.h>
 #include <grl-log.h>
 #include <grl-plugin-registry.h>
-#include <grl-media-plugin.h>
+#include <grl-plugin.h>
 #include <grl-media-source.h>
 #include <grl-metadata-source.h>
 #include <grl-metadata-key.h>
diff --git a/src/grl-log-priv.h b/src/grl-log-priv.h
index b29e1ed..d6049a2 100644
--- a/src/grl-log-priv.h
+++ b/src/grl-log-priv.h
@@ -33,7 +33,8 @@ GRL_LOG_DOMAIN_EXTERN(log_log_domain);
 GRL_LOG_DOMAIN_EXTERN(config_log_domain);
 GRL_LOG_DOMAIN_EXTERN(data_log_domain);
 GRL_LOG_DOMAIN_EXTERN(media_log_domain);
-GRL_LOG_DOMAIN_EXTERN(media_plugin_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);
diff --git a/src/grl-log.c b/src/grl-log.c
index 341b789..45127af 100644
--- a/src/grl-log.c
+++ b/src/grl-log.c
@@ -326,7 +326,8 @@ _grl_log_init_core_domains (void)
   DOMAIN_INIT (config_log_domain, "config");
   DOMAIN_INIT (data_log_domain, "data");
   DOMAIN_INIT (media_log_domain, "media");
-  DOMAIN_INIT (media_plugin_log_domain, "media-plugin");
+  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");
@@ -371,8 +372,9 @@ _grl_log_free_core_domains (void)
   DOMAIN_FREE (log_log_domain);
   DOMAIN_FREE (config_log_domain);
   DOMAIN_FREE (media_log_domain);
-  DOMAIN_FREE (media_plugin_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-media-source.c b/src/grl-media-source.c
index 1bfba42..e442a95 100644
--- a/src/grl-media-source.c
+++ b/src/grl-media-source.c
@@ -23,7 +23,7 @@
 /**
  * SECTION:grl-media-source
  * @short_description: Abstract class for media providers
- * @see_also: #GrlMediaPlugin, #GrlMetadataSource, #GrlMedia
+ * @see_also: #GrlPlugin, #GrlSource, #GrlMetadataSource, #GrlMedia
  *
  * GrlMediaSource is the abstract base class needed to construct a
  * source of media data.
@@ -40,10 +40,12 @@
  */
 
 #include "grl-media-source.h"
-#include "grl-metadata-source-priv.h"
+#include "grl-metadata-source.h"
+#include "grl-source-priv.h"
 #include "grl-operation.h"
 #include "grl-operation-priv.h"
 #include "grl-sync-priv.h"
+#include "grl-plugin-registry.h"
 #include "data/grl-media.h"
 #include "data/grl-media-box.h"
 #include "grl-error.h"
@@ -166,7 +168,7 @@ static void grl_media_source_set_property (GObject *object,
                                            GParamSpec *pspec);
 
 static GrlSupportedOps
-grl_media_source_supported_operations (GrlMetadataSource *metadata_source);
+grl_media_source_supported_operations (GrlSource *source);
 
 /* ================ GrlMediaSource GObject ================ */
 
@@ -178,22 +180,22 @@ static gint registry_signals[SIG_LAST];
 
 G_DEFINE_ABSTRACT_TYPE (GrlMediaSource,
                         grl_media_source,
-                        GRL_TYPE_METADATA_SOURCE);
+                        GRL_TYPE_SOURCE);
 
 static void
 grl_media_source_class_init (GrlMediaSourceClass *media_source_class)
 {
   GObjectClass *gobject_class;
-  GrlMetadataSourceClass *metadata_source_class;
+  GrlSourceClass *source_class;
 
   gobject_class = G_OBJECT_CLASS (media_source_class);
-  metadata_source_class = GRL_METADATA_SOURCE_CLASS (media_source_class);
+  source_class = GRL_SOURCE_CLASS (media_source_class);
 
   gobject_class->finalize = grl_media_source_finalize;
   gobject_class->set_property = grl_media_source_set_property;
   gobject_class->get_property = grl_media_source_get_property;
 
-  metadata_source_class->supported_operations =
+  source_class->supported_operations =
     grl_media_source_supported_operations;
 
   g_type_class_add_private (media_source_class,
@@ -206,13 +208,13 @@ grl_media_source_class_init (GrlMediaSourceClass *media_source_class)
    * 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));
+                                   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));
   /**
    * GrlMediaSource::content-changed:
    * @source: source that has changed
@@ -348,8 +350,8 @@ browse_idle (gpointer user_data)
   GRL_DEBUG ("browse_idle");
   GrlMediaSourceBrowseSpec *bs = (GrlMediaSourceBrowseSpec *) user_data;
   /* Check if operation was cancelled even before the idle kicked in */
-  if (!grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (bs->source),
-                                                   bs->browse_id)) {
+  if (!grl_source_operation_is_cancelled (GRL_SOURCE (bs->source),
+                                          bs->browse_id)) {
     GRL_MEDIA_SOURCE_GET_CLASS (bs->source)->browse (bs->source, bs);
   } else {
     GError *error;
@@ -370,8 +372,8 @@ search_idle (gpointer user_data)
   GRL_DEBUG ("search_idle");
   GrlMediaSourceSearchSpec *ss = (GrlMediaSourceSearchSpec *) user_data;
   /* Check if operation was cancelled even before the idle kicked in */
-  if (!grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (ss->source),
-                                                   ss->search_id)) {
+  if (!grl_source_operation_is_cancelled (GRL_SOURCE (ss->source),
+                                          ss->search_id)) {
     GRL_MEDIA_SOURCE_GET_CLASS (ss->source)->search (ss->source, ss);
   } else {
     GError *error;
@@ -389,8 +391,8 @@ query_idle (gpointer user_data)
 {
   GRL_DEBUG ("query_idle");
   GrlMediaSourceQuerySpec *qs = (GrlMediaSourceQuerySpec *) user_data;
-  if (!grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (qs->source),
-                                                   qs->query_id)) {
+  if (!grl_source_operation_is_cancelled (GRL_SOURCE (qs->source),
+                                          qs->query_id)) {
     GRL_MEDIA_SOURCE_GET_CLASS (qs->source)->query (qs->source, qs);
   } else {
     GError *error;
@@ -408,8 +410,8 @@ metadata_idle (gpointer user_data)
 {
   GRL_DEBUG ("metadata_idle");
   GrlMediaSourceMetadataSpec *ms = (GrlMediaSourceMetadataSpec *) user_data;
-  if (!grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (ms->source),
-                                                   ms->metadata_id)) {
+  if (!grl_source_operation_is_cancelled (GRL_SOURCE (ms->source),
+                                          ms->metadata_id)) {
     GRL_MEDIA_SOURCE_GET_CLASS (ms->source)->metadata (ms->source, ms);
   } else {
     GError *error;
@@ -460,6 +462,228 @@ remove_idle (gpointer user_data)
   return FALSE;
 }
 
+static GList *
+list_union (GList *original_set, GList *additional_set, GDestroyNotify free_func)
+{
+  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);
+
+    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;
+}
+
+/*
+ * @data: a GrlData instance
+ * @deps: a list of GrlKeyID
+ *
+ * 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 (GrlMediaSource *source,
+                 const GList *keys)
+{
+  const GList *iter;
+  GList *supported;
+
+  supported = (GList *) grl_source_supported_keys (GRL_SOURCE (source));
+
+  for (iter = keys; iter; iter = g_list_next (iter)) {
+    if (!g_list_find (supported, iter->data)) {
+      return FALSE;
+    }
+  }
+  return TRUE;
+}
+
+/*
+ * 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.
+ *
+ * @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.
+ */
+static GrlMetadataSource *
+get_additional_source_for_key (GrlMediaSource *source,
+                               GList *sources,
+                               GrlMedia *media,
+                               GrlKeyID key,
+                               GList **additional_keys,
+                               gboolean main_source_is_only_resolver)
+{
+  GList *iter;
+
+  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);
+
+ for (iter = sources; iter; iter = g_list_next (iter)) {
+    GList *_additional_keys = NULL;
+    GrlMetadataSource *_source = (GrlMetadataSource*)iter->data;
+
+    if (grl_metadata_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;
+}
+
+/*
+ * 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 (GrlMediaSource *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;
+    GrlMetadataSource *_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 (GRL_SOURCE (_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 (GrlMediaSource *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 = grl_source_filter_supported (GRL_SOURCE (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;
+}
+
 static void
 media_from_uri_relay_cb (GrlMediaSource *source,
                          guint media_from_uri_id,
@@ -476,11 +700,11 @@ media_from_uri_relay_cb (GrlMediaSource *source,
   mfsrc = (struct MediaFromUriRelayCb *) user_data;
   if (media) {
     grl_media_set_source (media,
-                          grl_metadata_source_get_id (GRL_METADATA_SOURCE (source)));
+                          grl_source_get_id (GRL_SOURCE (source)));
   }
 
-  if (grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (source),
-                                                  mfsrc->spec->media_from_uri_id)) {
+  if (grl_source_operation_is_cancelled (GRL_SOURCE (source),
+                                         mfsrc->spec->media_from_uri_id)) {
     /* if the plugin already set an error, we don't care because we're
      * cancelled */
     _error = g_error_new (GRL_CORE_ERROR, GRL_CORE_ERROR_OPERATION_CANCELLED,
@@ -517,8 +741,8 @@ media_from_uri_idle (gpointer user_data)
   GRL_DEBUG ("media_from_uri_idle");
   GrlMediaSourceMediaFromUriSpec *mfus =
     (GrlMediaSourceMediaFromUriSpec *) user_data;
-  if (!grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (mfus->source),
-                                                   mfus->media_from_uri_id)) {
+  if (!grl_source_operation_is_cancelled (GRL_SOURCE (mfus->source),
+                                          mfus->media_from_uri_id)) {
     GRL_MEDIA_SOURCE_GET_CLASS (mfus->source)->media_from_uri (mfus->source,
                                                                mfus);
   } else {
@@ -543,8 +767,8 @@ browse_result_relay_idle (gpointer user_data)
   /* Check if operation was cancelled (could be cancelled between the relay
      callback and this idle loop iteration). Remember that we do
      emit the last result (remaining == 0) in any case. */
-  if (grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (bri->source),
-                                                  bri->browse_id)) {
+  if (grl_source_operation_is_cancelled (GRL_SOURCE (bri->source),
+                                         bri->browse_id)) {
     if (bri->media) {
       g_object_unref (bri->media);
       bri->media = NULL;
@@ -574,8 +798,8 @@ browse_result_relay_idle (gpointer user_data)
   if (bri->remaining == 0 && !bri->chained) {
     /* This is the last post-processing callback, so we can remove
        the operation state data here */
-    grl_metadata_source_set_operation_finished (GRL_METADATA_SOURCE (bri->source),
-                                                bri->browse_id);
+    grl_source_set_operation_finished (GRL_SOURCE (bri->source),
+                                       bri->browse_id);
   }
 
   /* We copy the error if we do idle relay or might have created one above in
@@ -644,7 +868,7 @@ browse_result_relay_cb (GrlMediaSource *source,
 
   GRL_DEBUG ("browse_result_relay_cb, op:%u, source:%s, remaining:%u",
              browse_id,
-             grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)),
+             grl_source_get_name (GRL_SOURCE (source)),
              remaining);
 
   brc = (struct BrowseRelayCb *) user_data;
@@ -653,8 +877,8 @@ browse_result_relay_cb (GrlMediaSource *source,
 
   /* Check if operation is still valid , otherwise do not emit the result
      but make sure to free the operation data when remaining is 0 */
-  if (!grl_metadata_source_operation_is_ongoing (GRL_METADATA_SOURCE (source),
-                                                 browse_id)) {
+  if (!grl_source_operation_is_ongoing (GRL_SOURCE (source),
+                                        browse_id)) {
     GRL_DEBUG ("operation is cancelled or already finished, skipping result!");
     if (media) {
       g_object_unref (media);
@@ -668,8 +892,8 @@ browse_result_relay_cb (GrlMediaSource *source,
     if (remaining > 0) {
       return;
     }
-    if (grl_metadata_source_operation_is_completed (GRL_METADATA_SOURCE (source),
-                                                    browse_id)) {
+    if (grl_source_operation_is_completed (GRL_SOURCE (source),
+                                           browse_id)) {
       /* If the operation was cancelled, we ignore all results until
 	 we get the last one, which we let through so all chained callbacks
 	 have the chance to free their resources. If the operation is already
@@ -677,7 +901,7 @@ browse_result_relay_cb (GrlMediaSource *source,
 	 result through and doing it again would cause a crash */
       GRL_WARNING ("Source '%s' emitted 'remaining=0' more than once for "
                    "operation %d",
-                   grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)),
+                   grl_source_get_name (GRL_SOURCE (source)),
                    browse_id);
       return;
     }
@@ -714,13 +938,13 @@ browse_result_relay_cb (GrlMediaSource *source,
 
   /* This is to prevent crash when plugins emit remaining=0 more than once */
   if (remaining == 0) {
-    grl_metadata_source_set_operation_completed (GRL_METADATA_SOURCE (source),
-                                                 browse_id);
+    grl_source_set_operation_completed (GRL_SOURCE (source),
+                                        browse_id);
   }
 
   if (media) {
     grl_media_set_source (media,
-                          grl_metadata_source_get_id (GRL_METADATA_SOURCE (source)));
+                          grl_source_get_id (GRL_SOURCE (source)));
   }
 
   /* TODO: this should be TRUE if GRL_RESOLVE_FULL was requested too,
@@ -740,8 +964,8 @@ browse_result_relay_cb (GrlMediaSource *source,
     gboolean should_free_error = FALSE;
     GError *_error = (GError *)error;
     if (remaining == 0 &&
-        grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (source),
-                                                    browse_id)) {
+        grl_source_operation_is_cancelled (GRL_SOURCE (source),
+                                           browse_id)) {
       /* last callback call for a cancelled operation */
       /* if the plugin already set an error, we don't care because we're
        * cancelled */
@@ -763,8 +987,8 @@ browse_result_relay_cb (GrlMediaSource *source,
     if (remaining == 0 && !brc->chained) {
       /* This is the last post-processing callback, so we can remove
 	 the operation state data here */
-      grl_metadata_source_set_operation_finished (GRL_METADATA_SOURCE (source),
-                                                  browse_id);
+      grl_source_set_operation_finished (GRL_SOURCE (source),
+                                         browse_id);
     }
   }
 
@@ -782,7 +1006,7 @@ browse_result_relay_cb (GrlMediaSource *source,
   if (remaining == 0) {
     GRL_DEBUG ("Got remaining '0' for operation %d (%s)",
                browse_id,
-               grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)));
+               grl_source_get_name (GRL_SOURCE (source)));
     if (brc->bspec) {
       free_browse_operation_spec (brc->bspec);
     } else if (brc->sspec) {
@@ -844,11 +1068,11 @@ metadata_result_relay_cb (GrlMediaSource *source,
   mrc = (struct MetadataRelayCb *) user_data;
   if (media) {
     grl_media_set_source (media,
-                          grl_metadata_source_get_id (GRL_METADATA_SOURCE (source)));
+                          grl_source_get_id (GRL_SOURCE (source)));
   }
 
-  if (grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (source),
-                                                  mrc->spec->metadata_id)) {
+  if (grl_source_operation_is_cancelled (GRL_SOURCE (source),
+                                         mrc->spec->metadata_id)) {
     /* if the plugin already set an error, we don't care because we're
      * cancelled */
     _error = g_error_new (GRL_CORE_ERROR, GRL_CORE_ERROR_OPERATION_CANCELLED,
@@ -1026,8 +1250,8 @@ full_resolution_done_cb (GrlMetadataSource *source,
 
   /* Check if pending resolutions must be cancelled */
   if (!cb_info->cancelled &&
-      grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (cb_info->source),
-                                                  cb_info->browse_id)) {
+      grl_source_operation_is_cancelled (GRL_SOURCE (cb_info->source),
+                                         cb_info->browse_id)) {
     cb_info->cancelled = TRUE;
     g_hash_table_foreach (cb_info->pending_callbacks, cancel_resolve, NULL);
   }
@@ -1038,8 +1262,8 @@ full_resolution_done_cb (GrlMetadataSource *source,
     ctl_info = cb_info->ctl_info;
 
     /* Ignore elements coming after finishing the operation (out-of-order elements) */
-    if (grl_metadata_source_operation_is_finished (GRL_METADATA_SOURCE (cb_info->source),
-                                                                        cb_info->browse_id)) {
+    if (grl_source_operation_is_finished (GRL_SOURCE (cb_info->source),
+                                          cb_info->browse_id)) {
       GRL_DEBUG ("operation was finished, skipping full resolutuion done "
                  "result!");
       if (media) {
@@ -1103,8 +1327,8 @@ full_resolution_done_cb (GrlMetadataSource *source,
 	if (remaining == 0) {
 	  if (!ctl_info->chained) {
 	    /* We are the last post-processing callback, finish operation */
-	    grl_metadata_source_set_operation_finished (GRL_METADATA_SOURCE (cb_info->source),
-                                                        cb_info->browse_id);
+	    grl_source_set_operation_finished (GRL_SOURCE (cb_info->source),
+                                          cb_info->browse_id);
 	  }
 	  /* We are done, free the control information now */
 	  g_list_free (ctl_info->keys);
@@ -1167,9 +1391,9 @@ full_resolution_ctl_cb (GrlMediaSource *source,
        when fully resolved */
 
     sources =
-        grl_metadata_source_get_additional_sources (GRL_METADATA_SOURCE (source),
-                                                    media, ctl_info->keys,
-                                                    NULL, FALSE);
+      get_additional_sources (source,
+                              media, ctl_info->keys,
+                              NULL, FALSE);
 
     /* Use suggested sources to fill in missing metadata, the "done"
        callback will be used to emit the resulting object when all metadata has
@@ -1177,13 +1401,13 @@ full_resolution_ctl_cb (GrlMediaSource *source,
     for (iter = sources; iter; iter = g_list_next (iter)) {
       GrlMetadataSource *_source = (GrlMetadataSource *)iter->data;
       GRL_DEBUG ("Using '%s' to resolve extra metadata now",
-                 grl_metadata_source_get_name (_source));
+                 grl_source_get_name (GRL_SOURCE (_source)));
 
-      if (grl_metadata_source_supported_operations (_source) & GRL_OP_RESOLVE) {
+      if (grl_source_supported_operations (GRL_SOURCE (_source)) & GRL_OP_RESOLVE) {
         GrlOperationOptions *resolve_options = NULL;
         guint resolve_id;
         GrlCaps *source_caps =
-            grl_metadata_source_get_caps (_source, GRL_OP_RESOLVE);
+          grl_source_get_caps (GRL_SOURCE (_source), GRL_OP_RESOLVE);
 
         /* we only keep the options that make sense for _source/resolve */
         grl_operation_options_obey_caps (ctl_info->options, source_caps, &resolve_options, NULL);
@@ -1234,8 +1458,8 @@ metadata_full_resolution_done_cb (GrlMetadataSource *source,
 
   /* Check if pending resolutions must be cancelled */
   if (!cb_info->cancelled &&
-      grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (cb_info->source),
-                                                  cb_info->ctl_info->metadata_id)) {
+      grl_source_operation_is_cancelled (GRL_SOURCE (cb_info->source),
+                                         cb_info->ctl_info->metadata_id)) {
     cb_info->cancelled = TRUE;
     g_hash_table_foreach (cb_info->pending_callbacks, cancel_resolve, NULL);
   }
@@ -1244,8 +1468,8 @@ metadata_full_resolution_done_cb (GrlMetadataSource *source,
     GError *_error = NULL;
     g_hash_table_unref (cb_info->pending_callbacks);
 
-    if (grl_metadata_source_operation_is_cancelled (GRL_METADATA_SOURCE (cb_info->source),
-                                                    cb_info->ctl_info->metadata_id)) {
+    if (grl_source_operation_is_cancelled (GRL_SOURCE (cb_info->source),
+                                           cb_info->ctl_info->metadata_id)) {
       /* if the plugin already set an error, we don't care because we're
        * cancelled */
       _error = g_error_new (GRL_CORE_ERROR, GRL_CORE_ERROR_OPERATION_CANCELLED,
@@ -1263,8 +1487,8 @@ metadata_full_resolution_done_cb (GrlMetadataSource *source,
       g_error_free (_error);
     }
 
-    grl_metadata_source_set_operation_finished (GRL_METADATA_SOURCE (cb_info->source),
-                                                cb_info->ctl_info->metadata_id);
+    grl_source_set_operation_finished (GRL_SOURCE (cb_info->source),
+                                       cb_info->ctl_info->metadata_id);
 
     g_list_free (cb_info->ctl_info->keys);
     g_object_unref (cb_info->ctl_info->options);
@@ -1276,9 +1500,9 @@ metadata_full_resolution_done_cb (GrlMetadataSource *source,
 static void
 metadata_full_resolution_ctl_cb (GrlMediaSource *source,
                                  guint metadata_id,
-				 GrlMedia *media,
-				 gpointer user_data,
-				 const GError *error)
+                                 GrlMedia *media,
+                                 gpointer user_data,
+                                 const GError *error)
 {
   GList *sources, *iter;
   struct MetadataFullResolutionCtlCb *ctl_info =
@@ -1313,9 +1537,9 @@ metadata_full_resolution_ctl_cb (GrlMediaSource *source,
   done_info->cancelled = FALSE;
 
   sources =
-      grl_metadata_source_get_additional_sources (GRL_METADATA_SOURCE (source),
-                                                  media, ctl_info->keys,
-                                                  NULL, FALSE);
+    get_additional_sources (source,
+                            media, ctl_info->keys,
+                            NULL, FALSE);
 
   /* Use suggested sources to fill in missing metadata, the "done"
      callback will be used to emit the resulting object when all metadata has
@@ -1323,12 +1547,12 @@ metadata_full_resolution_ctl_cb (GrlMediaSource *source,
   for (iter = sources; iter; iter = g_list_next (iter)) {
     GrlMetadataSource *_source = (GrlMetadataSource *)iter->data;
     GRL_DEBUG ("Using '%s' to resolve extra metadata now",
-               grl_metadata_source_get_name (_source));
+               grl_source_get_name (GRL_SOURCE (_source)));
 
-    if (grl_metadata_source_supported_operations (_source) & GRL_OP_RESOLVE) {
+    if (grl_source_supported_operations (GRL_SOURCE (_source)) & GRL_OP_RESOLVE) {
       GrlOperationOptions *resolve_options = NULL;
       GrlCaps *source_caps =
-          grl_metadata_source_get_caps (_source, GRL_OP_RESOLVE);
+        grl_source_get_caps (GRL_SOURCE (_source), GRL_OP_RESOLVE);
       guint resolve_id;
 
 
@@ -1359,8 +1583,8 @@ metadata_full_resolution_ctl_cb (GrlMediaSource *source,
 			     ctl_info->user_data,
 			     NULL);
 
-    grl_metadata_source_set_operation_finished (GRL_METADATA_SOURCE (source),
-                                                ctl_info->metadata_id);
+    grl_source_set_operation_finished (GRL_SOURCE (source),
+                                       ctl_info->metadata_id);
 
     g_free (done_info);
   }
@@ -1377,8 +1601,7 @@ check_options (GrlMediaSource *source,
   if (grl_operation_options_get_count (options) == 0)
     return FALSE;
 
-  caps = grl_metadata_source_get_caps (GRL_METADATA_SOURCE (source),
-                                       operation);
+  caps = grl_source_get_caps (GRL_SOURCE (source), operation);
 
   return grl_operation_options_obey_caps (options, caps, NULL, NULL);
 }
@@ -1400,8 +1623,6 @@ check_options (GrlMediaSource *source,
  * This method is asynchronous.
  *
  * Returns: the operation identifier
- *
- * Since: 0.1.4
  */
 guint
 grl_media_source_browse (GrlMediaSource *source,
@@ -1424,8 +1645,8 @@ grl_media_source_browse (GrlMediaSource *source,
 
   g_return_val_if_fail (GRL_IS_MEDIA_SOURCE (source), 0);
   g_return_val_if_fail (callback != NULL, 0);
-  g_return_val_if_fail (grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
-			GRL_OP_BROWSE, 0);
+  g_return_val_if_fail (grl_source_supported_operations (GRL_SOURCE (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 */
@@ -1438,18 +1659,14 @@ grl_media_source_browse (GrlMediaSource *source,
 
   if (flags & GRL_RESOLVE_FAST_ONLY) {
     GRL_DEBUG ("requested fast keys only");
-    grl_metadata_source_filter_slow (GRL_METADATA_SOURCE (source),
-                                     &_keys,
-                                     FALSE);
+    grl_source_filter_slow (GRL_SOURCE (source), &_keys, FALSE);
   }
 
   /* Setup full resolution mode if requested */
   if (flags & GRL_RESOLVE_FULL) {
     struct FullResolutionCtlCb *c;
     GRL_DEBUG ("requested full resolution");
-    _keys =
-        grl_metadata_source_expand_operation_keys (GRL_METADATA_SOURCE (source),
-                                                   NULL, _keys);
+    _keys = expand_operation_keys (source, NULL, _keys);
 
     c = g_new0 (struct FullResolutionCtlCb, 1);
     c->user_callback = _callback;
@@ -1515,8 +1732,8 @@ grl_media_source_browse (GrlMediaSource *source,
                grl_operation_options_get_count (bs->options));
   }
 
-  grl_metadata_source_set_operation_ongoing (GRL_METADATA_SOURCE (source),
-                                             browse_id);
+  grl_source_set_operation_ongoing (GRL_SOURCE (source),
+                                    browse_id);
   g_idle_add_full (brc->use_idle? G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
                    browse_idle,
                    bs,
@@ -1600,8 +1817,6 @@ grl_media_source_browse_sync (GrlMediaSource *source,
  * This method is asynchronous.
  *
  * Returns: the operation identifier
- *
- * Since: 0.1.1
  */
 guint
 grl_media_source_search (GrlMediaSource *source,
@@ -1624,8 +1839,8 @@ grl_media_source_search (GrlMediaSource *source,
 
   g_return_val_if_fail (GRL_IS_MEDIA_SOURCE (source), 0);
   g_return_val_if_fail (callback != NULL, 0);
-  g_return_val_if_fail (grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
-			GRL_OP_SEARCH, 0);
+  g_return_val_if_fail (grl_source_supported_operations (GRL_SOURCE (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 */
@@ -1638,15 +1853,13 @@ grl_media_source_search (GrlMediaSource *source,
 
   if (flags & GRL_RESOLVE_FAST_ONLY) {
     GRL_DEBUG ("requested fast keys only");
-    grl_metadata_source_filter_slow (GRL_METADATA_SOURCE (source), &_keys, FALSE);
+    grl_source_filter_slow (GRL_SOURCE (source), &_keys, FALSE);
   }
 
   if (flags & GRL_RESOLVE_FULL) {
     struct FullResolutionCtlCb *c;
     GRL_DEBUG ("requested full search");
-    _keys =
-        grl_metadata_source_expand_operation_keys (GRL_METADATA_SOURCE (source),
-                                                   NULL, _keys);
+    _keys = expand_operation_keys (source, NULL, _keys);
 
     c = g_new0 (struct FullResolutionCtlCb, 1);
     c->user_callback = callback;
@@ -1701,8 +1914,8 @@ grl_media_source_search (GrlMediaSource *source,
                grl_operation_options_get_count (ss->options));
   }
 
-  grl_metadata_source_set_operation_ongoing (GRL_METADATA_SOURCE (source),
-                                             search_id);
+  grl_source_set_operation_ongoing (GRL_SOURCE (source),
+                                    search_id);
   g_idle_add_full (brc->use_idle? G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
                    search_idle,
                    ss,
@@ -1792,7 +2005,6 @@ grl_media_source_search_sync (GrlMediaSource *source,
  *
  * Returns: the operation identifier
  *
- * Since: 0.1.1
  */
 guint
 grl_media_source_query (GrlMediaSource *source,
@@ -1816,8 +2028,8 @@ grl_media_source_query (GrlMediaSource *source,
   g_return_val_if_fail (GRL_IS_MEDIA_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_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
-			GRL_OP_QUERY, 0);
+  g_return_val_if_fail (grl_source_supported_operations (GRL_SOURCE (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 */
@@ -1830,17 +2042,13 @@ grl_media_source_query (GrlMediaSource *source,
 
   if (flags & GRL_RESOLVE_FAST_ONLY) {
     GRL_DEBUG ("requested fast keys only");
-    grl_metadata_source_filter_slow (GRL_METADATA_SOURCE (source),
-                                     &_keys,
-                                     FALSE);
+    grl_source_filter_slow (GRL_SOURCE (source), &_keys, FALSE);
   }
 
   if (flags & GRL_RESOLVE_FULL) {
     struct FullResolutionCtlCb *c;
     GRL_DEBUG ("requested full search");
-    _keys =
-        grl_metadata_source_expand_operation_keys (GRL_METADATA_SOURCE (source),
-                                                   NULL, _keys);
+    _keys = expand_operation_keys (source, NULL, _keys);
 
     c = g_new0 (struct FullResolutionCtlCb, 1);
     c->user_callback = callback;
@@ -1895,8 +2103,8 @@ grl_media_source_query (GrlMediaSource *source,
                grl_operation_options_get_count (qs->options));
   }
 
-  grl_metadata_source_set_operation_ongoing (GRL_METADATA_SOURCE (source),
-                                             query_id);
+  grl_source_set_operation_ongoing (GRL_SOURCE (source),
+                                    query_id);
   g_idle_add_full (brc->use_idle? G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
                    query_idle,
                    qs,
@@ -1975,8 +2183,6 @@ grl_media_source_query_sync (GrlMediaSource *source,
  * This method is asynchronous.
  *
  * Returns: the operation identifier
- *
- * Since: 0.1.6
  */
 guint
 grl_media_source_metadata (GrlMediaSource *source,
@@ -1999,7 +2205,7 @@ grl_media_source_metadata (GrlMediaSource *source,
   g_return_val_if_fail (GRL_IS_MEDIA_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_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
+  g_return_val_if_fail (grl_source_supported_operations (GRL_SOURCE (source)) &
                         GRL_OP_METADATA, 0);
   g_return_val_if_fail (check_options (source, GRL_OP_METADATA, options), 0);
 
@@ -2011,8 +2217,7 @@ grl_media_source_metadata (GrlMediaSource *source,
   flags = grl_operation_options_get_flags (options);
 
   if (flags & GRL_RESOLVE_FAST_ONLY) {
-    grl_metadata_source_filter_slow (GRL_METADATA_SOURCE (source),
-                                     &_keys, FALSE);
+    grl_source_filter_slow (GRL_SOURCE (source), &_keys, FALSE);
   }
 
   metadata_id = grl_operation_generate_id ();
@@ -2020,9 +2225,7 @@ grl_media_source_metadata (GrlMediaSource *source,
   if (flags & GRL_RESOLVE_FULL) {
     struct MetadataFullResolutionCtlCb *c;
     GRL_DEBUG ("requested full metadata");
-    _keys =
-        grl_metadata_source_expand_operation_keys (GRL_METADATA_SOURCE (source),
-                                                   media, _keys);
+    _keys = expand_operation_keys (source, media, _keys);
 
       c = g_new0 (struct MetadataFullResolutionCtlCb, 1);
       c->user_callback = callback;
@@ -2065,8 +2268,8 @@ grl_media_source_metadata (GrlMediaSource *source,
      user_data so that we can free the spec there */
   mrc->spec = ms;
 
-  grl_metadata_source_set_operation_ongoing (GRL_METADATA_SOURCE (source),
-                                             metadata_id);
+  grl_source_set_operation_ongoing (GRL_SOURCE (source),
+                                    metadata_id);
   g_idle_add_full (flags & GRL_RESOLVE_IDLE_RELAY?
                    G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
                    metadata_idle,
@@ -2128,19 +2331,15 @@ grl_media_source_metadata_sync (GrlMediaSource *source,
 }
 
 static GrlSupportedOps
-grl_media_source_supported_operations (GrlMetadataSource *metadata_source)
+grl_media_source_supported_operations (GrlSource *source)
 {
-  GrlSupportedOps caps;
-  GrlMediaSource *source;
+  GrlSupportedOps caps = GRL_OP_NONE;
   GrlMediaSourceClass *media_source_class;
-  GrlMetadataSourceClass *metadata_source_class;
 
-  metadata_source_class =
-    GRL_METADATA_SOURCE_CLASS (grl_media_source_parent_class);
-  source = GRL_MEDIA_SOURCE (metadata_source);
+  g_return_val_if_fail (GRL_IS_MEDIA_SOURCE (source), GRL_OP_NONE);
+
   media_source_class = GRL_MEDIA_SOURCE_GET_CLASS (source);
 
-  caps = metadata_source_class->supported_operations (metadata_source);
   if (media_source_class->browse)
     caps |= GRL_OP_BROWSE;
   if (media_source_class->search)
@@ -2229,11 +2428,11 @@ grl_media_source_store (GrlMediaSource *source,
   g_return_if_fail (GRL_IS_MEDIA (media));
   g_return_if_fail (callback != NULL);
   g_return_if_fail ((!parent &&
-                     grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
-		     GRL_OP_STORE) ||
-		    (parent &&
-                     grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
-		     GRL_OP_STORE_PARENT));
+                     grl_source_supported_operations (GRL_SOURCE (source)) &
+                     GRL_OP_STORE) ||
+                    (parent &&
+                     grl_source_supported_operations (GRL_SOURCE (source)) &
+                     GRL_OP_STORE_PARENT));
 
   /* First, check that we have the minimum information we need */
   title = grl_media_get_title (media);
@@ -2337,8 +2536,8 @@ grl_media_source_remove (GrlMediaSource *source,
   g_return_if_fail (GRL_IS_MEDIA_SOURCE (source));
   g_return_if_fail (GRL_IS_MEDIA (media));
   g_return_if_fail (callback != NULL);
-  g_return_if_fail (grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
-		    GRL_OP_REMOVE);
+  g_return_if_fail (grl_source_supported_operations (GRL_SOURCE (source)) &
+                    GRL_OP_REMOVE);
 
   /* First, check that we have the minimum information we need */
   id = grl_media_get_id (media);
@@ -2478,7 +2677,7 @@ grl_media_source_get_media_from_uri (GrlMediaSource *source,
   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_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
+  g_return_val_if_fail (grl_source_supported_operations (GRL_SOURCE (source)) &
                         GRL_OP_MEDIA_FROM_URI, 0);
   g_return_val_if_fail (check_options (source, GRL_OP_MEDIA_FROM_URI, options), 0);
 
@@ -2487,8 +2686,7 @@ grl_media_source_get_media_from_uri (GrlMediaSource *source,
   flags = grl_operation_options_get_flags (options);
 
   if (flags & GRL_RESOLVE_FAST_ONLY) {
-    grl_metadata_source_filter_slow (GRL_METADATA_SOURCE (source),
-                                     &_keys, FALSE);
+    grl_source_filter_slow (GRL_SOURCE (source), &_keys, FALSE);
   }
 
   media_from_uri_id = grl_operation_generate_id ();
@@ -2519,8 +2717,8 @@ grl_media_source_get_media_from_uri (GrlMediaSource *source,
      user_data so that we can free the spec there */
   mfsrc->spec = mfus;
 
-  grl_metadata_source_set_operation_ongoing (GRL_METADATA_SOURCE (source),
-                                             media_from_uri_id);
+  grl_source_set_operation_ongoing (GRL_SOURCE (source),
+                                    media_from_uri_id);
   g_idle_add_full (flags & GRL_RESOLVE_IDLE_RELAY?
                    G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
                    media_from_uri_idle,
@@ -2604,7 +2802,7 @@ grl_media_source_notify_change_start (GrlMediaSource *source,
                                       GError **error)
 {
   g_return_val_if_fail (GRL_IS_MEDIA_SOURCE (source), FALSE);
-  g_return_val_if_fail (grl_media_source_supported_operations (GRL_METADATA_SOURCE (source)) &
+  g_return_val_if_fail (grl_source_supported_operations (GRL_SOURCE (source)) &
                         GRL_OP_NOTIFY_CHANGE, FALSE);
 
   return GRL_MEDIA_SOURCE_GET_CLASS (source)->notify_change_start (source,
@@ -2629,7 +2827,7 @@ grl_media_source_notify_change_stop (GrlMediaSource *source,
                                      GError **error)
 {
   g_return_val_if_fail (GRL_IS_MEDIA_SOURCE (source), FALSE);
-  g_return_val_if_fail (grl_media_source_supported_operations (GRL_METADATA_SOURCE (source)) &
+  g_return_val_if_fail (grl_source_supported_operations (GRL_SOURCE (source)) &
                         GRL_OP_NOTIFY_CHANGE, FALSE);
 
   return GRL_MEDIA_SOURCE_GET_CLASS (source)->notify_change_stop (source,
@@ -2672,7 +2870,7 @@ void grl_media_source_notify_change_list (GrlMediaSource *source,
   g_return_if_fail (changed_medias);
 
   /* Set the source */
-  source_id = grl_metadata_source_get_id (GRL_METADATA_SOURCE (source));
+  source_id = grl_source_get_id (GRL_SOURCE (source));
   g_ptr_array_foreach (changed_medias,
                        (GFunc) grl_media_set_source,
                        (gpointer) source_id);
diff --git a/src/grl-media-source.h b/src/grl-media-source.h
index ce1b85f..26ce089 100644
--- a/src/grl-media-source.h
+++ b/src/grl-media-source.h
@@ -27,8 +27,7 @@
 #ifndef _GRL_MEDIA_SOURCE_H_
 #define _GRL_MEDIA_SOURCE_H_
 
-#include <grl-media-plugin.h>
-#include <grl-metadata-source.h>
+#include <grl-source.h>
 #include <grl-data.h>
 #include <grl-media-box.h>
 #include <grl-definitions.h>
@@ -87,7 +86,7 @@ typedef struct _GrlMediaSourcePrivate GrlMediaSourcePrivate;
 
 struct _GrlMediaSource {
 
-  GrlMetadataSource parent;
+  GrlSource parent;
 
   /*< private >*/
   GrlMediaSourcePrivate *priv;
@@ -371,7 +370,7 @@ typedef struct _GrlMediaSourceClass GrlMediaSourceClass;
  */
 struct _GrlMediaSourceClass {
 
-  GrlMetadataSourceClass parent_class;
+  GrlSourceClass parent_class;
 
   void (*browse) (GrlMediaSource *source, GrlMediaSourceBrowseSpec *bs);
 
@@ -379,8 +378,6 @@ struct _GrlMediaSourceClass {
 
   void (*query) (GrlMediaSource *source, GrlMediaSourceQuerySpec *qs);
 
-  void (*cancel) (GrlMediaSource *source, guint operation_id);
-
   void (*metadata) (GrlMediaSource *source, GrlMediaSourceMetadataSpec *ms);
 
   void (*store) (GrlMediaSource *source, GrlMediaSourceStoreSpec *ss);
@@ -400,9 +397,10 @@ struct _GrlMediaSourceClass {
                                   GError **error);
 
   /*< private >*/
-  gpointer _grl_reserved[GRL_PADDING - 1];
+  gpointer _grl_reserved[GRL_PADDING];
 };
 
+
 G_BEGIN_DECLS
 
 GType grl_media_source_get_type (void);
diff --git a/src/grl-metadata-source-priv.h b/src/grl-metadata-source-priv.h
index 8911bd2..22f4411 100644
--- a/src/grl-metadata-source-priv.h
+++ b/src/grl-metadata-source-priv.h
@@ -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>
  *
@@ -23,62 +23,13 @@
 #ifndef _GRL_METADATA_SOURCE_PRIV_H_
 #define _GRL_METADATA_SOURCE_PRIV_H_
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 #include "grl-metadata-source.h"
 
 #include <glib.h>
 #include <glib-object.h>
 
-struct SourceKeyMap {
-  GrlMetadataSource *source;
-  GList *keys;
-};
-
-struct SourceKeyMapList {
-  GHashTable *source_maps;
-  GList *operation_keys;
-};
-
 G_BEGIN_DECLS
 
-GList * grl_metadata_source_expand_operation_keys (GrlMetadataSource *source,
-                                                   GrlMedia *media,
-                                                   GList *keys);
-
-GList * grl_metadata_source_get_additional_sources (GrlMetadataSource *source,
-                                                    GrlMedia *media,
-                                                    GList *keys,
-                                                    GList **additional_keys,
-                                                    gboolean main_source_is_only_resolver);
-
-guint grl_metadata_source_gen_operation_id (GrlMetadataSource *source);
-
-void grl_metadata_source_set_operation_finished (GrlMetadataSource *source,
-                                                 guint operation_id);
-
-gboolean grl_metadata_source_operation_is_finished (GrlMetadataSource *source,
-                                                    guint operation_id);
-
-void grl_metadata_source_set_operation_completed (GrlMetadataSource *source,
-                                                  guint operation_id);
-
-gboolean grl_metadata_source_operation_is_completed (GrlMetadataSource *source,
-                                                    guint operation_id);
-
-void grl_metadata_source_set_operation_cancelled (GrlMetadataSource *source,
-                                                  guint operation_id);
-
-gboolean grl_metadata_source_operation_is_cancelled (GrlMetadataSource *source,
-                                                     guint operation_id);
-
-void grl_metadata_source_set_operation_ongoing (GrlMetadataSource *source,
-                                                guint operation_id);
-
-gboolean grl_metadata_source_operation_is_ongoing (GrlMetadataSource *source,
-                                                   guint operation_id);
 
 G_END_DECLS
 
diff --git a/src/grl-metadata-source.c b/src/grl-metadata-source.c
index e88f8d6..03f1783 100644
--- a/src/grl-metadata-source.c
+++ b/src/grl-metadata-source.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>
  *
@@ -23,7 +23,7 @@
 /**
  * SECTION:grl-metadata-source
  * @short_description: Abstract base class for metadata providers
- * @see_also: #GrlMediaPlugin, #GrlMediaSource, #GrlMedia
+ * @see_also: #GrlPlugin, #GrlSource, #GrlMediaSource, #GrlMedia
  *
  * GrlMetadataSource is the abstract base class needed to construct a
  * source of metadata that can be used in a Grilo application.
@@ -44,9 +44,9 @@
  */
 
 #include "grl-metadata-source.h"
-#include "grl-metadata-source-priv.h"
 #include "grl-operation.h"
 #include "grl-operation-priv.h"
+#include "grl-source-priv.h"
 #include "grl-sync-priv.h"
 #include "grl-plugin-registry.h"
 #include "grl-error.h"
@@ -58,22 +58,9 @@
 #define GRL_LOG_DOMAIN_DEFAULT  metadata_source_log_domain
 GRL_LOG_DOMAIN(metadata_source_log_domain);
 
-#define GRL_METADATA_SOURCE_GET_PRIVATE(object)                 \
-  (G_TYPE_INSTANCE_GET_PRIVATE((object),                        \
-                               GRL_TYPE_METADATA_SOURCE,        \
-                               GrlMetadataSourcePrivate))
-
-enum {
-  PROP_0,
-  PROP_ID,
-  PROP_NAME,
-  PROP_DESC
-};
-
-struct _GrlMetadataSourcePrivate {
-  gchar *id;
-  gchar *name;
-  gchar *desc;
+struct SourceKeyMap {
+  GrlMetadataSource *source;
+  GList *keys;
 };
 
 struct ResolveRelayCb {
@@ -95,189 +82,29 @@ struct SetMetadataCtlCb {
   GList *specs;
 };
 
-struct OperationState {
-  GrlMetadataSource *source;
-  guint              operation_id;
-
-  gboolean cancelled;
-  gboolean completed;
-};
-
-static void grl_metadata_source_finalize (GObject *plugin);
-static void grl_metadata_source_get_property (GObject *plugin,
-                                              guint prop_id,
-                                              GValue *value,
-                                              GParamSpec *pspec);
-static void grl_metadata_source_set_property (GObject *object,
-                                              guint prop_id,
-                                              const GValue *value,
-                                              GParamSpec *pspec);
-
-static GrlSupportedOps grl_metadata_source_supported_operations_impl (GrlMetadataSource *source);
+static GrlSupportedOps grl_metadata_source_supported_operations (GrlSource *source);
 
 /* ================ GrlMetadataSource GObject ================ */
 
 G_DEFINE_ABSTRACT_TYPE (GrlMetadataSource,
                         grl_metadata_source,
-                        GRL_TYPE_MEDIA_PLUGIN);
+                        GRL_TYPE_SOURCE);
 
 static void
 grl_metadata_source_class_init (GrlMetadataSourceClass *metadata_source_class)
 {
-  GObjectClass *gobject_class;
-
-  gobject_class = G_OBJECT_CLASS (metadata_source_class);
-
-  gobject_class->finalize = grl_metadata_source_finalize;
-  gobject_class->set_property = grl_metadata_source_set_property;
-  gobject_class->get_property = grl_metadata_source_get_property;
-
-  metadata_source_class->supported_operations =
-    grl_metadata_source_supported_operations_impl;
-
-  /**
-   * GrlMetadataSource:source-id
-   *
-   * The identifier of the source.
-   */
-  g_object_class_install_property (gobject_class,
-				   PROP_ID,
-				   g_param_spec_string ("source-id",
-							"Source identifier",
-							"The identifier of the source",
-							"",
-							G_PARAM_READWRITE |
-							G_PARAM_CONSTRUCT |
-							G_PARAM_STATIC_STRINGS));
-  /**
-   * GrlMetadataSource:source-name
-   *
-   * The name of the source.
-   */
-  g_object_class_install_property (gobject_class,
-				   PROP_NAME,
-				   g_param_spec_string ("source-name",
-							"Source name",
-							"The name of the source",
-							"",
-							G_PARAM_READWRITE |
-							G_PARAM_CONSTRUCT |
-							G_PARAM_STATIC_STRINGS));
-  /**
-   * GrlMetadataSource:source-desc
-   *
-   * A description of the source
-   */
-  g_object_class_install_property (gobject_class,
-				   PROP_DESC,
-				   g_param_spec_string ("source-desc",
-							"Source description",
-							"A description of the source",
-							"",
-							G_PARAM_READWRITE |
-							G_PARAM_CONSTRUCT |
-							G_PARAM_STATIC_STRINGS));
-
-  g_type_class_add_private (metadata_source_class,
-                            sizeof (GrlMetadataSourcePrivate));
-}
-
-static void
-grl_metadata_source_init (GrlMetadataSource *source)
-{
-  source->priv = GRL_METADATA_SOURCE_GET_PRIVATE (source);
-}
-
-static void
-grl_metadata_source_finalize (GObject *object)
-{
-  GrlMetadataSource *source;
-
-  GRL_DEBUG ("grl_metadata_source_finalize");
-
-  source = GRL_METADATA_SOURCE (object);
-
-  g_free (source->priv->id);
-  g_free (source->priv->name);
-  g_free (source->priv->desc);
-
-  G_OBJECT_CLASS (grl_metadata_source_parent_class)->finalize (object);
-}
-
-static void
-set_string_property (gchar **property, const GValue *value)
-{
-  if (*property) {
-    g_free (*property);
-  }
-  *property = g_value_dup_string (value);
-}
-
-static void
-grl_metadata_source_set_property (GObject *object,
-                                  guint prop_id,
-                                  const GValue *value,
-                                  GParamSpec *pspec)
-{
-  GrlMetadataSource *source;
+  GrlSourceClass *source_class = GRL_SOURCE_CLASS (metadata_source_class);
 
-  source = GRL_METADATA_SOURCE (object);
-
-  switch (prop_id) {
-  case PROP_ID:
-    set_string_property (&source->priv->id, value);
-    break;
-  case PROP_NAME:
-    set_string_property (&source->priv->name, value);
-    break;
-  case PROP_DESC:
-    set_string_property (&source->priv->desc, value);
-    break;
-  default:
-    G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec);
-    break;
-  }
+  source_class->supported_operations = grl_metadata_source_supported_operations;
 }
 
 static void
-grl_metadata_source_get_property (GObject *object,
-                                  guint prop_id,
-                                  GValue *value,
-                                  GParamSpec *pspec)
+grl_metadata_source_init (GrlMetadataSource *source)
 {
-  GrlMetadataSource *source;
-
-  source = GRL_METADATA_SOURCE (object);
-
-  switch (prop_id) {
-  case PROP_ID:
-    g_value_set_string (value, source->priv->id);
-    break;
-  case PROP_NAME:
-    g_value_set_string (value, source->priv->name);
-    break;
-  case PROP_DESC:
-    g_value_set_string (value, source->priv->desc);
-    break;
-  default:
-    G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec);
-    break;
-  }
 }
 
 /* ================ Utilities ================ */
 
-static void __attribute__ ((unused))
-print_keys (gchar *label, const GList *keys)
-{
-  g_print ("%s: [", label);
-  while (keys) {
-    g_print (" %" GRL_KEYID_FORMAT, GRLPOINTER_TO_KEYID (keys->data));
-    keys = g_list_next (keys);
-  }
-  g_print (" ]\n");
-}
-
 static void
 free_set_metadata_ctl_cb_info (struct SetMetadataCtlCb *data)
 {
@@ -361,8 +188,8 @@ resolve_result_relay_cb (GrlMetadataSource *source,
 
   rrc = (struct ResolveRelayCb *) user_data;
 
-  if (grl_metadata_source_operation_is_cancelled (source,
-                                                  rrc->spec->resolve_id)) {
+  if (grl_source_operation_is_cancelled (GRL_SOURCE (source),
+                                         rrc->spec->resolve_id)) {
     /* if the plugin already set an error, we don't care because we're
      * cancelled */
     _error = g_error_new (GRL_CORE_ERROR, GRL_CORE_ERROR_OPERATION_CANCELLED,
@@ -379,7 +206,7 @@ resolve_result_relay_cb (GrlMetadataSource *source,
     g_error_free (_error);
   }
 
-  grl_metadata_source_set_operation_finished (source, rrc->spec->resolve_id);
+  grl_source_set_operation_finished (GRL_SOURCE (source), rrc->spec->resolve_id);
 
   g_object_unref (rrc->spec->source);
   g_object_unref (rrc->spec->media);
@@ -480,7 +307,7 @@ analyze_keys_to_write (GrlMetadataSource *source,
      'unsupportedy_keys' holds those that must be handled by other sources */
   GList *key_list = g_list_copy (keys);
   GList *unsupported_keys =
-    grl_metadata_source_filter_writable (source, &key_list, TRUE);
+    grl_source_filter_writable (GRL_SOURCE (source), &key_list, TRUE);
 
   if (key_list) {
     map = g_new0 (struct SourceKeyMap, 1);
@@ -516,7 +343,7 @@ analyze_keys_to_write (GrlMetadataSource *source,
 
     key_list = unsupported_keys;
     unsupported_keys =
-      grl_metadata_source_filter_writable (_source, &key_list, TRUE);
+      grl_source_filter_writable (GRL_SOURCE (_source), &key_list, TRUE);
     if (!key_list) {
       continue;
     }
@@ -533,259 +360,9 @@ analyze_keys_to_write (GrlMetadataSource *source,
   return maps;
 }
 
-/*
- * This method will _intersect two key lists_:
- *
- * @keys_to_filter: user provided set we want to filter leaving only
- * the keys that intersects with the @source_keys set.
- *
- * @source_keys: the %GrlMetadataSource<!-- -->'s key set if
- * @return_filtered is %TRUE a copy of the filtered set *complement*
- * will be returned (a list of the filtered out keys).
- */
-static GList *
-filter_key_list (GrlMetadataSource *source,
-                 GList **keys_to_filter,
-                 gboolean return_filtered,
-                 GList *source_keys)
-{
-  GList *iter_keys, *found;
-  GList *in_source = NULL;
-  GList *out_source = NULL;
-
-  for (iter_keys = *keys_to_filter;
-       iter_keys;
-       iter_keys = g_list_next (iter_keys)) {
-    found = g_list_find (source_keys, iter_keys->data);
-    if (found) {
-      in_source = g_list_prepend (in_source, iter_keys->data);
-    } else {
-      if (return_filtered) {
-        out_source = g_list_prepend (out_source, iter_keys->data);
-      }
-    }
-  }
-
-  g_list_free (*keys_to_filter);
-  *keys_to_filter = g_list_reverse (in_source);
-
-  return g_list_reverse (out_source);
-}
-
-/*
- * 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.
- *
- * 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.
- */
-static GList *
-list_union (GList *original_set, GList *additional_set, GDestroyNotify free_func)
-{
-  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);
-
-    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;
-}
-
-/*
- * @data: a GrlData instance
- *
- * @deps: a list of GrlKeyID
- *
- * 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 may resolve each of these keys, without needing
- * more keys
- */
-static gboolean
-may_directly_resolve (GrlMetadataSource *source,
-                      GrlMedia *media,
-                      const GList *keys)
-{
-  const GList *iter;
-  for (iter = keys; iter; iter = g_list_next (iter)) {
-    GrlKeyID key = GRLPOINTER_TO_KEYID (iter->data);
-
-    if (!grl_metadata_source_may_resolve (source, media, key, NULL))
-      return FALSE;
-  }
-  return TRUE;
-}
-
-/*
- * 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.
- *
- * @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.
- */
-static GrlMetadataSource *
-get_additional_source_for_key (GrlMetadataSource *source,
-                               GList *sources,
-                               GrlMedia *media,
-                               GrlKeyID key,
-                               GList **additional_keys,
-                               gboolean main_source_is_only_resolver)
-{
-  GList *iter;
-
-  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);
-
-  for (iter = sources; iter; iter = g_list_next (iter)) {
-    GList *_additional_keys = NULL;
-    GrlMetadataSource *_source = (GrlMetadataSource*)iter->data;
-
-    if (_source == source)
-      continue;
-
-    if (grl_metadata_source_may_resolve (_source, media, key, &_additional_keys))
-      return _source;
-
-    if (additional_keys && _additional_keys) {
-
-      if (main_source_is_only_resolver
-          && !may_directly_resolve (source, media, _additional_keys))
-        continue;
-
-      *additional_keys = _additional_keys;
-      return _source;
-    }
-
-  }
-
-  return NULL;
-}
-
 /* ================ API ================ */
 
 /**
- * grl_metadata_source_supported_keys:
- * @source: a metadata 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
- *
- * Since: 0.1.1
- */
-const GList *
-grl_metadata_source_supported_keys (GrlMetadataSource *source)
-{
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), NULL);
-  if (GRL_METADATA_SOURCE_GET_CLASS (source)->supported_keys) {
-    return GRL_METADATA_SOURCE_GET_CLASS (source)->supported_keys (source);
-  } else {
-    return NULL;
-  }
-}
-
-/**
- * grl_metadata_source_slow_keys:
- * @source: a metadata source
- *
- * Similar to grl_metadata_source_supported_keys(), but this 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
- *
- * Since: 0.1.1
- */
-const GList *
-grl_metadata_source_slow_keys (GrlMetadataSource *source)
-{
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), NULL);
-  if (GRL_METADATA_SOURCE_GET_CLASS (source)->slow_keys) {
-    return GRL_METADATA_SOURCE_GET_CLASS (source)->slow_keys (source);
-  } else {
-    return NULL;
-  }
-}
-
-/**
- * grl_metadata_source_writable_keys:
- * @source: a metadata source
- *
- * Similar to grl_metadata_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
- *
- * Since: 0.1.4
- */
-const GList *
-grl_metadata_source_writable_keys (GrlMetadataSource *source)
-{
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), NULL);
-  if (GRL_METADATA_SOURCE_GET_CLASS (source)->writable_keys) {
-    return GRL_METADATA_SOURCE_GET_CLASS (source)->writable_keys (source);
-  } else {
-    return NULL;
-  }
-}
-
-/**
  * grl_metadata_source_may_resolve:
  * @source: a metadata source
  * @media: a media on which we want more metadata
@@ -831,13 +408,13 @@ grl_metadata_source_may_resolve (GrlMetadataSource *source,
      * during a media source operation, and we assume they are likely to return
      * all of their supported_keys() in that case. If a media source wants to
      * behave differently, it should implement may_resolve().*/
-    const GList *supported_keys = grl_metadata_source_supported_keys (source);
+    const GList *supported_keys = grl_source_supported_keys (GRL_SOURCE (source));
     ret = NULL != g_list_find ((GList *)supported_keys,
                                GRLKEYID_TO_POINTER (key_id));
   } else {
     GRL_WARNING ("Source %s does not implement may_resolve(), considering it "
                  "can't resolve %s",
-                 grl_metadata_source_get_name (source),
+                 grl_source_get_name (GRL_SOURCE (source)),
                  GRL_METADATA_KEY_GET_NAME (key_id));
     ret = FALSE;
   }
@@ -883,7 +460,7 @@ grl_metadata_source_resolve (GrlMetadataSource *source,
   g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), 0);
   g_return_val_if_fail (callback != NULL, 0);
   g_return_val_if_fail (media != NULL, 0);
-  g_return_val_if_fail (grl_metadata_source_supported_operations (source) &
+  g_return_val_if_fail (grl_source_supported_operations (GRL_SOURCE (source)) &
                         GRL_OP_RESOLVE, 0);
 
   _keys = g_list_copy ((GList *) keys);
@@ -891,7 +468,7 @@ grl_metadata_source_resolve (GrlMetadataSource *source,
   flags = grl_operation_options_get_flags (options);
 
   if (flags & GRL_RESOLVE_FAST_ONLY) {
-    grl_metadata_source_filter_slow (source, &_keys, FALSE);
+    grl_source_filter_slow (GRL_SOURCE (source), &_keys, FALSE);
   }
 
   resolve_id = grl_operation_generate_id ();
@@ -916,7 +493,7 @@ grl_metadata_source_resolve (GrlMetadataSource *source,
      user_data so that we can free the spec there */
   rrc->spec = rs;
 
-  grl_metadata_source_set_operation_ongoing (source, resolve_id);
+  grl_source_set_operation_ongoing (GRL_SOURCE (source), resolve_id);
   g_idle_add_full (flags & GRL_RESOLVE_IDLE_RELAY?
                    G_PRIORITY_DEFAULT_IDLE: G_PRIORITY_HIGH_IDLE,
                    resolve_idle,
@@ -978,286 +555,6 @@ grl_metadata_source_resolve_sync (GrlMetadataSource *source,
 }
 
 /**
- * grl_metadata_source_filter_supported:
- * @source: a metadata 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 unsupported keys
- *
- * Compares the received @keys list with the supported key list by the
- * metadata @source, and deletes those keys which are not supported.
- *
- * Returns: (element-type GrlKeyID) (transfer container):
- * if @return_filtered is %TRUE will return the list of removed keys;
- * otherwise %NULL
- *
- * Since: 0.1.1
- */
-GList *
-grl_metadata_source_filter_supported (GrlMetadataSource *source,
-                                      GList **keys,
-                                      gboolean return_filtered)
-{
-  const GList *supported_keys;
-
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), NULL);
-
-  supported_keys = grl_metadata_source_supported_keys (source);
-
-  return filter_key_list (source, keys, return_filtered, (GList *) supported_keys);
-}
-
-/**
- * grl_metadata_source_filter_slow:
- * @source: a metadata 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 slow keys
- *
- * This function does the opposite of other filter functions: removes the slow
- * keys from @keys. If @return_filtered is %TRUE the removed slow keys are
- * returned in a new list.
- *
- * Returns: (element-type GrlKeyID) (transfer container): if
- * @return_filtered is %TRUE will return the list of slow keys; otherwise
- * %NULL
- *
- * Since: 0.1.1
- */
-GList *
-grl_metadata_source_filter_slow (GrlMetadataSource *source,
-                                 GList **keys,
-                                 gboolean return_filtered)
-{
-  const GList *slow_keys;
-  GList *fastest_keys, *tmp;
-
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), NULL);
-
-  slow_keys = grl_metadata_source_slow_keys (source);
-
-  /* Note that we want to do the opposite */
-  fastest_keys = filter_key_list (source, keys, TRUE, (GList *) slow_keys);
-  tmp = *keys;
-  *keys = fastest_keys;
-
-  if (!return_filtered) {
-    g_list_free (tmp);
-    return NULL;
-  } else {
-    return tmp;
-  }
-}
-
-/**
- * grl_metadata_source_filter_writable:
- * @source: a metadata 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_metadata_source_filter_supported() but applied to
- * the writable keys in grl_metadata_source_writable_keys().
- *
- * Filter the @keys list keeping only those keys that are writtable in
- * @source. If @return_filtered is %TRUE then the removed keys are returned in a
- * new list.
- *
- * Returns: (element-type GrlKeyID) (transfer container):
- * if @return_filtered is %TRUE will return the list of non-writtable keys;
- * otherwise %NULL
- *
- * Since: 0.1.4
- */
-GList *
-grl_metadata_source_filter_writable (GrlMetadataSource *source,
-				     GList **keys,
-				     gboolean return_filtered)
-{
-  const GList *writable_keys;
-
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), NULL);
-  g_return_val_if_fail (keys != NULL, NULL);
-
-  writable_keys = grl_metadata_source_writable_keys (source);
-
-  return filter_key_list (source, keys, return_filtered, (GList *) writable_keys);
-}
-
-/**
- * grl_metadata_source_expand_operation_keys: (skip)
- *
- * 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.
- */
-GList *
-grl_metadata_source_expand_operation_keys (GrlMetadataSource *source,
-                                           GrlMedia *media,
-                                           GList *keys)
-{
-  const GList *iter;
-  GList *remaining_keys = NULL,
-        *additional_keys = NULL,
-        *sources;
-
-  GRL_DEBUG ("grl_metadata_source_expand_operation_keys");
-
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), NULL);
-  if (!keys)
-    return NULL;
-
-  /* Ask @source about what it thinks it can resolve, to predict what we will
-   * have to ask from other sources.
-   */
-  for (iter = keys; iter; iter = g_list_next (iter)) {
-    GrlKeyID key = GRLPOINTER_TO_KEYID (iter->data);
-    if (grl_metadata_source_may_resolve (source, media, key, NULL)) {
-      GRL_INFO ("We (%s) can resolve %s",
-                 grl_metadata_source_get_name (source),
-                 GRL_METADATA_KEY_GET_NAME (key));
-    } else {
-      remaining_keys = g_list_append (remaining_keys, iter->data);
-    }
-  }
-
-  /* now, for each of the remaining 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 =
-      grl_metadata_source_get_additional_sources (source, media, remaining_keys,
-                                                  &additional_keys, TRUE);
-  g_list_free (sources);
-
-  keys = list_union (keys, additional_keys, NULL);
-
-  return keys;
-}
-
-/**
- * grl_metadata_source_get_additional_sources: (skip)
- *
- * 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.
- */
-GList *
-grl_metadata_source_get_additional_sources (GrlMetadataSource *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 = GRLPOINTER_TO_KEYID (iter->data);
-    GrlMetadataSource *_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_metadata_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);
-}
-
-/**
- * grl_metadata_source_get_id:
- * @source: a metadata source
- *
- * Returns: the ID of the @source
- *
- * Since: 0.1.1
- */
-const gchar *
-grl_metadata_source_get_id (GrlMetadataSource *source)
-{
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), NULL);
-
-  return source->priv->id;
-}
-
-/**
- * grl_metadata_source_get_name:
- * @source: a metadata source
- *
- * Returns: the name of the @source
- *
- * Since: 0.1.1
- */
-const gchar *
-grl_metadata_source_get_name (GrlMetadataSource *source)
-{
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), NULL);
-
-  return source->priv->name;
-}
-
-/**
- * grl_metadata_source_get_description:
- * @source: a metadata source
- *
- * Returns: the description of the @source
- *
- * Since: 0.1.1
- */
-const gchar *
-grl_metadata_source_get_description (GrlMetadataSource *source)
-{
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), NULL);
-
-  return source->priv->desc;
-}
-
-/**
  * grl_metadata_source_set_metadata:
  * @source: a metadata source
  * @media: the #GrlMedia object that we want to operate on.
@@ -1294,8 +591,8 @@ grl_metadata_source_set_metadata (GrlMetadataSource *source,
   g_return_if_fail (GRL_IS_METADATA_SOURCE (source));
   g_return_if_fail (media != NULL);
   g_return_if_fail (keys != NULL);
-  g_return_if_fail (grl_metadata_source_supported_operations (source) &
-		    GRL_OP_SET_METADATA);
+  g_return_if_fail (grl_source_supported_operations (GRL_SOURCE (source)) &
+                    GRL_OP_SET_METADATA);
 
   keymaps = analyze_keys_to_write (source, keys, flags, &failed_keys);
   if (!keymaps) {
@@ -1383,259 +680,15 @@ grl_metadata_source_set_metadata_sync (GrlMetadataSource *source,
   return failed;
 }
 
-/**
- * grl_metadata_source_supported_operations:
- * @source: a metadata source
- *
- * By default the derived objects of #GrlMetadataSource can only resolve.
- *
- * Returns: (type uint): a bitwise mangle with the supported operations by
- * the source
- *
- * Since: 0.1.1
- */
-GrlSupportedOps
-grl_metadata_source_supported_operations (GrlMetadataSource *source)
-{
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), GRL_OP_NONE);
-  return GRL_METADATA_SOURCE_GET_CLASS (source)->supported_operations (source);
-}
-
 static GrlSupportedOps
-grl_metadata_source_supported_operations_impl (GrlMetadataSource *source)
+grl_metadata_source_supported_operations (GrlSource *source)
 {
   GrlSupportedOps caps = GRL_OP_NONE;
-  GrlMetadataSourceClass *metadata_source_class;
-
-  g_return_val_if_fail (GRL_IS_METADATA_SOURCE (source), caps);
+  GrlMetadataSourceClass *metadata_source_class = GRL_METADATA_SOURCE_GET_CLASS (source);
 
-  metadata_source_class = GRL_METADATA_SOURCE_GET_CLASS (source);
   if (metadata_source_class->resolve)
     caps |= GRL_OP_RESOLVE;
   if (metadata_source_class->set_metadata)
     caps |= GRL_OP_SET_METADATA;
   return caps;
 }
-
-/*
- * 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_metadata_source_set_operation_finished:
- *
- * Sets operation as finished (we have already emitted the last result
- * to the user).
- */
-void
-grl_metadata_source_set_operation_finished (GrlMetadataSource *source,
-                                            guint operation_id)
-{
-  GRL_DEBUG ("grl_metadata_source_set_operation_finished (%d)", operation_id);
-
-  grl_operation_remove (operation_id);
-}
-
-/*
- * grl_metadata_source_operation_is_finished:
- *
- * Checks if operation is finished (we have already emitted the last
- * result to the user).
- */
-gboolean
-grl_metadata_source_operation_is_finished (GrlMetadataSource *source,
-                                           guint operation_id)
-{
-  struct OperationState *op_state;
-
-  op_state = grl_operation_get_private_data (operation_id);
-
-  return op_state == NULL;
-}
-
-/*
- * grl_metadata_source_set_operation_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_metadata_source_set_operation_completed (GrlMetadataSource *source,
-                                             guint operation_id)
-{
-  struct OperationState *op_state;
-
-  GRL_DEBUG ("grl_metadata_source_set_operation_completed (%d)", operation_id);
-
-  op_state = grl_operation_get_private_data (operation_id);
-
-  if (op_state) {
-    op_state->completed = TRUE;
-  }
-}
-
-/*
- * grl_metadata_source_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_metadata_source_operation_is_completed (GrlMetadataSource *source,
-                                            guint operation_id)
-{
-  struct OperationState *op_state;
-
-  op_state = grl_operation_get_private_data (operation_id);
-
-  return !op_state || op_state->completed;
-}
-
-/*
- * grl_metadata_source_set_operation_cancelled:
- *
- * Sets the operation as cancelled (a valid operation, i.e., not
- * finished, was cancelled)
- */
-void
-grl_metadata_source_set_operation_cancelled (GrlMetadataSource *source,
-                                             guint operation_id)
-{
-  struct OperationState *op_state;
-
-  GRL_DEBUG ("grl_metadata_source_set_operation_cancelled (%d)", operation_id);
-
-  op_state = grl_operation_get_private_data (operation_id);
-
-  if (op_state) {
-    op_state->cancelled = TRUE;
-  }
-}
-
-
-/*
- * grl_metadata_source_operation_is_cancelled:
- *
- * Checks if operation is cancelled (a valid operation that was
- * cancelled).
- */
-gboolean
-grl_metadata_source_operation_is_cancelled (GrlMetadataSource *source,
-                                            guint operation_id)
-{
-  struct OperationState *op_state;
-
-  op_state = grl_operation_get_private_data (operation_id);
-
-  return op_state && op_state->cancelled;
-}
-
-static void
-grl_metadata_source_cancel_cb (struct OperationState *op_state)
-{
-  GrlMetadataSource *source = op_state->source;
-
-  if (!grl_metadata_source_operation_is_ongoing (source,
-                                                 op_state->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) */
-  grl_metadata_source_set_operation_cancelled (source,
-                                               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 */
-  if (GRL_METADATA_SOURCE_GET_CLASS (source)->cancel) {
-    GRL_METADATA_SOURCE_GET_CLASS (source)->cancel (source,
-                                                    op_state->operation_id);
-  }
-}
-
-/*
- * grl_metadata_source_set_operation_ongoing:
- *
- * Sets the operation as ongoing (operation is valid, not finished and
- * not cancelled)
- */
-void
-grl_metadata_source_set_operation_ongoing (GrlMetadataSource *source,
-                                           guint operation_id)
-{
-  struct OperationState *op_state;
-
-  GRL_DEBUG ("set_operation_ongoing (%d)", operation_id);
-
-  op_state = g_new0 (struct OperationState, 1);
-  op_state->source       = source;
-  op_state->operation_id = operation_id;
-
-  grl_operation_set_private_data (operation_id,
-                                  op_state,
-                                  (GrlOperationCancelCb) grl_metadata_source_cancel_cb,
-                                  g_free);
-}
-
-/*
- * grl_metadata_source_operation_is_ongoing:
- *
- * Checks if operation is ongoing (operation is valid, and it is not
- * finished nor cancelled).
- */
-gboolean
-grl_metadata_source_operation_is_ongoing (GrlMetadataSource *source,
-                                          guint operation_id)
-{
-  struct OperationState *op_state;
-
-  op_state = grl_operation_get_private_data (operation_id);
-
-  return op_state && !op_state->cancelled;
-}
-
-/**
- * grl_metadata_source_get_caps:
- * @source: a metadata source
- * @operation: a supported operation. Even though the type allows to specify
- * several operations, only one should be provided here.
- *
- *
- * Get the capabilities of @source for @operation.
- *
- * Returns: (transfer none): The capabilities
- */
-GrlCaps *
-grl_metadata_source_get_caps (GrlMetadataSource *source,
-                              GrlSupportedOps operation)
-{
-  static GrlCaps *default_caps = NULL;
-  GrlMetadataSourceClass *klass = GRL_METADATA_SOURCE_GET_CLASS (source);
-
-  if (klass->get_caps)
-    return klass->get_caps (source, operation);
-
-  if (!default_caps)
-    default_caps = grl_caps_new ();
-
-  return default_caps;
-}
diff --git a/src/grl-metadata-source.h b/src/grl-metadata-source.h
index 2702c0d..bd204c8 100644
--- a/src/grl-metadata-source.h
+++ b/src/grl-metadata-source.h
@@ -27,7 +27,7 @@
 #ifndef _GRL_METADATA_SOURCE_H_
 #define _GRL_METADATA_SOURCE_H_
 
-#include <grl-media-plugin.h>
+#include <grl-source.h>
 #include <grl-metadata-key.h>
 #include <grl-media.h>
 #include <grl-definitions.h>
@@ -35,8 +35,6 @@
 #include <glib.h>
 #include <glib-object.h>
 
-#include <grl-operation-options.h>
-
 /* Macros */
 
 #define GRL_TYPE_METADATA_SOURCE                \
@@ -74,7 +72,7 @@
  */
 typedef enum {
   GRL_WRITE_NORMAL     = 0,        /* Normal mode */
-  GRL_WRITE_FULL       = (1 << 0)  /* Try other plugins if necessary */
+  GRL_WRITE_FULL       = (1 << 0), /* Try other plugins if necessary */
 } GrlMetadataWritingFlags;
 
 /* GrlMetadataSource object */
@@ -84,7 +82,7 @@ typedef struct _GrlMetadataSourcePrivate GrlMetadataSourcePrivate;
 
 struct _GrlMetadataSource {
 
-  GrlMediaPlugin parent;
+  GrlSource parent;
 
   /*< private >*/
   GrlMetadataSourcePrivate *priv;
@@ -181,52 +179,11 @@ typedef struct {
   gpointer _grl_reserved[GRL_PADDING];
 } GrlMetadataSourceSetMetadataSpec;
 
-/**
- * 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_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.
- *
- * Bitwise flags which reflect the kind of operations that a
- * #GrlMediaPlugin supports.
- */
-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_REMOVE          = 1 << 7,
-  GRL_OP_SET_METADATA    = 1 << 8,
-  GRL_OP_MEDIA_FROM_URI  = 1 << 9,
-  GRL_OP_NOTIFY_CHANGE   = 1 << 10
-} GrlSupportedOps;
-
-/* GrlMetadataSource class */
-
 typedef struct _GrlMetadataSourceClass GrlMetadataSourceClass;
 
 /**
  * GrlMetadataSourceClass:
  * @parent_class: the parent class structure
- * @operation_id: operation identifier
- * @supported_operations: the operations that can be called
- * @supported_keys: the list of keys that can be handled
- * @slow_keys: the list of slow keys that can be fetched
- * @writable_keys: the list of keys which value can be written
  * @resolve: resolve the metadata of a given transfer object
  * @set_metadata: update metadata values for a given object in a
  * permanent fashion
@@ -234,25 +191,13 @@ typedef struct _GrlMetadataSourceClass GrlMetadataSourceClass;
  * cannot be resolved for @media, TRUE otherwise. Optionally fill @missing_keys
  * with a list of keys that would be needed to resolve. See
  * grl_metadata_source_may_resolve().
- * @cancel: cancel the current operation
- * @get_caps: the capabilities that @source supports for @operation
  *
  * Grilo MetadataSource class. Override the vmethods to implement the
  * element functionality.
  */
 struct _GrlMetadataSourceClass {
 
-  GrlMediaPluginClass parent_class;
-
-  guint operation_id;
-
-  GrlSupportedOps (*supported_operations) (GrlMetadataSource *source);
-
-  const GList * (*supported_keys) (GrlMetadataSource *source);
-
-  const GList * (*slow_keys) (GrlMetadataSource *source);
-
-  const GList * (*writable_keys) (GrlMetadataSource *source);
+  GrlSourceClass parent_class;
 
   void (*resolve) (GrlMetadataSource *source,
 		   GrlMetadataSourceResolveSpec *rs);
@@ -263,10 +208,6 @@ struct _GrlMetadataSourceClass {
   gboolean (*may_resolve) (GrlMetadataSource *source, GrlMedia *media,
                            GrlKeyID key_id, GList **missing_keys);
 
-  void (*cancel) (GrlMetadataSource *source, guint operation_id);
-
-  GrlCaps * (*get_caps) (GrlMetadataSource *source, GrlSupportedOps operation);
-
   /*< private >*/
   gpointer _grl_reserved[GRL_PADDING - 4];
 };
@@ -275,26 +216,6 @@ G_BEGIN_DECLS
 
 GType grl_metadata_source_get_type (void);
 
-GrlSupportedOps grl_metadata_source_supported_operations (GrlMetadataSource *source);
-
-const GList *grl_metadata_source_supported_keys (GrlMetadataSource *source);
-
-const GList *grl_metadata_source_slow_keys (GrlMetadataSource *source);
-
-GList *grl_metadata_source_filter_supported (GrlMetadataSource *source,
-                                             GList **keys,
-                                             gboolean return_filtered);
-
-GList *grl_metadata_source_filter_slow (GrlMetadataSource *source,
-                                        GList **keys,
-                                        gboolean return_filtered);
-
-GList *grl_metadata_source_filter_writable (GrlMetadataSource *source,
-					    GList **keys,
-					    gboolean return_filtered);
-
-const GList *grl_metadata_source_writable_keys (GrlMetadataSource *source);
-
 gboolean grl_metadata_source_may_resolve (GrlMetadataSource *source,
                                           GrlMedia *media,
                                           GrlKeyID key_id,
@@ -326,15 +247,6 @@ GList *grl_metadata_source_set_metadata_sync (GrlMetadataSource *source,
                                               GrlMetadataWritingFlags flags,
                                               GError **error);
 
-const gchar *grl_metadata_source_get_id (GrlMetadataSource *source);
-
-const gchar *grl_metadata_source_get_name (GrlMetadataSource *source);
-
-const gchar *grl_metadata_source_get_description (GrlMetadataSource *source);
-
-GrlCaps *grl_metadata_source_get_caps (GrlMetadataSource *source,
-                                       GrlSupportedOps operation);
-
 G_END_DECLS
 
 #endif /* _GRL_METADATA_SOURCE_H_ */
diff --git a/src/grl-multiple.c b/src/grl-multiple.c
index cbe27fe..32ca126 100644
--- a/src/grl-multiple.c
+++ b/src/grl-multiple.c
@@ -215,8 +215,7 @@ start_multiple_search_operation (guint search_id,
 	skip = 0;
       }
 
-      source_caps = grl_metadata_source_get_caps (GRL_METADATA_SOURCE (source),
-                                                  GRL_OP_SEARCH);
+      source_caps = grl_source_get_caps (GRL_SOURCE (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);
@@ -230,7 +229,7 @@ start_multiple_search_operation (guint search_id,
 				    msd);
 
       GRL_DEBUG ("Operation %s:%u: Searching %u items from offset %u",
-                 grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)),
+                 grl_source_get_name (GRL_SOURCE (source)),
                  id, rc->count, skip);
 
       g_object_unref (source_options);
@@ -346,7 +345,7 @@ multiple_search_cb (GrlMediaSource *source,
 
   GRL_DEBUG ("multiple:remaining == %u, source:remaining = %u (%s)",
              msd->remaining, remaining,
-             grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)));
+             grl_source_get_name (GRL_SOURCE (source)));
 
   /* Check if operation is done, that is, if all the sources involved
      in the multiple operation have emitted remaining=0 */
@@ -399,7 +398,7 @@ multiple_search_cb (GrlMediaSource *source,
        we can use this to request more */
     msd->sources_more = g_list_prepend (msd->sources_more, source);
     GRL_DEBUG ("Source %s provided all requested results",
-               grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)));
+               grl_source_get_name (GRL_SOURCE (source)));
   }
 
   /* --- Manage NULL results --- */
@@ -584,7 +583,7 @@ multiple_search_cancel_cb (struct MultipleSearchData *msd)
   ids = msd->search_ids;
   while (sources) {
     GRL_DEBUG ("cancelling operation %s:%u",
-               grl_metadata_source_get_name (GRL_METADATA_SOURCE (sources->data)),
+               grl_source_get_name (GRL_SOURCE (sources->data)),
                GPOINTER_TO_UINT (ids->data));
     grl_operation_cancel (GPOINTER_TO_INT (ids->data));
     sources = g_list_next (sources);
diff --git a/src/grl-plugin-priv.h b/src/grl-plugin-priv.h
new file mode 100644
index 0000000..74e868a
--- /dev/null
+++ b/src/grl-plugin-priv.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010-2012 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/*
+ * Protected API for GrlPlugin class
+ */
+
+#ifndef _GRL_PLUGIN_PRIV_H_
+#define _GRL_PLUGIN_PRIV_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "grl-plugin.h"
+#include "grl-plugin-registry.h"
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void grl_plugin_set_optional_info (GrlPlugin *plugin,
+                                   GHashTable *info);
+
+void grl_plugin_set_load_func (GrlPlugin *plugin,
+                               gpointer load_function);
+
+void grl_plugin_set_unload_func (GrlPlugin *plugin,
+                                 gpointer unload_function);
+
+gboolean grl_plugin_load (GrlPlugin *plugin, GList *configurations);
+
+void grl_plugin_unload (GrlPlugin *plugin);
+
+void grl_plugin_set_id (GrlPlugin *plugin,
+                        const gchar *id);
+
+void grl_plugin_set_filename (GrlPlugin *plugin,
+                              const gchar *filename);
+
+void grl_plugin_set_module (GrlPlugin *plugin,
+                            GModule *module);
+
+void grl_plugin_set_info (GrlPlugin *plugin,
+                          const gchar *key,
+                          const gchar *value);
+
+G_END_DECLS
+
+#endif /* _GRL_PLUGIN_PRIV_H_ */
diff --git a/src/grl-plugin-registry.c b/src/grl-plugin-registry.c
index 564fc34..f7fc635 100644
--- a/src/grl-plugin-registry.c
+++ b/src/grl-plugin-registry.c
@@ -24,23 +24,23 @@
 /**
  * SECTION:grl-plugin-registry
  * @short_description: Grilo plugins loader and manager
- * @see_also: #GrlMediaPlugin, #GrlMetadataSource, #GrlMediaSource
+ * @see_also: #GrlPlugin, #GrlSource, #GrlMetadataSource, #GrlMediaSource
  *
  * The registry holds the metadata of a set of plugins.
  *
  * The #GrlPluginRegistry object is a list of plugins and some functions
- * for dealing with them. Each #GrlMediaPlugin is matched 1-1 with a file
+ * for dealing with them. Each #GrlPlugin is matched 1-1 with a file
  * on disk, and may or may not be loaded a given time. There only can be
  * a single instance of #GrlPluginRegistry (singleton pattern).
  *
- * A #GrlMediaPlugin can hold several data sources (#GrlMetadataSource or
+ * A #GrlPlugin can hold several data sources (#GrlMetadataSource or
  * #GrlMediaSource), and #GrlPluginRegistry shall register each one of
  * them.
  */
 
 #include "grl-plugin-registry.h"
 #include "grl-plugin-registry-priv.h"
-#include "grl-media-plugin-priv.h"
+#include "grl-plugin-priv.h"
 #include "grl-log.h"
 #include "grl-error.h"
 
@@ -72,28 +72,20 @@ struct KeyIDHandler {
 struct _GrlPluginRegistryPrivate {
   GHashTable *configs;
   GHashTable *plugins;
-  GHashTable *plugin_infos;
   GHashTable *sources;
   GHashTable *related_keys;
   GParamSpecPool *system_keys;
   GHashTable *ranks;
   GSList *plugins_dir;
   GSList *allowed_plugins;
-  gboolean all_plugin_info_loaded;
+  gboolean all_plugins_preloaded;
   struct KeyIDHandler key_id_handler;
 };
 
 static void grl_plugin_registry_setup_ranks (GrlPluginRegistry *registry);
 
-static GList *grl_plugin_registry_load_plugin_info_directory (GrlPluginRegistry *registry,
-                                                              const gchar *path,
-                                                              GError **error);
-
-static GrlPluginInfo *grl_plugin_registry_load_plugin_info (GrlPluginRegistry *registry,
-                                                            const gchar *plugin_id,
-                                                            const gchar *file);
-
-static void grl_plugin_registry_load_plugin_info_all (GrlPluginRegistry *registry);
+static void grl_plugin_registry_preload_plugins (GrlPluginRegistry *registry,
+                                                 GList **plugins_loaded);
 
 static void key_id_handler_init (struct KeyIDHandler *handler);
 
@@ -125,9 +117,9 @@ grl_plugin_registry_class_init (GrlPluginRegistryClass *klass)
   /**
    * GrlPluginRegistry::source-added:
    * @registry: the registry
-   * @plugin: the plugin that has been added
+   * @source: the source that has been added
    *
-   * Signals that a plugin has been added to the registry.
+   * Signals that a source has been added to the registry.
    */
   registry_signals[SIG_SOURCE_ADDED] =
     g_signal_new("source-added",
@@ -137,14 +129,14 @@ grl_plugin_registry_class_init (GrlPluginRegistryClass *klass)
 		 NULL,
 		 NULL,
 		 g_cclosure_marshal_VOID__OBJECT,
-		 G_TYPE_NONE, 1, GRL_TYPE_MEDIA_PLUGIN);
+		 G_TYPE_NONE, 1, GRL_TYPE_SOURCE);
 
   /**
    * GrlPluginRegistry::source-removed:
    * @registry: the registry
-   * @plugin: the plugin that has been removed
+   * @source: the source that has been removed
    *
-   * Signals that a plugin has been removed from the registry.
+   * Signals that a source has been removed from the registry.
    */
   registry_signals[SIG_SOURCE_REMOVED] =
     g_signal_new("source-removed",
@@ -154,7 +146,7 @@ grl_plugin_registry_class_init (GrlPluginRegistryClass *klass)
 		 NULL,
 		 NULL,
 		 g_cclosure_marshal_VOID__OBJECT,
-		 G_TYPE_NONE, 1, GRL_TYPE_MEDIA_PLUGIN);
+		 G_TYPE_NONE, 1, GRL_TYPE_SOURCE);
 }
 
 static void
@@ -165,9 +157,7 @@ grl_plugin_registry_init (GrlPluginRegistry *registry)
   registry->priv->configs =
     g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
   registry->priv->plugins =
-    g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
-  registry->priv->plugin_infos =
-    g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+    g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
   registry->priv->sources =
     g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
   registry->priv->related_keys =
@@ -183,26 +173,29 @@ grl_plugin_registry_init (GrlPluginRegistry *registry)
 /* ================ Utitilies ================ */
 
 static void
-config_plugin_rank (GrlPluginRegistry *registry,
-		    const gchar *plugin_id,
-		    gint rank)
+config_source_rank (GrlPluginRegistry *registry,
+                    const gchar *source_id,
+                    gint rank)
 {
-  GRL_DEBUG ("Rank configuration, '%s:%d'", plugin_id, rank);
+  GRL_DEBUG ("Rank configuration, '%s:%d'", source_id, rank);
   g_hash_table_insert (registry->priv->ranks,
-		       (gchar *) plugin_id,
-		       GINT_TO_POINTER (rank));
+                       g_strdup (source_id),
+                       GINT_TO_POINTER (rank));
 }
 
 static void
-set_plugin_rank (GrlPluginRegistry *registry, GrlPluginInfo *info)
+set_source_rank (GrlPluginRegistry *registry, GrlSource *source)
 {
-  info->rank =
+  gint rank;
+
+  rank =
     GPOINTER_TO_INT (g_hash_table_lookup (registry->priv->ranks,
-					  info->id));
-  if (!info->rank) {
-    info->rank = GRL_PLUGIN_RANK_DEFAULT;
+                                          grl_source_get_id (source)));
+  if (!rank) {
+    rank = GRL_RANK_DEFAULT;
   }
-  GRL_DEBUG ("Plugin rank '%s' : %d", info->id, info->rank);
+  g_object_set (source, "rank", rank, NULL);
+  GRL_DEBUG ("Source rank '%s' : %d", grl_source_get_id (source), rank);
 }
 
 static void
@@ -228,13 +221,13 @@ grl_plugin_registry_setup_ranks (GrlPluginRegistry *registry)
     gchar **rank_info = g_strsplit (*iter, ":", 2);
     if (rank_info[0] && rank_info[1]) {
       gchar *tmp;
-      gchar *plugin_id = rank_info[0];
-      gchar *plugin_rank = rank_info[1];
-      gint rank = (gint) g_ascii_strtoll (plugin_rank, &tmp, 10);
+      gchar *id = rank_info[0];
+      gchar *srank = rank_info[1];
+      gint rank = (gint) g_ascii_strtoll (srank, &tmp, 10);
       if (*tmp != '\0') {
-	GRL_WARNING ("Incorrect ranking definition: '%s'. Skipping...", *iter);
+        GRL_WARNING ("Incorrect ranking definition: '%s'. Skipping...", *iter);
       } else {
-	config_plugin_rank (registry, g_strdup (plugin_id), rank);
+        config_source_rank (registry, id, rank);
       }
     } else {
       GRL_WARNING ("Incorrect ranking definition: '%s'. Skipping...", *iter);
@@ -252,8 +245,8 @@ compare_by_rank (gconstpointer a,
   gint rank_a;
   gint rank_b;
 
-  rank_a = grl_media_plugin_get_rank (GRL_MEDIA_PLUGIN (a));
-  rank_b = grl_media_plugin_get_rank (GRL_MEDIA_PLUGIN (b));
+  rank_a = grl_source_get_rank (GRL_SOURCE (a));
+  rank_b = grl_source_get_rank (GRL_SOURCE (b));
 
   return (rank_a < rank_b) - (rank_a > rank_b);
 }
@@ -308,138 +301,190 @@ get_info_from_plugin_xml (const gchar *xml_path)
   return hash_table;
 }
 
-static GrlPluginInfo *
-grl_plugin_registry_load_plugin_info (GrlPluginRegistry *registry,
-                                      const gchar* plugin_id,
-                                      const gchar *file)
+static gboolean
+activate_plugin (GrlPluginRegistry *registry,
+                 GrlPlugin *plugin,
+                 GError **error)
+{
+  gboolean is_loaded;
+  GList *plugin_configs;
+
+  /* Check if plugin is already loaded */
+  g_object_get (plugin, "loaded", &is_loaded, NULL);
+  if (is_loaded) {
+    GRL_WARNING ("Plugin is already loaded: '%s'", grl_plugin_get_id (plugin));
+    g_set_error (error,
+                 GRL_CORE_ERROR,
+                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
+                 "Plugin '%s' is already loaded", grl_plugin_get_id (plugin));
+    return FALSE;
+  }
+
+  plugin_configs = g_hash_table_lookup (registry->priv->configs,
+                                        grl_plugin_get_id (plugin));
+
+  if (!grl_plugin_load (plugin, plugin_configs)) {
+    GRL_WARNING ("Failed to initialize plugin: '%s'", grl_plugin_get_filename (plugin));
+    g_set_error (error,
+                 GRL_CORE_ERROR,
+                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
+                 "Failed to initialize plugin at '%s'", grl_plugin_get_filename (plugin));
+    g_module_close (grl_plugin_get_module (plugin));
+    grl_plugin_set_module (plugin, NULL);
+    return FALSE;
+  }
+
+  GRL_DEBUG ("Loaded plugin '%s' from '%s'",
+             grl_plugin_get_id (plugin),
+             grl_plugin_get_filename (plugin));
+
+  return TRUE;
+}
+
+static GrlPlugin *
+grl_plugin_registry_preload_plugin (GrlPluginRegistry *registry,
+                                    const gchar *dirname,
+                                    const gchar *plugin_info_filename)
 {
   GHashTable *info;
-  GrlPluginInfo *plugin_info;
-  gchar *library_filename;
-  gchar *path;
-  gchar *plugin_name;
+  GrlPlugin *plugin;
+  gchar *file;
+  gchar *suffix;
+  gchar *id;
+  const gchar *module_name;
+  gchar *module_filename;
+  gchar *module_fullpathname;
 
-  /* Load plugin information */
-  info = get_info_from_plugin_xml (file);
+  if ((suffix = g_strrstr (plugin_info_filename, "." GRL_PLUGIN_INFO_SUFFIX)) == NULL) {
+    return NULL;
+  }
 
-  if (!info) {
-    GRL_WARNING ("Invalid information file for '%s' plugin",
-                 plugin_id);
+  id = g_strndup (plugin_info_filename, suffix - plugin_info_filename);
+  /* Check if plugin is already preloaded */
+  if (g_hash_table_lookup (registry->priv->plugins, id)) {
+    GRL_DEBUG ("Plugin '%s' already preloaded; skipping", id);
+    g_free (id);
     return NULL;
   }
 
-  /* Build plugin library filename */
-  plugin_name = g_hash_table_lookup (info, GRL_PLUGIN_INFO_MODULE);
-  if (!plugin_name) {
-    GRL_WARNING ("Information about '%s' plugin has no reference to module; skipping",
-                 plugin_id);
-    g_hash_table_unref (info);
+  /* Check if plugin is allowed */
+  if (registry->priv->allowed_plugins &&
+      !g_slist_find_custom (registry->priv->allowed_plugins,
+                            id,
+                            (GCompareFunc) g_strcmp0)) {
+    GRL_DEBUG ("Plugin '%s' not allowed; skipping", id);
+    g_free (id);
     return NULL;
   }
-  plugin_name = g_strconcat (plugin_name, "." G_MODULE_SUFFIX, NULL);
-  path = g_path_get_dirname (file);
-  library_filename = g_build_filename (path, plugin_name, NULL);
-  g_free (plugin_name);
-  g_free (path);
-
-  /* Build Plugin Info */
-  plugin_info = g_new0 (GrlPluginInfo, 1);
-  plugin_info->id = g_strdup (plugin_id);
-  plugin_info->filename = library_filename;
-  plugin_info->optional_info = info;
-
-  /* Set rank */
-  set_plugin_rank (registry, plugin_info);
-
-  return plugin_info;
+
+  file = g_build_filename (dirname, plugin_info_filename, NULL);
+  info = get_info_from_plugin_xml (file);
+  g_free (file);
+
+  if (info) {
+    plugin = g_object_new (GRL_TYPE_PLUGIN, NULL);
+    grl_plugin_set_id (plugin, id);
+    grl_plugin_set_optional_info (plugin, info);
+    module_name = grl_plugin_get_info (plugin, GRL_PLUGIN_INFO_MODULE);
+    if (!module_name) {
+      GRL_WARNING ("Unknown module file for plugin with id '%s'", id);
+      g_object_unref (plugin);
+      g_free (id);
+      return NULL;
+    }
+
+    if (g_path_is_absolute (module_name)) {
+      grl_plugin_set_filename (plugin, module_name);
+    } else {
+      if (g_str_has_suffix (module_name, "." G_MODULE_SUFFIX)) {
+        module_fullpathname = g_build_filename (dirname, module_name, NULL);
+      } else {
+        module_filename = g_strconcat (module_name, "." G_MODULE_SUFFIX, NULL);
+        module_fullpathname = g_build_filename (dirname, module_filename, NULL);
+        g_free (module_filename);
+      }
+      grl_plugin_set_filename (plugin, module_fullpathname);
+      g_free (module_fullpathname);
+    }
+
+    g_hash_table_insert (registry->priv->plugins,
+                         (gchar *) grl_plugin_get_id (plugin),
+                         g_object_ref (plugin));
+  }
+  g_free (id);
+  return plugin;
 }
 
-static GList *
-grl_plugin_registry_load_plugin_info_directory (GrlPluginRegistry *registry,
-                                                const gchar *path,
-                                                GError **error)
+static void
+grl_plugin_registry_preload_plugins_directory (GrlPluginRegistry *registry,
+                                               const gchar *directory,
+                                               GList **plugins_loaded)
 {
   GDir *dir;
-  GrlPluginInfo *plugin_info;
+  GError *error = NULL;
   const gchar *entry;
-  gchar *file;
-  gchar *id;
-  gchar *suffix;
-  GList *loaded_infos = NULL;
+  GrlPlugin *plugin;
 
-  dir = g_dir_open (path, 0, NULL);
+  dir = g_dir_open (directory, 0, &error);
   if (!dir) {
-    GRL_WARNING ("Could not open plugin directory: '%s'", path);
-    g_set_error (error,
-                 GRL_CORE_ERROR,
-                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "Failed to open plugin directory '%s'", path);
-    return NULL;
+    GRL_WARNING ("Could not open plugins' info directory '%s': %s",
+                 directory,
+                 error->message);
+    g_error_free (error);
+    return;
   }
 
   while ((entry = g_dir_read_name (dir)) != NULL) {
-    if ((suffix = g_strrstr (entry, "." GRL_PLUGIN_INFO_SUFFIX)) != NULL) {
-      file = g_build_filename (path, entry, NULL);
-      id = g_strndup (entry, suffix - entry);
-      /* Skip plugin info if it is already loaded */
-      if (g_hash_table_lookup (registry->priv->plugin_infos, id)) {
-        GRL_DEBUG ("Information about '%s' plugin already loaded; skipping",
-                   id);
-        g_free (id);
-        g_free (file);
-        continue;
-      }
-      /* Check if plugin is allowed or not */
-      if (registry->priv->allowed_plugins &&
-          !g_slist_find_custom (registry->priv->allowed_plugins,
-                                id,
-                                (GCompareFunc) g_strcmp0)) {
-        GRL_DEBUG ("'%s' plugin not allowed; skipping", id);
-        continue;
-      }
-      plugin_info = grl_plugin_registry_load_plugin_info (registry, id, file);
-      g_free (id);
-      g_free (file);
-
-      g_hash_table_insert (registry->priv->plugin_infos,
-                           plugin_info->id,
-                           plugin_info);
-      loaded_infos = g_list_append (loaded_infos, plugin_info);
+    plugin = grl_plugin_registry_preload_plugin (registry, directory, entry);
+    if (plugins_loaded && plugin) {
+      *plugins_loaded = g_list_prepend (*plugins_loaded, plugin);
     }
   }
 
   g_dir_close (dir);
-  return loaded_infos;
 }
 
 static void
-grl_plugin_registry_load_plugin_info_all (GrlPluginRegistry *registry)
+grl_plugin_registry_preload_plugins (GrlPluginRegistry *registry,
+                                     GList **plugins_loaded)
 {
   GSList *plugin_dir;
-  GList *loaded_plugins;
+  GList *plugins_directory_loaded = NULL;
 
   for (plugin_dir = registry->priv->plugins_dir;
        plugin_dir;
        plugin_dir = g_slist_next (plugin_dir)) {
-    loaded_plugins =
-      grl_plugin_registry_load_plugin_info_directory (registry,
-                                                      plugin_dir->data,
-                                                      NULL);
-    g_list_free (loaded_plugins);
+    if (plugins_loaded) {
+      grl_plugin_registry_preload_plugins_directory (registry,
+                                                     plugin_dir->data,
+                                                     &plugins_directory_loaded);
+      *plugins_loaded = g_list_concat (*plugins_loaded, plugins_directory_loaded);
+      plugins_directory_loaded = NULL;
+    } else {
+      grl_plugin_registry_preload_plugins_directory (registry,
+                                                     plugin_dir->data,
+                                                     NULL);
+    }
   }
 }
 
 static gboolean
 grl_plugin_registry_load_list (GrlPluginRegistry *registry,
-                               GList *plugin_info_list)
+                               GList *plugin_list)
 {
-  GrlPluginInfo *pinfo;
+  GrlPlugin *plugin;
   gboolean loaded_one = FALSE;
 
-  while (plugin_info_list) {
-    pinfo = (GrlPluginInfo *) plugin_info_list->data;
-    loaded_one |= grl_plugin_registry_load (registry, pinfo->filename, NULL);
-    plugin_info_list = g_list_next (plugin_info_list);
+  while (plugin_list) {
+    plugin = (GrlPlugin *) plugin_list->data;
+    if (grl_plugin_get_module (plugin)) {
+      loaded_one |= activate_plugin (registry, plugin, NULL);
+    } else {
+      loaded_one |= grl_plugin_registry_load (registry,
+                                              grl_plugin_get_filename (plugin),
+                                              NULL);
+    }
+    plugin_list = g_list_next (plugin_list);
   }
 
   return loaded_one;
@@ -536,7 +581,6 @@ key_id_handler_get_all_keys (struct KeyIDHandler *handler)
   return g_hash_table_get_values (handler->string_to_id);
 }
 
-
 /* ================ PRIVATE API ================ */
 
 /**
@@ -600,7 +644,7 @@ grl_plugin_registry_get_default (void)
 /**
  * grl_plugin_registry_register_source:
  * @registry: the registry instance
- * @plugin: the descriptor of the plugin which owns the source
+ * @plugin: the plugin which owns the source
  * @source: the source to register
  * @error: error return location or @NULL to ignore
  *
@@ -612,19 +656,20 @@ grl_plugin_registry_get_default (void)
  */
 gboolean
 grl_plugin_registry_register_source (GrlPluginRegistry *registry,
-                                     const GrlPluginInfo *plugin,
-                                     GrlMediaPlugin *source,
+                                     GrlPlugin *plugin,
+                                     GrlSource *source,
                                      GError **error)
 {
   gchar *id;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), FALSE);
-  g_return_val_if_fail (GRL_IS_MEDIA_PLUGIN (source), FALSE);
+  g_return_val_if_fail (GRL_IS_PLUGIN (plugin), FALSE);
+  g_return_val_if_fail (GRL_IS_SOURCE (source), FALSE);
 
   g_object_get (source, "source-id", &id, NULL);
   GRL_DEBUG ("New source available: '%s'", id);
 
-  /* Take ownership of the plugin */
+  /* Take ownership of the source */
   g_object_ref_sink (source);
   g_object_unref (source);
 
@@ -632,7 +677,11 @@ grl_plugin_registry_register_source (GrlPluginRegistry *registry,
      it will be freed when removed from the hash table */
   g_hash_table_insert (registry->priv->sources, id, source);
 
-  grl_media_plugin_set_plugin_info (source, plugin);
+  /* Set the plugin as owner of source */
+  g_object_set (source, "plugin", plugin, NULL);
+
+  /* Set source rank */
+  set_source_rank (registry, source);
 
   g_signal_emit (registry, registry_signals[SIG_SOURCE_ADDED], 0, source);
 
@@ -653,14 +702,14 @@ grl_plugin_registry_register_source (GrlPluginRegistry *registry,
  */
 gboolean
 grl_plugin_registry_unregister_source (GrlPluginRegistry *registry,
-                                       GrlMediaPlugin *source,
+                                       GrlSource *source,
                                        GError **error)
 {
   gchar *id;
   gboolean ret = TRUE;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), FALSE);
-  g_return_val_if_fail (GRL_IS_MEDIA_PLUGIN (source), FALSE);
+  g_return_val_if_fail (GRL_IS_SOURCE (source), FALSE);
 
   g_object_get (source, "source-id", &id, NULL);
   GRL_DEBUG ("Unregistering source '%s'", id);
@@ -702,6 +751,7 @@ grl_plugin_registry_add_directory (GrlPluginRegistry *registry,
      they were added */
   registry->priv->plugins_dir = g_slist_append (registry->priv->plugins_dir,
                                                 g_strdup (path));
+  registry->priv->all_plugins_preloaded = FALSE;
 }
 
 /**
@@ -722,12 +772,11 @@ grl_plugin_registry_load (GrlPluginRegistry *registry,
                           GError **error)
 {
   GModule *module;
-  GrlPluginDescriptor *plugin;
-  GrlPluginInfo *plugin_info;
-  GList *plugin_configs;
-  gchar *dirname;
-  gchar *plugin_info_filename;
-  gchar *plugin_info_fullpathname;
+  GrlPluginDescriptor *plugin_desc;
+  GrlPlugin *plugin;
+  gchar *module_name;
+  gchar *info_dirname;
+  gchar *info_filename;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), FALSE);
 
@@ -741,7 +790,7 @@ grl_plugin_registry_load (GrlPluginRegistry *registry,
     return FALSE;
   }
 
-  if (!g_module_symbol (module, "GRL_PLUGIN_DESCRIPTOR", (gpointer) &plugin)) {
+  if (!g_module_symbol (module, "GRL_PLUGIN_DESCRIPTOR", (gpointer) &plugin_desc)) {
     GRL_WARNING ("Did not find plugin descriptor: '%s'", library_filename);
     g_set_error (error,
                  GRL_CORE_ERROR,
@@ -751,8 +800,8 @@ grl_plugin_registry_load (GrlPluginRegistry *registry,
     return FALSE;
   }
 
-  if (!plugin->plugin_init ||
-      !plugin->plugin_id) {
+  if (!plugin_desc->plugin_init ||
+      !plugin_desc->plugin_id) {
     GRL_WARNING ("Plugin descriptor is not valid: '%s'", library_filename);
     g_set_error (error,
                  GRL_CORE_ERROR,
@@ -762,82 +811,52 @@ grl_plugin_registry_load (GrlPluginRegistry *registry,
     return FALSE;
   }
 
-  /* Check if plugin is already loaded */
-  if (g_hash_table_lookup (registry->priv->plugins, plugin->plugin_id)) {
-    GRL_WARNING ("Plugin is already loaded: '%s'", library_filename);
-    g_set_error (error,
-                 GRL_CORE_ERROR,
-                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "'%s' is already loaded", library_filename);
-    g_module_close (module);
-    return FALSE;
-  }
+  /* Check if plugin is preloaded; if not, then create one */
+  plugin = g_hash_table_lookup (registry->priv->plugins,
+                                plugin_desc->plugin_id);
 
-  plugin_info = g_hash_table_lookup (registry->priv->plugin_infos,
-                                     plugin->plugin_id);
-
-  /* This happens when the user invokes directly this function: plugin
-     information has not been loaded yet */
-  if (!plugin_info) {
-    plugin_info_filename = g_strconcat (plugin->plugin_id,
-                                        "." GRL_PLUGIN_INFO_SUFFIX,
-                                        NULL);
-    dirname = g_path_get_dirname (library_filename);
-    plugin_info_fullpathname = g_build_filename (dirname,
-                                                 plugin_info_filename,
-                                                 NULL);
-    plugin_info = grl_plugin_registry_load_plugin_info (registry,
-                                                        plugin->plugin_id,
-                                                        plugin_info_fullpathname);
-    if (!plugin_info) {
-      GRL_WARNING ("Plugin '%s' does not have XML information file '%s'",
-                   plugin->plugin_id,
-                   plugin_info_fullpathname);
-      /* Create a default one */
-      plugin_info = g_new0 (GrlPluginInfo, 1);
-      plugin_info->id = g_strdup (plugin->plugin_id);
-      plugin_info->filename = g_strdup (library_filename);
-      g_hash_table_insert (registry->priv->plugin_infos,
-                           plugin_info->id,
-                           plugin_info);
-      plugin_info->optional_info =
-        g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-      g_hash_table_insert (plugin_info->optional_info,
-                           g_strdup (GRL_PLUGIN_INFO_MODULE),
-                           g_path_get_basename (plugin_info->filename));
-
-      set_plugin_rank (registry, plugin_info);
+  if (!plugin) {
+    info_dirname = g_path_get_dirname (library_filename);
+    info_filename = g_strconcat (plugin_desc->plugin_id, "." GRL_PLUGIN_INFO_SUFFIX, NULL);
+    plugin = grl_plugin_registry_preload_plugin (registry, info_dirname, info_filename);
+    g_free (info_dirname);
+    g_free (info_filename);
+    if (!plugin) {
+      g_set_error (error,
+                   GRL_CORE_ERROR,
+                   GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
+                   "Unable to load plugin '%s'", plugin_desc->plugin_id);
+      g_module_close (module);
+      return FALSE;
+    }
+  } else {
+    /* Check if the existent plugin is for a different module */
+    if (g_strcmp0 (grl_plugin_get_filename (plugin), library_filename) != 0) {
+      GRL_WARNING ("Plugin '%s' already exists", library_filename);
+      g_set_error (error,
+                   GRL_CORE_ERROR,
+                   GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
+                   "Plugin '%s' already exists", library_filename);
+      return FALSE;
     }
-    g_free (plugin_info_filename);
-    g_free (dirname);
-    g_free (plugin_info_fullpathname);
   }
 
-  g_hash_table_insert (registry->priv->plugins,
-                       (gpointer) plugin->plugin_id, plugin);
+  if (!grl_plugin_get_module (plugin)) {
+    grl_plugin_set_load_func (plugin, plugin_desc->plugin_init);
+    grl_plugin_set_unload_func (plugin, plugin_desc->plugin_deinit);
 
-  plugin_configs = g_hash_table_lookup (registry->priv->configs,
-                                        plugin->plugin_id);
+    /* Insert module name as part of plugin information */
+    module_name = g_path_get_basename (library_filename);
+    grl_plugin_set_info (plugin, GRL_PLUGIN_INFO_MODULE, module_name);
+    g_free (module_name);
 
-  if (!plugin->plugin_init (registry, plugin_info, plugin_configs)) {
-    g_hash_table_remove (registry->priv->plugins, plugin->plugin_id);
-    GRL_INFO ("Failed to initialize plugin: '%s'", library_filename);
-    g_set_error (error,
-                 GRL_CORE_ERROR,
-                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "Failed to initialize plugin at '%s'", library_filename);
-    g_module_close (module);
-    return FALSE;
-  }
+    grl_plugin_set_module (plugin, module);
 
-  /* Make plugin resident */
-  g_module_make_resident (module);
-
-  plugin->module = module;
-
-  GRL_DEBUG ("Loaded plugin '%s' from '%s'", plugin->plugin_id, library_filename);
+    /* Make plugin resident */
+    g_module_make_resident (module);
+  }
 
-  return TRUE;
+  return activate_plugin (registry, plugin, error);
 }
 
 /**
@@ -858,20 +877,18 @@ grl_plugin_registry_load_directory (GrlPluginRegistry *registry,
                                     const gchar *path,
                                     GError **error)
 {
-  GList *plugin_infos;
+  GList *preloaded_plugins = NULL;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), FALSE);
 
-  /* Load plugins information */
-  plugin_infos = grl_plugin_registry_load_plugin_info_directory (registry,
-                                                                 path,
-                                                                 error);
+  /* Preload plugins */
+  grl_plugin_registry_preload_plugins_directory (registry, path, &preloaded_plugins);
 
   /* Load the plugins */
-  if (!grl_plugin_registry_load_list (registry, plugin_infos)) {
+  if (!grl_plugin_registry_load_list (registry, preloaded_plugins)) {
     GRL_WARNING ("No plugins loaded from directory '%s'", path);
   }
-  g_list_free (plugin_infos);
+  g_list_free (preloaded_plugins);
 
   return TRUE;
 }
@@ -895,22 +912,22 @@ grl_plugin_registry_load_directory (GrlPluginRegistry *registry,
 gboolean
 grl_plugin_registry_load_all (GrlPluginRegistry *registry, GError **error)
 {
-  GList *all_plugin_infos;
+  GList *all_plugins;
   gboolean loaded_one;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), TRUE);
 
-  /* Preload all plugin infos */
-  if (!registry->priv->all_plugin_info_loaded) {
-    grl_plugin_registry_load_plugin_info_all (registry);
-    registry->priv->all_plugin_info_loaded = TRUE;
+  /* Preload all plugins */
+  if (!registry->priv->all_plugins_preloaded) {
+    grl_plugin_registry_preload_plugins (registry, NULL);
+    registry->priv->all_plugins_preloaded = TRUE;
   }
 
   /* Now load all plugins */
-  all_plugin_infos = g_hash_table_get_values (registry->priv->plugin_infos);
-  loaded_one = grl_plugin_registry_load_list (registry, all_plugin_infos);
+  all_plugins = g_hash_table_get_values (registry->priv->plugins);
+  loaded_one = grl_plugin_registry_load_list (registry, all_plugins);
 
-  g_list_free (all_plugin_infos);
+  g_list_free (all_plugins);
 
   if (!loaded_one) {
     g_set_error (error,
@@ -944,31 +961,43 @@ grl_plugin_registry_load_by_id (GrlPluginRegistry *registry,
                                 const gchar *plugin_id,
                                 GError **error)
 {
-  GrlPluginInfo *plugin_info;
+  GrlPlugin *plugin;
+  gboolean is_loaded;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), FALSE);
+  g_return_val_if_fail (plugin_id, FALSE);
+
 
-  /* Check if there is information loaded */
-  plugin_info = g_hash_table_lookup (registry->priv->plugin_infos, plugin_id);
-    /* Maybe we need to load all plugins before */
-  if (!plugin_info &&
-      !registry->priv->all_plugin_info_loaded) {
-    grl_plugin_registry_load_plugin_info_all (registry);
-    registry->priv->all_plugin_info_loaded = TRUE;
-    /* Search again */
-    plugin_info = g_hash_table_lookup (registry->priv->plugin_infos, plugin_id);
+  /* Preload all plugins */
+  if (!registry->priv->all_plugins_preloaded) {
+    grl_plugin_registry_preload_plugins (registry, NULL);
+    registry->priv->all_plugins_preloaded = FALSE;
   }
 
-  if (!plugin_info) {
-    GRL_WARNING ("There is no information about a plugin with id '%s'", plugin_id);
+  /* Check if plugin is available */
+  plugin = g_hash_table_lookup (registry->priv->plugins, plugin_id);
+  if (!plugin) {
+    GRL_WARNING ("Plugin '%s' not available", plugin_id);
+    g_set_error (error,
+                 GRL_CORE_ERROR,
+                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
+                 "Plugin '%s' not available", plugin_id);
+    return FALSE;
+  }
+
+  /* Check if plugin is already loaded */
+  g_object_get (plugin, "loaded", &is_loaded, NULL);
+  if (is_loaded) {
+    GRL_WARNING ("Plugin '%s' is already loaded", plugin_id);
     g_set_error (error,
                  GRL_CORE_ERROR,
                  GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
-                 "There is no information about a plugin with id '%s'", plugin_id);
+                 "Plugin '%s' is already loaded", plugin_id);
     return FALSE;
   }
 
-  return grl_plugin_registry_load (registry, plugin_info->filename, error);
+  /* Load plugin */
+  return grl_plugin_registry_load (registry, grl_plugin_get_filename (plugin), error);
 }
 
 /**
@@ -982,14 +1011,15 @@ grl_plugin_registry_load_by_id (GrlPluginRegistry *registry,
  *
  * Since: 0.1.1
  */
-GrlMediaPlugin *
+GrlSource *
 grl_plugin_registry_lookup_source (GrlPluginRegistry *registry,
                                    const gchar *source_id)
 {
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), NULL);
   g_return_val_if_fail (source_id != NULL, NULL);
-  return (GrlMediaPlugin *) g_hash_table_lookup (registry->priv->sources,
-                                                 source_id);
+
+  return (GrlSource *) g_hash_table_lookup (registry->priv->sources,
+                                            source_id);
 }
 
 /**
@@ -1001,8 +1031,8 @@ grl_plugin_registry_lookup_source (GrlPluginRegistry *registry,
  *
  * If @ranked is %TRUE, the source list will be ordered by rank.
  *
- * Returns: (element-type Grl.MediaPlugin) (transfer container): a #GList of
- * available #GrlMediaPlugins<!-- -->s. The content of the list should not be
+ * Returns: (element-type Grl.Source) (transfer container): a #GList of
+ * available #GrlSource<!-- -->s. The content of the list should not be
  * modified or freed. Use g_list_free() when done using the list.
  *
  * Since: 0.1.7
@@ -1013,13 +1043,13 @@ grl_plugin_registry_get_sources (GrlPluginRegistry *registry,
 {
   GHashTableIter iter;
   GList *source_list = NULL;
-  GrlMediaPlugin *current_plugin;
+  GrlSource *current_source;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), NULL);
 
   g_hash_table_iter_init (&iter, registry->priv->sources);
-  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &current_plugin)) {
-    source_list = g_list_prepend (source_list, current_plugin);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &current_source)) {
+    source_list = g_list_prepend (source_list, current_source);
   }
 
   if (ranked) {
@@ -1040,8 +1070,8 @@ grl_plugin_registry_get_sources (GrlPluginRegistry *registry,
  *
  * If @ranked is %TRUE, the source list will be ordered by rank.
  *
- * Returns: (element-type Grl.MediaPlugin) (transfer container): a #GList of
- * available #GrlMediaPlugins<!-- -->s. The content of the list should not be
+ * Returns: (element-type Grl.Source) (transfer container): a #GList of
+ * available #GrlSource<!-- -->s. The content of the list should not be
  * modified or freed. Use g_list_free() when done using the list.
  *
  * Since: 0.1.7
@@ -1053,17 +1083,17 @@ grl_plugin_registry_get_sources_by_operations (GrlPluginRegistry *registry,
 {
   GHashTableIter iter;
   GList *source_list = NULL;
-  GrlMediaPlugin *p;
+  GrlSource *source;
 
   g_return_val_if_fail (GRL_IS_PLUGIN_REGISTRY (registry), NULL);
 
   g_hash_table_iter_init (&iter, registry->priv->sources);
-  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &p)) {
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &source)) {
     GrlSupportedOps source_ops;
     source_ops =
-      grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (p));
+      grl_source_supported_operations (source);
     if ((source_ops & ops) == ops) {
-      source_list = g_list_prepend (source_list, p);
+      source_list = g_list_prepend (source_list, source);
     }
   }
 
@@ -1092,7 +1122,7 @@ grl_plugin_registry_unload (GrlPluginRegistry *registry,
                             const gchar *plugin_id,
                             GError **error)
 {
-  GrlPluginDescriptor *plugin;
+  GrlPlugin *plugin;
   GList *sources = NULL;
   GList *sources_iter;
 
@@ -1118,12 +1148,8 @@ grl_plugin_registry_unload (GrlPluginRegistry *registry,
 
   for (sources_iter = sources; sources_iter;
       sources_iter = g_list_next (sources_iter)) {
-    const gchar *id;
-    GrlMediaPlugin *source;
-
-    source = GRL_MEDIA_PLUGIN (sources_iter->data);
-    id = grl_media_plugin_get_id (source);
-    if (!strcmp (plugin_id, id)) {
+    GrlSource *source = GRL_SOURCE (sources_iter->data);
+    if (grl_source_get_plugin (source) == plugin) {
       grl_plugin_registry_unregister_source (registry, source, NULL);
     }
   }
@@ -1131,14 +1157,10 @@ grl_plugin_registry_unload (GrlPluginRegistry *registry,
 
   /* Third, shut down the plugin */
   GRL_DEBUG ("Unloading plugin '%s'", plugin_id);
-  if (plugin->plugin_deinit) {
-    plugin->plugin_deinit ();
-  }
-
-  g_hash_table_remove (registry->priv->plugins, plugin_id);
+   grl_plugin_unload (plugin);
 
-  if (plugin->module) {
-    g_module_close (plugin->module);
+  if (grl_plugin_get_module (plugin)) {
+      g_module_close (grl_plugin_get_module (plugin));
   }
 
   return TRUE;
diff --git a/src/grl-plugin-registry.h b/src/grl-plugin-registry.h
index 8695495..637bbc5 100644
--- a/src/grl-plugin-registry.h
+++ b/src/grl-plugin-registry.h
@@ -35,6 +35,7 @@
 #include <grl-metadata-key.h>
 #include <grl-config.h>
 #include <grl-definitions.h>
+#include <grl-plugin.h>
 
 #define GRL_PLUGIN_PATH_VAR "GRL_PLUGIN_PATH"
 #define GRL_PLUGIN_LIST_VAR "GRL_PLUGIN_LIST"
@@ -73,12 +74,12 @@
 /**
  * GRL_PLUGIN_REGISTER:
  * @init: the module initialization. It shall instantiate
- * the #GrlMediaPlugins provided
+ * the #GrlPlugins provided
  * @deinit: (allow-none): function to execute when the registry needs to dispose the module
  * @id: the module identifier
  *
  * Define the boilerplate for loadable modules. Defines a new module
- * descriptor which provides a set of #GrlMediaPlugins
+ * descriptor which provides a set of #GrlPlugins
  */
 #define GRL_PLUGIN_REGISTER(init,               \
                             deinit,             \
@@ -94,35 +95,13 @@
 
 typedef struct _GrlPluginRegistry GrlPluginRegistry;
 
-typedef struct _GrlPluginInfo GrlPluginInfo;
-
-/**
- * GrlPluginInfo:
- * @id: the module identifier
- * @filename: the filename containing the plugin
- * @optional_info: Key/value pairs with extra information about the plugin.
- * @rank: the plugin priority rank
- *
- * This structure stores the information related to a module
-*/
-
-struct _GrlPluginInfo {
-  gchar *id;
-  gchar *filename;
-  GHashTable *optional_info;
-  gint rank;
-
-  /*< private >*/
-  gpointer _grl_reserved[GRL_PADDING];
-};
-
 typedef struct _GrlPluginDescriptor  GrlPluginDescriptor;
 
 /**
 * GrlPluginDescriptor:
 * @plugin_id: the module identifier
 * @plugin_init: the module initialization. It shall instantiate
-* the #GrlMediaPlugins provided
+* the #GrlPlugins provided
 * @plugin_deinit: function to execute when the registry needs
 * to dispose the module.
 * @module: the #GModule instance.
@@ -131,8 +110,8 @@ typedef struct _GrlPluginDescriptor  GrlPluginDescriptor;
 */
 struct _GrlPluginDescriptor {
   gchar *plugin_id;
-  gboolean (*plugin_init) (GrlPluginRegistry *, const GrlPluginInfo *, GList *);
-  void (*plugin_deinit) (void);
+  gboolean (*plugin_init) (GrlPluginRegistry *, GrlPlugin *, GList *);
+  void (*plugin_deinit) (GrlPlugin *);
   GModule *module;
 
   /*< private >*/
@@ -142,28 +121,28 @@ struct _GrlPluginDescriptor {
 /* Plugin ranks */
 
 /**
- * GrlPluginRank:
- * @GRL_PLUGIN_RANK_LOWEST: will be chosen last or not at all
- * @GRL_PLUGIN_RANK_LOW: unlikely to be chosen
- * @GRL_PLUGIN_RANK_DEFAULT: likely to be chosen
- * @GRL_PLUGIN_RANK_HIGH: will be chosen
- * @GRL_PLUGIN_RANK_HIGHEST: will be chosen first
+ * GrlRank:
+ * @GRL_RANK_LOWEST: will be chosen last or not at all
+ * @GRL_RANK_LOW: unlikely to be chosen
+ * @GRL_RANK_DEFAULT: likely to be chosen
+ * @GRL_RANK_HIGH: will be chosen
+ * @GRL_RANK_HIGHEST: will be chosen first
  *
- * Module priority ranks. Defines the order in which the resolver
- * (or similar rank-picking mechanisms) will choose this plugin
+ * Source priority ranks. Defines the order in which the resolver
+ * (or similar rank-picking mechanisms) will choose this source
  * over an alternative one with the same function.
  *
  * These constants serve as a rough guidance for defining the rank
- * of a GrlPluginInfo. Any value is valid, including values bigger
- * than GRL_PLUGIN_RANK_HIGHEST.
+ * of a GrlSource. Any value is valid, including values bigger
+ * than GRL_RANK_HIGHEST.
  */
 typedef enum {
-  GRL_PLUGIN_RANK_LOWEST  = -64,
-  GRL_PLUGIN_RANK_LOW     = -32,
-  GRL_PLUGIN_RANK_DEFAULT =   0,
-  GRL_PLUGIN_RANK_HIGH    =  32,
-  GRL_PLUGIN_RANK_HIGHEST =  64
-} GrlPluginRank;
+  GRL_RANK_LOWEST  = -64,
+  GRL_RANK_LOW     = -32,
+  GRL_RANK_DEFAULT =   0,
+  GRL_RANK_HIGH    =  32,
+  GRL_RANK_HIGHEST =  64,
+} GrlRank;
 
 /* GrlPluginRegistry object */
 
@@ -226,16 +205,16 @@ gboolean grl_plugin_registry_load_by_id (GrlPluginRegistry *registry,
                                          GError **error);
 
 gboolean grl_plugin_registry_register_source (GrlPluginRegistry *registry,
-                                              const GrlPluginInfo *plugin,
-                                              GrlMediaPlugin *source,
+                                              GrlPlugin *plugin,
+                                              GrlSource *source,
                                               GError **error);
 
 gboolean grl_plugin_registry_unregister_source (GrlPluginRegistry *registry,
-                                                GrlMediaPlugin *source,
+                                                GrlSource *source,
                                                 GError **error);
 
-GrlMediaPlugin *grl_plugin_registry_lookup_source (GrlPluginRegistry *registry,
-                                                   const gchar *source_id);
+GrlSource *grl_plugin_registry_lookup_source (GrlPluginRegistry *registry,
+                                              const gchar *source_id);
 
 GList *grl_plugin_registry_get_sources (GrlPluginRegistry *registry,
 						  gboolean ranked);
diff --git a/src/grl-plugin.c b/src/grl-plugin.c
new file mode 100644
index 0000000..f10b073
--- /dev/null
+++ b/src/grl-plugin.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2010-2012 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**
+ * SECTION:grl-plugin
+ * @short_description: Base class for Grilo Plugins
+ * @see_also: #GrlMetadataSource, #GrlMediaSource
+ *
+ * Grilo is extensible, so #GrlMetadataSource or #GrlMediaSource instances can be
+ * loaded at runtime.
+ * A plugin system can provide one or more of the basic
+ * <application>Grilo</application> #GrlSource subclasses.
+ *
+ * This is a base class for anything that can be added to a Grilo Plugin.
+ */
+
+#include "grl-plugin.h"
+#include "grl-plugin-priv.h"
+#include "grl-plugin-registry.h"
+#include "grl-log.h"
+
+#include <string.h>
+
+#define GRL_LOG_DOMAIN_DEFAULT  plugin_log_domain
+GRL_LOG_DOMAIN(plugin_log_domain);
+
+#define GRL_PLUGIN_GET_PRIVATE(object)            \
+  (G_TYPE_INSTANCE_GET_PRIVATE((object),                \
+                               GRL_TYPE_PLUGIN,   \
+                               GrlPluginPrivate))
+
+enum {
+  PROP_0,
+  PROP_LOADED,
+  PROP_LAST,
+};
+
+static GParamSpec *properties[PROP_LAST] = { 0 };
+
+struct _GrlPluginPrivate {
+  gchar *id;
+  gchar *filename;
+  gint rank;
+  GModule *module;
+  GHashTable *optional_info;
+  gboolean loaded;
+  gboolean (*load_func) (GrlPluginRegistry *, GrlPlugin *, GList *);
+  void (*unload_func) (GrlPlugin *);
+};
+
+static void grl_plugin_finalize (GObject *object);
+
+static void grl_plugin_get_property (GObject *object,
+                                     guint prop_id,
+                                     GValue *value,
+                                     GParamSpec *pspec);
+
+/* ================ GrlPlugin GObject ================ */
+
+G_DEFINE_TYPE (GrlPlugin, grl_plugin, G_TYPE_OBJECT);
+
+static void
+grl_plugin_class_init (GrlPluginClass *plugin_class)
+{
+  GObjectClass *gobject_class;
+  gobject_class = G_OBJECT_CLASS (plugin_class);
+
+  gobject_class->finalize = grl_plugin_finalize;
+  gobject_class->get_property = grl_plugin_get_property;
+
+  properties[PROP_LOADED] = g_param_spec_boolean ("loaded",
+                                                  "Loaded",
+                                                  "Plugin is loaded",
+                                                  FALSE,
+                                                  G_PARAM_READABLE |
+                                                  G_PARAM_STATIC_STRINGS);
+
+  /**
+   * GrlPlugin:loaded
+   *
+   * @TRUE if plugin is loaded.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_LOADED,
+                                   properties[PROP_LOADED]);
+
+  g_type_class_add_private (plugin_class,
+                            sizeof (GrlPluginPrivate));
+}
+
+static void
+grl_plugin_init (GrlPlugin *plugin)
+{
+  plugin->priv = GRL_PLUGIN_GET_PRIVATE (plugin);
+  plugin->priv->optional_info = g_hash_table_new_full (g_str_hash,
+                                                       g_str_equal,
+                                                       g_free,
+                                                       g_free);
+}
+
+static void
+grl_plugin_finalize (GObject *object)
+{
+  GrlPlugin *plugin = GRL_PLUGIN (object);
+
+  g_free (plugin->priv->id);
+  g_free (plugin->priv->filename);
+  g_hash_table_unref (plugin->priv->optional_info);
+
+  G_OBJECT_CLASS (grl_plugin_parent_class)->finalize (object);
+}
+
+static void
+grl_plugin_get_property (GObject *object,
+                         guint prop_id,
+                         GValue *value,
+                         GParamSpec *pspec)
+{
+  GrlPlugin *plugin = GRL_PLUGIN (object);
+
+  switch (prop_id) {
+  case PROP_LOADED:
+    g_value_set_boolean (value, plugin->priv->loaded);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+/* ================ API ================ */
+
+/**
+ * grl_plugin_set_optional_info:
+ * @plugin: a plugin
+ * @info: a hashtable containing optional information
+ *
+ * Sets the optional information. Takes ownership of @info table.
+ **/
+void
+grl_plugin_set_optional_info (GrlPlugin *plugin,
+                              GHashTable *info)
+{
+  g_return_if_fail (GRL_IS_PLUGIN (plugin));
+
+  g_hash_table_unref (plugin->priv->optional_info);
+  plugin->priv->optional_info = info;
+}
+
+/**
+ * grl_plugin_set_load_func:
+ * @plugin: a plugin
+ * @load_function: a function
+ *
+ * Sets the function to be executed when plugin is loaded
+ **/
+void
+grl_plugin_set_load_func (GrlPlugin *plugin,
+                          gpointer load_function)
+{
+  g_return_if_fail (GRL_IS_PLUGIN (plugin));
+
+  plugin->priv->load_func = load_function;
+}
+
+/**
+ * grl_plugin_set_unload_func:
+ * @plugin: a plugin
+ * @unload_function: a function
+ *
+ * Sets the function to be executed when plugin is unloaded
+ **/
+void
+grl_plugin_set_unload_func (GrlPlugin *plugin,
+                            gpointer unload_function)
+{
+  g_return_if_fail (GRL_IS_PLUGIN (plugin));
+
+  plugin->priv->unload_func = unload_function;
+}
+
+/**
+ * grl_plugin_load:
+ * @plugin: a plugin
+ * @configurations: a list of configurations
+ *
+ * Load the plugin
+ *
+ * Returns: @TRUE if loaded was successful
+ **/
+gboolean
+grl_plugin_load (GrlPlugin *plugin,
+                 GList *configurations)
+{
+  GrlPluginRegistry *registry;
+
+  g_return_val_if_fail (GRL_IS_PLUGIN (plugin), FALSE);
+
+  if (!plugin->priv->load_func) {
+    return FALSE;
+  }
+
+  registry = grl_plugin_registry_get_default ();
+
+  if (!plugin->priv->load_func (registry, plugin, configurations)) {
+    return FALSE;
+  }
+
+  plugin->priv->loaded = TRUE;
+  g_object_notify_by_pspec (G_OBJECT (plugin), properties[PROP_LOADED]);
+
+  return TRUE;
+}
+
+/**
+ * grl_plugin_unload:
+ * @plugin: a plugin
+ *
+ * Unloads the plugin
+ **/
+void
+grl_plugin_unload (GrlPlugin *plugin)
+{
+  g_return_if_fail (GRL_IS_PLUGIN (plugin));
+
+  if (plugin->priv->unload_func) {
+    plugin->priv->unload_func (plugin);
+  }
+
+  plugin->priv->loaded = FALSE;
+  g_object_notify_by_pspec (G_OBJECT (plugin), properties[PROP_LOADED]);
+}
+
+/**
+ * grl_plugin_get_name:
+ * @plugin: a plugin
+ *
+ * Get the name of the plugin
+ *
+ * Returns: the name of the @plugin
+ */
+const gchar *
+grl_plugin_get_name (GrlPlugin *plugin)
+{
+  return grl_plugin_get_info (plugin, GRL_PLUGIN_NAME);
+}
+
+/**
+ * grl_plugin_get_description:
+ * @plugin: a plugin
+ *
+ * Get the description of the plugin
+ *
+ * Returns: the description of the @plugin
+ */
+const gchar *
+grl_plugin_get_description (GrlPlugin *plugin)
+{
+  return grl_plugin_get_info (plugin, GRL_PLUGIN_DESCRIPTION);
+}
+
+/**
+ * grl_plugin_get_version:
+ * @plugin: a plugin
+ *
+ * Get the version of the plugin
+ *
+ * Returns: the version of the @plugin
+ */
+const gchar *
+grl_plugin_get_version (GrlPlugin *plugin)
+{
+  return grl_plugin_get_info (plugin, GRL_PLUGIN_VERSION);
+}
+
+/**
+ * grl_plugin_get_license:
+ * @plugin: a plugin
+ *
+ * Get the license of the plugin
+ *
+ * Returns: the license of the @plugin
+ */
+const gchar *
+grl_plugin_get_license (GrlPlugin *plugin)
+{
+  return grl_plugin_get_info (plugin, GRL_PLUGIN_LICENSE);
+}
+
+/**
+ * grl_plugin_get_author:
+ * @plugin: a plugin
+ *
+ * Get the author of the plugin
+ *
+ * Returns: the author of the @plugin
+ */
+const gchar *
+grl_plugin_get_author (GrlPlugin *plugin)
+{
+  return grl_plugin_get_info (plugin, GRL_PLUGIN_AUTHOR);
+}
+
+/**
+ * grl_plugin_get_site:
+ * @plugin: a plugin
+ *
+ * Get the site of the plugin
+ *
+ * Returns: the site of the @plugin
+ */
+const gchar *
+grl_plugin_get_site (GrlPlugin *plugin)
+{
+  return grl_plugin_get_info (plugin, GRL_PLUGIN_SITE);
+}
+
+/**
+ * grl_plugin_get_id:
+ * @plugin: a plugin
+ *
+ * Get the id of the plugin
+ *
+ * Returns: the id of the @plugin
+ */
+const gchar *
+grl_plugin_get_id (GrlPlugin *plugin)
+{
+  g_return_val_if_fail (GRL_IS_PLUGIN (plugin), NULL);
+
+  return plugin->priv->id;
+}
+
+/**
+ * grl_plugin_set_id:
+ * @plugin: a plugin
+ * @id: plugin identifier
+ *
+ * Sets the id of the plugin
+ **/
+void
+grl_plugin_set_id (GrlPlugin *plugin,
+                   const gchar *id)
+{
+  g_return_if_fail (GRL_IS_PLUGIN (plugin));
+
+  if (plugin->priv->id) {
+    g_free (plugin->priv->id);
+  }
+  plugin->priv->id = g_strdup (id);
+}
+
+/**
+ * grl_plugin_get_filename:
+ * @plugin: a plugin
+ *
+ * Get the filename containing the plugin
+ *
+ * Returns: the filename containing @plugin
+ */
+const gchar *
+grl_plugin_get_filename (GrlPlugin *plugin)
+{
+  g_return_val_if_fail (GRL_IS_PLUGIN (plugin), NULL);
+
+  return plugin->priv->filename;
+}
+
+/**
+ * grl_plugin_set_filename:
+ * @plugin: a plugin
+ * @filename: a filename
+ *
+ * Sets the filename containing the plugin
+ **/
+void
+grl_plugin_set_filename (GrlPlugin *plugin,
+                         const gchar *filename)
+{
+  g_return_if_fail (GRL_IS_PLUGIN (plugin));
+
+  if (plugin->priv->filename) {
+    g_free (plugin->priv->filename);
+  }
+
+  plugin->priv->filename = g_strdup (filename);
+}
+
+/**
+ * grl_plugin_get_module: (skip)
+ * @plugin: a plugin
+ *
+ * Gets the #GModule containing the plugin
+ *
+ * Returns: a #GModule
+ **/
+GModule *
+grl_plugin_get_module (GrlPlugin *plugin)
+{
+  g_return_val_if_fail (GRL_IS_PLUGIN (plugin), NULL);
+  return plugin->priv->module;
+}
+
+void
+grl_plugin_set_module (GrlPlugin *plugin,
+                       GModule *module)
+{
+  g_return_if_fail (GRL_IS_PLUGIN (plugin));
+  plugin->priv->module = module;
+}
+
+/**
+ * grl_plugin_get_info_keys:
+ * @plugin: a plugin
+ *
+ * Returns a list of keys that can be queried to retrieve information about the
+ * plugin.
+ *
+ * Returns: (transfer container) (element-type utf8):
+ * a #GList of strings containing the keys. The content of the list is
+ * owned by the plugin and should not be modified or freed. Use g_list_free()
+ * when done using the list.
+ **/
+GList *
+grl_plugin_get_info_keys (GrlPlugin *plugin)
+{
+  g_return_val_if_fail (GRL_IS_PLUGIN (plugin), NULL);
+
+  if (plugin->priv->optional_info) {
+    return g_hash_table_get_keys (plugin->priv->optional_info);
+  } else {
+    return NULL;
+  }
+}
+
+/**
+ * grl_plugin_get_info:
+ * @plugin: a plugin
+ * @key: a key representing information about this plugin
+ *
+ * Get the information of the @plugin that is associated with the given key
+ *
+ * Returns: the information assigned to the given @key or NULL if there is no such information
+ */
+const gchar *
+grl_plugin_get_info (GrlPlugin *plugin, const gchar *key)
+{
+  g_return_val_if_fail (GRL_IS_PLUGIN (plugin), NULL);
+
+  if (!plugin->priv->optional_info) {
+    return NULL;
+  }
+
+  return g_hash_table_lookup (plugin->priv->optional_info, key);
+}
+
+/**
+ * grl_plugin_set_info:
+ * @plugin: a plugin
+ * @key: key representing information about this plugin
+ * @value: the information itself
+ *
+ * Sets the information of the @plugin that is associaed with the given @key.
+ **/
+void
+grl_plugin_set_info (GrlPlugin *plugin,
+                     const gchar *key,
+                     const gchar *value)
+{
+  g_return_if_fail (GRL_IS_PLUGIN (plugin));
+
+  g_hash_table_insert (plugin->priv->optional_info,
+                       g_strdup (key),
+                       g_strdup (value));
+}
diff --git a/src/grl-plugin.h b/src/grl-plugin.h
new file mode 100644
index 0000000..3fd8370
--- /dev/null
+++ b/src/grl-plugin.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010-2012 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#if !defined (_GRILO_H_INSIDE_) && !defined (GRILO_COMPILATION)
+#error "Only <grilo.h> can be included directly."
+#endif
+
+#ifndef _GRL_PLUGIN_H_
+#define _GRL_PLUGIN_H_
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gmodule.h>
+
+#include "grl-definitions.h"
+
+/* Info */
+
+#define GRL_PLUGIN_NAME "name"
+#define GRL_PLUGIN_DESCRIPTION "description"
+#define GRL_PLUGIN_VERSION "version"
+#define GRL_PLUGIN_LICENSE "license"
+#define GRL_PLUGIN_AUTHOR "author"
+#define GRL_PLUGIN_SITE "site"
+
+/* Macros */
+
+#define GRL_TYPE_PLUGIN                         \
+  (grl_plugin_get_type ())
+
+#define GRL_PLUGIN(obj)                                 \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj),                   \
+                               GRL_TYPE_PLUGIN,         \
+                               GrlPlugin))
+
+#define GRL_IS_PLUGIN(obj)                        \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),             \
+                               GRL_TYPE_PLUGIN))
+
+#define GRL_PLUGIN_CLASS(klass)                         \
+  (G_TYPE_CHECK_CLASS_CAST((klass),                     \
+                           GRL_TYPE_PLUGIN,             \
+                           GrlPluginClass))
+
+#define GRL_IS_PLUGIN_CLASS(klass)                \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),               \
+                           GRL_TYPE_PLUGIN))
+
+#define GRL_PLUGIN_GET_CLASS(obj)                       \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj),                    \
+                              GRL_TYPE_PLUGIN,          \
+                              GrlPluginClass))
+
+/* GrlPlugin object */
+
+typedef struct _GrlPlugin        GrlPlugin;
+typedef struct _GrlPluginPrivate GrlPluginPrivate;
+
+struct _GrlPlugin {
+
+  GObject parent;
+
+  /*< private >*/
+  GrlPluginPrivate *priv;
+
+  gpointer _grl_reserved[GRL_PADDING];
+};
+
+/* GrlPlugin class */
+
+typedef struct _GrlPluginClass GrlPluginClass;
+
+/**
+ * GrlPluginClass:
+ * @parent_class: the parent class structure
+ */
+struct _GrlPluginClass {
+
+  GObjectClass parent_class;
+
+  /*< private >*/
+  gpointer _grl_reserved[GRL_PADDING];
+};
+
+/* Function prototypes */
+
+G_BEGIN_DECLS
+
+GType grl_plugin_get_type (void);
+
+const gchar *grl_plugin_get_name (GrlPlugin *plugin);
+
+const gchar *grl_plugin_get_description (GrlPlugin *plugin);
+
+const gchar *grl_plugin_get_version (GrlPlugin *plugin);
+
+const gchar *grl_plugin_get_license (GrlPlugin *plugin);
+
+const gchar *grl_plugin_get_author (GrlPlugin *plugin);
+
+const gchar *grl_plugin_get_site (GrlPlugin *plugin);
+
+const gchar *grl_plugin_get_id (GrlPlugin *plugin);
+
+const gchar *grl_plugin_get_filename (GrlPlugin *plugin);
+
+GModule *grl_plugin_get_module (GrlPlugin *plugin);
+
+GList *grl_plugin_get_info_keys (GrlPlugin *plugin);
+
+const gchar *grl_plugin_get_info (GrlPlugin *plugin,
+                                  const gchar *key);
+
+G_END_DECLS
+
+#endif /* _GRL_PLUGIN_H_ */
diff --git a/src/grl-source-priv.h b/src/grl-source-priv.h
new file mode 100644
index 0000000..0b6113d
--- /dev/null
+++ b/src/grl-source-priv.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010-2012 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_SOURCE_PRIV_H_
+#define _GRL_SOURCE_PRIV_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "grl-source.h"
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+GList *grl_source_filter_supported (GrlSource *source,
+                                    GList **keys,
+                                    gboolean return_filtered);
+
+GList *grl_source_filter_slow (GrlSource *source,
+                               GList **keys,
+                               gboolean return_filtered);
+
+GList *grl_source_filter_writable (GrlSource *source,
+                                   GList **keys,
+                                   gboolean return_filtered);
+
+void grl_source_set_operation_finished (GrlSource *source,
+                                        guint operation_id);
+
+gboolean grl_source_operation_is_finished (GrlSource *source,
+                                           guint operation_id);
+
+void grl_source_set_operation_completed (GrlSource *source,
+                                         guint operation_id);
+
+gboolean grl_source_operation_is_completed (GrlSource *source,
+                                            guint operation_id);
+
+void grl_source_set_operation_cancelled (GrlSource *source,
+                                         guint operation_id);
+
+gboolean grl_source_operation_is_cancelled (GrlSource *source,
+                                            guint operation_id);
+
+void grl_source_set_operation_ongoing (GrlSource *source,
+                                       guint operation_id);
+
+gboolean grl_source_operation_is_ongoing (GrlSource *source,
+                                          guint operation_id);
+
+G_END_DECLS
+
+#endif /* _GRL_SOURCE_PRIV_H_ */
diff --git a/src/grl-source.c b/src/grl-source.c
new file mode 100644
index 0000000..d15dde9
--- /dev/null
+++ b/src/grl-source.c
@@ -0,0 +1,817 @@
+/*
+ * Copyright (C) 2010-2012 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**
+ * SECTION:grl-source
+ * @short_description: Abstract base class for sources
+ * @see_also: #GrlPlugin, #GrlMediaSource, #GrlMetadataSource, #GrlMedia
+ *
+ * GrlSource is the abstract base class needed to construct a source providing
+ * multimedia information that can be used in a Grilo application.
+ *
+ * The sources fetch information from different online or local
+ * databases and store them in the #GrlMedia.
+ */
+
+#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-sync-priv.h"
+#include "grl-plugin-registry.h"
+#include "grl-error.h"
+#include "grl-log.h"
+#include "data/grl-media.h"
+
+#include <string.h>
+
+#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,                 \
+                               GrlSourcePrivate))
+
+enum {
+  PROP_0,
+  PROP_ID,
+  PROP_NAME,
+  PROP_DESC,
+  PROP_PLUGIN,
+  PROP_RANK,
+};
+
+struct _GrlSourcePrivate {
+  gchar *id;
+  gchar *name;
+  gchar *desc;
+  gint rank;
+  GrlPlugin *plugin;
+};
+
+struct OperationState {
+  GrlSource *source;
+  guint operation_id;
+  gboolean cancelled;
+  gboolean completed;
+};
+
+static void grl_source_finalize (GObject *object);
+
+static void grl_source_dispose (GObject *object);
+
+static void grl_source_get_property (GObject *object,
+                                     guint prop_id,
+                                     GValue *value,
+                                     GParamSpec *pspec);
+
+static void grl_source_set_property (GObject *object,
+                                     guint prop_id,
+                                     const GValue *value,
+                                     GParamSpec *pspec);
+
+/* ================ GrlSource GObject ================ */
+
+G_DEFINE_ABSTRACT_TYPE (GrlSource,
+                        grl_source,
+                        G_TYPE_OBJECT);
+
+static void
+grl_source_class_init (GrlSourceClass *source_class)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (source_class);
+
+  gobject_class->dispose = grl_source_dispose;
+  gobject_class->finalize = grl_source_finalize;
+  gobject_class->set_property = grl_source_set_property;
+  gobject_class->get_property = grl_source_get_property;
+
+  /**
+   * GrlSource:source-id
+   *
+   * The identifier of the source.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_ID,
+                                   g_param_spec_string ("source-id",
+                                                        "Source identifier",
+                                                        "The identifier of the source",
+                                                        "",
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT |
+                                                        G_PARAM_STATIC_STRINGS));
+  /**
+   * GrlSource:source-name
+   *
+   * The name of the source.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_NAME,
+                                   g_param_spec_string ("source-name",
+                                                        "Source name",
+                                                        "The name of the source",
+                                                        "",
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT |
+                                                        G_PARAM_STATIC_STRINGS));
+  /**
+   * GrlSource:source-desc
+   *
+   * A description of the source
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_DESC,
+                                   g_param_spec_string ("source-desc",
+                                                        "Source description",
+                                                        "A description of the source",
+                                                        "",
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT |
+                                                        G_PARAM_STATIC_STRINGS));
+  /**
+   * GrlSource:plugin
+   *
+   * Plugin the source belongs to
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_PLUGIN,
+                                   g_param_spec_object ("plugin",
+                                                        "Plugin",
+                                                        "Plugin source belongs to",
+                                                        GRL_TYPE_PLUGIN,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT |
+                                                        G_PARAM_STATIC_STRINGS));
+  /**
+   * GrlSource:rank
+   *
+   * Source rank
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_RANK,
+                                   g_param_spec_int ("rank",
+                                                     "Rank",
+                                                     "Source rank",
+                                                     G_MININT,
+                                                     G_MAXINT,
+                                                     GRL_RANK_DEFAULT,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT |
+                                                     G_PARAM_STATIC_STRINGS));
+
+  g_type_class_add_private (source_class,
+                            sizeof (GrlSourcePrivate));
+}
+
+static void
+grl_source_init (GrlSource *source)
+{
+  source->priv = GRL_SOURCE_GET_PRIVATE (source);
+}
+
+static void
+grl_source_dispose (GObject *object)
+{
+  GrlSource *source = GRL_SOURCE (object);
+
+  if (source->priv->plugin) {
+    g_object_unref (source->priv->plugin);
+    source->priv->plugin = NULL;
+  }
+
+  G_OBJECT_CLASS (grl_source_parent_class)->dispose (object);
+}
+
+static void
+grl_source_finalize (GObject *object)
+{
+  GrlSource *source = GRL_SOURCE (object);
+
+  g_free (source->priv->id);
+  g_free (source->priv->name);
+  g_free (source->priv->desc);
+
+  G_OBJECT_CLASS (grl_source_parent_class)->finalize (object);
+}
+
+static void
+set_string_property (gchar **property, const GValue *value)
+{
+  if (*property) {
+    g_free (*property);
+  }
+  *property = g_value_dup_string (value);
+}
+
+static void
+grl_source_set_property (GObject *object,
+                         guint prop_id,
+                         const GValue *value,
+                         GParamSpec *pspec)
+{
+  GrlSource *source = GRL_SOURCE (object);
+
+  switch (prop_id) {
+  case PROP_ID:
+    set_string_property (&source->priv->id, value);
+    break;
+  case PROP_NAME:
+    set_string_property (&source->priv->name, value);
+    break;
+  case PROP_DESC:
+    set_string_property (&source->priv->desc, value);
+    break;
+  case PROP_PLUGIN:
+    if (source->priv->plugin) {
+      g_object_unref (source->priv->plugin);
+    }
+    source->priv->plugin = g_value_dup_object (value);
+    break;
+  case PROP_RANK:
+    source->priv->rank = g_value_get_int (value);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+grl_source_get_property (GObject *object,
+                         guint prop_id,
+                         GValue *value,
+                         GParamSpec *pspec)
+{
+  GrlSource *source = GRL_SOURCE (object);
+
+  switch (prop_id) {
+  case PROP_ID:
+    g_value_set_string (value, source->priv->id);
+    break;
+  case PROP_NAME:
+    g_value_set_string (value, source->priv->name);
+    break;
+  case PROP_DESC:
+    g_value_set_string (value, source->priv->desc);
+    break;
+  case PROP_PLUGIN:
+    g_value_set_object (value, source->priv->plugin);
+    break;
+  case PROP_RANK:
+    g_value_set_int (value, source->priv->rank);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec);
+    break;
+  }
+}
+
+/* ================ Utilities ================ */
+
+/*
+ * This method will _intersect two key lists_:
+ *
+ * @keys_to_filter: user provided set we want to filter leaving only
+ * the keys that intersects with the @source_keys set.
+ *
+ * @source_keys: the %GrlSource<!-- -->'s key set if
+ * @return_filtered is %TRUE a copy of the filtered set *complement*
+ * will be returned (a list of the filtered out keys).
+ */
+static GList *
+filter_key_list (GrlSource *source,
+                 GList **keys_to_filter,
+                 gboolean return_filtered,
+                 GList *source_keys)
+{
+  GList *iter_keys, *found;
+  GList *in_source = NULL;
+  GList *out_source = NULL;
+
+  for (iter_keys = *keys_to_filter;
+       iter_keys;
+       iter_keys = g_list_next (iter_keys)) {
+    found = g_list_find (source_keys, iter_keys->data);
+    if (found) {
+      in_source = g_list_prepend (in_source, iter_keys->data);
+    } else {
+      if (return_filtered) {
+        out_source = g_list_prepend (out_source, iter_keys->data);
+      }
+    }
+  }
+
+  g_list_free (*keys_to_filter);
+  *keys_to_filter = g_list_reverse (in_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)
+{
+  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_filter_supported:
+ * @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 unsupported keys
+ *
+ * Compares the received @keys list with the supported key list by the
+ * @source, and deletes those keys which are not supported.
+ *
+ * Returns: (element-type GrlKeyID) (transfer container):
+ * 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)
+{
+  const GList *supported_keys;
+
+  g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+
+  supported_keys = grl_source_supported_keys (source);
+
+  return filter_key_list (source, keys, return_filtered, (GList *) supported_keys);
+}
+
+/**
+ * grl_source_filter_slow:
+ * @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 slow keys
+ *
+ * This function does the opposite of other filter functions: removes the slow
+ * keys from @keys. If @return_filtered is %TRUE the removed slow keys are
+ * returned in a new list.
+ *
+ * Returns: (element-type GrlKeyID) (transfer container): if
+ * @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)
+{
+  const GList *slow_keys;
+  GList *fastest_keys, *tmp;
+
+  g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+
+  slow_keys = grl_source_slow_keys (source);
+
+  /* Note that we want to do the opposite */
+  fastest_keys = filter_key_list (source, keys, TRUE, (GList *) slow_keys);
+  tmp = *keys;
+  *keys = fastest_keys;
+
+  if (!return_filtered) {
+    g_list_free (tmp);
+    return NULL;
+  } else {
+    return tmp;
+  }
+}
+
+/**
+ * grl_source_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
+ * grl_source_writable_keys().
+ *
+ * Filter the @keys list keeping only those keys that are writtable in
+ * @source. If @return_filtered is %TRUE then the removed keys are returned in a
+ * new list.
+ *
+ * Returns: (element-type GrlKeyID) (transfer container):
+ * if @return_filtered is %TRUE will return the list of non-writtable keys;
+ * otherwise %NULL
+ */
+GList *
+grl_source_filter_writable (GrlSource *source,
+                            GList **keys,
+                            gboolean return_filtered)
+{
+  const GList *writable_keys;
+
+  g_return_val_if_fail (GRL_IS_SOURCE (source), NULL);
+  g_return_val_if_fail (keys != NULL, NULL);
+
+  writable_keys = grl_source_writable_keys (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
+ */
+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_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);
+
+  if (GRL_SOURCE_GET_CLASS (source)->supported_operations) {
+    return GRL_SOURCE_GET_CLASS (source)->supported_operations (source);
+  } else {
+    return GRL_OP_NONE;
+  }
+}
+
+/*
+ * 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:
+ *
+ * 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)
+{
+  GRL_DEBUG ("grl_source_set_operation_finished (%d)", operation_id);
+
+  grl_operation_remove (operation_id);
+}
+
+/*
+ * grl_source_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)
+{
+  struct OperationState *op_state;
+
+  op_state = grl_operation_get_private_data (operation_id);
+
+  return op_state == NULL;
+}
+
+/*
+ * grl_source_set_operation_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)
+{
+  struct OperationState *op_state;
+
+  GRL_DEBUG ("grl_source_set_operation_completed (%d)", operation_id);
+
+  op_state = grl_operation_get_private_data (operation_id);
+
+  if (op_state) {
+    op_state->completed = TRUE;
+  }
+}
+
+/*
+ * grl_source_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)
+{
+  struct OperationState *op_state;
+
+  op_state = grl_operation_get_private_data (operation_id);
+
+  return !op_state || op_state->completed;
+}
+
+/*
+ * grl_source_set_operation_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)
+{
+  struct OperationState *op_state;
+
+  GRL_DEBUG ("grl_source_set_operation_cancelled (%d)", operation_id);
+
+  op_state = grl_operation_get_private_data (operation_id);
+
+  if (op_state) {
+    op_state->cancelled = TRUE;
+  }
+}
+
+/*
+ * grl_source_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)
+{
+  struct OperationState *op_state;
+
+  op_state = grl_operation_get_private_data (operation_id);
+
+  return op_state && op_state->cancelled;
+}
+
+static void
+grl_source_cancel_cb (struct OperationState *op_state)
+{
+  GrlSource *source = op_state->source;
+
+  if (!grl_source_operation_is_ongoing (source,
+                                        op_state->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) */
+  grl_source_set_operation_cancelled (source,
+                                      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 */
+  if (GRL_SOURCE_GET_CLASS (source)->cancel) {
+    GRL_SOURCE_GET_CLASS (source)->cancel (source,
+                                           op_state->operation_id);
+  }
+}
+
+/*
+ * grl_source_set_operation_ongoing:
+ *
+ * Sets the operation as ongoing (operation is valid, not finished and
+ * not cancelled)
+ */
+void
+grl_source_set_operation_ongoing (GrlSource *source,
+                                  guint operation_id)
+{
+  struct OperationState *op_state;
+
+  GRL_DEBUG ("grl_source_set_operation_ongoing (%d)", operation_id);
+
+  op_state = g_new0 (struct OperationState, 1);
+  op_state->source = source;
+  op_state->operation_id = operation_id;
+
+  grl_operation_set_private_data (operation_id,
+                                  op_state,
+                                  (GrlOperationCancelCb) grl_source_cancel_cb,
+                                  g_free);
+}
+
+/*
+ * grl_source_operation_is_ongoing:
+ *
+ * Checks if operation is ongoing (operation is valid, and it is not
+ * finished nor cancelled).
+ */
+gboolean
+grl_source_operation_is_ongoing (GrlSource *source,
+                                 guint operation_id)
+{
+  struct OperationState *op_state;
+
+  op_state = grl_operation_get_private_data (operation_id);
+
+  return op_state && !op_state->cancelled;
+}
+
+/**
+ * grl_source_get_caps:
+ * @source: a source
+ * @operation: a supported operation. Even though the type allows to specify
+ * several operations, only one should be provided here.
+ *
+ * Get the capabilities of @source for @operation.
+ *
+ * Returns: (transfer none): The capabilities
+ */
+GrlCaps *
+grl_source_get_caps (GrlSource *source,
+                     GrlSupportedOps operation)
+{
+  static GrlCaps *default_caps = NULL;
+  GrlSourceClass *klass = GRL_SOURCE_GET_CLASS (source);
+
+  if (klass->get_caps)
+    return klass->get_caps (source, operation);
+
+  if (!default_caps)
+    default_caps = grl_caps_new ();
+
+  return default_caps;
+}
diff --git a/src/grl-source.h b/src/grl-source.h
new file mode 100644
index 0000000..e317e8e
--- /dev/null
+++ b/src/grl-source.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#if !defined (_GRILO_H_INSIDE_) && !defined (GRILO_COMPILATION)
+#error "Only <grilo.h> can be included directly."
+#endif
+
+#ifndef _GRL_SOURCE_H_
+#define _GRL_SOURCE_H_
+
+#include <grl-metadata-key.h>
+#include <grl-media.h>
+#include <grl-definitions.h>
+#include <grl-plugin.h>
+#include <grl-operation-options.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+/* Macros */
+
+#define GRL_TYPE_SOURCE                         \
+  (grl_source_get_type ())
+
+#define GRL_SOURCE(obj)                         \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj),           \
+                               GRL_TYPE_SOURCE, \
+                               GrlSource))
+
+#define GRL_IS_SOURCE(obj)                         \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),              \
+                               GRL_TYPE_SOURCE))
+
+#define GRL_SOURCE_CLASS(klass)                 \
+  (G_TYPE_CHECK_CLASS_CAST((klass),             \
+                           GRL_TYPE_SOURCE,     \
+                           GrlSourceClass))
+
+#define GRL_IS_SOURCE_CLASS(klass)              \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),             \
+                           GRL_TYPE_SOURCE))
+
+#define GRL_SOURCE_GET_CLASS(obj)               \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj),            \
+                              GRL_TYPE_SOURCE,  \
+                              GrlSourceClass))
+
+typedef struct _GrlSource        GrlSource;
+typedef struct _GrlSourcePrivate GrlSourcePrivate;
+
+struct _GrlSource {
+
+  GObject parent;
+
+  /*< private >*/
+  GrlSourcePrivate *priv;
+
+  gpointer _grl_reserved[GRL_PADDING];
+};
+
+/**
+ * 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_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.
+ *
+ * Bitwise flags which reflect the kind of operations that a
+ * #GrlSource supports.
+ */
+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_REMOVE          = 1 << 7,
+  GRL_OP_SET_METADATA    = 1 << 8,
+  GRL_OP_MEDIA_FROM_URI  = 1 << 9,
+  GRL_OP_NOTIFY_CHANGE   = 1 << 10,
+} GrlSupportedOps;
+
+/* GrlSource class */
+
+typedef struct _GrlSourceClass GrlSourceClass;
+
+/**
+ * GrlSourceClass:
+ * @parent_class: the parent class structure
+ * @supported_operations: the operations that can be called
+ * @supported_keys: the list of keys that can be handled
+ * @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
+ * @cancel: cancel the current operation
+
+ * Grilo Source class. Override the vmethods to implement the
+ * element functionality.
+ */
+struct _GrlSourceClass {
+
+  GObjectClass parent_class;
+
+  GrlSupportedOps (*supported_operations) (GrlSource *source);
+
+  const GList * (*supported_keys) (GrlSource *source);
+
+  const GList * (*slow_keys) (GrlSource *source);
+
+  const GList * (*writable_keys) (GrlSource *source);
+
+  GrlCaps * (*get_caps) (GrlSource *source, GrlSupportedOps operation);
+
+  void (*cancel) (GrlSource *source, guint operation_id);
+
+  /*< private >*/
+  gpointer _grl_reserved[GRL_PADDING];
+};
+
+G_BEGIN_DECLS
+
+GType grl_source_get_type (void);
+
+GrlSupportedOps grl_source_supported_operations (GrlSource *source);
+
+const GList *grl_source_supported_keys (GrlSource *source);
+
+const GList *grl_source_slow_keys (GrlSource *source);
+
+const GList *grl_source_writable_keys (GrlSource *source);
+
+GrlCaps *grl_source_get_caps (GrlSource *source,
+                              GrlSupportedOps operation);
+
+const gchar *grl_source_get_id (GrlSource *source);
+
+const gchar *grl_source_get_name (GrlSource *source);
+
+const gchar *grl_source_get_description (GrlSource *source);
+
+GrlPlugin *grl_source_get_plugin (GrlSource *source);
+
+gint grl_source_get_rank (GrlSource *source);
+
+G_END_DECLS
+
+#endif /* _GRL_SOURCE_H_ */
diff --git a/tools/grilo-inspect/grl-inspect.c b/tools/grilo-inspect/grl-inspect.c
index 6f1f54a..abe32c6 100644
--- a/tools/grilo-inspect/grl-inspect.c
+++ b/tools/grilo-inspect/grl-inspect.c
@@ -65,12 +65,15 @@ list_all_sources ()
 
   for (sources_iter = sources; sources_iter;
       sources_iter = g_list_next (sources_iter)) {
-    GrlMediaPlugin *source;
+    GrlSource *source;
+    GrlPlugin *plugin;
+
+    source = GRL_SOURCE (sources_iter->data);
+    plugin = grl_source_get_plugin (source);
 
-    source = GRL_MEDIA_PLUGIN (sources_iter->data);
     g_print ("%s:  %s\n",
-             grl_media_plugin_get_id (source),
-             grl_metadata_source_get_id (GRL_METADATA_SOURCE (source)));
+             grl_plugin_get_id (plugin),
+             grl_source_get_id (source));
   }
   g_list_free (sources);
 }
@@ -99,7 +102,8 @@ print_version()
 static void
 introspect_source (const gchar *source_id)
 {
-  GrlMediaPlugin *source;
+  GrlPlugin *plugin;
+  GrlSource *source;
   GrlSupportedOps supported_ops;
   const gchar *value;
   gchar *key;
@@ -109,37 +113,40 @@ introspect_source (const gchar *source_id)
   source = grl_plugin_registry_lookup_source (registry, source_id);
 
   if (source) {
-    g_print ("Plugin Details:\n");
-    g_print ("  %-20s %s\n", "Identifier:", grl_media_plugin_get_id (source));
-    g_print ("  %-20s %s\n", "Filename:",
-             grl_media_plugin_get_filename (source));
-    g_print ("  %-20s %d\n", "Rank:", grl_media_plugin_get_rank (source));
-
-    info_keys = grl_media_plugin_get_info_keys (source);
-    for (info_key = info_keys; info_key; info_key = g_list_next (info_key)) {
-      key = g_strdup_printf ("%s:", (gchar *) info_key->data);
-      key[0] = g_ascii_toupper (key[0]);
-      value = grl_media_plugin_get_info (source, info_key->data);
-      g_print ("  %-20s %s\n", key, value);
-      g_free (key);
+    plugin = grl_source_get_plugin (source);
+
+    if (plugin) {
+      g_print ("Plugin Details:\n");
+      g_print ("  %-20s %s\n", "Identifier:", grl_plugin_get_id (plugin));
+      g_print ("  %-20s %s\n", "Filename:",
+               grl_plugin_get_filename (plugin));
+
+      info_keys = grl_plugin_get_info_keys (plugin);
+      for (info_key = info_keys; info_key; info_key = g_list_next (info_key)) {
+        key = g_strdup_printf ("%s:", (gchar *) info_key->data);
+        key[0] = g_ascii_toupper (key[0]);
+        value = grl_plugin_get_info (plugin, info_key->data);
+        g_print ("  %-20s %s\n", key, value);
+        g_free (key);
+      }
+      g_list_free (info_keys);
+      g_print ("\n");
     }
-    g_list_free (info_keys);
-    g_print ("\n");
 
     g_print ("Source Details:\n");
     g_print ("  %-20s %s\n", "Identifier:",
-             grl_metadata_source_get_id (GRL_METADATA_SOURCE (source)));
+             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_metadata_source_get_name (GRL_METADATA_SOURCE (source)));
+             grl_source_get_name (source));
     g_print ("  %-20s %s\n", "Description:",
-             grl_metadata_source_get_description (GRL_METADATA_SOURCE (source)));
+             grl_source_get_description (source));
     g_print ("\n");
 
     /* Print supported operations */
     supported_ops =
-      grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source));
+      grl_source_supported_operations (source);
     g_print ("Supported operations:\n");
     if (supported_ops & GRL_OP_RESOLVE) {
       g_print ("  grl_metadata_source_resolve():\tResolve Metadata\n");
@@ -180,10 +187,10 @@ introspect_source (const gchar *source_id)
     /* Print supported keys */
     g_print ("Supported keys:\n");
     g_print ("  Readable Keys:\t");
-    print_keys (grl_metadata_source_supported_keys (GRL_METADATA_SOURCE (source)));
+    print_keys (grl_source_supported_keys (source));
     g_print ("\n");
     g_print ("  Writable Keys:\t");
-    print_keys (grl_metadata_source_writable_keys (GRL_METADATA_SOURCE (source)));
+    print_keys (grl_source_writable_keys (source));
     g_print ("\n");
     g_print ("\n");
   } else {
diff --git a/tools/grilo-test-ui/main.c b/tools/grilo-test-ui/main.c
index 402ba77..c30e87e 100644
--- a/tools/grilo-test-ui/main.c
+++ b/tools/grilo-test-ui/main.c
@@ -195,7 +195,7 @@ static const gchar *ui_definition =
 static GrlOperationOptions *default_options = NULL;
 static GrlOperationOptions *default_metadata_options = NULL;
 
-static void show_plugins (void);
+static void show_browsable_sources (void);
 static void quit_cb (GtkAction *action);
 
 static gchar *authorize_flickr (void);
@@ -266,7 +266,7 @@ changes_notification_cb (GtkToggleAction *action)
   registry = grl_plugin_registry_get_default ();
   sources = grl_plugin_registry_get_sources (registry, FALSE);
   for (source = sources; source; source = g_list_next (source)) {
-    if (grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source->data)) &
+    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),
@@ -710,7 +710,7 @@ browse_search_query_cb (GrlMediaSource *source,
 
    GrlOperationOptions *supported_options = NULL;
    grl_operation_options_obey_caps (options,
-                                    grl_metadata_source_get_caps (GRL_METADATA_SOURCE (source), GRL_OP_SEARCH),
+                                    grl_source_get_caps (GRL_SOURCE (source), GRL_OP_SEARCH),
                                     &supported_options,
                                     NULL);
 	switch (state->type) {
@@ -789,7 +789,7 @@ browse (GrlMediaSource *source, GrlMedia *container)
                                          state);
     operation_started (source, browse_id, FALSE);
   } else {
-    show_plugins ();
+    show_browsable_sources ();
   }
 
   set_cur_browse (source, container);
@@ -846,7 +846,7 @@ metadata (GrlMediaSource *source, GrlMedia *media)
   if (source) {
     /* If source does not support metadata() operation, then use the current
        media */
-    if ((grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
+    if ((grl_source_supported_operations (GRL_SOURCE (source)) &
          GRL_OP_METADATA)) {
           grl_media_source_metadata (source,
                                      media,
@@ -888,11 +888,11 @@ browser_row_selected_cb (GtkTreeView *tree_view,
 
   /* Check if we can store content in the selected item */
   if (content == NULL &&
-      (grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
+      (grl_source_supported_operations (GRL_SOURCE (source)) &
        GRL_OP_STORE)) {
     gtk_widget_set_sensitive (view->store_btn, TRUE);
   } else if (content && GRL_IS_MEDIA_BOX (content) &&
-	     grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
+	     grl_source_supported_operations (GRL_SOURCE (source)) &
 	     GRL_OP_STORE_PARENT) {
     gtk_widget_set_sensitive (view->store_btn, TRUE);
   } else {
@@ -901,7 +901,7 @@ browser_row_selected_cb (GtkTreeView *tree_view,
 
   /* Check if we can remove the selected item */
   if (content != NULL &&
-      (grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
+      (grl_source_supported_operations (GRL_SOURCE (source)) &
        GRL_OP_REMOVE)) {
     gtk_widget_set_sensitive (view->remove_btn, TRUE);
   } else {
@@ -1171,7 +1171,7 @@ search (GrlMediaSource *source, const gchar *text)
     /* Normal search */
     state->type = OP_TYPE_SEARCH;
     grl_operation_options_obey_caps (options,
-                                     grl_metadata_source_get_caps (GRL_METADATA_SOURCE (source), GRL_OP_SEARCH),
+                                     grl_source_get_caps (GRL_SOURCE (source), GRL_OP_SEARCH),
                                      &supported_options,
                                      NULL);
     g_object_unref (options);
@@ -1295,8 +1295,7 @@ set_filter_cb (GtkComboBox *widget,
     return;
   }
 
-  caps = grl_metadata_source_get_caps (GRL_METADATA_SOURCE (source),
-                                       GRL_OP_SEARCH);
+  caps = grl_source_get_caps (GRL_SOURCE (source), GRL_OP_SEARCH);
 
 
   filter = grl_caps_get_type_filter (caps);
@@ -1328,8 +1327,8 @@ query_combo_setup (void)
                                                            FALSE);
   for (sources_iter = sources; sources_iter;
       sources_iter = g_list_next (sources_iter)) {
-    GrlMetadataSource *source = GRL_METADATA_SOURCE (sources_iter->data);
-    const gchar *name = grl_metadata_source_get_name (source);
+    GrlSource *source = GRL_SOURCE (sources_iter->data);
+    const gchar *name = grl_source_get_name (source);
 
     gtk_list_store_append (GTK_LIST_STORE (view->query_combo_model), &iter);
     gtk_list_store_set (GTK_LIST_STORE (view->query_combo_model),
@@ -1359,8 +1358,8 @@ search_combo_setup (void)
                                                            FALSE);
   for (sources_iter = sources; sources_iter;
       sources_iter = g_list_next (sources_iter)) {
-    GrlMetadataSource *source = GRL_METADATA_SOURCE (sources_iter->data);
-    const gchar *name = grl_metadata_source_get_name (source);
+    GrlSource *source = GRL_SOURCE (sources_iter->data);
+    const gchar *name = grl_source_get_name (source);
 
     gtk_list_store_append (GTK_LIST_STORE (view->search_combo_model), &iter);
     gtk_list_store_set (GTK_LIST_STORE (view->search_combo_model),
@@ -1929,14 +1928,14 @@ ui_setup (void)
 			       METADATA_MIN_WIDTH,
 			       METADATA_MIN_HEIGHT);
 
-  /* Populate the browser with the plugins */
-  show_plugins ();
+  /* Populate the browser with the sources */
+  show_browsable_sources ();
 
   gtk_widget_show_all (view->window);
 }
 
 static void
-show_plugins ()
+show_browsable_sources ()
 {
   GList *sources;
   GList *sources_iter;
@@ -1952,13 +1951,13 @@ show_plugins ()
                                                            FALSE);
   for (sources_iter = sources; sources_iter;
       sources_iter = g_list_next (sources_iter)) {
-    GrlMetadataSource *source;
+    GrlSource *source;
     const gchar *name;
     GdkPixbuf *icon;
 
-    source = GRL_METADATA_SOURCE (sources_iter->data);
+    source = GRL_SOURCE (sources_iter->data);
     icon = load_icon (GTK_STOCK_DIRECTORY);
-    name = grl_metadata_source_get_name (source);
+    name = grl_source_get_name (source);
     GRL_DEBUG ("Loaded source: '%s'", name);
     gtk_list_store_append (GTK_LIST_STORE (view->browser_model), &iter);
     gtk_list_store_set (GTK_LIST_STORE (view->browser_model),
@@ -2006,7 +2005,7 @@ reset_ui (void)
   cancel_current_operation ();
   clear_panes ();
   reset_browse_history ();
-  show_plugins ();
+  show_browsable_sources ();
 }
 
 static gboolean
@@ -2055,14 +2054,14 @@ content_changed_cb (GrlMediaSource *source,
     if (GRL_IS_MEDIA_BOX (media)) {
       message =
         g_strdup_printf ("%s: container '%s' has %s%s",
-                         grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)),
+                         grl_source_get_name (GRL_SOURCE (source)),
                          media_id? media_id: "root",
                          change_type_string,
                          location_string);
     } else {
       message =
         g_strdup_printf ("%s: element '%s' has %s",
-                         grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)),
+                         grl_source_get_name (GRL_SOURCE (source)),
                          media_id,
                          change_type_string);
     }
@@ -2080,22 +2079,18 @@ content_changed_cb (GrlMediaSource *source,
 
 static void
 source_added_cb (GrlPluginRegistry *registry,
-		 GrlMediaPlugin *source,
-		 gpointer user_data)
+                 GrlSource *source,
+                 gpointer user_data)
 {
   GRL_DEBUG ("Detected new source available: '%s'",
-	   grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)));
+             grl_source_get_name (source));
 
-  GRL_DEBUG ("\tPlugin's name: %s", grl_media_plugin_get_name (GRL_MEDIA_PLUGIN (source)));
-  GRL_DEBUG ("\tPlugin's description: %s", grl_media_plugin_get_description (GRL_MEDIA_PLUGIN (source)));
-  GRL_DEBUG ("\tPlugin's author: %s", grl_media_plugin_get_author (GRL_MEDIA_PLUGIN (source)));
-  GRL_DEBUG ("\tPlugin's license: %s", grl_media_plugin_get_license (GRL_MEDIA_PLUGIN (source)));
-  GRL_DEBUG ("\tPlugin's version: %s", grl_media_plugin_get_version (GRL_MEDIA_PLUGIN (source)));
-  GRL_DEBUG ("\tPlugin's web site: %s", grl_media_plugin_get_site (GRL_MEDIA_PLUGIN (source)));
+  GRL_DEBUG ("\tPlugin's name: %s", grl_source_get_name (source));
+  GRL_DEBUG ("\tPlugin's description: %s", grl_source_get_description (source));
 
   /* If showing the plugin list, refresh it */
   if (!ui_state->cur_source && !ui_state->cur_container) {
-    show_plugins ();
+    show_browsable_sources ();
   }
 
   /* Also refresh the search combos */
@@ -2104,7 +2099,7 @@ source_added_cb (GrlPluginRegistry *registry,
 
   /* Check for changes in source (if supported) */
   if (ui_state->changes_notification &&
-      (grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (source)) &
+      (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",
@@ -2117,15 +2112,15 @@ source_added_cb (GrlPluginRegistry *registry,
 
 static void
 source_removed_cb (GrlPluginRegistry *registry,
-		   GrlMediaPlugin *source,
-		   gpointer user_data)
+                   GrlSource *source,
+                   gpointer user_data)
 {
   GRL_DEBUG ("Source '%s' is gone",
-             grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)));
+             grl_source_get_name (source));
 
   if (!ui_state->cur_source && !ui_state->cur_container) {
     /* If showing the plugin list, refresh it */
-    show_plugins ();
+    show_browsable_sources ();
   } else if ((gpointer)ui_state->cur_source == user_data ) {
     /* If we were browsing that source, cancel operation and  go back to
        plugin list view */
@@ -2175,10 +2170,12 @@ shutdown_plugins (void)
   sources = grl_plugin_registry_get_sources (registry, FALSE);
   while (sources) {
     const gchar *plugin_id;
-    GrlMediaPlugin *source;
+    GrlPlugin *plugin;
+    GrlSource *source;
 
-    source = GRL_MEDIA_PLUGIN (sources->data);
-    plugin_id = grl_media_plugin_get_id (source);
+    source = GRL_SOURCE (sources->data);
+    plugin = grl_source_get_plugin (source);
+    plugin_id = grl_plugin_get_id (plugin);
     grl_plugin_registry_unload (registry, plugin_id, NULL);
 
     g_list_free (sources);
diff --git a/tools/vala/grilo-test.vala b/tools/vala/grilo-test.vala
index 38040f0..c7bee94 100644
--- a/tools/vala/grilo-test.vala
+++ b/tools/vala/grilo-test.vala
@@ -17,9 +17,7 @@ public class SimplePlaylist : Object {
 
 	}
 
-	public void source_added_cb (MediaPlugin plugin) {
-		var source = plugin as MetadataSource;
-
+	public void source_added_cb (Grl.Source source) {
 		var ops = source.supported_operations ();
 		if ((ops & Grl.SupportedOps.SEARCH) != 0) {
 			debug ("Detected new source availabe: '%s' and it supports search", source.get_name ());
@@ -28,8 +26,8 @@ public class SimplePlaylist : Object {
 		}
 	}
 
-	public void source_removed_cb (MediaPlugin source) {
-		debug ("Source '%s' is gone", (source as MetadataSource).get_name ());
+	public void source_removed_cb (Grl.Source source) {
+		debug ("Source '%s' is gone", source.get_name ());
 	}
 
 	public SimplePlaylist () {



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