[grilo/debian: 43/44] Merge commit 'grilo-0.1.16' into debian



commit 58a0818f553eed0596539cc97aa19aa16f1718aa
Merge: bee47d8 476ca57
Author: Juan A. Suarez Romero <jasuarez igalia com>
Date:   Mon Aug 1 08:59:24 2011 +0200

    Merge commit 'grilo-0.1.16' into debian
    
    Conflicts resolved:
    	configure.ac

 Makefile.am                              |   15 +-
 NEWS                                     |   20 +
 configure.ac                             |   34 +-
 doc/grilo/Makefile.am                    |   17 +-
 doc/grilo/environment-setup.xml          |  199 +++++
 doc/grilo/grilo-docs.sgml                |   44 +-
 doc/grilo/grilo-sections.txt             |  154 ++--
 doc/grilo/grilo.types                    |    2 +-
 doc/grilo/grl-inspect.1                  |   51 ++
 doc/grilo/overview.xml                   |   91 ++--
 doc/grilo/plugins-media-sources.xml      | 1171 ++++++++++++++++++++++++++++++
 doc/grilo/plugins-metadata-sources.xml   |  397 ++++++++++
 doc/grilo/plugins-testing.xml            |   99 +++
 doc/grilo/quick-start-using-grilo.xml    |  868 ----------------------
 doc/grilo/quick-start.xml                |   73 ++
 doc/grilo/writing-apps.xml               |  648 +++++++++++++++++
 doc/libs/grilo-libs-sections.txt         |    5 +-
 examples/Makefile.am                     |   20 +
 examples/browsing.c                      |  128 ++++
 examples/configuring-plugins.c           |   69 ++
 examples/efficient-metadata-resolution.c |  149 ++++
 examples/loading-plugins.c               |   71 ++
 examples/multivalues.c                   |  123 ++++
 examples/searching.c                     |  105 +++
 libs/net/grl-net-wc.c                    |    9 +-
 libs/net/grl-net-wc.h                    |   17 +-
 libs/net/wc-test.c                       |    4 +-
 src/Makefile.am                          |    5 +-
 src/data/grl-config.h                    |   19 +-
 src/data/grl-data.c                      |   11 +-
 src/data/grl-data.h                      |   21 +-
 src/data/grl-media-audio.h               |   16 +-
 src/data/grl-media-box.h                 |   16 +-
 src/data/grl-media-image.h               |   16 +-
 src/data/grl-media-video.h               |   16 +-
 src/data/grl-media.h                     |   16 +-
 src/data/grl-related-keys.h              |   20 +-
 src/grilo.c                              |    6 +-
 src/grilo.h                              |    1 +
 src/grl-log.c                            |   19 +-
 src/grl-media-source.c                   |   52 +-
 src/grl-metadata-key.c                   |   10 +
 src/grl-metadata-source.c                |  356 +++++-----
 src/grl-metadata-source.h                |   13 +-
 src/grl-multiple.c                       |   89 +--
 src/grl-multiple.h                       |    2 +-
 src/grl-operation-priv.h                 |   43 ++
 src/grl-operation.c                      |  168 +++++
 src/grl-operation.h                      |   42 ++
 src/grl-plugin-registry.c                |    6 +-
 src/grl-plugin-registry.h                |    2 +-
 src/grl-sync.c                           |    2 +-
 src/grl-util.c                           |    5 +
 tests/python/test_metadata_source.py     |    7 +-
 tools/grilo-test-ui/main.c               |  117 +++-
 55 files changed, 4296 insertions(+), 1383 deletions(-)
---
diff --cc configure.ac
index 77dd924,b7b25ac..a75ca8e
--- a/configure.ac
+++ b/configure.ac
@@@ -275,11 -282,7 +282,12 @@@ AC_CONFIG_FILES(
    doc/Makefile
    doc/grilo/Makefile
    doc/libs/Makefile
+   examples/Makefile
 +  debian/libgrilo-0.1-dev.install
 +  debian/libgrilo-0.1-vala.install
 +  debian/libgrilo-0.1-doc.install
 +  debian/libgrilo-0.1-doc.links
 +  debian/libgrilo-0.1.install
  ])
  
  AC_OUTPUT
diff --cc doc/grilo/environment-setup.xml
index 0000000,012705c..d5ed7f5
mode 000000,100644..100644
--- a/doc/grilo/environment-setup.xml
+++ b/doc/grilo/environment-setup.xml
@@@ -1,0 -1,199 +1,199 @@@
+ <?xml version="1.0"?>
+ <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"; [
+ <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+ ]>
+ 
+ <section id="environment-setup">
+ 
+   <para>
+     This section provides information on how to configure a proper environment for
+     building Grilo, as well building and running Grilo based programs.
+   </para>
+ 
+   <section id="building-grilo-detailed">
+     <title>Building Grilo </title>
+ 
+     <section id="building-grilo-sources-install">
+       <title>Building and installing Grilo</title>
+ 
+       <para>
+         You can check the basics of how to build and install Grilo from the
+         source code repositories in the
+         <link linkend="quick-start">Quick start guide</link>.
+       </para>
+     </section>
+ 
+     <section id="building-grilo-sources-uninstalled">
+       <title>Building and not installing Grilo</title>
 -      
++
+       <para>
+         If you do not want to install Grilo and rather use it in uninstalled
+         form, do:
+       </para>
 -    
++
+       <programlisting>
+ # Building Grilo
+ $ git clone git://git.gnome.org/grilo
+ $ cd grilo
+ $ ./autogen.sh
+ $ make
+ 
+ # Building Grilo Plugins
+ $ export PKG_CONFIG_PATH=$PWD:$PKG_CONFIG_PATH
+ $ cd ..
+ $ git clone git://git.gnome.org/grilo-plugins
+ $ cd grilo-plugins
+ $ ./autogen.sh --enable-uninstalled
+ $ make
+ $ source ./setup-plugins-env.sh
+       </programlisting>
 -    
++
+       <para>
+         Please, notice the '--enable-uninstalled' switch passed to the autogen.sh
+         script in grilo-plugins and the PKG_CONFIG_PATH export.
+       </para>
 -      
++
+       <para>
+         The last 'source' command looks for the plugins built in the local repository
+         and exports the GRL_PLUGIN_PATH environment variable accordingly, making them
+         visible to Grilo based applications. Notice that if that if this last step
+         is ommitted Grilo based applications (like grilo-test-ui) will not
+         see any of the plugins available.
+       </para>
+     </section>
+ 
+     <section id="compilation-options">
+       <title>Compilation options</title>
+       <para>
+         This is a reference of the most useful compilation switches available:
+       </para>
+       <itemizedlist>
+         <listitem>
+           --enable-gtk-doc: Build documentation
+           tutorials and API reference. Check doc/ directory for
+           the generated documentation. Only in grilo core.
+         </listitem>
+         <listitem>
+           --enable-debug: Build with debug information.
+         </listitem>
+         <listitem>
+           --enable-uninstalled: Prepare for uninstalled use. Only
+           in grilo-plugins.
+         </listitem>
+       </itemizedlist>
 -      
++
+       <para>
+         For more options please run './configure --help'.
+       </para>
+     </section>
+   </section>
+ 
+   <section id="compiling-grilo-programs-detailed">
+     <title>Compiling Grilo based programs</title>
 -    
++
+     <para>
+       You can check how to compile Grilo based apps
+       by checking the
+       <link linkend="quick-start">Quick start guide</link>.
+     </para>
+   </section>
+ 
+   <section id="running-grilo-programs-detailed">
+     <title>Running Grilo based programs</title>
 -    
++
+     <para>
+       If you have installed the Grilo libraries in your system
+       make sure they have been installed in a path that is
+       included in the dynamic linker search path, otherwise
+       export the environment variable LD_LIBRARY_PATH to
+       include them, for example:
+     </para>
+ 
+   <programlisting>
+ $ export LD_LIBRARY_PATH=/usr/local/lib
+   </programlisting>
+ 
+ 
+     <para>
+       For plugins to be visible to application they must be
+       stored in the default plugin search path. This should be
+       the case if you are not running Grilo in uninstalled form.
+       If you are running Grilo uninstalled, or for some reason
+       you have plugins available in directories other than
+       the default plugin path, export the GRL_PLUGIN_PATH to
+       point to the list of directories containing Grilo plugins,
+       for example:
+     </para>
 -    
++
+     <programlisting>
+ $ export GRL_PLUGIN_PATH=/usr/local/lib/grilo-0.2
+     </programlisting>
+ 
+     <para>
+       Notice that if you are running Grilo uninstalled the above will not
+       be necessary if you have followed the instructions provided in this
+       documentation, since sourcing the setup-plugins-env.sh script will
+       export the variable automatically for you.
+     </para>
+   </section>
+ 
+   <section id="debugging-with-grilo">
+     <title>Debugging with Grilo</title>
+ 
+     <para>
+       If you want to debug Grilo itself, make sure you have built it with
+       --enable-debug (both the framework and the plugins).
+     </para>
+ 
+     <para>
+       You can also control logging by setting the environment variable GRL_DEBUG.
+       Here are some examples:
+     </para>
+ 
+     <programlisting>
+ # Disable logging for all modules
+ $ export GRL_DEBUG=*:-
+ 
+ # Enable full logging for all modules
+ $ export GRL_DEBUG=*:*
+ 
+ # Enable logging of messages of level WARNING or higher  for all modules
+ $ export GRL_DEBUG=*:warning
+ 
+ # Enable logging of messages of level INFO or higher for all modules
+ $ export GRL_DEBUG=*:info
+ 
+ # Enable full logging for the plugin registry module
+ $ export GRL_DEBUG=plugin-registry:*
+ 
+ # Enable full logging for the plugin registry module
+ # and WARNING or higher for the Youtube plugin
+ $ export GRL_DEBUG=plugin-registry:*,youtube:warning
+     </programlisting>
+ 
+     <para>
+       Programs can also control logging programatically. See the
+       <link linkend="grilo-grl-log">GrlLog</link> API reference for details.
+     </para>
+ 
+     <para>
+       Plugins can be ranked. Ranks can be used to sort plugins
+       by rank and also in case of conflict when two plugins offer the same
+       feature, to decide on the one to use. By default, all the plugins
+       have the same rank (0). Higher ranks give plugins more relevance /
+       priority.
+     </para>
+ 
+     <para>
+       Ranks can be controlled with the environment variable GRL_PLUGIN_RANKS.
+       Here is one example:
+     </para>
+ 
+     <programlisting>
+ # Set the rank of the Youtube plugin to 5 and the Bookmarks plugin to -4
+ $ export GRL_PLUGIN_RANKS=youtube:5,bookmarks:-4
+     </programlisting>
+ 
+   </section>
+ </section>
diff --cc doc/grilo/overview.xml
index be9f6fb,aa169ae..4c65edc
--- a/doc/grilo/overview.xml
+++ b/doc/grilo/overview.xml
@@@ -14,49 -14,50 +14,50 @@@ Jamendo, Flickr, SHOUTCast or UPnP are 
  
  
  <para>
- Creating powerful applications that are capable of integrating all
- these services is a time consuming task that requires to invest
- a lot of effort not only in coding but also in learning about all
- these services and the particularities of each one.
+ Creating powerful, modern media applications that integrate content
 -from multiple services is a time consuming task that demands 
++from multiple services is a time consuming task that demands
+ a lot of effort on various fronts:
 -<itemizedlist> 
 -  <listitem>Learning all the APIs involved.</listitem> 
++<itemizedlist>
++  <listitem>Learning all the APIs involved.</listitem>
+   <listitem>Learning and integrating all the required technologies.</listitem>
 -  <listitem>Abstracting the differences among content providers.</listitem> 
++  <listitem>Abstracting the differences among content providers.</listitem>
+   <listitem>Maintenance.</listitem>
 -</itemizedlist> 
