Thumbnail management DBus specification

This is a Draft

Authors

Core authors

Language and spelling improvements

Introduction

The thumbnail management DBus specification is a standard for a DBus API to deal with the Thumbnail Managing Standard.

With a DBus specification for thumbnail management, applications don't have to implement thumbnail management themselves. If a thumbnailer is available they can delegate thumbnail work to a specialized service. The service then callbacks when it has finished generating the thumbnail.

Delegating this work has advantages:

Delegation of this work to a service doesn't mean that the service is necessarily the only process writing in the directory specified by the Thumbnail Managing Standard. File managers that want to have full control over their real-time thumbnail mechanisms don't need to go through the service. Although they can consume the service, too.

Dependencies of this specification

Issues To Solve

There are some issues to solve to make this work correctly. Specifically these issues are:

All these things are discussed and solutions are presented in the next chapters.

VFS issues

It's not possible today to specify that all URIs of all VFS systems can be guaranteed to work by specification. Therefore is only the file:/// scheme of URIs guaranteed to work cross desktop. Other URI schemes will most likely work if your client software shares the host's VFS layer with you.

Generic thumbnailer

All requests for thumbnails, a.k.a. queuable tasks, are received by the generic thumbnailer service.

This service ...

When the generic thumbnailer service proxies it to specialized thumbnailers, it must make sure that it doesn't send files to the specialized thumbnailer's Create method which the specialized thumbnailer can't handle. Refer to the section Specialized thumbnailers below to learn about the generic thumbnailer knowing which MIME types a specialized thumbnailer can handle. The generic thumbnailer must group URIs coming from a single Queue with identical MIME types together and pass this group to the Create method. Refer to the section org.freedesktop.thumbnailer.Generic below to learn about the Queue method. The generic thumbnailer must only pass URIs that the specialized thumbnailer will support. Refer to the UriSchemes item in the file layout at the section Registering your thumbnailer for more information about this.

It's allowed but not required to send files that are already thumbnailed to the Create method of the specialized thumbnailer. It's up to the specialized thumbnailer to determine what this means: does is have to recreate the thumbnail or not? More information about the Create method of specialized thumbnailers can be found in the section Specialized thumbnailers.

Whenever the generic thumbnailer knows that a thumbnail is finished, it must send a Ready signal. Preferably but not required it does this as soon as possible. It can, for example, send one Ready signal with all the thumbnails that are already cached, and then individual Ready signals each time when a thumbnail was created.

The org.freedesktop.thumbnailer.service file

[D-BUS Service]
Name=org.freedesktop.thumbnailer
Exec=your-thumbnailerd

You install this file in $XDG_DATA_DIRS/dbus-1/services. Check out The basedir specification for more information about $XDG_DATA_DIRS. In a build environment that can use pkg-config use this command:

~$ pkg-config --variable session_bus_services_dir dbus-1
/usr/share/dbus-1/services
~$ 

It's required to install this file. This makes your generic thumbnailer's service activatable by DBus whenever it is needed.

org.freedesktop.thumbnailer.Generic

<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
         "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 <node name="/org/freedesktop/Thumbnailer">
  <interface name="org.freedesktop.thumbnailer.Generic">

Request the creation of thumbnails

    <method name="Queue">
      <arg type="as" name="uris" direction="in" />
      <arg type="as" name="mime_hints" direction="in" />
      <arg type="u" name="handle_to_unqueue" direction="in" />
      <arg type="u" name="handle" direction="out" />
    </method>

If you want just one file to be thumbnailed, you only put one URI in uris. You can, however, put a lot of files in the array. Especially if you have a large amount of files to thumbnail, it's preferred to group them together rather than sending an individual request per file.

This method always returns a handle different than 0. The returned handle can be used in a subsequent Queue as handle_to_unqueue or in a Unqueue request.

Unqueue a queued item

    <method name="Unqueue">
      <arg type="u" name="handle" direction="in" />
    </method>

You can't unqueue requests that are currently running. You can't unqueue individual files in an enqueued operation unless you queue operations of one file at the time (and then simply unqueue the one-file containing queued operations). To unqueue is similar to canceling a queued request, with the exception that you can't interrupt a request that is active. A queued request that got unqueued will not cause Ready signals to be emitted, but it will still emit Finished and Started signals.

