Thumbnail management DBus specification
Contents
This is a Draft
Authors
Core authors
Philip Van Hoof <philip at codeminded dot be>
Rob Taylor <rob.taylor at codethink dot co dot uk>
Language and spelling improvements
Tinne Hannes <tinne dot hannes at gmail dot com>
- Spellchecks are a bit outdated as sections got added. Feel free to contribute spell fixes (this is a wiki).
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:
- No need to link complex pieces of software into all the applications that want to work with thumbnails
- Possibility for thumbnailers of closed formats (which might have patents) to coexist with free software desktop applications that (just) want to display the files as thumbnails.
- Reuse of existing infrastructure instead of making all applications reinvent it
- Complexity of a LIFO queue is no longer the responsibility of the application developer
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:
- Requesting to create a thumbnail for a thumbnail-able file
- Informing the system of a deletion of a thumbnail-able file
- Informing the system of a move of a thumbnail-able file to another location
- Enabling third party thumbnailers to register a thumbnailer for a specific MIME type
- Asynchronous handling and prioritizing
- Large amounts of thumbnail-able items, reducing the amount of DBus messages
- Requiring the service to be activatable (requirement to install a service file)
- Requiring third party thumbnailers to be activatable (requirement to install a service file)
- Requiring third party thumbnailer to register themselves
- Implementation recommendations about registration of specialized thumbnailers
- VFS issues, clients might have a different VFS layer as the Thumbnailer service
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 ...
Proxies the request to one or more specialized thumbnailers and emits Ready signals when those specialized ones are finished thumbnailing or;
Emits Ready signals for example when the thumbnail is already cached or;
Emits Ready signals when it solved thumbnailing internally (there was no specialized thumbnailer available but the generic thumbnailer was able to create thumnbails for the MIME type) or;
- Immediately returns with a failure or;
Emit failures in a signal called Error.
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.
uris: The uris that you pass must be URIs, not paths. This means that for a file on the local filesystem you use file:///home/user/Desktop/Screenshot.png instead of /home/user/Desktop/Screenshot.png.
mime_hints: The optional mime_hints is either empty or of the same size as the uris. If passed as not empty then this field contains mime-type hints for the items in uris (item n in mime_hints corresponds to item n in uris). For example mime_hints[0] = "image/jpeg" for uris[o] = "file:///home/user/image.jpeg". Specifying the optional mime type when known is strongly adviced. Sub-par behaviour might result when it is lacking.
handle_to_unqueue: If you pass a number different than 0 to handle_to_unqueue, the request which yielded that handle is unqueued before a new request is queued. Passing handle_to_unqueue is similar to issuing a Unqueue (see below) before issuing a Queue. It is made possible to pass it during Queue so that you can reduce the amount of DBus traffic.
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:
handle: The handle that got returned to you by Queue
Thumbnails are ready for use
<signal name="Ready">
<arg type="as" name="uris" />
</signal>Emitted when thumbnails are ready.
Parameters being passed to you:
uris: The uris are the URIs of the original files. Not the thumbnails. To find the path to the thumbnail, use the thumbnail spec.
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:
handle: The handle that got returned to you by Queue
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:
handle: The handle that got returned to you by Queue
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:
handle:The handle that got returned to you by Queue
failed_uris: The URIs that failed and that caused this error to occur. This is not necessarily the same list of URIs that got passed during Queue. The list can be shortened, reordered, etc.
error_code: An error_code of 0 means that within the queued request where files with MIME types that are not supported by the thumbnailer infrastructure.
message: Any other code is not yet specified and means "generic error, take a look at 'message' for the error reason".
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:
from_uris: the URIs that got moved
to_uris: the destination URIs to where from_uris got moved
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:
from_uris: the URIs that got copied
to_uris: the destination URIs to where from_uris got copied
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:
uris: the URIs that got deleted
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:
uri_prefix: the prefix that must match the URIs of original thumbnailables of whom the thumbnail must be deleted
since: filter so that only thumbnails of files that have a modified time since get deleted
</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:
uri_scheme: ftp, file, http, etc
mime_type: image/png, image/jpg, etc
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:
mime_types: an array of strings like image/png, image/jpeg, etc
</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:
uri: A URI to create thumbnail for. For example `uri = "file:///home/user/image1.jpeg".
mime_hint: A MIME type hint for the file in uri. For example (in this case) "image/jpeg"
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:
uri: The uri is the URI of the original file. Not the thumbnail. To find the path to the thumbnail, use the thumbnail spec.
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:
failed_uri: The URI that failed and that caused this error to occur.
error_code: An error_code of 0 means that within the queued request where files with MIME types that are not supported by the thumbnailer infrastructure.
message: Any other code is not yet specified and means "generic error, take a look at 'message' for the error reason".
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:
Name: the name on D-Bus
MimeTypes: MIME types this thumbnailer supports
Optional fields:
Comment: Free comment field (for example copyright notes)
UriSchemes: The URI schemes this thumbnailer supports. For example ftp;file;http. If this field is ommitted then the default value is file.
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:
The section name is formed by concatenating the URI scheme together with a dash and the MIME type. For each combination that you want to override you need to make a new section.
The Name is the bus name on D-Bus
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.