++</itemizedlist>
  </para>
  
- 
  <para>
- Unfortunately, applications that already implement support for
- some of these services do so in a way that is application-specific,
- disabling the possibility to reuse this code directly in other
- projects. This means that other applications out there have to do all
- that work again if they want to integrate the same services.
+ Unfortunately, applications that implement support for
+ these services nowadays do so in a way that is too application-specific.
+ This disables the possibility of reusing the solution in other
+ projects directly, meaning that other solutions have to do all
+ that work again if they want to accomplish the same target, which is
+ rather inefficient and makes this type of solutions quite expensive
+ in terms of development time and maintenance.
  </para>
  
  <para>
  In this context, the target of Grilo is to provide application
  developers with a framework where they can work together in the
- creation of application-independent plugins for managing media content
- that can be reused directly by applications. This model adds a number
- of important benefits:
+ development of application-independent plugins for managing media content
+ that can be reused directly in modern media applications. This model comes
+ with a number of important benefits:
  </para>
  
 -<itemizedlist> 
++<itemizedlist>
+ 
+ <listitem>
  <para><emphasis>Less work on the application side.</emphasis>
- All the plugin development happens in the framework and application
- developers can focus on making good user interfaces. It is the
- same with GStreamer if you think about it, application developers
- do not have to write decoders to playback content any more, they
- get the decoders from GStreamer (the framework) and that eases a
- lot application development. Well, this is the same idea, but applied
- to media browsing and discovery instead of media playback.
+ Plugin development happens in the framework and application
+ developers can focus on making good user interfaces instead.
  </para>
+ </listitem>
  
+ <listitem>
  <para>
  <emphasis>Code reuse.</emphasis>
- There are many media player applications allowing users
- to access contents from various services. This is nice, but all this
- is done at the application level, which usually means that all that
- code cannot be directly reused in other projects. Because of that
- there are developers writing application specific plugins for all
- these services in various applications (Totem, Rhythmbox, Amarok,
- etc), replicating code that cannot be reused directly in other
- applications. If the plugins were developed on the framework side, all
+ There are many applications allowing users to access media content
+ from various providers though application-specific plugins.
+ If the plugins were developed on the framework side, all
  these developers could share efforts and write support for these
  services only once in the framework, making them available for all
  the applications using the framework for free. Every one wins.
diff --cc doc/grilo/plugins-media-sources.xml
index 0000000,77422b1..8aa3c62
mode 000000,100644..100644
--- a/doc/grilo/plugins-media-sources.xml
+++ b/doc/grilo/plugins-media-sources.xml
@@@ -1,0 -1,1171 +1,1171 @@@
+ <?xml version="1.0"?>
+ <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"; [
+ <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+ ]>
+ 
+ <section>
+ <section id="media-source-plugins-intro">
+   <title>Introduction</title>
+ 
+   <para>
+     Media Source plugins provide access to media content. Examples
+     of Media Source plugins are the Jamendo or UPnP plugins, which
+     give access to content offered by Jamendo or content available
+     on UPnP servers respectively.
+   </para>
+ 
+   <para>
+     Usually, clients interact with these plugins in various ways:
+     <itemizedlist>
+       <listitem>
+         <emphasis>Search.</emphasis>
+         Users can instruct the media provider to search for
+         content that matches certain keywords. This is how people
+         typically interact with services like YouTube, for example.
+       </listitem>
+       <listitem>
+         <emphasis>Browse.</emphasis>
+         Users navigate through a fixed hierarchy of
+         categorized content interactively. This is how people
+         typically interact with UPnP services, for example.
+       </listitem>
+       <listitem>
+         <emphasis>Query.</emphasis>
+         Some times services provide features that
+         are too specific to be transported to a generic,
+         cross-service API. An example of this could be certain
 -        search filtering options. Queries allow users to 
++        search filtering options. Queries allow users to
+         interact with services using service-specific language
+         that can be used to exploit these features.
+       </listitem>
+       <listitem>
+         <emphasis>Metadata.</emphasis>
+         Users can request additional information (metadata)
+         for a specific media item served by a media provider through
+         a previous browse, search or query operation that was configured
+         to retrieve only partial metadata (typically for optimization
+         purposes). Metadata operations are usually used when showing
+         detailed information about specific media items.
+       </listitem>
+       <listitem>
+         <emphasis>Store.</emphasis>
+         Some media providers allow (or even require) users
+         to push content to them. This is how people interact with
+         Podcasts for example, they "store" the feeds they are
+         interested in following first.
+       </listitem>
+       <listitem>
+         <emphasis>Remove.</emphasis>
+         The opposite to the Store operation, used to remove
+         content from the source.
+       </listitem>
 -    </itemizedlist> 
++    </itemizedlist>
+   </para>
+ </section>
+ 
+ <section id="media-source-plugins-basics">
+   <title>Registering Media Source Plugins</title>
 -  
++
+   <para>
+     Grilo plugins must use the macro GRL_PLUGIN_REGISTER(), which defines the
+     entry and exit points of the plugin (called when the plugin is loaded and
+     unloaded respectively) as well as its plugin identifier (a string
+     identifying the plugin).
+   </para>
+ 
+   <para>
+     The plugin identifier will be used by clients to identify
+     the plugin when interacting with the plugin registry API. See
+     the <link linkend="GrlPluginRegistry">GrlPluginRegistry</link>
+     API reference for more details.
+   </para>
+ 
+   <para>
+     The plugin initialization function is mandatory.
+     the plugin deinitialization function is optional.
+   </para>
+ 
+   <para>
+     Usually the plugin initialization function will create at least one <link
+     linkend="GrlMediaSource">GrlMediaSource</link> instance and register it
+     using <link
+     linkend="grl-plugin-registry-register-source">grl_plugin_registry_register_source()</link>.
+   </para>
+ 
+   <para>
+     A <link linkend="GrlMediaSource">GrlMediaSource</link> instance represents a
+     particular source of media. Usually each plugin would spawn just one media
+     source, but some plugins may spawn multiple media sources.  For example, a
+     UPnP plugin spawning one media source object for each UPnP server
+     discovered.
+   </para>
+ 
+   <para>
+     Users can query the registry for available media sources and then use the
+     <link linkend="GrlMediaSource">GrlMediaSource</link> API to interact with
+     them.
+   </para>
+ 
+   <para>
+     If the plugin requires configuration this should be processed
+     during the plugin initialization function, which should return
+     TRUE upon successful initialization or FALSE otherwise.
+   </para>
+ 
+   <para>
+     The parameter "configs" of the plugin initialization function provides
+     available configuration information provided by the user for this plugin, if
+     any. This parameter is a list of <link linkend="GrlConfig">GrlConfig</link>
+     objects. Usually there would be only one <link
+     linkend="GrlConfig">GrlConfig</link> object in the list, but there might be
+     more in the cases of plugins spawning multiple media sources that require
+     different configuration options.
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ gboolean
+ grl_foo_plugin_init (GrlPluginRegistry *registry,
+                      const GrlPluginInfo *plugin,
+                      GList *configs)
+ {
+   gchar *api_key;
+   GrlConfig *config;
+ 
+   config = GRL_CONFIG (configs->data);
+ 
+   api_key = grl_config_get_api_key (config);
+   if (!api_key) {
+     GRL_INFO ("Missing API Key, cannot load plugin");
+     return FALSE;
+   }
+ 
+   GrlFooSource *source = grl_foo_source_new (api_key);
+   grl_plugin_registry_register_source (registry,
+                                        plugin,
+                                        GRL_MEDIA_PLUGIN (source),
+                                        NULL);
+   g_free (api_key);
+   return TRUE;
+ }
+ 
+ GRL_PLUGIN_REGISTER (grl_foo_plugin_init, NULL, "grl-foo");
+ ]]>
+   </programlisting>
+ 
+   <para>
+     The next step is to implement the plugin code, for that Media Source plugins
+     must extend the <link linkend="GrlMediaSource">GrlMediaSource</link> class.
+   </para>
+ 
+   <para>
+     In typical GObject fashion, developers should use the G_DEFINE_TYPE macro,
+     and then provide the class initialization function
+     (grl_foo_source_class_init() in the example below) and the instance
+     initialization function (grl_foo_source_init() in the example below). A
+     constructor function, although not mandatory, is usually nice to have
+     (grl_foo_source_new() in the example below).
+   </para>
+ 
+   <para>
+     When creating a new <link linkend="GrlMediaSource">GrlMediaSource</link>
+     instance, a few properties should be provided:
+     <itemizedlist>
+       <listitem>
+         <emphasis>source-id:</emphasis> An identifier for the source object.
+         This is not the same as the plugin identifier (remember that a plugin
+         can spawn multiple media source objects). This identifier can be
+         used by clients when interacting with available media sources
+         through the plugin registry API. See
+         the <link linkend="GrlPluginRegistry">GrlPluginRegistry</link>
+         API reference for more details.
+       </listitem>
+       <listitem>
+         <emphasis>source-name:</emphasis> A name for the source object
+         (typically the name that clients would show in the user interface).
+       </listitem>
+       <listitem>
+         <emphasis>source-desc</emphasis>: A description of the media source.
+       </listitem>
+     </itemizedlist>
+   </para>
+ 
+   <para>
+     In the class initialization function the plugin developer should
+     provide implementations for the operations that the plugin will
+     support. Most operations are optional, but for media sources
+     at least one of Search, Browse and Query are expected to be
+     implemented. Store and Remove are optional. Metadata is expected
+     to be implemented, just like supported_keys. Slow_keys is optional.
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ /* Foo class initialization code */
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+   GrlMediaSourceClass *source_class = GRL_MEDIA_SOURCE_CLASS (klass);
+   GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass);
+ 
+   metadata_class->supported_keys = grl_foo_source_supported_keys;
+   metadata_class->slow_keys = grl_foo_source_supported_keys;
+ 
+   source_class->browse = grl_foo_source_browse;
+   source_class->search = grl_foo_source_search;
+   source_class->query = grl_foo_source_query;
+   source_class->store = grl_foo_source_store;
+   source_class->remove = grl_foo_source_remove;
+   source_class->metadata = grl_foo_source_metadata;
+ }
+ 
+ /* Foo instance initialization code */
+ static void
+ grl_foo_source_init (GrlFooSource *source)
+ {
+   /* Here you would initialize 'source', which is an instance
+      of this class type. */
+   source->api_key = NULL;
+ }
+ 
+ /* GrlFooSource constructor */
+ static GrlFooSource *
+ grl_foo_source_new (const gchar *api_key)
+ {
+   GrlFooSource *source;
+ 
+   source = GRL_FOO_SOURCE (g_object_new (GRL_FOO_SOURCE_TYPE,
+                                          "source-id", "grl-foo",
+                                          "source-name", "Foo",
+                                          "source-desc", "Foo media provider",
+                                          NULL));
+   source->api_key = g_strdup (api_key);
+   return source;
+ }
+ 
+ G_DEFINE_TYPE (GrlFooSource, grl_foo_source, GRL_TYPE_MEDIA_SOURCE);
+ ]]>
+   </programlisting>
+ </section>
+ 
+ <section id="media-source-plugins-supported-keys">
+   <title>Implementing Supported Keys</title>
 -  
++
+   <para>
+     An implementation for the "supported_keys" method is mandatory for
+     the plugin to work.
+   </para>
+ 
+   <para>
 -    This method is declarative, and it only has to return a list of 