Parameters:

Thumbnails are ready for use

    <signal name="Ready">
      <arg type="as" name="uris" />
    </signal>

Emitted when thumbnails are ready.

Parameters being passed to you:

Every URI that completed successfully must be reported using this signal. It's possible that succeeded URIs are grouped together in uris. It's also possible that only one URI is available in uris and that multiple Ready signals will be emitted.

Enqueued request was started

    <signal name="Started">
      <arg type="u" name="handle" />
    </signal>

This signal is emitted when an enqueued request is started to be processed.

Parameters being passed to you:

Started and Finished must happen for all requests that got scheduled using Queue. Also for the requests that failed for example completely.

Enqueued request was completed

    <signal name="Finished">
      <arg type="u" name="handle" />
    </signal>

Emitted when an enqueued request has been completed (when thumbnails for the entire request have been created).

Parameters being passed to you:

Started and Finished must happen for all requests that got scheduled using Queue. Also for the requests that failed for example completely.

Error occured

    <signal name="Error">
      <arg type="u" name="handle" />
      <arg type="as" name="failed_uris" />
      <arg type="i" name="error_code" />
      <arg type="s" name="message" />
    </signal>

Emitted when an error occurred during the thumbnails creation of an enqueued request.

Parameters being passed to you:

An error does not necessarily mean that the entire queued request will have stopped running and that therefore Ready signals wont happen anymore. It's normal behaviour to emit multiple errors per queued request. It's on the other hand also normal behaviour to emit just one error and have all URIs that failed in failed_uris. An error does not imply that either Started or Finished won't happen anymore. Started and Finished must happen for all requests that got scheduled using Queue. Also for the requests that failed for example completely.

Source files have moved

    <method name="Move">
      <arg type="as" name="from_uris" direction="in" />
      <arg type="as" name="to_uris" direction="in" />
    </method>

This method is called by the client to inform the thumbnailer that source files have been moved on the filesystem. File n in from_uris corresponds to the same n in to_uris. For example from_uris[20] got moved to to_uris[20] in case >= 20 files got moved. If only one file got moved, you only put one file in each array.

Parameters:

Source files have been copied

    <method name="Copy">
      <arg type="as" name="from_uris" direction="in" />
      <arg type="as" name="to_uris" direction="in" />
    </method>

This method is called by the client to let the thumbnailer know about the copy of an array of source files. File n in from_uris corresponds to the same n in to_uris. For example from_uris[20] got copied to to_uris[20] in case >= 20 files got moved. If only one file got copied, you only put one file in each array.

Parameters:

Source files have been deleted

    <method name="Delete">
      <arg type="as" name="uris" direction="in" />
    </method>

This method is called by the client to inform the thumbnailer about the deletion of an array of source files. If only one file got removed, you only put one file in the array.

Parameters:

Cleaning up thumbnails

    <method name="Cleanup">
      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
      <arg type="s" name="uri_prefix" direction="in" />
      <arg type="u" name="since" direction="in" />
    </method>

This method is called by the client to ask the thumbnailer to cleanup old or invalid thumbnails.

Parameters:

  </interface>
</node> 

Thumbnail manager

This is a recommended interface, it's not required to implement it.

<?xml version="1.0" encoding="UTF-8"?>
<node name="/">
  <interface name="org.freedesktop.thumbnailer.Manager">  

Register a specialized thumbnailer dynamically

    <method name="Register">
      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
      <arg type="s" name="uri_scheme" direction="in" />
      <arg type="s" name="mime_type" direction="in" />
    </method>