++    This method is declarative, and it only has to return a list of
+     metadata keys that the plugin supports, that is, it is a declaration
+     of the metadata that the plugin can provide for the media content
+     that it exposes.
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass);
+   metadata_class->supported_keys = grl_foo_source_supported_keys;
+ }
+ 
+ static const GList *
+ grl_foo_source_supported_keys (GrlMetadataSource *source)
+ {
+   static GList *keys = NULL;
+   if (!keys) {
+     keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
+                                       GRL_METADATA_KEY_TITLE,
+                                       GRL_METADATA_KEY_URL,
+                                       GRL_METADATA_KEY_THUMBNAIL,
+                                       GRL_METADATA_KEY_MIME,
+                                       GRL_METADATA_KEY_ARTIST,
+                                       GRL_METADATA_KEY_DURATION,
+                                       NULL);
+   }
+   return keys;
+ }
+ ]]>
+   </programlisting>
+ </section>
+ 
+ <section id="media-source-plugins-slow-keys">
+   <title>Implementing Slow Keys</title>
 -  
++
+   <para>
+     Implementation of the "slow_keys" method is optional, but in some
+     cases it can help to improve performance remarkably.
+   </para>
+ 
+   <para>
+     This method is intended to provide the framework with information
+     on metadata that is particularly expensive for the framework
 -    to retrieve. The framework (or the plugin users) can then 
++    to retrieve. The framework (or the plugin users) can then
+     use this information to remove this metadata from their requests
+     when performance is important. This is, again, a declarative
+     interface providing a list of keys.
+   </para>
+ 
+   <para>
+     If the plugin does not provide an implementation for "slow_keys"
+     the framework assumes that all keys are equally expensive to
+     retrieve and will not perform optimizations in any case.
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass);
+   metadata_class->slow_keys = grl_foo_source_slow_keys;
+ }
+ 
+ static const GList *
+ grl_foo_source_slow_keys (GrlMetadataSource *source)
+ {
+   static GList *keys = NULL;
+   if (!keys) {
+     keys = grl_metadata_key_list_new (GRL_METADATA_KEY_URL,
+                                       NULL);
+   }
+   return keys;
+ }
+ ]]>
+   </programlisting>
+ </section>
+ 
+ <section id="media-source-plugins-search">
+   <title>Implementing Search</title>
 -  
++
+   <para>
+     Implementation of the "search" method is optional, but at least one
+     of Search, Browse and Query are expected to be implemented.
+   </para>
+ 
+   <para>
+     This method implements text based searches, retrieving media
+     that matches the text keywords provided by the user.
+   </para>
+ 
+   <para>
+     Typically, the way this method operates is like this:
+     <itemizedlist>
+       <listitem>Plugin checks the input parameters and encodes the
+         search operation as expected by the service provider
+         (that could be a SQL query, a HTTP request, etc)</listitem>
+       <listitem>Plugin executes the search on the backend. Typically this
+         involves some kind blocking operation (networking, disk access, etc)
+         that should be executed asynchronously when possible.</listitem>
+       <listitem>Plugin receives the results from the media provider.  For each
+       result received the plugin creates a <link
+       linkend="GrlMedia">GrlMedia</link> object encapsulating the metadata
+       obtained for that particular match.</listitem>
+       <listitem>Plugin sends the <link linkend="GrlMedia">GrlMedia</link>
+       objects back to the client one by one by invoking the user provided
+       callback.</listitem>
+     </itemizedlist>
+   </para>
+ 
+   <para>
+     Below you can see some source code that illustrates this process:
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ /* In this example we assume a media provider that can be
+    queried over http, and that provides its results in xml format */
+ 
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMediaSourceClass *media_class = GRL_MEDIA_SOURCE_CLASS (klass);
+   media_class->search = grl_foo_source_search;
+ }
+ 
+ static void
+ foo_execute_search_async_cb (gchar *xml, GrlMediaSourceSearchSpec *ss)
+ {
+   GrlMedia *media;
+   gint count;
+ 
+   count = count_results (xml);
+ 
+   if (count == 0) {
+     /* Signal "no results" */
+     ss->callback (ss->source, ss->operation_id,
+                   NULL, 0, ss->user_data, NULL);
+   } else {
+     /* parse_next parses the next media item in the XML
+        and creates a GrlMedia instance with the data extracted */
+     while (media = parse_next (xml))
+       ss->callback (ss->source,       /* Source emitting the data */
+                     ss->operation_id, /* Operation identifier */
+                     media,            /* Media that matched the query */
+                     --count,          /* Remaining count */
+                     ss->user_data,    /* User data for the callback */
+                     NULL);            /* GError instance (if an error occurred) */
+   }
+ }
+ 
+ static void
+ grl_foo_source_search (GrlMediaSource *source, GrlMediaSourceSearchSpec *ss)
+ {
+   gchar *foo_http_search:
 -  
++
+   foo_http_search =
+     g_strdup_printf("http://media.foo.com?text=%s&offset=%d&count=%d";,
+                     ss->text, ss->skip, ss->count);
+ 
+   /* This executes an async http query and then invokes
+      foo_execute_search_async_cb with the response */
+   foo_execute_search_async (foo_http_search, ss);
+ }
+ ]]>
+   </programlisting>
+ 
+   <para>
+     Please, check <link linkend="media-source-plugins-common-considerations">
+       Common considerations for Search, Browse and Query implementations</link>
+     for more information on how to implement Search operations properly.
+   </para>
+ 
+   <para>
+     Examples of plugins implementing Search functionality are
+     grl-jamendo, grl-youtube or grl-vimeo among others.
+   </para>
+ </section>
+ 
+ <section id="media-source-plugins-browse">
+   <title>Implementing Browse</title>
 -  
++
+   <para>
+     Implementation of the "browse" method is optional, but at least one
+     of Search, Browse and Query are expected to be implemented.
+   </para>
+ 
+   <para>
+     Browsing is an interactive process, where users navigate by exploring
+     these boxes exposed by the media source in hierarchical form. The idea
+     of browsing a media source is the same as browsing a file system.
+   </para>
+ 
+   <para>
+     The signature and way of operation of the Browse operation is the same as in
+     the Search operation with one difference: instead of a text parameter with
+     the search keywords, it receives a <link linkend="GrlMedia">GrlMedia</link>
+     object representing the container (box) the user wants to browse.
+   </para>
+ 
+   <para>
+     For the most part, plugin developers that write Browse implementations
+     should consider the same rules and guidelines explained for
+     Search operations.
+   </para>
+ 
+   <para>
+     Below you can see some source code that illustrates this process:
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ /* In this example we assume a media provider that can be queried over
+    http, providing results in XML format. The media provider organizes
+    content according to a list of categories. */
+ 
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMediaSourceClass *media_class = GRL_MEDIA_SOURCE_CLASS (klass);
+   media_class->browse = grl_foo_source_browse;
+ }
+ 
+ static void
+ foo_execute_categories_async_cb (gchar *xml, GrlMediaSourceBrowseSpec *bs)
+ {
+   GrlMedia *media;
+   gint count;
+ 
+   count = count_results (xml);
+ 
+   if (count == 0) {
+     /* Signal "no results" */
+     bs->callback (bs->source, bs->operation_id,
+                   NULL, 0, bs->user_data, NULL);
+   } else {
+     /* parse_next parses the next category item in the XML
+        and creates a GrlMedia instance with the data extracted,
+        which should be of type GrlMediaBox */
+     while (media = parse_next_cat (xml))
+       bs->callback (bs->source,       /* Source emitting the data */
+                     bs->operation_id, /* Operation identifier */
+                     media,            /* The category (box)  */
+                     --count,          /* Remaining count */
+                     bs->user_data,    /* User data for the callback */
+                     NULL);            /* GError instance (if an error occurred) */
+   }
+ }
+ 
+ static void
+ foo_execute_media_async_cb (gchar *xml, GrlMediaSourceBrowseSpec *os)
+ {
+   GrlMedia *media;
+   gint count;
+ 
+   count = count_results (xml);
+ 
+   if (count == 0) {
+     /* Signal "no results" */
+     bs->callback (bs->source, bs->operation_id,
+                   NULL, 0, bs->user_data, NULL);
+   } else {
+     /* parse_next parses the next media item in the XML
+        and creates a GrlMedia instance with the data extracted,
+        which should be of type GrlMediaImage, GrlMediaAudio or
+        GrlMediaVideo */
+     while (media = parse_next_media (xml))
+       os->callback (os->source,       /* Source emitting the data */
+                     os->operation_id, /* Operation identifier */
+                     media,            /* Media that matched the query */
+                     --count,          /* Remaining count */
+                     os->user_data,    /* User data for the callback */
+                     NULL);            /* GError instance (if an error occurred) */
+   }
+ }
+ 
+ static void
+ grl_foo_source_browse (GrlMediaSource *source, GrlMediaSourceBrowseSpec *bs)
+ {
+   gchar *foo_http_browse:
 -  
++
+   /* We use the names of the categories as their media identifiers */
+   box_id = grl_media_get_id (bs->container);
+ 
+   if (!box_id) {
+     /* Browsing the root box, the result must be the list of
+        categories provided by the service */
+     foo_http_browse =
+       g_strdup_printf("http://media.foo.com/category_list";,
+                       os->skip, os->count);
+      /* This executes an async http query and then invokes
+         foo_execute_categories_async_cb with the response */
+      foo_execute_categories_async (foo_http_browse, os);
+   } else {
+     /* Browsing a specific category */
+     foo_http_browse =
+       g_strdup_printf("http://media.foo.com/content/%s?offset=%d&count=%d";,
+                       box_id, os->skip, os->count);
+      /* This executes an async http query and then invokes
+         foo_execute_browse_async_cb with the response */
+      foo_execute_media_async (foo_http_browse, os);
+   }
+ }
+ ]]>
+   </programlisting>
+ 
+   <para>
+     Some considerations that plugin developers should take into account:
+     <itemizedlist>
+       <listitem>
+         In the example we are assuming that the content hierarchy only has two
+         levels, the first level exposes a list of categories (each one exposed
+         as a <link linkend="GrlMediaBox">GrlMediaBox</link> object so the user
+         knows they can be browsed again), and then a second level with the
+         contents within these categories, that we assume are all media items,
+         although in real life they could very well be more <link
+         linkend="GrlMediaBox">GrlMediaBox</link> objects, leading to more
+         complex hierarchies.
+       </listitem>
+       <listitem>
+         <link linkend="GrlMediaBox">GrlMediaBox</link> objects returned by a
+         browse operation can be browsed by clients in future Browse operations.
+       </listitem>
+       <listitem>
+         The input parameter that informs the plugin about the box that should be
+         browsed (bs->container) is of type <link
+         linkend="GrlMediaBox">GrlMediaBox</link>. The plugin developer must map
+         that to something the media provider understands. Typically, when <link
+         linkend="GrlMedia">GrlMedia</link> objects are returned from a plugin to
+         the client, they are created so their "id" property (<link
+         linkend="grl-media-set-id">grl_media_set_id()</link>) can be used for
+         this purpose, identifying these media resources uniquely in the context
+         of the media provider.
+       </listitem>
+       <listitem>
+         A <link linkend="GrlMediaBox">GrlMediaBox</link> object with NULL id
+         always represents the root box/category in the content hierarchy exposed
+         by the plugin.
+       </listitem>
+     </itemizedlist>
+   </para>
+ 
+   <para>
+     Please, check <link linkend="media-source-plugins-common-considerations">
+       Common considerations for Search, Browse and Query implementations</link>
+     for more information on how to implement Browse operations properly.
+   </para>
+ 
+   <para>
+     Examples of plugins implementing browse functionality are
+     grl-jamendo, grl-filesystem or grl-upnp among others.
+   </para>
+ 
+ </section>
+ 
+ <section id="media-source-plugins-query">
+   <title>Implementing Query</title>
 -  
++
+   <para>
+     Implementation of the "query" method is optional, but at least one
+     of Search, Browse and Query are expected to be implemented.
+   </para>
+ 
+   <para>
+     This method provides plugin developers with means to expose
+     service-specific functionality that cannot be achieved
+     through regular Browse and Search operations.
+   </para>
+ 
+   <para>
+     This method operates just like the Search method, but the text
+     parameter does not represent a list of keywords to search for,
+     instead, its meaning is plugin specific and defined by the plugin
 -    developer. 
++    developer.
+   </para>
+ 
+   <para>
+     Normally, Query implementations involve parsing and decoding this
+     input string into something meaningful for the media provider
+     (a specific operation with its parameters).
+   </para>
+ 
+   <para>
+     Usually, Query implementations are intended to provide advanced
+     filtering capabilities and similar features that make use of
+     specific features of the service that cannot be
+     exposed through more service agnostic APIs, like Search or
+     Browse. For example, a plugin that provides media content
 -    stored in a database can implement Query to give users the 
++    stored in a database can implement Query to give users the
+     possibility to execute SQL queries directly, by encoding the
+     SQL commands in this input string, giving a lot of flexibility
+     in how they access the content stored in the database in
+     exchange for writing plugin-specific code in the application.
+   </para>
+ 
+   <para>
+     The example below shows the case of a plugin implementing
+     Query to let the user specify filters directly in SQL
+     format for additional flexibility.
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMediaSourceClass *media_class = GRL_MEDIA_SOURCE_CLASS (klass);
+   media_class->query = grl_foo_source_query;
+ }
+ 
+ static void
+ grl_foo_source_query (GrlMediaSource *source, GrlMediaSourceQuerySpec *qs)
+ {
+   const gchar *sql_filter;
+   GList *results;
+   GrlMedia *media;
+   gint count;
+ 
+   /* qs->text is expected to contain a suitable SQL filter */
+   sql_query = prepare_sql_with_custom_filter (qs->text, qs->skip, qs->count);
 -  
++
+   /* Execute the resulting SQL query, which incorporates
+      the filter provided by the user */
+   results = execute_sql (sql_query);
+ 
+   /* For each result obtained, invoke the user callback as usual */
+   count = g_list_length (results);
+ 
+   if (count == 0) {
+     /* Signal "no results" */
+     qs->callback (qs->source, qs->operation_id,
+                   NULL, 0, qs->user_data, NULL);
+   } else {
+     while (media = next_result (&results))
+       qs->callback (qs->source,       /* Source emitting the data */
+                     qs->operation_id, /* Operation identifier */
+                     media,            /* Media that matched the query */
+                     --count,          /* Remaining count */
+                     qs->user_data,    /* User data for the callback */
+                     NULL);            /* GError instance (if an error occurred) */
+   }
+ }
+ ]]>
+   </programlisting>
+ 
+   <para>
+     Please, check <link linkend="media-source-plugins-common-considerations">
+       Common considerations for Search, Browse and Query implementations</link>
+     for more information on how to implement Query operations properly.
+   </para>
+ 
+   <para>
+     Examples of plugins implementing Query are grl-jamendo,
+     grl-upnp or grl-bookmarks among others.
+   </para>
+ </section>
+ 
+ <section id="media-source-plugins-common-considerations">
+   <title>Common considerations for Search, Browse and Query implementations</title>
+ 
+   <para>
+     <itemizedlist>
+       <listitem>Making operations synchronous would block the client
+         application while the operation is executed, so providing
+         a non-blocking implementation is mostly mandatory for most practical
+         purposes.</listitem>
+       <listitem>
+         Grilo invokes plugin operations in idle callbacks to ensure that control
+         is returned to the client as soon as possible. Still, plugin developers
+         are encouraged to write efficient code that avoids blocking as much as
+         possible, since this good practice will make applications behave
+         smoother, granting a much better user experience. Use of threads in
 -        plugin code is not recommended, instead, splitting the work to do in 
++        plugin code is not recommended, instead, splitting the work to do in
+         chunks using the idle loop is encouraged.
+       </listitem>
+       <listitem>Creating <link linkend="GrlMedia">GrlMedia</link> instances is
+       easy, depending on the type of media you should instantiate one of the
+       <link linkend="GrlMedia">GrlMedia</link> subclasses (<link
+       linkend="GrlMediaImage">GrlMediaImage</link>, <link
+       linkend="GrlMediaVideo">GrlMediaVideo</link>, <link
+       linkend="GrlMediaAudio">GrlMediaAudio</link> or <link
+       linkend="GrlMediaBox">GrlMediaBox</link>), and then use the API to set the
+       corresponding data. Check the <link linkend="GrlData">GrlData</link>
+       hierarchy in the API reference for more details.
+       </listitem>
+       <listitem>The remaining count parameter present in the callbacks is intended
+         to provide the client with an <emphasis>estimation</emphasis> of how many
+         more results will come after the current one as part of the same operation.</listitem>
+       <listitem>Finalization of the operation must <emphasis>always</emphasis>
+         be signaled by invoking the user callback with remaining count set to 0,
+         even on error conditions.
+       </listitem>
+       <listitem>Plugin developers must ensure that all operations
+         end by invoking the user callback with the remaining count parameter
+         set to 0, and that this is done only once per operation. This
+         behavior is expected and must be guaranteed by the plugin developer.</listitem>
+       <listitem>
+         Once the user callback has been invoked with the remaining count
+         parameter set to 0, the operations is considered finished and the
+         plugin developer must <emphasis>never</emphasis>
+         invoke the user callback again for that operation again.
+       </listitem>
+       <listitem>
+         In case of error, the plugin developer must invoke the user
+         callback like this:
+         <itemizedlist>
+           <listitem>Set the last parameter to a non-NULL GError instance.</listitem>
+           <listitem>Set the media parameter to NULL.</listitem>
+           <listitem>Set the remaining count parameter to 0.</listitem>
+         </itemizedlist>
+         The plugin developer is responsible for releasing the error once
+         the user callback is invoked.
+       </listitem>
+       <listitem>It is possible to finalize the operation with a NULL
+         media and remaining count set to 0 if that is convenient for the
+         plugin developer.
+       </listitem>
+       <listitem>
+         Returned <link linkend="GrlMedia">GrlMedia</link> objects are owned by
+         the client and should not be freed by the plugin.
+       </listitem>
+       <listitem>
+         The list of metadata information requested by the client is
+         available in the "keys" field of the Spec structure. Typically plugin
+         developers don't have to care about the list of keys requested and
+         would just resolve all metadata available. The only situation in which
+         the plugin developer should  check the specific list of keys requested
+         is when there are keys that are particularly expensive to
+         resolve, in these cases the plugin should only resolve these keys if
+         the user has indeed requested that information.
+       </listitem>
+     </itemizedlist>
+   </para>
+ </section>
+ 
+ 
+ <section id="media-source-plugins-metadata">
+   <title>Implementing Metadata</title>
 -  
++
+   <para>
+     Implementation of the "metadata" method is not mandatory
+     but would be usually expected by application developers.
+   </para>
+ 
+   <para>
+     The purpose of the metadata method is to provide additional metadata for
+     <link linkend="GrlMedia">GrlMedia</link> objects produced by the media
+     source.
+   </para>
+ 
+   <para>
+     Typically, the use case for Metadata operations is applications obtaining a
+     list of <link linkend="GrlMedia">GrlMedia</link> objects by executing a
+     Browse, Search or Query operation , requesting limited metadata (for
+     performance reasons), and then requesting additional metadata for specific
+     items selected by the user.
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMediaSourceClass *media_class = GRL_MEDIA_SOURCE_CLASS (klass);
+   media_class->metadata = grl_foo_source_metadata;
+ }
+ 
+ static void
+ foo_media_info_cb (gchar *xml, GrlMediaSourceMetadataSpec *ms)
+ {
+   /* This resolves metadata for keys "ms->keys" from "xml" and
+      stores them in "ms->media" */
+   resolve_metadata_from_xml (ms->media, ms->keys, xml);
+ 
+   /* Send updated ms->media back to the user */
+   ms->callback (ms->source, ms->metadata_id, ms->media, ms->user_data, NULL);
+ }
+ 
+ static void
+ grl_foo_source_metadata (GrlMediaSource *source, GrlMediaSourceMetadataSpec *ms)
+ {
+   const gchar *media_id;
+ 
+   media_id = grl_media_get_id (ms->media);
+ 
+   foo_media_info =
+     g_strdup_printf("http://media.foo.com/media-info/%s";, media_id);
+ 
+   /* This executes an async http query and then invokes
+      foo_metadata_cb with the response */
+      foo_execute_metadata_async (foo_media_info, ms);
+ }
+ ]]>
+   </programlisting>
+ 
+   <para>
+     Some considerations that plugin developers should take into account:
+     <itemizedlist>
+       <listitem>
+         Clients invoke this method passing the <link
+         linkend="GrlMedia">GrlMedia</link> object that they want to update
+         (ms->media). Plugin developers should resolve the requested metadata
+         (ms->keys) and store it in that <link linkend="GrlMedia">GrlMedia</link>
+         object.
+       </listitem>
+       <listitem>
+         Just like in other APIs, implementation of this method is expected
+         to be asynchronous to avoid blocking the user code.
+       </listitem>
+     </itemizedlist>
+   </para>
 -  
++
+   <para>
+     Examples of plugins implementing Metadata are grl-youtube,
+     grl-upnp or grl-jamendo among others.
+   </para>
+ </section>
+ 
+ 
+ <section id="media-source-plugins-store">
+   <title>Implementing Store</title>
 -  
++
+   <para>
+     Implementation of the "store" method is optional.
+   </para>
+ 
+   <para>
+     The Store method is used to push new content to the media source.
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMediaSourceClass *media_class = GRL_MEDIA_SOURCE_CLASS (klass);
+   media_class->store = grl_foo_source_store;
+ }
+ 
+ static void
+ grl_foo_source_store (GrlMediaSource *source, GrlMediaSourceStoreSpec *ss)
+ {
+   const gchar *title;
+   const gchar *uri;
+   const gchar *parent_id;
+   guint row_id;
+ 
+   /* We get the id of the parent container where we want
+      to put the new content */
+   parent_id = grl_media_get_id (GRL_MEDIA (parent));
+ 
+   /* We get he metadata of the media we want to push, in this case
+      only URI and Title */
+   uri = grl_media_get_uri ();
+   title = grl_media_get_title ();
 -  
++
+   /* Push the data to the media provider (in this case a database) */
+   row_id = run_sql_insert (parent_id, uri, title);
+ 
+   /* Set the media id in the GrlMedia object */
+   grl_media_set_id (ss->media, row_id_to_media_id (row_id));
+ 
+   /* Inform the user that the operation is done (NULL error means everything
+      was ok */
+   ss->callback (ss->source, ss->parent, ss->media, ss->user_data, NULL);
+ }
+ ]]>
+   </programlisting>
+ 
+   <para>
+     Some considerations that plugin developers should take into account:
+     <itemizedlist>
+       <listitem>
+         After successfully storing the media, the method should assign
+         a proper media id to it before invoking the user callback.
+       </listitem>
+     </itemizedlist>
+   </para>
+ 
+   <para>
+     Examples of plugins implementing Store are grl-bookmarks or
+     grl-podcasts.
+   </para>
+ </section>
+ 
+ 
+ <section id="media-source-plugins-remove">
+   <title>Implementing Remove</title>
 -  