Register the caller as a Specialized thumbnailer (that doesn't have a persistent registery (take a look at Registering your thumbnailer below for more information about persistent registry of specialized thumbnailers).

Parameters:

Get a list of supported MIME types

    <method name="GetSupported">
      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
      <arg type="as" name="mime_types" direction="out" />
    </method>

Get the list of supported MIME types. This can be used by a consumer of the service to determine whether or not it makes sense to request the creation of a thumbnail for a specific file.

Returns:

  </interface>
</node>

Specialized thumbnailers

Specialized thumbnailers provide the capability of making a thumbnail for specific MIME types. The generic thumbnailer is required to give priority to any registered specialized thumbnailer. This makes the system extensible.

The Service name of a specialized thumbnailer

The service name of a specialized thumbnailer is the location where the thumbnailer lives on the bus. The scheme to pick a name is to use your Internet domain name, with its elements reversed. For example, an organization with an Internet domain name company.com would name its thumbnailer's service name com.company.Thumbnailer and the Path on the bus would be named /com/company/Thumbnailer. The interface to implement must be org.freedesktop.thumbnailer.Thumbnailer. The generic thumbnailer learns these values by reading its thumbnailer registry.

org.freedesktop.thumbnailer.Thumbnailer

The Create method

Specialized thumbnailers must provide a method called Create on their service. The Create method can result in an emit of Error in case of failure. The generic thumbnailer must proxy this error as an Error signal (specified above in the section Generic thumbnailer).

The Create method must only emit a Ready signal as soon as the thumbnail is created and available as specified in The Thumbnail spec. The Create method must create all sizes of the thumbnail (currently this is large and normal, as specified in The Thumbnail spec). The Create method is allowed to store other sizes and other data in another path if this doesn't result in a conflict with what is specified in The Thumbnail spec.

It's up to the specialized thumbnailer to determine that the Create method has to recreate or not an already existing thumbnail.

Example with service name as com.company.Thumbnailer:

<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
         "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 <node name="/com/company/Thumbnailer">
  <interface name="org.freedesktop.thumbnailer.Thumbnailer">  
    <method name="Create">
      <arg type="s" name="uri" direction="in" />
      <arg type="s" name="mime_hint" direction="in" />
    </method>
  </interface>
</node> 

The parameters that will be passed with Create are:

Only URIs that have a URI scheme and the MIME type that your specialized thumbnailer supports will be passed to you. To specify which ones you support refer to Registering your thumbnailer lower in this section, the UriSchemes and MimeTypes fields.

Thumbnail is ready for use

    <signal name="Ready">
      <arg type="s" name="uri" />
    </signal>

Emitted when thumbnail is ready.

Parameters being passed to you:

Every URI that completed successfully must be reported using this signal.

Error occured

    <signal name="Error">
      <arg type="s" name="failed_uri" />
      <arg type="i" name="error_code" />
      <arg type="s" name="message" />
    </signal>

Emitted when an error occurred during the thumbnails creation of an enqueued request.

Parameters being passed to you:

Your com.company.Thumbnailer.service file

[D-BUS Service]
Name=com.company.Thumbnailer
Exec=/opt/company/thumbnailer/libexec/thumbnailerd

You install this file in $XDG_DATA_DIRS/dbus-1/services. Check out The basedir specification for more information about $XDG_DATA_DIRS. In a build environment that can use pkg-config use this command:

~$ pkg-config --variable session_bus_services_dir dbus-1
/usr/share/dbus-1/services
~$ 

It's a requirement to install this file. This makes your specialized thumbnailer's service activatable by DBus whenever needed.

Registering your thumbnailer

The generic thumbnailer can't magically know about thumbnailers that got installed unless it can get itself a list of the BusNames correlated with the MIME types that they support. For this you write a file called BusName.service in $XDG_DATA_DIRS/thumbnailers. Users can install their own BusName.service files in $XDG_DATA_HOME/thumbnailers. Check out The basedir specification for more information about $XDG_DATA_DIRS and $XDG_DATA_HOME.

Example: $XDG_DATA_DIRS/thumbnailers/com.company.Thumbnailer.service

[D-BUS Thumbnailer]
Name=com.company.Thumbnailer
MimeTypes=image/png;image/jpeg
Comment=free comment
UriSchemes=file

The required fields:

Optional fields:

To install this file in a autotools build environment, you can use something like this in a Makefile.am:

thumbnailersdir = /usr/share/thumbnailers
thumbnailers_DATA = com.company.Thumbnailer.service

Conflict resolution

Multiple specialized thumbnailers can end up trying to support the same MIME type and the same URI scheme. The same MIME type but a different URI scheme means that it's not a conflict. The sample below means that there's a conflict for image/jpeg and image/xpm between com.X.Thumbnailer and com.Y.Thumbnailer. But only for the URI scheme file.

Consider this situation:

$XDG_DATA_DIRS/thumbnailers/com.X.Thumbnailer.service

[D-BUS Thumbnailer]
Name=com.X.Thumbnailer
MimeTypes=image/png;image/jpeg;image/xpm
Comment=free comment
UriSchemes=file;ftp;ftps;http;https;nfs;smb

$XDG_DATA_DIRS/thumbnailers/com.Y.Thumbnailer.service

[D-BUS Thumbnailer]
Name=com.Y.Thumbnailer
MimeTypes=image/jpeg;image/xpm
Comment=free comment
UriSchemes=file

The specialized thumbnailer that must by default be picked by the thumbnailer system for image/jpeg is the one where the modification time of the service file in $XDG_DATA_DIRS/thumbnailers or $XDG_DATA_HOME/thumbnailers is the newest (the one with the last installed service file gets priority). On top of that, files in $XDG_DATA_HOME/thumbnailers do have priority over files in $XDG_DATA_DIRS/thumbnailers.

You can override this decision by providing a $XDG_DATA_DIRS/thumbnailers/overrides or $XDG_DATA_HOME/thumbnailers/overrides (with the latter overwriting the former, in case both exist). This file looks like this:

Example: $XDG_DATA_DIRS/thumbnailers/overrides

[file-image/jpeg]
Name=com.Y.Thumbnailer

[file-image/xpm]
Name=com.Y.Thumbnailer

Layout of the overrides file:

Combined with the sample above this overrides means that for local files we have a very efficient image/jpeg and image/xpm thumbnailer located on com.Y.Thumbnailer that only supports local filesystems. At com.X.Thumbnailer we have a less efficient thumbnailer, but this one supports on top of local filesystems also a few remote filesystems like http, ftp and ftps, nfs and smb.

An example could be a specialized thumbnailer that uses a library like EPeg. EPeg is at the moment of writing not converted to use a VFS layer like KIO or GIO, so it can only handle local JPEG files. Meanwhile you might have another specalized thumbnailer that can handle remote files over the VFS, like a GdkPixbuf based one that uses a GInputStream and a GFile from a URI. Yet for local files you want to prioritize the system so that the EPeg one is elected over the GdkPixbuf one. For remote files you want it to elect the GdkPixbuf one. This use-case is why there's a UriSchemes complexity involved here.

Implementation recommendations

Asynchronous handling and prioritizing

It's strongly recommended for implementers of the generic thumbnailer to make it handle DBus requests asynchronously and, if handled as a queue instead of handling in parallel, to handle incoming Queue requests in LIFO order (Last In First Out). This will improve responsiveness to consumers of the service.

Specialized thumbnailer's registrations

As the generic thumbnailer is responsible for calling Create methods on specialized thumbnailers based on a registration of a MIME type (see above), it's recommended (but not required) to use a file notification monitor in $XDG_DATA_DIRS/thumbnailers and $XDG_DATA_HOME/thumbnailers to detect the registration of a new specialized thumbnailer.

Another (less optimal) possibility is to restart and reinitialize your generic thumbnailer each time a specialized thumbnailer is installed.

Yet another possibility is to check for newly installed specialized thumbnailers at the start of each request in the queue (see the Queue method above). If any new ones got installed, take them into account (and add the registration to your register).

Obviously, these are just recommendations. As an implementer, it's up to you to optimize this for your implementation's purposes.

Resource usage, shutting down after timeout

As mentioned above, we require you to install an org.freedesktop.thumbnailer.service file in $XDG_DATA_DIRS/dbus-1/services. This means that we require your generic thumbnailer to be activatable. Therefore it's allowed, if your implementation permits it, to shutdown the daemon (if implemented as a daemon) after a period of inactivity.

The amount of inactive time required before you shutdown is unspecified and up to you. It is recommended to pick a value that is more than a minute after the last queued request was finished. Such a timeout avoids that the daemon (again, if implemented as a daemon) has to be launched often.

Strongly note that it's not required to shutdown, but strongerly note that it's required to be activatable.

ThumbnailerSpec (last edited 2009-05-26 10:04:15 by PhilipVanHoof)