++
+   <para>
+     Implementation of the "remove" method is optional.
+   </para>
+ 
+   <para>
+     The Remove method is used to remove content from the media source.
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMediaSourceClass *media_class = GRL_MEDIA_SOURCE_CLASS (klass);
+   media_class->remove = grl_foo_source_remove;
+ }
+ 
+ static void
+ grl_foo_source_remove (GrlMediaSource *source, GrlMediaSourceRemoveSpec *rs)
+ {
+   /* Remove the data from the media provider (in this case a database) */
+   run_sql_delete (ss->media_id);
+ 
+   /* Inform the user that the operation is done (NULL error means everything
+      was ok */
 -  rs->callback (rs->source, rs->media, rs->user_data, NULL);  
++  rs->callback (rs->source, rs->media, rs->user_data, NULL);
+ }
+ ]]>
+   </programlisting>
+ 
+   <para>
+     Examples of plugins implementing Remove are grl-bookmarks or
+     grl-podcasts.
+   </para>
+ </section>
+ 
+ <section id="media-source-plugins-media-from-uri">
+   <title>Implementing Media from URI</title>
+ 
+   <para>Implementation of the "media_from_uri" method is optional.</para>
+ 
+   <para>
+     Some times clients have access to the URI of the media, and they want to
+     retrieve metadata for it. A couple of examples where this may come in handy:
+     A file system browser that needs to obtain additional metadata for a
+     particular media item located in the filesystem. A browser plugin that can
+     obtain additional metadata for a media item given its URL. In these cases we
+     know the URI of the media, but we need to create a <link
+     linkend="GrlMedia">GrlMedia</link> object representing it.
+   </para>
+ 
+   <para>
+     Plugins that want to support URI to <link linkend="GrlMedia">GrlMedia</link>
+     conversions must implement the "test_media_from_uri" and "media_from_uri"
+     methods.
+   </para>
+ 
+   <para>
+     The method "test_media_from_uri" should return TRUE if, upon inspection of
+     the media URI, the plugin decides that it can convert it to a <link
+     linkend="GrlMedia">GrlMedia</link> object. For example, a YouTube plugin
+     would check that the URI of the media is a valid YouTube URL. This method is
+     asynchronous and should not block.  If the plugin cannot decide if it can or
+     cannot convert the URI to a <link linkend="GrlMedia">GrlMedia</link> object
+     by inspecting the URI without doing blocking operations, it should return
+     TRUE. This method is used to discard efficiently plugins that cannot resolve
+     the media.
+   </para>
+ 
+   <para>
+     The method "media_from_uri" is used to do the actual conversion from the URI
+     to the <link linkend="GrlMedia">GrlMedia</link> object.
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMediaSourceClass *media_class = GRL_MEDIA_SOURCE_CLASS (klass);
+   media_class->test_media_from_uri = grl_foo_source_test_media_from_uri;
+   media_class->media_from_uri = grl_foo_source_media_from_uri;
+ }
+ 
+ static gboolean
+ grl_filesystem_test_media_from_uri (GrlMediaSource *source,
+                                     const gchar *uri)
+ {
+   if (strstr (uri, "http://media.foo.com/media-info/";) == uri) {
+     return TRUE;
+   }
+   return FALSE;
+ }
+ 
+ static void
+ grl_filesystem_media_from_uri (GrlMediaSource *source,
+                                GrlMediaSourceMediaFromUriSpec *mfus)
+ {
+   gchar *media_id;
+   GrlMedia *media;
+ 
+   media_id = get_media_id_from_uri (mfus->uri);
+   media = create_media_from_id (media_id);
+   mfus->callback (source, mfus->media_from_uri_id, media, mfus->user_data, NULL);
+   g_free (media_id);
+ }
+ ]]>
+   </programlisting>
+ 
+   <para>
+     Some considerations that plugin developers should take into account:
+     <itemizedlist>
+       <listitem>
+         Typically "media_from_uri" involves a blocking operation, and hence
+         its implementation should be asynchronous.
+       </listitem>
+     </itemizedlist>
+ 
+     <para>
+       Examples of plugins implementing "media_from_uri" are grl-filesystem
+       or grl-youtube.
+     </para>
+   </para>
+ </section>
+ 
+ <section id="media-source-plugins-change_notification">
+   <title>Notifying changes</title>
+ 
+   <para>
+     Source can signal clients when available media content has been
+     changed. This is an optional feature.
+   </para>
+ 
+   <para>
+     Plugins supporting content change notification must implement
+     "notify_change_start" and "notify_change_stop", which let the
+     user start or stop content change notification at will.
+   </para>
+ 
+   <para>
+     Once users have activated notifications by invoking
+     "notify_change_start", media sources should communicate
+     any changes detected by calling grl_media_source_notify_change_list
+     with a list of the media items changed.
+   </para>
+ 
+   <para>
+     Upon calling "notify_changes_stop" the plugin must stop
+     communicating changes until "notify_changes_start" is
+     called again.
+   </para>
+ 
+   <programlisting role="C">
+     <![CDATA[
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMediaSourceClass *media_class = GRL_MEDIA_SOURCE_CLASS (klass);
+   media_class->notify_change_start = grl_foo_source_notify_change_start;
+   media_class->notify_change_stop = grl_foo_source_notify_change_stop;
+ }
+ 
+ static void
+ content_changed_cb (GList *changes)
+ {
+   GPtrArray *changed_medias;
+ 
+   changed_medias = g_ptr_array_sized_new (g_list_length (changes));
+   while (media = next_media_from_changes (&changes)) {
+     g_ptr_array_add (changed_medias, media);
+   }
+ 
+   grl_media_source_notify_change_list (source,
+                                        changed_medias,
+                                        GRL_CONTENT_CHANGED,
+                                        FALSE);
+ }
+ 
+ static gboolean
+ grl_foo_source_notify_change_start (GrlMediaSource *source,
+                                     GError **error)
+ {
+   GrlFooMediaSource *foo_source;
+ 
+   /* Listen to changes in the media content provider */
+   foo_source = GRL_FOO_MEDIA_SOURCE (source);
+   foo_source->listener_id = foo_subscribe_listener_new (content_changed_cb);
+ 
+   return TRUE;
+ }
+ 
+ static gboolean
+ grl_foo_source_notify_change_stop (GrlMediaSource *source,
+                                    GError **error)
+ {
+   GrlFooMediaSource *foo_source;
+ 
+   /* Stop listening to changes in the media content provider */
+   foo_source = GRL_FOO_MEDIA_SOURCE (source);
+   foo_listener_destroy (foo_source->listener_id);
+ 
+   return TRUE;
+ }
+ ]]>
+   </programlisting>
 -  
++
+   <para>
+     Please check the <link linkend="GrlMediaSource">GrlMediaSource</link> API
+     reference for more details on how <link
+     linkend="grl-media-source-notify-change-list">grl_media_source_notify_change_list()</link>
+     should be used.
+   </para>
+ 
+   <para>Examples of plugins implementing change notification are
+     grl-upnp and grl-tracker among others
+   </para>
+ </section>
+ </section>
diff --cc doc/grilo/plugins-metadata-sources.xml
index 0000000,6fe824d..a7df9b7
mode 000000,100644..100644
--- a/doc/grilo/plugins-metadata-sources.xml
+++ b/doc/grilo/plugins-metadata-sources.xml
@@@ -1,0 -1,398 +1,397 @@@
+ <?xml version="1.0"?>
+ <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"; [
+ <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+ ]>
+ 
+ 
+ <section>
+   <section id="metadata-source-plugins-intro">
+     <title>Introduction</title>
+ 
+     <para>
+       Metadata source plugins provide access to additional metadata information.
+     </para>
+ 
+     <para>
+       Unlike media sources, metadata sources do not provide access to media content,
+       but additional metadata information about content that was provided by
+       media sources.
+     </para>
+ 
+     <para>
+       An example of a metadata source would be one which is able to provide
+       thumbnail information for local audio content from an online service.
+     </para>
+ 
+     <para>
+       Media sources extend <link
+       linkend="GrlMetadataSource">GrlMetadataSource</link>, so they are also
+       metadata sources.
+     </para>
+ 
+     <para>
+       Typically, users interact with metadata sources to:
+       <itemizedlist>
+         <listitem>Resolve additional metadata for a particular media item.</listitem>
+         <listitem>Update metadata for a particular media item.</listitem>
+       </itemizedlist>
+     </para>
+   </section>
+ 
+   <section id="metadata-source-plugins-basics">
+     <title>Registering the plugin</title>
+ 
+     <para>
+       Registering a new metadata source plugin is done by following the same
+       procedure as for media source plugins, except that they must extend <link
+       linkend="GrlMetadataSource">GrlMetadataSource</link>. Please, check <link
+       linkend="media-source-plugins-basics">Registering Media Source
+       Plugins</link> for details.
+     </para>
+ 
+     <para>
+       Metadata source plugins must also implement "supported_keys", and optionally
 -      "slow_keys". Please check 
++      "slow_keys". Please check
+       <link linkend="media-source-plugins-supported-keys">Implementing Supported Keys</link>
+       and
 -      <link linkend="media-source-plugins-slow-keys">Implementing Slow Keys</link>        
++      <link linkend="media-source-plugins-slow-keys">Implementing Slow Keys</link>
+       respectively for further details.
+     </para>
+   </section>
+ 
+ 
+   <section id="metadata-source-plugins-resolve">
+     <title>Implementing Resolve</title>
 -    
++
+     <para>
+       An implementation of the "resolve" method is mandatory for metadata
+       plugins to work.
+     </para>
+ 
+     <para>
+       Resolve operations are issued in order to grab additional information on a
+       given media (<link linkend="GrlMedia">GrlMedia</link>).
+     </para>
+ 
+     <para>
+       Typically, implementing Resolve implies inspecting the metadata already
+       known for that media and use that information to gain access to new
+       information. For example, a plugin can use the artist and album
+       information of a given <link linkend="GrlMediaAudio">GrlMediaAudio</link>
+       item to obtain additional information, like the album cover thumbnail.
+     </para>
+ 
+     <para>
+       Plugins implementing "resolve" must also implement "may_resolve". The
+       purpose of this method is to analyze if the <link
+       linkend="GrlMedia">GrlMedia</link> contains enough metadata to enable the
+       plugin to extract the additional metadata requested.
+     </para>
+ 
+     <programlisting role="C">
+       <![CDATA[
 -/* In this example we assume a plugin that can resolve thumbnail 
++/* In this example we assume a plugin that can resolve thumbnail
+    information for audio items given that we have artist and album
+    information available  */
+ 
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass);
+   metadata_class->may_resolve = grl_foo_source_may_resolve;
+   metadata_class->resolve = grl_foo_source_resolve;
+ }
+ 
+ static gboolean
+ grl_foo_source_may_resolve (GrlMetadataSource *source,
+                             GrlMedia *media,
+                             GrlKeyID key_id,
+                             GList **missing_keys)
+ {
+   gboolean needs_artist = FALSE;
+   gboolean needs_album  = FALSE;
+ 
+   /* We only support thumbnail resolution */
+   if (key_id != GRL_METADATA_KEY_THUMBNAIL)
+     return FALSE;
+ 
+   /* We only support audio items */
+   if (media) {
+     if (!GRL_IS_MEDIA_AUDIO (media))
+       return FALSE;
+ 
+     /* We need artist information available */
+     if (grl_media_audio_get_artist (GRL_MEDIA_AUDIO (media)) == NULL) {
+       if (missing_keys)
+         *missing_keys = g_list_add (*missing_keys,
+                                     GRLKEYID_TO_POINTER (GRL_METADATA_KEY_ARTIST));
+       needs_artist = TRUE;
+     }
+ 
+     /* We need album information available */
+     if (grl_media_audio_get_album (GRL_MEDIA_AUDIO (media)) == NULL)) {
+       if (missing_keys)
+         *missing_keys = g_list_add (*missing_keys,
+                                     GRLKEYID_TO_POINTER (GRL_METADATA_KEY_ALBUM));
+       needs_album = TRUE;
+     }
+   }
+ 
+   if (needs_album || needs_artist)
+     return FALSE;
+ 
+   return TRUE;
+ }
+ 
+ static void
+ grl_foo_source_resolve (GrlMetadataSource *source,
+                         GrlMetadataSourceResolveSpec *rs)
+ {
+   const gchar *album;
+   const gchar *artist,
+   gchar *thumb_uri;
+   const GError *error = NULL;
+ 
+   if (contains_key (rs->keys, GRL_METADATA_KEY_THUMBNAIL) {
 -    artist = grl_media_audio_get_artist (GRL_MEDIA_AUDIO (rs->media)); 
 -    album = grl_media_audio_get_album (GRL_MEDIA_AUDIO (rs->media)); 
++    artist = grl_media_audio_get_artist (GRL_MEDIA_AUDIO (rs->media));
++    album = grl_media_audio_get_album (GRL_MEDIA_AUDIO (rs->media));
+     if (artist && album) {
+       thumb_uri = resolve_thumb_uri (artist, album);
+       grl_media_set_thumbnail (rs->media, thumb_uri);
+     } else {
+       error = g_error_new (GRL_CORE_ERROR,
+                            GRL_CORE_ERROR_RESOLVE_FAILED,
+                            "Can't resolve thumbnail, artist and album not known");
+     }
+   } else {
+       error = g_error_new (GRL_CORE_ERROR,
+                            GRL_CORE_ERROR_RESOLVE_FAILED,
+                            "Can't resolve requested keys");
+   }
+ 
+   rs->callback (source, rs->resolve_id, rs->media, rs->user_data, error);
+ 
+   if (error)
+     g_error_free (error);
+ }
+ ]]>
+     </programlisting>
+ 
+     <para>
+       Some considerations that plugin developers should take into account:
+       <itemizedlist>
+         <listitem>
+           The method "may_resolve" is synchronous, should be fast and
+           never block. If the plugin cannot confirm if it can resolve the
+           metadata requested without doing blocking operations then it should
 -          return TRUE. Then, when "resolve" is invoked further checking 
++          return TRUE. Then, when "resolve" is invoked further checking
+           can be done.
+         </listitem>
+         <listitem>
+           Typically "resolve" involves a blocking operation, and hence
+           its implementation should be asynchronous.
+         </listitem>
+       </itemizedlist>
+     </para>
+ 
+     <para>
+       Examples of plugins implementing "resolve" are grl-lastfm-albumart
+       or grl-local-metadata among others.
+     </para>
+   </section>
+ 
+   <section id="metadata-source-plugins-set-metadata">
+     <title>Implementing Set Metadata</title>
 -    
++
+     <para>
+       Implementing "set_metadata" is optional.
+     </para>
+ 
+     <para>
+       Some plugins may provide users with the option of updating the metadata
+       available for specific media items. For example, a plugin may store user
+       metadata like the last time that a certain media resource was played
 -      or its play count. These metadata properties do not make sense if 
++      or its play count. These metadata properties do not make sense if
+       applications do not have means to change and update their values.
+     </para>
+ 
+     <para>
+       Plugins that support this feature must implement two methods:
+       <itemizedlist>
+         <listitem>
+           <emphasis>writable_keys:</emphasis> just like "supported_keys"
+           or "slow_keys", it is a declarative method, intended to provide
+           information on what keys supported by the plugin are writable, that is,
+           their values can be changed by the user.
+         </listitem>
+         <listitem>
+           <emphasis>set_metadata:</emphasis> which is the method used
+           by clients to update metadata values for specific keys.
+         </listitem>
+       </itemizedlist>
+     </para>
+ 
+     <programlisting role="C">
+       <![CDATA[
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass);
+   metadata_class->writable_keys = grl_foo_source_writable_keys;
+   metadata_class->set_metadata = grl_foo_source_set_metadata;
+ }
+ 
+ static const GList *
+ grl_foo_source_writable_keys (GrlMetadataSource *source)
+ {
+   static GList *keys = NULL;
+   if (!keys) {
+     keys = grl_metadata_key_list_new (GRL_METADATA_KEY_RATING,
+                                       GRL_METADATA_KEY_PLAY_COUNT,
+                                       GRL_METADATA_KEY_LAST_PLAYED,
+                                       NULL);
+   }
+   return keys;
+ }
+ 
+ static void
+ grl_foo_source_set_metadata (GrlMetadataSource *source,
+ 			     GrlMetadataSourceSetMetadataSpec *sms)
+ {
+   GList *iter;
+   const gchar *media_id;
+   GList *failed_keys = NULL;
+ 
+   /* 'sms->media' contains the media with updated values */
+   media_id = grl_media_get_id (sms->media);
+ 
+   /* Go through all the keys that need update ('sms->keys'), take
+      the new values (from 'sms->media') and update them in the
+      media provider  */
+   iter = sms->keys;
+   while (iter) {
+     GrlKeyID key = GRLPOINTER_TO_KEYID (iter->data);
+     if (!foo_update_value_for_key (sms->media, key)) {
+       /* Save a list with keys that we failed to update */
+       failed_keys = g_list_prepend (failed_keys, iter->data);
+     }
+     iter = g_list_next (iter);
+   }
+ 
+   /* We are done, invoke user callback to signal client */
+   sms->callback (sms->source, sms->media, failed_keys, sms->user_data, NULL);
+   g_list_free (failed_keys);
+ }
+ ]]>
+     </programlisting>
+ 
+     <para>
+       Some considerations that plugin developers should take into account:
+       <itemizedlist>
+         <listitem>
+           Typically, updating metadata keys in the media provider would involve
+           one or more blocking operations, so asynchronous implementations
+           of "set_metadata" should be considered.
+         </listitem>
+         <listitem>
+           Some media providers may allow for the possibility of updating
 -          multiple keys in just one operation. 
++          multiple keys in just one operation.
+         </listitem>
+         <listitem>
+           The user callback for "set_metadata" receives a list with all the keys
+           that failed to be updated, which the plugin should free after calling
+           the user callback.
+         </listitem>
+       </itemizedlist>
+     </para>
+ 
+     <para>
+       Examples of plugins implementing "set_metadata" are grl-metadata-store or
+       grl-tracker.
+     </para>
+   </section>
+ 
+   <section id="metadata-source-plugins-cancel">
+     <title>Cancelling ongoing operations</title>
 -    
++
+     <para>
+       Implementing the "cancel" method is optional. This method provided means
+       for application developers to cancel ongoing operations on metadata
+       sources (and hence, also in media sources).
+     </para>
+ 
+     <para>
+       The "cancel" method receives the identifier of the operation to be
+       cancelled.
+     </para>
+ 
+     <para>
+       Typically, plugin developers would implement cancellation support
+       by storing relevant information for the cancellation process
+       along with the operation data when this is started, and then
+       retrieving this information when a cancellation request is received.
+     </para>
+ 
+     <para>
+       Grilo provides plugin developers with API to attach arbitrary data
+       to a certain operation given its identifier. These APIs are:
+       <itemizedlist>
+         <listitem><link
+         linkend="grl-operation-set-data">grl_operation_set_data()</link></listitem>
+         <listitem><link
+         linkend="grl-operation-get-data">grl_operation_get_data()</link></listitem>
+       </itemizedlist>
+       See the API reference documentation for
+       <link linkend="grilo-grl-operation">grl-operation</link> for
+       more details.
+     </para>
+ 
+     <programlisting role="C">
+       <![CDATA[
+ static void
+ grl_foo_source_class_init (GrlFooSourceClass * klass)
+ {
+   GrlMediaSourceClass *media_class = GRL_MEDIA_SOURCE_CLASS (klass);
+   GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass);
+ 
+   media_class->search = grl_foo_source_search;
+   metadata_class->cancel = grl_foo_source_cancel;
+ }
+ 
+ static void
+ grl_foo_source_search (GrlMediaSource *source,
+                        GrlMediaSourceSearchSpec *ss)
+ {
+   ...
+   gint op_handler = foo_service_search_start (ss->text, ...);
+   grl_operation_set_data (ss->operation_id,
+                           GINT_TO_POINTER (op_handler));
+   ...
+ }
+ 
+ static void
+ grl_foo_source_cancel (GrlMetadataSource *source, guint operation_id)
+ {
+   gint op_handler;
 -  
++
+   op_handler =
+     GPOINTER_TO_INT (grl_operation_get_data (operation_id));
+   if (op_handler > 0) {
+     foo_service_search_cancel (op_handler);
+   }
+ }
+ ]]>
+     </programlisting>
+ 
+     <para>
+       Some examples of plugins implementing cancellation support are
+       grl-youtube, grl-jamendo or grl-filesystem, among others.
+     </para>
+ 
+     <para>
+       Developers must free any data stored before the operation finishes.
+     </para>
+ 
+   </section>
+ </section>
 -
diff --cc doc/grilo/plugins-testing.xml
index 0000000,2f6c9ab..837c2a1
mode 000000,100644..100644
--- a/doc/grilo/plugins-testing.xml
+++ b/doc/grilo/plugins-testing.xml
@@@ -1,0 -1,99 +1,99 @@@
+ <?xml version="1.0"?>
+ <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"; [
+ <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+ ]>
+ 
+ <section id="media-source-testing-plugins">
+   <title>Testing your plugins</title>
+ 
+   <para>
+     Grilo ships a GTK+ test user interface called <emphasis>grilo-test-ui</emphasis>
+     that can be used to test new plugins. This simple playground application can be
+     found in the 'grilo' core source code under tools/grilo-test-ui/. If you have
+     Grilo installed on your system, you may have this application installed as
+     well.
+   </para>
+ 
+   <para>
+     This application loads plugins from the default plugin installation directory
+     in your system or, alternatively, by inspecting the GRL_PLUGIN_PATH environment
+     variable, which can be set to contain a list of directories where Grilo
+     should look for plugins.
+   </para>
+ 
+   <para>
+     Once the plugin library is visible to Grilo one only has to start the
+     grilo-test-ui application and it will load it along with other Grilo
+     plugins available in the system.
+   </para>
+ 
+   <para>
+     In case there is some problem with the initialization of the plugin it should
 -    be logged on the console. Remember that you can control the amount of 
++    be logged on the console. Remember that you can control the amount of
+     logging done by Grilo through the GRL_DEBUG environment variable. You
+     may want to set this variable to do full logging, in which case
+     you should type this in your console:
+   </para>
+ 
 -  <programlisting>   
++  <programlisting>
+ $ export GRL_DEBUG="*:*"
 -  </programlisting>   
++  </programlisting>
+ 
+   <para>
+     If you want to focus only on logging the plugin loading process, configure
+     Grilo to log full details from the plugin registry module alone
+     by doing this instead:
+   </para>
+ 
 -  <programlisting>   
++  <programlisting>
+ $ export GRL_DEBUG="plugin-registry:*"
 -  </programlisting>   
++  </programlisting>
+ 
+   <para>
+     In case your plugin has been loaded successfully you should see something like
+     this in the log:
+   </para>
+ 
 -  <programlisting> 
++  <programlisting>
+ (lt-grilo-test-ui:14457): Grilo-DEBUG: [plugin-registry] grl-plugin-registry.c:188: Plugin rank [plugin-id]' : 0
+ (lt-grilo-test-ui:14457): Grilo-DEBUG: [plugin-registry] grl-plugin-registry.c:476: New source available: [source-id]
+ (lt-grilo-test-ui:14457): Grilo-DEBUG: [plugin-registry] grl-plugin-registry.c:683: Loaded plugin '[plugin-id]' from '[plugin-file-absolute-path.so]'
+   </programlisting>
+ 
+   <para>
+     If your plugin is a Media Source (not a Metadata Source) you should be able
+     to see it in the user interface of grilo-test-ui like this:
+     <itemizedlist>
+       <listitem>
+         If the plugin implements Browse you should see the media source objects
+         spawned by the plugin in the list shown in the main view. You can
+         browse the plugin by double-clicking on any of its sources.
+       </listitem>
+       <listitem>
+         If the plugin implements Search you should see the media source objects
+         spawned by the plugin in the combo box next to the "Search" button.
+         You can now search content by selecting the media source you want to test
+         in the combo, inputting a search text in the text entry right next to it
+         and clicking the Search button.
+       </listitem>
+       <listitem>
+         If the plugin implements query you should see the media source objects
+         spawned by the plugin in the combo box next to the "Query" button.
+         You can now query content by selecting the media source you want to test
 -        in the combo, inputting the plugin-specific query string in the text 
++        in the combo, inputting the plugin-specific query string in the text
+         entry right next to it and clicking the Query button.
+       </listitem>
+     </itemizedlist>
+   </para>
 -  
++
+   <para>
+     If your plugin is a Metadata Source then you should test it by doing
+     a Browse, Search or Query operation in some other Media Source available
+     and then click on any of the media items showed as result. By doing this
+     grilo-test-ui will execute a Metadata operation which would use any
+     available metadata plugins to gather as much information as possible.
+     Available metadata obtained for the selected item will be shown in the
+     right pane for users to inspect.
+   </para>
+ </section>
diff --cc doc/grilo/writing-apps.xml
index 0000000,46c3aef..c10eb08
mode 000000,100644..100644
--- a/doc/grilo/writing-apps.xml
+++ b/doc/grilo/writing-apps.xml
@@@ -1,0 -1,648 +1,648 @@@
+ <?xml version="1.0"?>
+ <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"; [
+ <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+ ]>
+ 
+ <section id="writing-apps">
+ 
+   <para>
+     This section intends to be a starting point for developers interested
+     in getting started with Grilo, and as such it does not try to be
+     a comprehensive tutorial covering all topics and features of Grilo,
+     instead, this is intends to focus on typical use case scenarios
+     and code examples to put the developer on the right track.
+   </para>
+ 
+   <section id="programming-with-grilo-concepts">
+     <title>Basic concepts</title>
+ 
+     <section id="programming-with-grilo-concepts-understanding">
+       <title>Understanding Grilo</title>
+       <para>
+         Grilo provides a high-level API that application developers
 -        can use to interact with heterogeneous media providers using 
++        can use to interact with heterogeneous media providers using
+         an homogeneous language, reducing remarkably the effort required
+         to integrate media content from multiple sources.
+       </para>
 -      
++
+       <para>
+         This high-level, provider-independent API is then implemented
+         for all the providers supported by Grilo by means of plugins.
+         These plugins encapsulate all the provider-specific details,
+         effectively hiding all the technical tidbits and particularities
+         of specific media providers from application developers.
+       </para>
 -      
++
+       <para>
+         There are two kinds of plugins in Grilo:
+         <itemizedlist>
+           <listitem>Media Sources, which provide access to media content
+             that can be browsed or searched.</listitem>
+           <listitem>Metadata Sources, which provide additional metadata
+             about media content obtained through media sources.</listitem>
+         </itemizedlist>
+       </para>
 -      
++
+       <para>
 -        Management of available plugins is done through the 
++        Management of available plugins is done through the
+         <link linkend="GrlPluginRegistry">Plugin Registry</link>.
+         This object exposes API to load plugins and retrieve
+         available media source and metadata source objects.
+       </para>
 -      
++
+       <para>
+         Some plugins may require certain configuration information
+         in order to work properly. Examples of configuration information
+         that may be needed include API keys, service credentials,
 -        etc. Please check the section 
++        etc. Please check the section
+         <link linkend="programming-with-grilo-configuring-plugins">
+           Configuring you plugins
+         </link>
+         for more details on this subject.
+       </para>
 -      
++
+       <para>
+         You can instruct Grilo to load all available plugins via
+         <link linkend="grl-plugin-registry-load-all">grl_plugin_registry_load_all()</link>.
+         This will look into the default
+         installation directory for the plugins and also the paths
+         included via the environment variable GRL_PLUGIN_PATH.
 -        Please check section 
++        Please check section
+         <link linkend="running-grilo-programs-detailed">
+           Running Grilo based programs
+         </link>
+         for more details on this subject.
+       </para>
 -      
++
+       <para>
+         Users can also load plugins using their plugin
+         identifiers. These plugin identifier strings
+         can be obtained by running the tool 'grl-inspect'.
+       </para>
 -      
++
+       <para>
+         Users have two options to gain access to the media source and
+         metadata source objects loaded by the plugin registry:
+       </para>
 -      
++
+       <itemizedlist>
+         <listitem>
+           Call
+           <link linkend="grl-plugin-registry-get-sources">
+             grl_plugin_registry_get_sources*
+           </link> or
+           <link linkend="grl-plugin-registry-lookup-source">
+             grl_plugin_registry_lookup_source
+           </link>. These should be used
+           when plugins have already been loaded.
+         </listitem>
+         <listitem>
+           By connecting to the
+           <link linkend="GrlPluginRegistry-source-added">source-added</link>
+           signal, that is emitted whenever a new media source or metadata
+           source object is found.
+         </listitem>
+       </itemizedlist>
 -      
++
+       <para>
+         For more information on how to use the plugin registry, please
+         check <link linkend="GrlPluginRegistry">its API reference</link>
+         or any of the code examples below.
+       </para>
 -      
++
+       <para>
 -        Media source and metadata source objects are the objects 
++        Media source and metadata source objects are the objects
+         application developers will be interacting with for most
+         of their Grilo work. These objects provide developers
+         with a well known interface (API) to interact with
+         media content that is provider-independent. Typically,
+         each media source object provides access to content
+         delivered by one particular media provider.
+       </para>
 -      
++
+     </section>
+ 
+     <section id="programming-with-grilo-media-sources">
 -      
++
+       <title>Media Sources</title>
 -      
++
+       <para>
+         Media Source objects provide access to media content. Application
+         developers would usually interact  with these objects issuing
+         Browse, Search or Query operations on them. The purpose of
+         these operations is explained below:
+       </para>
 -      
++
+       <itemizedlist>
+         <listitem>
+           <emphasis>Search.</emphasis>
+           Instructs the media source to look for media matching
+           specific keywords. Example: YouTube search.
+         </listitem>
+         <listitem>
+           <emphasis>Browse:.</emphasis>
+           Navigates through a hierarchy of content exposed by the
+           media provider. Example: Filesystem browsing.
+         </listitem>
+         <listitem>
+           <emphasis>Query:</emphasis>
+           An advanced search mode that uses service-specific
+           commands to exploit features specific to a particular
+           media provider.
+         </listitem>
+         <listitem>
+           <emphasis>Metadata</emphasis>
+           Obtain (more) metadata information for a particular media resource.
+         </listitem>
+         <listitem>
+           <emphasis>Store and Remove</emphasis>
+           Push and remove content from the media provider.
+         </listitem>
+       </itemizedlist>
+     </section>
 -    
++
+     <section id="programming-with-grilo-metadata-sources">
 -      
++
+       <title>Metadata Sources</title>
+ 
+       <para>
+         Metadata Source objects provide means to acquire additional
+         metadata that was not provided by the original media source
+         that delivered the media. Application developers would usually
+         interact with metadata sources via Resolve or SetMetadata
+         operations, which are described below:
+       </para>
+ 
+       <itemizedlist>
+         <listitem>
+           <emphasis>Resolve:</emphasis>
+           Acquire additional metadata for a particular media resource.
+         </listitem>
+         <listitem>
+           <emphasis>SetMetadata:</emphasis>
+           Update metadata information available for a particular media resource.
+         </listitem>
+       </itemizedlist>
+ 
+     </section>
+ 
+     <section id="programming-with-grilo-async-apis">
 -      
++
+       <title>Asynchronous APIs</title>
+ 
+       <para>
+         Most of the APIs exposed by Media Source and Metadata Source
 -        objects are asynchronous. The reason for this is that 
++        objects are asynchronous. The reason for this is that
+         these operations usually involve I/O  which
+         would block the user interface if executed synchronously,
+         specially for the more heavy operations like Browse or
+         Search, leading to unresponsive GUIs and bad user
+         experience.
+       </para>
+ 
+       <para>
 -        For this reason, most of the APIs in Grilo accept a 
++        For this reason, most of the APIs in Grilo accept a
+         user callback and user data parameters that are used
+         to hand over the result of the operation to the
+         client asynchronously.
+       </para>
+ 
+       <para>
+         In cases where asynchrony is not needed, for example
+         for programs that do not have a graphical user
+         interface and are designed to run in the background,
+         Grilo also provides synchronous versions of some of
+         the APIs, but for situations other than this, use of
+         the asynchronous APIs is <emphasis>strongly</emphasis>
+         encouraged.
+       </para>
+ 
+       <para>
+         For operations that can produce multiple results,
+         like Browse, Search or Query, callbacks are
+         invoked once per matching result. For operations
+         that are expected to produce a single result, like
+         Metadata or Resolve, callbacks are invoked just
+         once.
+       </para>
+ 
+       <para>
+         In any case, clients should always expect the result
+         callback to be invoked at least once after an
+         asynchronous operation has been issued, even if the
 -        operation fails or is cancelled (in which case the 
++        operation fails or is cancelled (in which case the
+         error parameter of the callback will be set to a
+         non-NULL value).
+       </para>
+ 
+     </section>
+ 
+     <section id="programming-with-grilo-operation-identifiers">
+ 
+       <title>Operation identifiers</title>
+ 
+       <para>
+         Some Media Source and Metadata Source operations will return
+         an operation identifier that serves various purposes:
+       </para>
+ 
+       <itemizedlist>
+         <listitem>Identify the operation within the framework.</listitem>
+         <listitem>Match asynchronously results with the issuing operations.</listitem>
+         <listitem>Provide an operation identifier that can be used to cancel the operation.</listitem>
+       </itemizedlist>
+ 
+       <para>
+         For example, issuing an asynchronous Search operation on a Media
+         Source object would synchronously return an operation identifier.
+         The developer can store this identifier and use it to cancel.
+       </para>
+ 
+       <para>
+         Also, if the operation was not cancelled, the results callback
+         will be invoked at least once (actually, for the case
+         of a Browse operation is will be invoked once per matching
+         result) handing the results of the operation to the application.
+         Each time the results callback is invoked, the operation identifier
+         will be provided so that the callback can identify the operation
+         that triggered the result (there can be multiple operations
+         ongoing simultaneously).
+       </para>
+ 
+     </section>
+ 
+     <section id="programmaing-with-grilo-media-objects">
+ 
+       <title>Media objects</title>
+ 
+       <para>
+         As discussed before, Media Source objects provide means
+         to access  media content exposed by  media providers through
+         operations like Search, Browse or Query. Media content is
+         represented in Grilo via
+         <link linkend="GrlMedia">GrlMedia</link>
+         objects that contain metadata information related with
+         a particular media resource.
+       </para>
+ 
+       <para>
+         There are various subclasses of GrlMedia, which provide
+         specific APIs to get and set metadata associated with
+         specific media types, like video, audio or image
+         resources.
+       </para>
+ 
+       <para>
+         Typically, these GrlMedia objects will be obtained by
+         issuing Search, Browse or Query operations on Media
+         Source objects, which produce these objects
+         as results. Upon reception, clients would
+         usually use the API exposed by these objects to
+         collect the information of interest.
+       </para>
 -      
++
+       <para>
+         Grilo also supports the concept of media container,
+         represented by the  GrlMediaBox subclass. These objects
+         represent categories or folders that contain other
+         media objects (including maybe more GrlMediaBox objects),
+         allowing tree-like hierarchies of content, like the ones
+         would usually traverse during Browse operations.
+       </para>
+ 
+       <para>
+         Please, check the <link linkend="GrlMedia">GrlMedia</link>
+         API reference for more information on this subject.
+       </para>
+ 
+     </section>
+ 
+     <section>
+ 
+       <title>Metadata keys</title>
+ 
+       <para>
+         Metadata keys identify pieces of information (metadata).
+         They are often an input parameter for many operations
+         in Grilo, with the purpose of specifying what pieces of
+         information are relevant for a particular operations.
+       </para>
+ 
+       <para>
+         Grilo has a list of predefined supported keys. For each
+         of these keys, Grilo provides definitions
+         (identifiers) that clients can use to refer to
+         specific pieces of information. Some
+         examples are GRL_METADATA_KEY_TITLE or
+         GRL_METADATA_KEY_ARTIST.
+       </para>
+ 
+       <para>
+         For a complete list of all System Keys, please check
+         <link linkend="grilo-grl-metadata-key">grl-metadata-key.h</link>.
+       </para>
+ 
+       <para>
+         Some plugins can also introduce additional metadata
+         keys known as User Keys. The difference with the System
+         Keys is that these do not have definitions available, and
+         hence clients must query the plugin registry to know
+         their identifiers first using
+         <link linkend="grl-plugin-registry-lookup-metadata-key">
+           grl_plugin_registry_lookup_metadata_key
+         </link>.
+       </para>
+ 
+     </section>
+   </section>
+ 
+   <section id="programming-with-grilo-use-cases-explained">
+ 
+     <title>Typical use cases explained</title>
+ 
+     <para>
+       The following sections will guide the reader through some
+       of the typical use cases of Grilo, using code examples
+       to make the ideas even more clear.
+     </para>
+ 
+     <section id="programming-with-grilo-configuring-plugins">
+ 
+       <title>Configuring your plugins</title>
+ 
+       <para>For some plugins to work properly, it is required that the user (or
+         application developer) provides certain configuration. For example,
+         some plugins may require a username and a password, others may
+         require an API token to gain access to the underlying media provider.
+       </para>
+       <para>
+         These configuration options could be mandatory (without them
+         the plugin cannot operate and will fail to load), or
+         optional, in which case they are intended to allow users to tweak
+         certain aspects of how a particular plugin should work.
+       </para>
+       <para>
+         An example of a mandatory configuration is in the Youtube plugin: in order
+         for it to work a valid API key must be provided. It is the responsibility
+         of the user (or the application developer) to get that key (usually
+         registering in Youtube and applying for an application key) and provide
+         it to the plugin.
+       </para>
+       <para>
+         In order to know what configuration options are available for a certain
+         plugin, users/developers must check the plugin documentation.
+       </para>
+       <para>
+         Here is a small program illustrating how to configure a plugin form your
+         application:
+       </para>
+       <programlisting role="C">
+         <xi:include  href="../../examples/configuring-plugins.c"
+                      parse="text"
+                      xmlns:xi="http://www.w3.org/2001/XInclude"/>
+       </programlisting>
+ 
+       <para>
+         For more information on how to configure plugins
+         please check the
+         <link linkend="GrlConfig">GrlConfig</link>
+         API reference.
+       </para>
+     </section>
+ 
+     <section id="programming-with-grilo-browsing">
+ 
+       <title>Browsing media content</title>
+ 
+       <para>Here is a small program illustrating how you can browse
+         content from a particular media source (a similar approach
+         can be used for searching content instead of browsing):</para>
+       <programlisting role="C">
+         <xi:include  href="../../examples/browsing.c"
+                      parse="text"
+                      xmlns:xi="http://www.w3.org/2001/XInclude"/>
+       </programlisting>
+ 
+       <para>
+         Please notice:
+         <itemizedlist>
+           <listitem>
+             Finalization of the Browse operation is always
+             indicated by setting the 'remaining' parameter to 0.
+           </listitem>
+           <listitem>
+             The user callback used to receive the results is
+             guaranteed to be invoked at least once (with remaining
+             set to 0), even if the operation fails or is cancelled
+             (in these cases the error parameter will be set to a
+             non-NULL value).
+           </listitem>
+           <listitem>
+             Received GrlMedia objects are owned by the application
+             and must be freed when no longer needed.
+           </listitem>
+           <listitem>
+             If an error occurs, the GError parameter is set in the
+             callback, but this error parameter is owned by Grilo
+             and will be freed immediately after the callback.
+             If you want to keep the error beyond the scope of
+             the callback you must add a reference to it in the
+             callback code.
+           </listitem>
+           <listitem>
+             When the media source cannot estimate the number
 -            of remaining items, remaining is set to 
++            of remaining items, remaining is set to
+             GRL_SOURCE_REMAINING_UNKNOWN.
+           </listitem>
+         </itemizedlist>
+       </para>
+ 
+       <para>
+         For more information on how to operate media sources
+         please check the
+         <link linkend="GrlMediaSource">GrlMediaSource</link>
+         API reference.
+       </para>
+     </section>
+ 
+     <section id="programming-with-grilo-searching">
+ 
+       <title>Searching media content</title>
+ 
+       <para>Here is a small program illustrating how you can search
+         content by text from a particular media source (Jamendo
+         in this example):</para>
+       <programlisting role="C">
+         <xi:include  href="../../examples/searching.c"
+                      parse="text"
+                      xmlns:xi="http://www.w3.org/2001/XInclude"/>
+       </programlisting>
+ 
+       <para>
+         Please notice that all the considerations remarked for the
+         Browse operations above  apply also to Search operations.
+       </para>
+ 
+       <para>
+         For more information on how to operate media sources
+         please check the
+         <link linkend="GrlMediaSource">GrlMediaSource</link>
+         API reference.
+       </para>
+     </section>
+ 
+     <section id="programming-with-grilo-multivalued-data">
+ 
+       <title>Handling multi-valued metadata</title>
+ 
+       <para>When working with multimedia content, it can happen that certain
+         attributes of a particular media resource are multi-valued. For example,
+         a particular video resource may have multiple URIs associated, considering
+         different resolutions, streaming protocols and/or formats.
+       </para>
+       <para>
+         Grilo provides plugin and application developers with means
+         to handle multi-valued properties.
+       </para>
+       <para>
+         One issue concerning multi-valued properties are their relations with
+         other properties. Following the example of the video resource with
+         multiple URIs, each of these URIs should have its own mime-type associated,
+         as well as its own width and height values. When dealing with multi-valued
+         properties it is necessary to make this relations among keys more explicit.
+       </para>
+       <para>
+         Grilo provides application and plugin developers with a high-level APIs to
+         handle certain relations among keys consistently. Continuing with the example
+         of the video resource with multiple URIs, there is
+         <link linkend="grl-media-video-add-url-data">
+           grl_media_video_add_url_data
+         </link> and
+         <link linkend="grl-media-video-add-url-data-nth">
+           grl_media_video_get_url_data_nth
+         </link> to add and retrieve all the metadata
+         associated with a particular instance of the video resource (URI, mime-type,
+         framerate, width and height, etc) in one go.
+       </para>
+       <para>
+         Grilo allows plugin developers to define their own metadata keys.
+         In this case, the plugin developer must also provide information
+         on the relations that these keys hold with others. In this scenario
+         plugin developers must use GrlRelatedKeys objects to group related keys
+         together.
+       </para>
+       <para>
+         Here is a small program illustrating how get all available URLs from
+         a video resource, as well the corresponding MIME value for each one.
+         We use GrlRelatedKeys instead of the high-level API from GrlMediaVideo
+         to illustrate how to use it:
+       </para>
+       <programlisting role="C">
+         <xi:include  href="../../examples/multivalues.c"
+                      parse="text"
+                      xmlns:xi="http://www.w3.org/2001/XInclude"/>
+       </programlisting>
+ 
+       <para>
+         For more information on how to operate with media objects
+         please check the
+         <link linkend="GrlData">GrlData</link> hierarchy
+         API reference.
+       </para>
+     </section>
+ 
+     <section id="programming-with-grilo-efficient-metadata-resolution">
+ 
+       <title>Efficient metadata resolution</title>
+ 
+       <para>When executing operations that return lists of media items (like
+         Browse or Search) it is convenient to ensure that we do not
+         request metadata that could slow down the operation unless it
+         is really necessary.
+       </para>
+       <para>
+         A clear example of this situation is the way Youtube video
+         URLs are resolved: a browse operation on Youtube can usually
+         be resolved in a single HTTP query, however, if one wants to
+         obtain the URLs of the videos then for each video in the result
+         set another HTTP request is needed. This would slow down
+         the browse operation remarkably and would spam Youtube
+         with requests to obtain URLs that may not be ever needed.
+         Indeed, a normal player would browse a list of videos and show
+         information useful for the user to select the one he/she is
+         interested in (title, duration, artist, etc), the URL is not
+         interesting at this stage, it is only interesting when the user
+         selected a video to play, and we would be interested only
+         in that single URL and not in all the URLs of the videos
+         we are displaying.
+       </para>
+       <para>
+         Grilo provides methods to application developers to query
+         the keys (if any) that may have an impact on the performance
+         for a particular source (like the URL in the case of Youtube),
+         but it is usually easier to just use the GRL_RESOLVE_FAST_ONLY
+         flag when issuing Search, Browse or Query operations.
+       </para>
+       <para>
+         By using this flag, Grilo will resolve only the keys
+         that do not have an impact on performance. If you browse
+         Youtube with this flag Grilo won't request the URL key
+         to the Youtube source. However, if the source could resolve
+         the URL without performance penalties, it would do so
+         normally.
+       </para>
+       <para>
+         Usually, for operations like Browse or Search that operate
+         with large result sets it is recommended to use
+         GRL_RESOLVE_FAST_ONLY. If you really need to get the metadata
+         you requested for a specific item (for example if you want to
+         play a video from Youtube you really need the URL), then
+         you can safely use the Metadata operation without the
+         GRL_RESOLVE_FAST_ONLY flag, which would only operate on this
+         particular item, reducing the performance penalty and providing
+         a more efficient solution.
+       </para>
+       <para>
+         The program below demonstrates how this works, it accepts as
+         argument the id of the source we want to operate with, when the
+         source is registered it will issue a search operation requesting
+         only fast keys. When the search callback is invoked we will print
+         both the title information and the URL of the first media
+         that matched the search text.
+       </para>
+       <para>
+         If we run the program using grl-jamendo as target source, we will
+         see that it retrieves both the title and the URL of the media
+         immediately, however, if we use grl-youtube, it won't and in order
+         to obtain the URL we issue a new Metadata operation, this time
+         without the GRL_RESOLVE_FAST_ONLY flag.
+       </para>
+       <para>
+         Of course this is a very simple example, in a real application the way
+         this would work is that we would request the URL in a
+         Browse/Search that could return hundreds of results
+         and we may or may not get the URLs depending on the source
+         we are operating with, but in any case we will ensure the operation
+         will run as fast as possible: the user will see the results
+         of the search fast. Then, when the user selects
+         a media item to be played from that result set we would check if
+         we have the URL already (and we will have the URL ready if the source
+         could resolve it fast) in which case we can play the media right away (no
+         time penalty at all from the user point of view). If the URL could not
+         be resolved because it was slow for the source (like in the case of Youtube)
+         then we just have to issue a Metadata operation requesting the URL,
+         but that won't be too bad because we are requesting it only
+         for the item that the user selected, so from the user's perspective
+         the playback will take slightly more to start but would still
+         be an acceptable delay.
+       </para>
+       <programlisting role="C">
+         <xi:include  href="../../examples/efficient-metadata-resolution.c"
+                      parse="text"
+                      xmlns:xi="http://www.w3.org/2001/XInclude"/>
+       </programlisting>
+     </section>
+ 
+   </section>
+ </section>
diff --cc doc/libs/grilo-libs-sections.txt
index f1605d4,a37f968..7788927
--- a/doc/libs/grilo-libs-sections.txt
+++ b/doc/libs/grilo-libs-sections.txt
@@@ -20,9 -22,7 +22,6 @@@ grl_net_wc_get_typ
  GRL_NET_WC_CLASS
  GRL_IS_NET_WC_CLASS
  GRL_NET_WC_GET_CLASS
- </SECTION>
  <SUBSECTION Private>
- GRL_NET_WC_ERROR
  GrlNetWcPrivate
- grl_net_wc_error_quark
  </SECTION>
 -



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